summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--Android.bp8
-rw-r--r--ApiDocs.bp1
-rw-r--r--StubLibraries.bp104
-rw-r--r--apex/Android.bp41
-rw-r--r--apex/media/framework/Android.bp59
-rw-r--r--apex/permission/framework/Android.bp59
-rw-r--r--apex/permission/service/java/com/android/permission/persistence/RuntimePermissionsPersistence.java3
-rw-r--r--apex/permission/service/java/com/android/permission/persistence/RuntimePermissionsState.java3
-rw-r--r--apex/permission/service/java/com/android/role/persistence/RolesPersistence.java3
-rw-r--r--apex/permission/service/java/com/android/role/persistence/RolesState.java3
-rw-r--r--apex/sdkextensions/framework/Android.bp58
-rw-r--r--apex/statsd/framework/Android.bp73
-rw-r--r--apex/statsd/framework/java/android/os/StatsDimensionsValue.java (renamed from core/java/android/os/StatsDimensionsValue.java)3
-rw-r--r--api/current.txt41
-rw-r--r--api/module-app-current.txt1
-rw-r--r--api/module-app-removed.txt1
-rw-r--r--api/module-lib-current.txt4
-rwxr-xr-xapi/system-current.txt41
-rw-r--r--api/test-current.txt69
-rw-r--r--cmds/bootanimation/BootAnimation.cpp17
-rw-r--r--cmds/statsd/Android.bp2
-rw-r--r--cmds/statsd/src/atom_field_options.proto69
-rw-r--r--cmds/statsd/src/atoms.proto103
-rw-r--r--cmds/statsd/src/external/StatsPullerManager.cpp2
-rw-r--r--cmds/statsd/src/metrics/GaugeMetricProducer.cpp9
-rw-r--r--cmds/statsd/src/metrics/ValueMetricProducer.cpp11
-rw-r--r--cmds/statsd/src/state/StateTracker.cpp80
-rw-r--r--cmds/statsd/src/state/StateTracker.h2
-rw-r--r--cmds/statsd/tests/e2e/GaugeMetric_e2e_pull_test.cpp39
-rw-r--r--cmds/statsd/tests/e2e/PartialBucket_e2e_test.cpp17
-rw-r--r--cmds/statsd/tests/e2e/ValueMetric_pull_e2e_test.cpp15
-rw-r--r--cmds/statsd/tests/metrics/ValueMetricProducer_test.cpp39
-rw-r--r--cmds/statsd/tests/state/StateTracker_test.cpp103
-rw-r--r--cmds/statsd/tests/statsd_test_util.cpp36
-rw-r--r--cmds/statsd/tests/statsd_test_util.h19
-rw-r--r--core/java/android/app/ActivityManagerInternal.java2
-rw-r--r--core/java/android/app/ActivityTaskManager.java12
-rw-r--r--core/java/android/app/AppOpsManagerInternal.java13
-rw-r--r--core/java/android/app/IActivityTaskManager.aidl3
-rw-r--r--core/java/android/app/admin/DevicePolicyEventLogger.java7
-rw-r--r--core/java/android/app/prediction/AppPredictionContext.java10
-rw-r--r--core/java/android/app/timedetector/ManualTimeSuggestion.java22
-rw-r--r--core/java/android/app/timedetector/NetworkTimeSuggestion.java22
-rw-r--r--core/java/android/app/timedetector/PhoneTimeSuggestion.java67
-rw-r--r--core/java/android/app/timezonedetector/ManualTimeZoneSuggestion.java18
-rw-r--r--core/java/android/app/timezonedetector/PhoneTimeZoneSuggestion.java115
-rw-r--r--core/java/android/content/Context.java1
-rw-r--r--core/java/android/content/Intent.java10
-rw-r--r--core/java/android/content/integrity/AppIntegrityManager.java21
-rw-r--r--core/java/android/content/integrity/AtomicFormula.java10
-rw-r--r--core/java/android/content/integrity/IAppIntegrityManager.aidl1
-rw-r--r--core/java/android/content/integrity/IntegrityFormula.java2
-rw-r--r--core/java/android/content/integrity/Rule.java2
-rw-r--r--core/java/android/content/integrity/RuleSet.java2
-rw-r--r--core/java/android/content/om/OverlayManager.java10
-rw-r--r--core/java/android/content/pm/PackageInstaller.java26
-rw-r--r--core/java/android/content/pm/PackageManager.java10
-rw-r--r--core/java/android/content/pm/PackageParser.java9
-rw-r--r--core/java/android/content/pm/PermissionInfo.java15
-rw-r--r--core/java/android/content/pm/parsing/ApkLiteParseUtils.java4
-rw-r--r--core/java/android/content/pm/parsing/ApkParseUtils.java5
-rw-r--r--core/java/android/content/pm/parsing/ComponentParseUtils.java12
-rw-r--r--core/java/android/hardware/biometrics/BiometricManager.java17
-rw-r--r--core/java/android/net/IConnectivityManager.aidl10
-rw-r--r--core/java/android/net/VpnManager.java86
-rw-r--r--core/java/android/net/VpnService.java2
-rw-r--r--core/java/android/os/Binder.java12
-rw-r--r--core/java/android/os/BinderProxy.java4
-rw-r--r--core/java/android/os/IBinder.java12
-rw-r--r--core/java/android/os/Process.java6
-rw-r--r--core/java/android/os/UpdateEngine.java19
-rw-r--r--core/java/android/os/incremental/IncrementalFileStorages.java155
-rw-r--r--core/java/android/permission/PermissionManager.java10
-rw-r--r--core/java/android/provider/ContactsContract.java20
-rw-r--r--core/java/android/provider/DeviceConfig.java8
-rw-r--r--core/java/android/service/autofill/Dataset.java25
-rw-r--r--core/java/android/service/chooser/ChooserTarget.java5
-rw-r--r--core/java/android/service/chooser/ChooserTargetService.java6
-rw-r--r--core/java/android/service/contentcapture/ContentCaptureService.java4
-rw-r--r--core/java/android/timezone/CountryTimeZones.java32
-rw-r--r--core/java/android/util/RotationUtils.java72
-rw-r--r--core/java/android/util/StatsLog.java11
-rw-r--r--core/java/android/view/GestureDetector.java19
-rw-r--r--core/java/android/view/IWindowSession.aidl8
-rw-r--r--core/java/android/view/ImeFocusController.java8
-rw-r--r--core/java/android/view/SurfaceControl.java161
-rw-r--r--core/java/android/view/SurfaceControlViewHost.java2
-rw-r--r--core/java/android/view/View.java23
-rw-r--r--core/java/android/view/ViewRootImpl.java19
-rw-r--r--core/java/android/view/WindowlessWindowManager.java43
-rw-r--r--core/java/android/view/inputmethod/InputMethodManager.java5
-rw-r--r--core/java/android/widget/Magnifier.java60
-rw-r--r--core/java/com/android/ims/internal/uce/common/CapInfo.java36
-rw-r--r--core/java/com/android/internal/app/AccessibilityButtonChooserActivity.java43
-rw-r--r--core/java/com/android/internal/config/sysui/SystemUiDeviceConfigFlags.java7
-rw-r--r--core/java/com/android/internal/logging/MetricsLogger.java6
-rw-r--r--core/java/com/android/internal/logging/UiEventLoggerImpl.java8
-rw-r--r--core/java/com/android/internal/os/BatteryStatsImpl.java73
-rw-r--r--core/java/com/android/internal/os/StatsdHiddenApiUsageLogger.java15
-rw-r--r--core/java/com/android/internal/os/logging/MetricsLoggerWrapper.java38
-rw-r--r--core/java/com/android/internal/util/ObjectUtils.java8
-rw-r--r--core/java/com/android/server/BootReceiver.java40
-rw-r--r--core/jni/android_util_Binder.cpp54
-rw-r--r--core/jni/android_view_SurfaceControl.cpp121
-rw-r--r--core/jni/com_android_internal_os_Zygote.cpp1
-rw-r--r--core/proto/android/app/settings_enums.proto5
-rw-r--r--core/proto/android/server/connectivity/data_stall_event.proto6
-rw-r--r--core/proto/android/server/vibratorservice.proto66
-rw-r--r--core/res/AndroidManifest.xml21
-rw-r--r--core/res/res/drawable/ic_open_in_new.xml26
-rw-r--r--core/res/res/drawable/media_seamless_background.xml2
-rw-r--r--core/res/res/values-mcc334/config.xml23
-rw-r--r--core/res/res/values-mcc732/config.xml23
-rw-r--r--core/res/res/values-mcc740/config.xml23
-rw-r--r--core/res/res/values/attrs_manifest.xml3
-rw-r--r--core/res/res/values/config.xml25
-rw-r--r--core/res/res/values/dimens.xml6
-rw-r--r--core/res/res/values/symbols.xml8
-rw-r--r--core/tests/coretests/AndroidManifest.xml4
-rw-r--r--core/tests/coretests/src/android/os/BinderProxyService.java35
-rw-r--r--core/tests/coretests/src/android/os/BinderProxyTest.java43
-rw-r--r--core/tests/coretests/src/android/os/BinderTest.java14
-rw-r--r--core/tests/coretests/src/com/android/internal/os/BatteryStatsHelperTest.java27
-rw-r--r--data/etc/privapp-permissions-platform.xml4
-rw-r--r--libs/hwui/pipeline/skia/SkiaPipeline.cpp8
-rw-r--r--libs/hwui/renderthread/CanvasContext.cpp2
-rw-r--r--libs/hwui/renderthread/ReliableSurface.h2
-rw-r--r--libs/hwui/tests/common/TestContext.cpp74
-rw-r--r--libs/hwui/tests/common/TestContext.h11
-rw-r--r--libs/hwui/tests/macrobench/TestSceneRunner.cpp8
-rw-r--r--location/java/com/android/internal/location/gnssmetrics/GnssMetrics.java4
-rw-r--r--media/java/android/media/session/MediaSessionManager.java1
-rw-r--r--media/java/android/media/tv/TvInputManager.java2
-rw-r--r--media/java/android/media/tv/tuner/frontend/Atsc3FrontendSettings.java4
-rw-r--r--media/java/android/media/tv/tuner/frontend/DvbcFrontendSettings.java10
-rw-r--r--media/tests/MediaRouteProvider/Android.bp18
-rw-r--r--media/tests/MediaRouteProvider/AndroidManifest.xml30
-rw-r--r--media/tests/MediaRouteProvider/res/values/strings.xml5
-rw-r--r--media/tests/MediaRouter/AndroidManifest.xml8
-rw-r--r--media/tests/MediaRouter/src/com/android/mediaroutertest/MediaRouterManagerTest.java69
-rw-r--r--media/tests/MediaRouter/src/com/android/mediaroutertest/SampleMediaRoute2ProviderService.java (renamed from media/tests/MediaRouteProvider/src/com/android/mediarouteprovider/example/SampleMediaRoute2ProviderService.java)2
-rw-r--r--packages/CarrierDefaultApp/res/values-ky/strings.xml2
-rw-r--r--packages/SettingsLib/src/com/android/settingslib/bluetooth/A2dpProfile.java11
-rw-r--r--packages/SettingsLib/src/com/android/settingslib/bluetooth/HeadsetProfile.java9
-rw-r--r--packages/SettingsLib/src/com/android/settingslib/bluetooth/HearingAidProfile.java11
-rw-r--r--packages/SettingsLib/src/com/android/settingslib/license/LicenseHtmlGeneratorFromXml.java48
-rw-r--r--packages/SettingsLib/src/com/android/settingslib/media/InfoMediaDevice.java15
-rw-r--r--packages/SettingsLib/src/com/android/settingslib/media/MediaDevice.java53
-rw-r--r--packages/SettingsLib/src/com/android/settingslib/wifi/WifiUtils.java2
-rw-r--r--packages/SettingsLib/tests/robotests/src/com/android/settingslib/license/LicenseHtmlGeneratorFromXmlTest.java35
-rw-r--r--packages/SettingsProvider/src/com/android/providers/settings/SettingsBackupAgent.java2
-rw-r--r--packages/SettingsProvider/src/com/android/providers/settings/SettingsProtoDumpUtil.java3
-rw-r--r--packages/Shell/AndroidManifest.xml4
-rw-r--r--packages/SystemUI/AndroidManifest.xml4
-rw-r--r--packages/SystemUI/plugin/src/com/android/systemui/plugins/qs/QSTile.java20
-rw-r--r--packages/SystemUI/proguard.flags1
-rw-r--r--packages/SystemUI/res-keyguard/layout/controls_management.xml34
-rw-r--r--packages/SystemUI/res/color/control_background.xml (renamed from packages/SystemUI/res/color/lock_background.xml)0
-rw-r--r--packages/SystemUI/res/color/control_foreground.xml (renamed from packages/SystemUI/res/color/unknown_foreground.xml)0
-rw-r--r--packages/SystemUI/res/color/thermo_cool_background.xml6
-rw-r--r--packages/SystemUI/res/color/thermo_cool_foreground.xml6
-rw-r--r--packages/SystemUI/res/color/thermo_heat_background.xml6
-rw-r--r--packages/SystemUI/res/color/thermo_heat_foreground.xml (renamed from packages/SystemUI/res/color/lock_foreground.xml)2
-rw-r--r--packages/SystemUI/res/drawable/auth_dialog_enterprise.xml25
-rw-r--r--packages/SystemUI/res/drawable/notif_dungeon_bg_gradient.xml25
-rw-r--r--packages/SystemUI/res/drawable/qs_media_background.xml26
-rw-r--r--packages/SystemUI/res/layout-land/auth_credential_pattern_view.xml67
-rw-r--r--packages/SystemUI/res/layout/app_item.xml70
-rw-r--r--packages/SystemUI/res/layout/auth_credential_password_view.xml30
-rw-r--r--packages/SystemUI/res/layout/auth_credential_pattern_view.xml69
-rw-r--r--packages/SystemUI/res/layout/control_item.xml72
-rw-r--r--packages/SystemUI/res/layout/controls_app_item.xml87
-rw-r--r--packages/SystemUI/res/layout/controls_base_item.xml32
-rw-r--r--packages/SystemUI/res/layout/controls_row.xml2
-rw-r--r--packages/SystemUI/res/layout/controls_with_favorites.xml2
-rw-r--r--packages/SystemUI/res/layout/foreground_service_dungeon.xml61
-rw-r--r--packages/SystemUI/res/layout/foreground_service_dungeon_row.xml43
-rw-r--r--packages/SystemUI/res/layout/media_carousel.xml2
-rw-r--r--packages/SystemUI/res/layout/qqs_media_panel.xml28
-rw-r--r--packages/SystemUI/res/layout/qs_footer_impl.xml5
-rw-r--r--packages/SystemUI/res/layout/qs_media_panel.xml144
-rw-r--r--packages/SystemUI/res/values-af/strings.xml1
-rw-r--r--packages/SystemUI/res/values-am/strings.xml1
-rw-r--r--packages/SystemUI/res/values-ar/strings.xml2
-rw-r--r--packages/SystemUI/res/values-as/strings.xml2
-rw-r--r--packages/SystemUI/res/values-az/strings.xml2
-rw-r--r--packages/SystemUI/res/values-b+sr+Latn/strings.xml1
-rw-r--r--packages/SystemUI/res/values-be/strings.xml2
-rw-r--r--packages/SystemUI/res/values-bg/strings.xml1
-rw-r--r--packages/SystemUI/res/values-bn/strings.xml2
-rw-r--r--packages/SystemUI/res/values-bs/strings.xml1
-rw-r--r--packages/SystemUI/res/values-ca/strings.xml1
-rw-r--r--packages/SystemUI/res/values-cs/strings.xml1
-rw-r--r--packages/SystemUI/res/values-da/strings.xml1
-rw-r--r--packages/SystemUI/res/values-de/strings.xml2
-rw-r--r--packages/SystemUI/res/values-el/strings.xml1
-rw-r--r--packages/SystemUI/res/values-en-rAU/strings.xml1
-rw-r--r--packages/SystemUI/res/values-en-rCA/strings.xml1
-rw-r--r--packages/SystemUI/res/values-en-rGB/strings.xml1
-rw-r--r--packages/SystemUI/res/values-en-rIN/strings.xml1
-rw-r--r--packages/SystemUI/res/values-en-rXC/strings.xml1
-rw-r--r--packages/SystemUI/res/values-es-rUS/strings.xml1
-rw-r--r--packages/SystemUI/res/values-es/strings.xml1
-rw-r--r--packages/SystemUI/res/values-et/strings.xml2
-rw-r--r--packages/SystemUI/res/values-eu/strings.xml1
-rw-r--r--packages/SystemUI/res/values-fa/strings.xml2
-rw-r--r--packages/SystemUI/res/values-fi/strings.xml1
-rw-r--r--packages/SystemUI/res/values-fr-rCA/strings.xml2
-rw-r--r--packages/SystemUI/res/values-fr/strings.xml2
-rw-r--r--packages/SystemUI/res/values-gl/strings.xml2
-rw-r--r--packages/SystemUI/res/values-gu/strings.xml2
-rw-r--r--packages/SystemUI/res/values-hi/strings.xml6
-rw-r--r--packages/SystemUI/res/values-hr/strings.xml1
-rw-r--r--packages/SystemUI/res/values-hu/strings.xml1
-rw-r--r--packages/SystemUI/res/values-hy/strings.xml1
-rw-r--r--packages/SystemUI/res/values-in/strings.xml1
-rw-r--r--packages/SystemUI/res/values-is/strings.xml1
-rw-r--r--packages/SystemUI/res/values-it/strings.xml1
-rw-r--r--packages/SystemUI/res/values-iw/strings.xml2
-rw-r--r--packages/SystemUI/res/values-ja/strings.xml1
-rw-r--r--packages/SystemUI/res/values-ka/strings.xml2
-rw-r--r--packages/SystemUI/res/values-kk/strings.xml2
-rw-r--r--packages/SystemUI/res/values-km/strings.xml1
-rw-r--r--packages/SystemUI/res/values-kn/strings.xml2
-rw-r--r--packages/SystemUI/res/values-ko/strings.xml2
-rw-r--r--packages/SystemUI/res/values-ky/strings.xml2
-rw-r--r--packages/SystemUI/res/values-lo/strings.xml1
-rw-r--r--packages/SystemUI/res/values-lt/strings.xml1
-rw-r--r--packages/SystemUI/res/values-lv/strings.xml2
-rw-r--r--packages/SystemUI/res/values-mk/strings.xml1
-rw-r--r--packages/SystemUI/res/values-ml/strings.xml2
-rw-r--r--packages/SystemUI/res/values-mn/strings.xml1
-rw-r--r--packages/SystemUI/res/values-mr/strings.xml2
-rw-r--r--packages/SystemUI/res/values-ms/strings.xml2
-rw-r--r--packages/SystemUI/res/values-my/strings.xml2
-rw-r--r--packages/SystemUI/res/values-nb/strings.xml1
-rw-r--r--packages/SystemUI/res/values-ne/strings.xml2
-rw-r--r--packages/SystemUI/res/values-nl/strings.xml1
-rw-r--r--packages/SystemUI/res/values-or/strings.xml2
-rw-r--r--packages/SystemUI/res/values-pa/strings.xml2
-rw-r--r--packages/SystemUI/res/values-pl/strings.xml3
-rw-r--r--packages/SystemUI/res/values-pt-rBR/strings.xml1
-rw-r--r--packages/SystemUI/res/values-pt-rPT/strings.xml1
-rw-r--r--packages/SystemUI/res/values-pt/strings.xml1
-rw-r--r--packages/SystemUI/res/values-ro/strings.xml1
-rw-r--r--packages/SystemUI/res/values-ru/strings.xml2
-rw-r--r--packages/SystemUI/res/values-si/strings.xml1
-rw-r--r--packages/SystemUI/res/values-sk/strings.xml1
-rw-r--r--packages/SystemUI/res/values-sl/strings.xml1
-rw-r--r--packages/SystemUI/res/values-sq/strings.xml2
-rw-r--r--packages/SystemUI/res/values-sr/strings.xml1
-rw-r--r--packages/SystemUI/res/values-sv/strings.xml1
-rw-r--r--packages/SystemUI/res/values-sw/strings.xml3
-rw-r--r--packages/SystemUI/res/values-ta/strings.xml2
-rw-r--r--packages/SystemUI/res/values-te/strings.xml6
-rw-r--r--packages/SystemUI/res/values-television/config.xml3
-rw-r--r--packages/SystemUI/res/values-th/strings.xml1
-rw-r--r--packages/SystemUI/res/values-tl/strings.xml1
-rw-r--r--packages/SystemUI/res/values-tr/strings.xml1
-rw-r--r--packages/SystemUI/res/values-uk/strings.xml1
-rw-r--r--packages/SystemUI/res/values-ur/strings.xml1
-rw-r--r--packages/SystemUI/res/values-uz/strings.xml7
-rw-r--r--packages/SystemUI/res/values-vi/strings.xml2
-rw-r--r--packages/SystemUI/res/values-zh-rCN/strings.xml2
-rw-r--r--packages/SystemUI/res/values-zh-rHK/strings.xml1
-rw-r--r--packages/SystemUI/res/values-zh-rTW/strings.xml1
-rw-r--r--packages/SystemUI/res/values-zu/strings.xml1
-rw-r--r--packages/SystemUI/res/values/dimens.xml30
-rw-r--r--packages/SystemUI/res/values/strings.xml22
-rw-r--r--packages/SystemUI/res/values/styles.xml65
-rw-r--r--packages/SystemUI/shared/src/com/android/systemui/shared/tracing/FrameProtoTracer.java38
-rw-r--r--packages/SystemUI/src/com/android/systemui/ForegroundServiceNotificationListener.java2
-rw-r--r--packages/SystemUI/src/com/android/systemui/biometrics/AuthBiometricView.java1
-rw-r--r--packages/SystemUI/src/com/android/systemui/biometrics/AuthCredentialView.java14
-rw-r--r--packages/SystemUI/src/com/android/systemui/bubbles/BubbleController.java7
-rw-r--r--packages/SystemUI/src/com/android/systemui/bubbles/BubbleExpandedView.java6
-rw-r--r--packages/SystemUI/src/com/android/systemui/controls/controller/ControlsController.kt1
-rw-r--r--packages/SystemUI/src/com/android/systemui/controls/controller/ControlsControllerImpl.kt6
-rw-r--r--packages/SystemUI/src/com/android/systemui/controls/management/AppAdapter.kt28
-rw-r--r--packages/SystemUI/src/com/android/systemui/controls/management/ControlAdapter.kt58
-rw-r--r--packages/SystemUI/src/com/android/systemui/controls/management/ControlsFavoritingActivity.kt24
-rw-r--r--packages/SystemUI/src/com/android/systemui/controls/management/ControlsProviderSelectorActivity.kt16
-rw-r--r--packages/SystemUI/src/com/android/systemui/controls/ui/Behavior.kt21
-rw-r--r--packages/SystemUI/src/com/android/systemui/controls/ui/ControlViewHolder.kt310
-rw-r--r--packages/SystemUI/src/com/android/systemui/controls/ui/ControlsUiControllerImpl.kt5
-rw-r--r--packages/SystemUI/src/com/android/systemui/controls/ui/RenderInfo.kt117
-rw-r--r--packages/SystemUI/src/com/android/systemui/controls/ui/TemperatureControlBehavior.kt56
-rw-r--r--packages/SystemUI/src/com/android/systemui/controls/ui/ToggleBehavior.kt66
-rw-r--r--packages/SystemUI/src/com/android/systemui/controls/ui/ToggleRangeBehavior.kt183
-rw-r--r--packages/SystemUI/src/com/android/systemui/controls/ui/UnknownBehavior.kt25
-rw-r--r--packages/SystemUI/src/com/android/systemui/dagger/DefaultComponentBinder.java3
-rw-r--r--packages/SystemUI/src/com/android/systemui/dagger/SystemUIDefaultModule.java2
-rw-r--r--packages/SystemUI/src/com/android/systemui/dagger/SystemUIRootComponent.java1
-rw-r--r--packages/SystemUI/src/com/android/systemui/qs/DoubleLineTileLayout.kt2
-rw-r--r--packages/SystemUI/src/com/android/systemui/qs/QSCarrier.java5
-rw-r--r--packages/SystemUI/src/com/android/systemui/qs/QSCarrierGroupController.java23
-rw-r--r--packages/SystemUI/src/com/android/systemui/qs/QSColorController.kt140
-rw-r--r--packages/SystemUI/src/com/android/systemui/qs/QSMediaPlayer.java78
-rw-r--r--packages/SystemUI/src/com/android/systemui/qs/QSPanel.java28
-rw-r--r--packages/SystemUI/src/com/android/systemui/qs/QSTileHost.java8
-rw-r--r--packages/SystemUI/src/com/android/systemui/qs/QuickQSMediaPlayer.java72
-rw-r--r--packages/SystemUI/src/com/android/systemui/qs/QuickQSPanel.java17
-rw-r--r--packages/SystemUI/src/com/android/systemui/qs/TileLayout.java5
-rw-r--r--packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSIconViewImpl.java48
-rw-r--r--packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSTileBaseView.java15
-rw-r--r--packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSTileImpl.java43
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/NotificationRemoveInterceptor.java12
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/NotificationViewHierarchyManager.java10
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/ForegroundServiceDismissalFeatureController.kt49
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationEntryManager.java45
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/ListDumper.java17
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotifCollection.java260
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotifInflaterImpl.java3
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotificationEntry.java124
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/HideLocallyDismissedNotifsCoordinator.java41
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/NotifCoordinators.java1
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/PreparationCoordinator.java2
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/notifcollection/NotifCollectionListener.java5
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/notifcollection/NotifCollectionLogger.kt29
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/row/DungeonRow.kt43
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRow.java5
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ForegroundServiceDungeonView.kt38
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/ForegroundServiceSectionController.kt171
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayout.java37
-rw-r--r--packages/SystemUI/src/com/android/systemui/tracing/sysui_trace.proto26
-rw-r--r--packages/SystemUI/src/com/android/systemui/tracing/sysui_trace_file.proto49
-rw-r--r--packages/SystemUI/src/com/android/systemui/tv/TvSystemUIBinder.java28
-rw-r--r--packages/SystemUI/src/com/android/systemui/tv/TvSystemUIFactory.java35
-rw-r--r--packages/SystemUI/src/com/android/systemui/tv/TvSystemUIRootComponent.java58
-rw-r--r--packages/SystemUI/src/com/android/systemui/wm/DisplayLayout.java106
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/bubbles/BubbleControllerTest.java15
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationViewHierarchyManagerTest.java4
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/NotificationEntryManagerTest.java11
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/TestableNotificationEntryManager.kt5
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/NotifCollectionTest.java290
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutTest.java15
-rw-r--r--packages/Tethering/res/values/config.xml1
-rw-r--r--packages/VpnDialogs/AndroidManifest.xml7
-rw-r--r--packages/VpnDialogs/src/com/android/vpndialogs/ConfirmDialog.java13
-rw-r--r--packages/VpnDialogs/src/com/android/vpndialogs/PlatformVpnConfirmDialog.java29
-rw-r--r--packages/overlays/Android.mk1
-rw-r--r--packages/overlays/DisplayCutoutEmulationHoleOverlay/Android.mk14
-rw-r--r--packages/overlays/DisplayCutoutEmulationHoleOverlay/AndroidManifest.xml26
-rw-r--r--packages/overlays/DisplayCutoutEmulationHoleOverlay/res/values-land/config.xml22
-rw-r--r--packages/overlays/DisplayCutoutEmulationHoleOverlay/res/values/config.xml61
-rw-r--r--packages/overlays/DisplayCutoutEmulationHoleOverlay/res/values/strings.xml22
-rw-r--r--packages/overlays/IconShapePebbleOverlay/Android.mk29
-rw-r--r--packages/overlays/IconShapePebbleOverlay/AndroidManifest.xml29
-rw-r--r--packages/overlays/IconShapePebbleOverlay/res/values/config.xml30
-rw-r--r--packages/overlays/IconShapePebbleOverlay/res/values/strings.xml23
-rw-r--r--services/Android.bp2
-rw-r--r--services/accessibility/java/com/android/server/accessibility/gestures/EventDispatcher.java6
-rw-r--r--services/appprediction/java/com/android/server/appprediction/AppPredictionPerUserService.java125
-rw-r--r--services/autofill/java/com/android/server/autofill/ui/SaveUi.java16
-rw-r--r--services/backup/backuplib/java/com/android/server/backup/transport/TransportClient.java5
-rw-r--r--services/contentcapture/java/com/android/server/contentcapture/ContentCaptureMetricsLogger.java17
-rw-r--r--services/contentcapture/java/com/android/server/contentcapture/ContentCapturePerUserService.java12
-rw-r--r--services/contentcapture/java/com/android/server/contentcapture/RemoteContentCaptureService.java18
-rw-r--r--services/core/java/android/content/pm/PackageManagerInternal.java2
-rw-r--r--services/core/java/com/android/server/AlarmManagerService.java88
-rw-r--r--services/core/java/com/android/server/BluetoothManagerService.java11
-rw-r--r--services/core/java/com/android/server/ConnectivityService.java231
-rw-r--r--services/core/java/com/android/server/RescueParty.java4
-rw-r--r--services/core/java/com/android/server/StorageManagerService.java8
-rw-r--r--services/core/java/com/android/server/SystemService.java5
-rw-r--r--services/core/java/com/android/server/TelephonyRegistry.java13
-rw-r--r--services/core/java/com/android/server/VibratorService.java160
-rw-r--r--services/core/java/com/android/server/Watchdog.java5
-rw-r--r--services/core/java/com/android/server/adb/AdbDebuggingManager.java6
-rw-r--r--services/core/java/com/android/server/am/ActivityManagerService.java21
-rw-r--r--services/core/java/com/android/server/am/ProcessList.java5
-rw-r--r--services/core/java/com/android/server/am/ProcessRecord.java13
-rw-r--r--services/core/java/com/android/server/am/TEST_MAPPING8
-rw-r--r--services/core/java/com/android/server/am/UserController.java10
-rw-r--r--services/core/java/com/android/server/appop/AppOpsService.java73
-rw-r--r--services/core/java/com/android/server/attention/AttentionManagerService.java11
-rw-r--r--services/core/java/com/android/server/camera/CameraServiceProxy.java19
-rw-r--r--services/core/java/com/android/server/connectivity/LingerMonitor.java29
-rw-r--r--services/core/java/com/android/server/connectivity/Vpn.java263
-rw-r--r--services/core/java/com/android/server/content/SyncManager.java59
-rw-r--r--services/core/java/com/android/server/display/LocalDisplayAdapter.java160
-rw-r--r--services/core/java/com/android/server/incremental/IncrementalManagerService.java12
-rw-r--r--services/core/java/com/android/server/integrity/AppIntegrityManagerServiceImpl.java50
-rw-r--r--services/core/java/com/android/server/integrity/IntegrityFileManager.java20
-rw-r--r--services/core/java/com/android/server/integrity/model/IntegrityCheckResult.java9
-rw-r--r--services/core/java/com/android/server/integrity/serializer/RuleBinarySerializer.java8
-rw-r--r--services/core/java/com/android/server/locksettings/RebootEscrowData.java44
-rw-r--r--services/core/java/com/android/server/locksettings/RebootEscrowKey.java67
-rw-r--r--services/core/java/com/android/server/locksettings/RebootEscrowManager.java84
-rw-r--r--services/core/java/com/android/server/media/MediaKeyDispatcher.java39
-rw-r--r--services/core/java/com/android/server/media/MediaSession2Record.java20
-rw-r--r--services/core/java/com/android/server/media/MediaSessionRecord.java23
-rw-r--r--services/core/java/com/android/server/media/MediaSessionRecordImpl.java14
-rw-r--r--services/core/java/com/android/server/media/MediaSessionService.java53
-rw-r--r--services/core/java/com/android/server/media/MediaSessionStack.java9
-rw-r--r--services/core/java/com/android/server/media/SessionPolicyProvider.java63
-rw-r--r--services/core/java/com/android/server/notification/NotificationRecordLoggerImpl.java4
-rw-r--r--services/core/java/com/android/server/pm/ApexManager.java24
-rw-r--r--services/core/java/com/android/server/pm/AppsFilter.java1
-rw-r--r--services/core/java/com/android/server/pm/DataLoaderManagerService.java3
-rw-r--r--services/core/java/com/android/server/pm/PackageInstallerService.java7
-rw-r--r--services/core/java/com/android/server/pm/PackageInstallerSession.java47
-rw-r--r--services/core/java/com/android/server/pm/PackageManagerService.java165
-rw-r--r--services/core/java/com/android/server/pm/PackageManagerServiceUtils.java18
-rw-r--r--services/core/java/com/android/server/pm/PackageManagerShellCommand.java3
-rw-r--r--services/core/java/com/android/server/pm/PackageSettingBase.java5
-rw-r--r--services/core/java/com/android/server/pm/Settings.java32
-rw-r--r--services/core/java/com/android/server/pm/ShortcutService.java15
-rw-r--r--services/core/java/com/android/server/pm/permission/BasePermission.java4
-rw-r--r--services/core/java/com/android/server/pm/permission/PermissionManagerService.java19
-rw-r--r--services/core/java/com/android/server/policy/PermissionPolicyService.java23
-rw-r--r--services/core/java/com/android/server/policy/PhoneWindowManager.java22
-rw-r--r--services/core/java/com/android/server/power/AttentionDetector.java5
-rw-r--r--services/core/java/com/android/server/power/Notifier.java36
-rw-r--r--services/core/java/com/android/server/rollback/Rollback.java40
-rw-r--r--services/core/java/com/android/server/rollback/RollbackManagerServiceImpl.java85
-rw-r--r--services/core/java/com/android/server/rollback/RollbackPackageHealthObserver.java17
-rw-r--r--services/core/java/com/android/server/rollback/WatchdogRollbackLogger.java59
-rw-r--r--services/core/java/com/android/server/security/KeyAttestationApplicationIdProviderService.java14
-rw-r--r--services/core/java/com/android/server/signedconfig/GlobalSettingsConfigApplicator.java13
-rw-r--r--services/core/java/com/android/server/signedconfig/SignatureVerifier.java18
-rw-r--r--services/core/java/com/android/server/signedconfig/SignedConfigEvent.java10
-rw-r--r--services/core/java/com/android/server/signedconfig/SignedConfigService.java7
-rw-r--r--services/core/java/com/android/server/storage/DeviceStorageMonitorService.java10
-rw-r--r--services/core/java/com/android/server/wm/ActivityRecord.java2
-rw-r--r--services/core/java/com/android/server/wm/ActivityServiceConnectionsHolder.java59
-rw-r--r--services/core/java/com/android/server/wm/ActivityTaskManagerService.java22
-rw-r--r--services/core/java/com/android/server/wm/DisplayContent.java10
-rw-r--r--services/core/java/com/android/server/wm/EmbeddedWindowController.java42
-rw-r--r--services/core/java/com/android/server/wm/Session.java17
-rw-r--r--services/core/java/com/android/server/wm/WindowManagerService.java73
-rw-r--r--services/core/java/com/android/server/wm/utils/WmDisplayCutout.java87
-rw-r--r--services/core/jni/com_android_server_incremental_IncrementalManagerService.cpp5
-rw-r--r--services/core/jni/com_android_server_location_GnssLocationProvider.cpp10
-rw-r--r--services/incremental/BinderIncrementalService.cpp18
-rw-r--r--services/incremental/IncrementalService.cpp133
-rw-r--r--services/incremental/IncrementalService.h13
-rw-r--r--services/incremental/ServiceWrappers.h2
-rw-r--r--services/incremental/include/incremental_service.h1
-rw-r--r--services/incremental/test/IncrementalServiceTest.cpp55
-rw-r--r--services/java/com/android/server/SystemServer.java21
-rw-r--r--services/people/java/com/android/server/people/SessionInfo.java10
-rw-r--r--services/people/java/com/android/server/people/data/CallLogQueryHelper.java121
-rw-r--r--services/people/java/com/android/server/people/data/DataManager.java199
-rw-r--r--services/people/java/com/android/server/people/data/MmsQueryHelper.java151
-rw-r--r--services/people/java/com/android/server/people/data/PackageData.java33
-rw-r--r--services/people/java/com/android/server/people/data/SmsQueryHelper.java119
-rw-r--r--services/people/java/com/android/server/people/data/UserData.java49
-rw-r--r--services/people/java/com/android/server/people/data/Utils.java49
-rw-r--r--services/people/java/com/android/server/people/prediction/AppTargetPredictor.java128
-rw-r--r--services/people/java/com/android/server/people/prediction/ConversationPredictor.java142
-rw-r--r--services/people/java/com/android/server/people/prediction/ShareTargetPredictor.java139
-rw-r--r--services/tests/mockingservicestests/src/com/android/server/AlarmManagerServiceTest.java48
-rw-r--r--services/tests/mockingservicestests/src/com/android/server/am/CachedAppOptimizerTest.java117
-rw-r--r--services/tests/mockingservicestests/src/com/android/server/display/LocalDisplayAdapterTest.java78
-rw-r--r--services/tests/servicestests/src/com/android/server/accessibility/gestures/TouchExplorerTest.java38
-rw-r--r--services/tests/servicestests/src/com/android/server/integrity/AppIntegrityManagerServiceImplTest.java89
-rw-r--r--services/tests/servicestests/src/com/android/server/integrity/model/IntegrityCheckResultTest.java10
-rw-r--r--services/tests/servicestests/src/com/android/server/integrity/serializer/RuleBinarySerializerTest.java10
-rw-r--r--services/tests/servicestests/src/com/android/server/locksettings/RebootEscrowDataTest.java22
-rw-r--r--services/tests/servicestests/src/com/android/server/locksettings/RebootEscrowManagerTests.java38
-rw-r--r--services/tests/servicestests/src/com/android/server/people/PeopleServiceTest.java2
-rw-r--r--services/tests/servicestests/src/com/android/server/people/data/CallLogQueryHelperTest.java169
-rw-r--r--services/tests/servicestests/src/com/android/server/people/data/DataManagerTest.java180
-rw-r--r--services/tests/servicestests/src/com/android/server/people/data/MmsQueryHelperTest.java174
-rw-r--r--services/tests/servicestests/src/com/android/server/people/data/PackageDataTest.java10
-rw-r--r--services/tests/servicestests/src/com/android/server/people/data/SmsQueryHelperTest.java151
-rw-r--r--services/tests/servicestests/src/com/android/server/people/prediction/ShareTargetPredictorTest.java137
-rw-r--r--services/tests/servicestests/src/com/android/server/rollback/RollbackUnitTest.java11
-rw-r--r--services/tests/servicestests/src/com/android/server/rollback/WatchdogRollbackLoggerTest.java13
-rwxr-xr-xservices/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java80
-rw-r--r--services/tests/wmtests/src/com/android/server/wm/utils/WmDisplayCutoutTest.java184
-rw-r--r--telephony/java/android/telephony/BarringInfo.java56
-rw-r--r--telephony/java/android/telephony/PreciseDataConnectionState.java4
-rw-r--r--telephony/java/android/telephony/SmsCbMessage.java1
-rw-r--r--telephony/java/android/telephony/SubscriptionManager.java24
-rw-r--r--tests/RollbackTest/StagedRollbackTest/src/com/android/tests/rollback/host/StagedRollbackTest.java44
-rw-r--r--tests/net/java/android/net/VpnManagerTest.java75
-rw-r--r--tests/net/java/com/android/server/ConnectivityServiceTest.java34
-rw-r--r--tests/net/java/com/android/server/connectivity/VpnTest.java252
-rw-r--r--tools/aapt2/link/ManifestFixer.cpp6
-rw-r--r--tools/stats_log_api_gen/Android.bp1
-rw-r--r--tools/stats_log_api_gen/Collation.cpp46
-rw-r--r--tools/stats_log_api_gen/Collation.h3
-rw-r--r--tools/stats_log_api_gen/atoms_info_writer.cpp28
-rw-r--r--tools/stats_log_api_gen/test.proto35
-rw-r--r--wifi/java/android/net/wifi/IScoreChangeCallback.aidl4
-rw-r--r--wifi/java/android/net/wifi/WifiConfiguration.java54
-rw-r--r--wifi/java/android/net/wifi/WifiEnterpriseConfig.java4
-rw-r--r--wifi/java/android/net/wifi/WifiManager.java18
-rw-r--r--wifi/java/android/net/wifi/WifiScanner.java2
-rw-r--r--wifi/java/android/net/wifi/wificond/NativeScanResult.java2
-rw-r--r--wifi/java/android/net/wifi/wificond/NativeWifiClient.java3
-rw-r--r--wifi/java/android/net/wifi/wificond/RadioChainInfo.java4
-rw-r--r--wifi/java/android/net/wifi/wificond/WifiCondManager.java17
-rw-r--r--wifi/tests/src/android/net/wifi/wificond/WifiCondManagerTest.java36
496 files changed, 10776 insertions, 4014 deletions
diff --git a/Android.bp b/Android.bp
index c89917975e8e..f48e6feb1c14 100644
--- a/Android.bp
+++ b/Android.bp
@@ -401,7 +401,7 @@ java_defaults {
"app-compat-annotations",
"ext",
"unsupportedappusage",
- "updatable_media_stubs",
+ "framework-media-stubs-systemapi",
"framework_mediaprovider_stubs",
"framework-tethering",
"framework-telephony-stubs",
@@ -469,7 +469,7 @@ java_library {
"framework-appsearch-stubs",
"framework-sdkextensions-stubs-systemapi",
"framework-statsd", // TODO(b/146167933): Use framework-statsd-stubs
- "framework-permission-stubs",
+ "framework-permission-stubs-systemapi",
"framework-wifi-stubs",
"ike-stubs",
],
@@ -517,10 +517,10 @@ java_library {
installable: false, // this lib is a build-only library
static_libs: [
"framework-minus-apex",
- "updatable_media_stubs",
+ "framework-media-stubs-systemapi",
"framework_mediaprovider_stubs",
"framework-appsearch", // TODO(b/146218515): should be framework-appsearch-stubs
- "framework-permission-stubs",
+ "framework-permission-stubs-systemapi",
"framework-sdkextensions-stubs-systemapi",
// TODO(b/146167933): Use framework-statsd-stubs instead.
"framework-statsd",
diff --git a/ApiDocs.bp b/ApiDocs.bp
index c40004cf8e5c..04ddc50a94c4 100644
--- a/ApiDocs.bp
+++ b/ApiDocs.bp
@@ -176,6 +176,7 @@ droiddoc {
hdf: [
"android.whichdoc offline",
],
+ compat_config: ":global-compat-config",
proofread_file: "offline-sdk-docs-proofrerad.txt",
args: framework_docs_only_args + " -offlinemode -title \"Android SDK\"",
static_doc_index_redirect: "docs/docs-preview-index.html",
diff --git a/StubLibraries.bp b/StubLibraries.bp
index 0f805655b33d..d4db7373504b 100644
--- a/StubLibraries.bp
+++ b/StubLibraries.bp
@@ -113,6 +113,16 @@ droidstubs {
jdiff_enabled: true,
}
+priv_apps = " " +
+ "--show-annotation android.annotation.SystemApi\\(" +
+ "client=android.annotation.SystemApi.Client.PRIVILEGED_APPS" +
+ "\\) "
+
+module_libs = " " +
+ " --show-annotation android.annotation.SystemApi\\(" +
+ "client=android.annotation.SystemApi.Client.MODULE_LIBRARIES" +
+ "\\) "
+
droidstubs {
name: "system-api-stubs-docs",
defaults: ["metalava-api-stubs-default"],
@@ -124,10 +134,7 @@ droidstubs {
arg_files: [
"core/res/AndroidManifest.xml",
],
- args: metalava_framework_docs_args +
- " --show-annotation android.annotation.SystemApi\\(" +
- "client=android.annotation.SystemApi.Client.PRIVILEGED_APPS," +
- "process=android.annotation.SystemApi.Process.ALL\\)",
+ args: metalava_framework_docs_args + priv_apps,
check_api: {
current: {
api_file: "api/system-current.txt",
@@ -175,49 +182,17 @@ droidstubs {
// @SystemApi(client=MODULE_APPS) and @SystemApi(client=MODULE_LIBRARIES)
/////////////////////////////////////////////////////////////////////
-// TODO(b/146727827) remove the *-api modules when we can teach metalava
+// TODO(b/146727827) remove the *-api module when we can teach metalava
// about the relationship among the API surfaces. Currently, these modules are only to generate
// the API signature files and ensure that the APIs evolve in a backwards compatible manner.
// They however are NOT used for building the API stub.
-droidstubs {
- name: "module-app-api",
- defaults: ["metalava-api-stubs-default"],
- libs: ["framework-all"],
- arg_files: ["core/res/AndroidManifest.xml"],
- args: metalava_framework_docs_args +
- " --show-annotation android.annotation.SystemApi\\(" +
- "client=android.annotation.SystemApi.Client.MODULE_APPS," +
- "process=android.annotation.SystemApi.Process.ALL\\)",
- check_api: {
- current: {
- api_file: "api/module-app-current.txt",
- removed_api_file: "api/module-app-removed.txt",
- },
- // TODO(b/147559833) enable the compatibility check against the last release API
- // and the API lint
- //last_released: {
- // api_file: ":last-released-module-app-api",
- // removed_api_file: "api/module-app-removed.txt",
- // baseline_file: ":module-app-api-incompatibilities-with-last-released"
- //},
- //api_lint: {
- // enabled: true,
- // new_since: ":last-released-module-app-api",
- // baseline_file: "api/module-app-lint-baseline.txt",
- //},
- },
- //jdiff_enabled: true,
-}
droidstubs {
name: "module-lib-api",
defaults: ["metalava-api-stubs-default"],
libs: ["framework-all"],
arg_files: ["core/res/AndroidManifest.xml"],
- args: metalava_framework_docs_args +
- " --show-annotation android.annotation.SystemApi\\(" +
- "client=android.annotation.SystemApi.Client.MODULE_LIBRARIES," +
- "process=android.annotation.SystemApi.Process.ALL\\)",
+ args: metalava_framework_docs_args + module_libs,
check_api: {
current: {
api_file: "api/module-lib-current.txt",
@@ -239,39 +214,17 @@ droidstubs {
//jdiff_enabled: true,
}
-// The following two droidstubs modules generate source files for the API stub libraries for
-// modules. Note that they not only include their own APIs but also other APIs that have
-// narrower scope. For example, module-lib-api-stubs-docs includes all @SystemApis not just
-// the ones with 'client=MODULE_LIBRARIES'.
-droidstubs {
- name: "module-app-api-stubs-docs",
- defaults: ["metalava-api-stubs-default"],
- libs: ["framework-all"],
- arg_files: ["core/res/AndroidManifest.xml"],
- args: metalava_framework_docs_args +
- " --show-annotation android.annotation.SystemApi\\(" +
- "client=android.annotation.SystemApi.Client.PRIVILEGED_APPS," +
- "process=android.annotation.SystemApi.Process.ALL\\)" +
- " --show-annotation android.annotation.SystemApi\\(" +
- "client=android.annotation.SystemApi.Client.MODULE_APPS," +
- "process=android.annotation.SystemApi.Process.ALL\\)",
-}
+
+// The following droidstub module generates source files for the API stub library for
+// modules. Note that it not only includes its own APIs but also other APIs that have
+// narrower scope (all @SystemApis, not just the ones with 'client=MODULE_LIBRARIES').
droidstubs {
name: "module-lib-api-stubs-docs",
defaults: ["metalava-api-stubs-default"],
libs: ["framework-all"],
arg_files: ["core/res/AndroidManifest.xml"],
- args: metalava_framework_docs_args +
- " --show-annotation android.annotation.SystemApi\\(" +
- "client=android.annotation.SystemApi.Client.PRIVILEGED_APPS," +
- "process=android.annotation.SystemApi.Process.ALL\\)" +
- " --show-annotation android.annotation.SystemApi\\(" +
- "client=android.annotation.SystemApi.Client.MODULE_APPS," +
- "process=android.annotation.SystemApi.Process.ALL\\)" +
- " --show-annotation android.annotation.SystemApi\\(" +
- "client=android.annotation.SystemApi.Client.MODULE_LIBRARIES," +
- "process=android.annotation.SystemApi.Process.ALL\\)",
+ args: metalava_framework_docs_args + priv_apps + module_libs,
}
/////////////////////////////////////////////////////////////////////
@@ -340,21 +293,6 @@ java_library_static {
}
java_library_static {
- name: "android_module_app_stubs_current",
- srcs: [
- ":module-app-api-stubs-docs",
- ],
- libs: [
- "stub-annotations",
- "framework-all",
- ],
- static_libs: [
- "private-stub-annotations-jar",
- ],
- defaults: ["framework-stubs-default"],
-}
-
-java_library_static {
name: "android_module_lib_stubs_current",
srcs: [
":module-lib-api-stubs-docs",
@@ -401,7 +339,7 @@ droidstubs {
merge_annotations_dirs: [
"metalava-manual",
],
- args: " --show-annotation android.annotation.SystemApi\\(client=android.annotation.SystemApi.Client.PRIVILEGED_APPS,process=android.annotation.SystemApi.Process.ALL\\)",
+ args: priv_apps,
}
java_library_static {
@@ -427,7 +365,7 @@ droidstubs {
removed_dex_api_filename: "removed-dex.txt",
args: metalava_framework_docs_args +
" --show-unannotated " +
- " --show-annotation android.annotation.SystemApi\\(client=android.annotation.SystemApi.Client.PRIVILEGED_APPS,process=android.annotation.SystemApi.Process.ALL\\) " +
+ priv_apps +
" --show-annotation android.annotation.TestApi ",
}
@@ -446,7 +384,7 @@ droidstubs {
" --hide ReferencesHidden " +
" --hide UnhiddenSystemApi " +
" --show-unannotated " +
- " --show-annotation android.annotation.SystemApi\\(client=android.annotation.SystemApi.Client.PRIVILEGED_APPS,process=android.annotation.SystemApi.Process.ALL\\) " +
+ priv_apps +
" --show-annotation android.annotation.TestApi ",
}
diff --git a/apex/Android.bp b/apex/Android.bp
index abebfa39fada..362cf95b3832 100644
--- a/apex/Android.bp
+++ b/apex/Android.bp
@@ -29,6 +29,16 @@ mainline_stubs_args =
// TODO: remove this server classes are cleaned up.
mainline_stubs_args += "--hide-package com.android.server "
+priv_apps = " " +
+ "--show-annotation android.annotation.SystemApi\\(" +
+ "client=android.annotation.SystemApi.Client.PRIVILEGED_APPS," +
+ "\\) "
+
+module_libs = " " +
+ " --show-annotation android.annotation.SystemApi\\(" +
+ "client=android.annotation.SystemApi.Client.MODULE_LIBRARIES," +
+ "\\) "
+
stubs_defaults {
name: "framework-module-stubs-defaults-publicapi",
args: mainline_stubs_args,
@@ -37,36 +47,23 @@ stubs_defaults {
stubs_defaults {
name: "framework-module-stubs-defaults-systemapi",
- args: mainline_stubs_args +
- " --show-annotation android.annotation.SystemApi\\(" +
- "client=android.annotation.SystemApi.Client.PRIVILEGED_APPS," +
- "process=android.annotation.SystemApi.Process.ALL\\) ",
+ args: mainline_stubs_args + priv_apps,
installable: false,
}
+// The defaults for module_libs comes in two parts - defaults for API checks
+// and defaults for stub generation. This is because we want the API txt
+// files to *only* include the module_libs_api, but the stubs to include
+// module_libs_api as well as priv_apps.
+
stubs_defaults {
- name: "framework-module-stubs-defaults-module_apps_api",
- args: mainline_stubs_args +
- " --show-annotation android.annotation.SystemApi\\(" +
- "client=android.annotation.SystemApi.Client.PRIVILEGED_APPS," +
- "process=android.annotation.SystemApi.Process.ALL\\) " +
- " --show-annotation android.annotation.SystemApi\\(" +
- "client=android.annotation.SystemApi.Client.MODULE_APPS," +
- "process=android.annotation.SystemApi.Process.ALL\\) ",
+ name: "framework-module-api-defaults-module_libs_api",
+ args: mainline_stubs_args + module_libs,
installable: false,
}
stubs_defaults {
name: "framework-module-stubs-defaults-module_libs_api",
- args: mainline_stubs_args +
- " --show-annotation android.annotation.SystemApi\\(" +
- "client=android.annotation.SystemApi.Client.PRIVILEGED_APPS," +
- "process=android.annotation.SystemApi.Process.ALL\\) " +
- " --show-annotation android.annotation.SystemApi\\(" +
- "client=android.annotation.SystemApi.Client.MODULE_APPS," +
- "process=android.annotation.SystemApi.Process.ALL\\) " +
- " --show-annotation android.annotation.SystemApi\\(" +
- "client=android.annotation.SystemApi.Client.MODULE_LIBRARIES," +
- "process=android.annotation.SystemApi.Process.ALL\\) ",
+ args: mainline_stubs_args + module_libs + priv_apps,
installable: false,
}
diff --git a/apex/media/framework/Android.bp b/apex/media/framework/Android.bp
index 2266d049d70a..91df09865037 100644
--- a/apex/media/framework/Android.bp
+++ b/apex/media/framework/Android.bp
@@ -99,13 +99,10 @@ filegroup {
path: "java"
}
-droidstubs {
- name: "updatable-media-stubs",
- srcs: [
- ":updatable-media-srcs",
- ":framework-media-annotation-srcs",
- ],
- defaults: [ "framework-module-stubs-defaults-systemapi" ],
+stubs_defaults {
+ name: "framework-media-stubs-srcs-defaults",
+ srcs: [ ":updatable-media-srcs" ],
+ libs: [ "framework_media_annotation" ],
aidl: {
// TODO(b/135922046) remove this
include_dirs: ["frameworks/base/core/java"],
@@ -113,9 +110,53 @@ droidstubs {
sdk_version: "system_current",
}
+droidstubs {
+ name: "framework-media-stubs-srcs-publicapi",
+ defaults: [
+ "framework-media-stubs-srcs-defaults",
+ "framework-module-stubs-defaults-publicapi",
+ ],
+}
+
+droidstubs {
+ name: "framework-media-stubs-srcs-systemapi",
+ defaults: [
+ "framework-media-stubs-srcs-defaults",
+ "framework-module-stubs-defaults-systemapi",
+ ],
+}
+
+droidstubs {
+ name: "framework-media-api-module_libs_api",
+ defaults: [
+ "framework-media-stubs-srcs-defaults",
+ "framework-module-api-defaults-module_libs_api",
+ ],
+}
+
+droidstubs {
+ name: "framework-media-stubs-srcs-module_libs_api",
+ defaults: [
+ "framework-media-stubs-srcs-defaults",
+ "framework-module-stubs-defaults-module_libs_api",
+ ],
+}
+
+java_library {
+ name: "framework-media-stubs-publicapi",
+ srcs: [":framework-media-stubs-srcs-publicapi"],
+ sdk_version: "current",
+}
+
+java_library {
+ name: "framework-media-stubs-systemapi",
+ srcs: [":framework-media-stubs-srcs-systemapi"],
+ sdk_version: "system_current",
+}
+
java_library {
- name: "updatable_media_stubs",
- srcs: [":updatable-media-stubs"],
+ name: "framework-media-stubs-module_libs_api",
+ srcs: [":framework-media-stubs-srcs-module_libs_api"],
sdk_version: "system_current",
}
diff --git a/apex/permission/framework/Android.bp b/apex/permission/framework/Android.bp
index 09571a1cd111..126fa00a31f0 100644
--- a/apex/permission/framework/Android.bp
+++ b/apex/permission/framework/Android.bp
@@ -44,23 +44,66 @@ java_library {
],
}
+stubs_defaults {
+ name: "framework-permission-stubs-defaults",
+ srcs: [ ":framework-permission-sources" ],
+ libs: [ "framework-annotations-lib" ],
+ sdk_version: "system_current",
+}
+
droidstubs {
- name: "framework-permission-stubs-sources",
- srcs: [
- ":framework-annotations",
- ":framework-permission-sources",
+ name: "framework-permission-stubs-srcs-publicapi",
+ sdk_version: "system_current",
+ defaults: [
+ "framework-module-stubs-defaults-publicapi",
+ "framework-permission-stubs-defaults",
],
+}
+
+droidstubs {
+ name: "framework-permission-stubs-srcs-systemapi",
sdk_version: "system_current",
defaults: [
"framework-module-stubs-defaults-systemapi",
+ "framework-permission-stubs-defaults",
],
}
-java_library {
- name: "framework-permission-stubs",
- srcs: [
- ":framework-permission-stubs-sources",
+droidstubs {
+ name: "framework-permission-api-module_libs_api",
+ sdk_version: "system_current",
+ defaults: [
+ "framework-module-api-defaults-module_libs_api",
+ "framework-permission-stubs-defaults",
+ ],
+}
+
+droidstubs {
+ name: "framework-permission-stubs-srcs-module_libs_api",
+ sdk_version: "system_current",
+ defaults: [
+ "framework-module-stubs-defaults-module_libs_api",
+ "framework-permission-stubs-defaults",
],
+}
+
+java_library {
+ name: "framework-permission-stubs-publicapi",
+ srcs: [ ":framework-permission-stubs-srcs-publicapi" ],
+ sdk_version: "system_current",
+ installable: false,
+}
+
+java_library {
+ name: "framework-permission-stubs-systemapi",
+ srcs: [ ":framework-permission-stubs-srcs-systemapi" ],
+ sdk_version: "system_current",
+ installable: false,
+}
+
+java_library {
+ name: "framework-permission-stubs-module_libs_api",
+ srcs: [ ":framework-permission-stubs-srcs-module_libs_api" ],
sdk_version: "system_current",
installable: false,
}
diff --git a/apex/permission/service/java/com/android/permission/persistence/RuntimePermissionsPersistence.java b/apex/permission/service/java/com/android/permission/persistence/RuntimePermissionsPersistence.java
index 5f2d94441965..6c7f82a11908 100644
--- a/apex/permission/service/java/com/android/permission/persistence/RuntimePermissionsPersistence.java
+++ b/apex/permission/service/java/com/android/permission/persistence/RuntimePermissionsPersistence.java
@@ -19,6 +19,7 @@ package com.android.permission.persistence;
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.annotation.SystemApi;
+import android.annotation.SystemApi.Client;
import android.os.UserHandle;
/**
@@ -27,7 +28,7 @@ import android.os.UserHandle;
* TODO(b/147914847): Remove @hide when it becomes the default.
* @hide
*/
-@SystemApi(client = SystemApi.Client.MODULE_LIBRARIES, process = SystemApi.Process.SYSTEM_SERVER)
+@SystemApi(client = Client.SYSTEM_SERVER)
public interface RuntimePermissionsPersistence {
/**
diff --git a/apex/permission/service/java/com/android/permission/persistence/RuntimePermissionsState.java b/apex/permission/service/java/com/android/permission/persistence/RuntimePermissionsState.java
index 2a939e51b98e..cd2750a0bee5 100644
--- a/apex/permission/service/java/com/android/permission/persistence/RuntimePermissionsState.java
+++ b/apex/permission/service/java/com/android/permission/persistence/RuntimePermissionsState.java
@@ -19,6 +19,7 @@ package com.android.permission.persistence;
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.annotation.SystemApi;
+import android.annotation.SystemApi.Client;
import java.util.List;
import java.util.Map;
@@ -29,7 +30,7 @@ import java.util.Map;
* TODO(b/147914847): Remove @hide when it becomes the default.
* @hide
*/
-@SystemApi(client = SystemApi.Client.MODULE_LIBRARIES, process = SystemApi.Process.SYSTEM_SERVER)
+@SystemApi(client = Client.SYSTEM_SERVER)
public final class RuntimePermissionsState {
/**
diff --git a/apex/permission/service/java/com/android/role/persistence/RolesPersistence.java b/apex/permission/service/java/com/android/role/persistence/RolesPersistence.java
index 63c8eedd6285..2908a3872df9 100644
--- a/apex/permission/service/java/com/android/role/persistence/RolesPersistence.java
+++ b/apex/permission/service/java/com/android/role/persistence/RolesPersistence.java
@@ -19,6 +19,7 @@ package com.android.role.persistence;
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.annotation.SystemApi;
+import android.annotation.SystemApi.Client;
import android.os.UserHandle;
/**
@@ -27,7 +28,7 @@ import android.os.UserHandle;
* TODO(b/147914847): Remove @hide when it becomes the default.
* @hide
*/
-@SystemApi(client = SystemApi.Client.MODULE_LIBRARIES, process = SystemApi.Process.SYSTEM_SERVER)
+@SystemApi(client = Client.SYSTEM_SERVER)
public interface RolesPersistence {
/**
diff --git a/apex/permission/service/java/com/android/role/persistence/RolesState.java b/apex/permission/service/java/com/android/role/persistence/RolesState.java
index bff980e2e126..7da9d11f172f 100644
--- a/apex/permission/service/java/com/android/role/persistence/RolesState.java
+++ b/apex/permission/service/java/com/android/role/persistence/RolesState.java
@@ -19,6 +19,7 @@ package com.android.role.persistence;
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.annotation.SystemApi;
+import android.annotation.SystemApi.Client;
import java.util.Map;
import java.util.Set;
@@ -29,7 +30,7 @@ import java.util.Set;
* TODO(b/147914847): Remove @hide when it becomes the default.
* @hide
*/
-@SystemApi(client = SystemApi.Client.MODULE_LIBRARIES, process = SystemApi.Process.SYSTEM_SERVER)
+@SystemApi(client = Client.SYSTEM_SERVER)
public final class RolesState {
/**
diff --git a/apex/sdkextensions/framework/Android.bp b/apex/sdkextensions/framework/Android.bp
index 245a96b99148..86f4ab7c1128 100644
--- a/apex/sdkextensions/framework/Android.bp
+++ b/apex/sdkextensions/framework/Android.bp
@@ -44,34 +44,68 @@ java_library {
],
}
+stubs_defaults {
+ name: "framework-sdkextensions-stubs-defaults",
+ srcs: [ ":framework-sdkextensions-sources" ],
+ libs: [ "framework-annotations-lib" ],
+ sdk_version: "system_current",
+}
+
droidstubs {
- name: "framework-sdkextensions-droidstubs-publicapi",
+ name: "framework-sdkextensions-stubs-srcs-publicapi",
defaults: [
- "framework-sdkextensions-stubs-defaults",
"framework-module-stubs-defaults-publicapi",
+ "framework-sdkextensions-stubs-defaults",
]
}
droidstubs {
- name: "framework-sdkextensions-droidstubs-systemapi",
+ name: "framework-sdkextensions-stubs-srcs-systemapi",
defaults: [
- "framework-sdkextensions-stubs-defaults",
"framework-module-stubs-defaults-systemapi",
+ "framework-sdkextensions-stubs-defaults",
]
}
-stubs_defaults {
- name: "framework-sdkextensions-stubs-defaults",
- srcs: [
- ":framework-sdkextensions-sources",
- ":framework-annotations",
- ],
- sdk_version: "system_current",
+droidstubs {
+ name: "framework-sdkextensions-api-module_libs_api",
+ defaults: [
+ "framework-module-api-defaults-module_libs_api",
+ "framework-sdkextensions-stubs-defaults",
+ ]
+}
+
+droidstubs {
+ name: "framework-sdkextensions-stubs-srcs-module_libs_api",
+ defaults: [
+ "framework-module-stubs-defaults-module_libs_api",
+ "framework-sdkextensions-stubs-defaults",
+ ]
+}
+
+java_library {
+ name: "framework-sdkextensions-stubs-publicapi",
+ srcs: [":framework-sdkextensions-stubs-srcs-publicapi"],
+ sdk_version: "current",
+ visibility: [
+ "//frameworks/base", // Framework
+ "//frameworks/base/apex/sdkextensions", // sdkextensions SDK
+ ]
}
java_library {
name: "framework-sdkextensions-stubs-systemapi",
- srcs: [":framework-sdkextensions-droidstubs-systemapi"],
+ srcs: [":framework-sdkextensions-stubs-srcs-systemapi"],
+ sdk_version: "system_current",
+ visibility: [
+ "//frameworks/base", // Framework
+ "//frameworks/base/apex/sdkextensions", // sdkextensions SDK
+ ]
+}
+
+java_library {
+ name: "framework-sdkextensions-stubs-module_libs_api",
+ srcs: [":framework-sdkextensions-stubs-srcs-module_libs_api"],
sdk_version: "system_current",
visibility: [
"//frameworks/base", // Framework
diff --git a/apex/statsd/framework/Android.bp b/apex/statsd/framework/Android.bp
index 231c91026bb6..d85ae69b799f 100644
--- a/apex/statsd/framework/Android.bp
+++ b/apex/statsd/framework/Android.bp
@@ -30,6 +30,7 @@ java_library {
],
permitted_packages: [
"android.app",
+ "android.os",
"android.util",
],
libs: [
@@ -43,6 +44,7 @@ java_library {
"//frameworks/base/apex/statsd:__subpackages__",
//TODO(b/146167933) remove this when framework is built with framework-statsd-stubs
"//frameworks/base",
+ "//frameworks/opt/net/wifi/service",
],
apex_available: [
"com.android.os.statsd",
@@ -50,31 +52,70 @@ java_library {
],
}
+stubs_defaults {
+ name: "framework-statsd-stubs-srcs-defaults",
+ srcs: [ ":framework-statsd-sources" ],
+ libs: [
+ // TODO(b/148218250): Change to android_system_stubs_current
+ "framework-all",
+ "framework-annotations-lib",
+ ],
+ sdk_version: "core_platform",
+}
+
droidstubs {
- name: "framework-statsd-stubs-docs",
+ name: "framework-statsd-stubs-srcs-publicapi",
defaults: [
- "framework-module-stubs-defaults-systemapi"
+ "framework-module-stubs-defaults-systemapi",
+ "framework-statsd-stubs-srcs-defaults",
],
- srcs: [
- ":framework-annotations",
- ":framework-statsd-sources",
+}
+
+droidstubs {
+ name: "framework-statsd-stubs-srcs-systemapi",
+ defaults: [
+ "framework-module-stubs-defaults-systemapi",
+ "framework-statsd-stubs-srcs-defaults",
],
- libs: [
- // TODO(b/148218250): Change to android_system_stubs_current
- "framework-all",
+}
+
+droidstubs {
+ name: "framework-statsd-api-module_libs_api",
+ defaults: [
+ "framework-module-api-defaults-module_libs_api",
+ "framework-statsd-stubs-srcs-defaults",
+ ],
+}
+
+droidstubs {
+ name: "framework-statsd-stubs-srcs-module_libs_api",
+ defaults: [
+ "framework-module-stubs-defaults-module_libs_api",
+ "framework-statsd-stubs-srcs-defaults",
],
+}
+
+java_library {
+ name: "framework-statsd-stubs-publicapi",
+ srcs: [ ":framework-statsd-stubs-srcs-publicapi" ],
+ // TODO(b/148218250): Change to current
+ libs: [ "framework-all" ],
sdk_version: "core_platform",
}
// TODO(b/146167933): Use these stubs in frameworks/base/Android.bp
java_library {
- name: "framework-statsd-stubs",
- srcs: [
- ":framework-statsd-stubs-docs",
- ],
- libs: [
- // TODO(b/148218250): Change to android_system_stubs_current
- "framework-all",
- ],
+ name: "framework-statsd-stubs-systemapi",
+ srcs: [ ":framework-statsd-stubs-srcs-systemapi" ],
+ // TODO(b/148218250): Change to system_current
+ libs: [ "framework-all" ],
+ sdk_version: "core_platform",
+}
+
+java_library {
+ name: "framework-statsd-stubs-module_libs_api",
+ srcs: [ ":framework-statsd-stubs-srcs-systemapi" ],
+ // TODO(b/148218250): Change to system_current
+ libs: [ "framework-all" ],
sdk_version: "core_platform",
}
diff --git a/core/java/android/os/StatsDimensionsValue.java b/apex/statsd/framework/java/android/os/StatsDimensionsValue.java
index da13ea13d5fa..886130fc5f14 100644
--- a/core/java/android/os/StatsDimensionsValue.java
+++ b/apex/statsd/framework/java/android/os/StatsDimensionsValue.java
@@ -264,7 +264,8 @@ public final class StatsDimensionsValue implements Parcelable {
/**
* Parcelable Creator for StatsDimensionsValue.
*/
- public static final @android.annotation.NonNull Parcelable.Creator<StatsDimensionsValue> CREATOR = new
+ public static final @android.annotation.NonNull
+ Parcelable.Creator<StatsDimensionsValue> CREATOR = new
Parcelable.Creator<StatsDimensionsValue>() {
public StatsDimensionsValue createFromParcel(Parcel in) {
return new StatsDimensionsValue(in);
diff --git a/api/current.txt b/api/current.txt
index b8eaa9d4febe..9b2e6d42b56f 100644
--- a/api/current.txt
+++ b/api/current.txt
@@ -29,7 +29,7 @@ package android {
field public static final String BIND_CARRIER_MESSAGING_CLIENT_SERVICE = "android.permission.BIND_CARRIER_MESSAGING_CLIENT_SERVICE";
field @Deprecated public static final String BIND_CARRIER_MESSAGING_SERVICE = "android.permission.BIND_CARRIER_MESSAGING_SERVICE";
field public static final String BIND_CARRIER_SERVICES = "android.permission.BIND_CARRIER_SERVICES";
- field public static final String BIND_CHOOSER_TARGET_SERVICE = "android.permission.BIND_CHOOSER_TARGET_SERVICE";
+ field @Deprecated public static final String BIND_CHOOSER_TARGET_SERVICE = "android.permission.BIND_CHOOSER_TARGET_SERVICE";
field public static final String BIND_CONDITION_PROVIDER_SERVICE = "android.permission.BIND_CONDITION_PROVIDER_SERVICE";
field public static final String BIND_DEVICE_ADMIN = "android.permission.BIND_DEVICE_ADMIN";
field public static final String BIND_DREAM_SERVICE = "android.permission.BIND_DREAM_SERVICE";
@@ -31293,7 +31293,7 @@ package android.net.wifi {
field public static final String ACTION_PICK_WIFI_NETWORK = "android.net.wifi.PICK_WIFI_NETWORK";
field public static final String ACTION_REQUEST_SCAN_ALWAYS_AVAILABLE = "android.net.wifi.action.REQUEST_SCAN_ALWAYS_AVAILABLE";
field public static final String ACTION_WIFI_NETWORK_SUGGESTION_POST_CONNECTION = "android.net.wifi.action.WIFI_NETWORK_SUGGESTION_POST_CONNECTION";
- field public static final String ACTION_WIFI_SCAN_AVAILABLE = "android.net.wifi.action.WIFI_SCAN_AVAILABLE";
+ field public static final String ACTION_WIFI_SCAN_AVAILABILITY_CHANGED = "android.net.wifi.action.WIFI_SCAN_AVAILABILITY_CHANGED";
field @Deprecated public static final int ERROR_AUTHENTICATING = 1; // 0x1
field @Deprecated public static final String EXTRA_BSSID = "bssid";
field public static final String EXTRA_NETWORK_INFO = "networkInfo";
@@ -39565,7 +39565,7 @@ package android.provider {
protected static interface ContactsContract.RawContactsColumns {
field public static final String ACCOUNT_TYPE_AND_DATA_SET = "account_type_and_data_set";
field public static final String AGGREGATION_MODE = "aggregation_mode";
- field @Deprecated public static final String BACKUP_ID = "backup_id";
+ field public static final String BACKUP_ID = "backup_id";
field public static final String CONTACT_ID = "contact_id";
field public static final String DATA_SET = "data_set";
field public static final String DELETED = "deleted";
@@ -43175,25 +43175,25 @@ package android.service.carrier {
package android.service.chooser {
- public final class ChooserTarget implements android.os.Parcelable {
- ctor public ChooserTarget(CharSequence, android.graphics.drawable.Icon, float, android.content.ComponentName, @Nullable android.os.Bundle);
- method public int describeContents();
- method public android.content.ComponentName getComponentName();
- method public android.graphics.drawable.Icon getIcon();
- method public android.os.Bundle getIntentExtras();
- method public float getScore();
- method public CharSequence getTitle();
- method public void writeToParcel(android.os.Parcel, int);
- field @NonNull public static final android.os.Parcelable.Creator<android.service.chooser.ChooserTarget> CREATOR;
+ @Deprecated public final class ChooserTarget implements android.os.Parcelable {
+ ctor @Deprecated public ChooserTarget(CharSequence, android.graphics.drawable.Icon, float, android.content.ComponentName, @Nullable android.os.Bundle);
+ method @Deprecated public int describeContents();
+ method @Deprecated public android.content.ComponentName getComponentName();
+ method @Deprecated public android.graphics.drawable.Icon getIcon();
+ method @Deprecated public android.os.Bundle getIntentExtras();
+ method @Deprecated public float getScore();
+ method @Deprecated public CharSequence getTitle();
+ method @Deprecated public void writeToParcel(android.os.Parcel, int);
+ field @Deprecated @NonNull public static final android.os.Parcelable.Creator<android.service.chooser.ChooserTarget> CREATOR;
}
- public abstract class ChooserTargetService extends android.app.Service {
- ctor public ChooserTargetService();
- method public android.os.IBinder onBind(android.content.Intent);
- method public abstract java.util.List<android.service.chooser.ChooserTarget> onGetChooserTargets(android.content.ComponentName, android.content.IntentFilter);
- field public static final String BIND_PERMISSION = "android.permission.BIND_CHOOSER_TARGET_SERVICE";
- field public static final String META_DATA_NAME = "android.service.chooser.chooser_target_service";
- field public static final String SERVICE_INTERFACE = "android.service.chooser.ChooserTargetService";
+ @Deprecated public abstract class ChooserTargetService extends android.app.Service {
+ ctor @Deprecated public ChooserTargetService();
+ method @Deprecated public android.os.IBinder onBind(android.content.Intent);
+ method @Deprecated public abstract java.util.List<android.service.chooser.ChooserTarget> onGetChooserTargets(android.content.ComponentName, android.content.IntentFilter);
+ field @Deprecated public static final String BIND_PERMISSION = "android.permission.BIND_CHOOSER_TARGET_SERVICE";
+ field @Deprecated public static final String META_DATA_NAME = "android.service.chooser.chooser_target_service";
+ field @Deprecated public static final String SERVICE_INTERFACE = "android.service.chooser.ChooserTargetService";
}
}
@@ -47296,6 +47296,7 @@ package android.telephony {
public final class PreciseDataConnectionState implements android.os.Parcelable {
method public int describeContents();
+ method @Nullable public android.telephony.data.ApnSetting getApnSetting();
method public int getLastCauseCode();
method @Nullable public android.net.LinkProperties getLinkProperties();
method public int getNetworkType();
diff --git a/api/module-app-current.txt b/api/module-app-current.txt
deleted file mode 100644
index d802177e249b..000000000000
--- a/api/module-app-current.txt
+++ /dev/null
@@ -1 +0,0 @@
-// Signature format: 2.0
diff --git a/api/module-app-removed.txt b/api/module-app-removed.txt
deleted file mode 100644
index d802177e249b..000000000000
--- a/api/module-app-removed.txt
+++ /dev/null
@@ -1 +0,0 @@
-// Signature format: 2.0
diff --git a/api/module-lib-current.txt b/api/module-lib-current.txt
index d11801b359c8..b42594740433 100644
--- a/api/module-lib-current.txt
+++ b/api/module-lib-current.txt
@@ -87,8 +87,8 @@ package android.timezone {
method @NonNull public java.util.List<android.timezone.CountryTimeZones.TimeZoneMapping> getEffectiveTimeZoneMappingsAt(long);
method public boolean hasUtcZone(long);
method public boolean isDefaultTimeZoneBoosted();
- method public boolean isForCountryCode(@NonNull String);
method @Nullable public android.timezone.CountryTimeZones.OffsetResult lookupByOffsetWithBias(int, @Nullable Boolean, @Nullable Integer, long, @Nullable android.icu.util.TimeZone);
+ method public boolean matchesCountryCode(@NonNull String);
}
public static final class CountryTimeZones.OffsetResult {
@@ -98,7 +98,7 @@ package android.timezone {
}
public static final class CountryTimeZones.TimeZoneMapping {
- method @Nullable public android.icu.util.TimeZone getTimeZone();
+ method @NonNull public android.icu.util.TimeZone getTimeZone();
method @NonNull public String getTimeZoneId();
}
diff --git a/api/system-current.txt b/api/system-current.txt
index 3cc5dd2fe67b..cdcef536892b 100755
--- a/api/system-current.txt
+++ b/api/system-current.txt
@@ -222,7 +222,6 @@ package android {
field public static final String SUSPEND_APPS = "android.permission.SUSPEND_APPS";
field public static final String SYSTEM_CAMERA = "android.permission.SYSTEM_CAMERA";
field public static final String TETHER_PRIVILEGED = "android.permission.TETHER_PRIVILEGED";
- field public static final String TUNER_RESOURCE_ACCESS = "android.permission.TUNER_RESOURCE_ACCESS";
field public static final String TV_INPUT_HARDWARE = "android.permission.TV_INPUT_HARDWARE";
field public static final String TV_VIRTUAL_REMOTE_CONTROLLER = "android.permission.TV_VIRTUAL_REMOTE_CONTROLLER";
field public static final String UNLIMITED_SHORTCUTS_API_CALLS = "android.permission.UNLIMITED_SHORTCUTS_API_CALLS";
@@ -2304,6 +2303,7 @@ package android.content.pm {
field public static final int PROTECTION_FLAG_DOCUMENTER = 262144; // 0x40000
field public static final int PROTECTION_FLAG_INCIDENT_REPORT_APPROVER = 1048576; // 0x100000
field public static final int PROTECTION_FLAG_OEM = 16384; // 0x4000
+ field public static final int PROTECTION_FLAG_RETAIL_DEMO = 16777216; // 0x1000000
field public static final int PROTECTION_FLAG_SYSTEM_TEXT_CLASSIFIER = 65536; // 0x10000
field public static final int PROTECTION_FLAG_TELEPHONY = 4194304; // 0x400000
field public static final int PROTECTION_FLAG_WELLBEING = 131072; // 0x20000
@@ -2453,8 +2453,8 @@ package android.hardware {
package android.hardware.biometrics {
public static interface BiometricManager.Authenticators {
- field public static final int BIOMETRIC_CONVENIENCE = 4095; // 0xfff
- field public static final int EMPTY_SET = 0; // 0x0
+ field @RequiresPermission(android.Manifest.permission.WRITE_DEVICE_CONFIG) public static final int BIOMETRIC_CONVENIENCE = 4095; // 0xfff
+ field @RequiresPermission(android.Manifest.permission.WRITE_DEVICE_CONFIG) public static final int EMPTY_SET = 0; // 0x0
}
}
@@ -4686,7 +4686,6 @@ package android.media.tv {
method @RequiresPermission(android.Manifest.permission.MODIFY_PARENTAL_CONTROLS) public void addBlockedRating(@NonNull android.media.tv.TvContentRating);
method @RequiresPermission(android.Manifest.permission.CAPTURE_TV_INPUT) public boolean captureFrame(String, android.view.Surface, android.media.tv.TvStreamConfig);
method @RequiresPermission(android.Manifest.permission.CAPTURE_TV_INPUT) public java.util.List<android.media.tv.TvStreamConfig> getAvailableTvStreamConfigList(String);
- method @RequiresPermission(android.Manifest.permission.TUNER_RESOURCE_ACCESS) public int getClientPid(@NonNull String);
method @NonNull @RequiresPermission("android.permission.DVB_DEVICE") public java.util.List<android.media.tv.DvbDeviceInfo> getDvbDeviceList();
method @RequiresPermission(android.Manifest.permission.TV_INPUT_HARDWARE) public java.util.List<android.media.tv.TvInputHardwareInfo> getHardwareList();
method @RequiresPermission(android.Manifest.permission.READ_CONTENT_RATING_SYSTEMS) public java.util.List<android.media.tv.TvContentRatingSystemInfo> getTvContentRatingSystemList();
@@ -4698,7 +4697,6 @@ package android.media.tv {
method @RequiresPermission(android.Manifest.permission.TV_INPUT_HARDWARE) public void releaseTvInputHardware(int, android.media.tv.TvInputManager.Hardware);
method @RequiresPermission(android.Manifest.permission.MODIFY_PARENTAL_CONTROLS) public void removeBlockedRating(@NonNull android.media.tv.TvContentRating);
method @RequiresPermission(android.Manifest.permission.MODIFY_PARENTAL_CONTROLS) public void setParentalControlsEnabled(boolean);
- field public static final int UNKNOWN_CLIENT_PID = -1; // 0xffffffff
}
public static final class TvInputManager.Hardware {
@@ -5378,7 +5376,7 @@ package android.media.tv.tuner.frontend {
public static class Atsc3FrontendSettings.Builder extends android.media.tv.tuner.frontend.FrontendSettings.Builder<android.media.tv.tuner.frontend.Atsc3FrontendSettings.Builder> {
method @NonNull public android.media.tv.tuner.frontend.Atsc3FrontendSettings build();
method @NonNull public android.media.tv.tuner.frontend.Atsc3FrontendSettings.Builder setBandwidth(int);
- method @NonNull public android.media.tv.tuner.frontend.Atsc3FrontendSettings.Builder setDemodOutputFormat(byte);
+ method @NonNull public android.media.tv.tuner.frontend.Atsc3FrontendSettings.Builder setDemodOutputFormat(int);
method @NonNull public android.media.tv.tuner.frontend.Atsc3FrontendSettings.Builder setPlpSettings(@NonNull android.media.tv.tuner.frontend.Atsc3PlpSettings[]);
}
@@ -5432,7 +5430,7 @@ package android.media.tv.tuner.frontend {
public class DvbcFrontendSettings extends android.media.tv.tuner.frontend.FrontendSettings {
method @NonNull @RequiresPermission(android.Manifest.permission.ACCESS_TV_TUNER) public static android.media.tv.tuner.frontend.DvbcFrontendSettings.Builder builder(@NonNull android.content.Context);
- method public byte getAnnex();
+ method public int getAnnex();
method public long getFec();
method public int getModulation();
method public int getOuterFec();
@@ -5460,7 +5458,7 @@ package android.media.tv.tuner.frontend {
public static class DvbcFrontendSettings.Builder extends android.media.tv.tuner.frontend.FrontendSettings.Builder<android.media.tv.tuner.frontend.DvbcFrontendSettings.Builder> {
method @NonNull public android.media.tv.tuner.frontend.DvbcFrontendSettings build();
- method @NonNull public android.media.tv.tuner.frontend.DvbcFrontendSettings.Builder setAnnex(byte);
+ method @NonNull public android.media.tv.tuner.frontend.DvbcFrontendSettings.Builder setAnnex(int);
method @NonNull public android.media.tv.tuner.frontend.DvbcFrontendSettings.Builder setFec(long);
method @NonNull public android.media.tv.tuner.frontend.DvbcFrontendSettings.Builder setModulation(int);
method @NonNull public android.media.tv.tuner.frontend.DvbcFrontendSettings.Builder setOuterFec(int);
@@ -7510,20 +7508,16 @@ package android.net.wifi {
@Deprecated public class WifiConfiguration implements android.os.Parcelable {
method @Deprecated public int getAuthType();
- method @Deprecated @NonNull public android.net.IpConfiguration.IpAssignment getIpAssignment();
method @Deprecated @NonNull public android.net.IpConfiguration getIpConfiguration();
method @Deprecated @NonNull public android.net.wifi.WifiConfiguration.NetworkSelectionStatus getNetworkSelectionStatus();
method @Deprecated @NonNull public String getPrintableSsid();
- method @Deprecated @NonNull public android.net.IpConfiguration.ProxySettings getProxySettings();
method @Deprecated public int getRecentFailureReason();
- method @Deprecated @Nullable public android.net.StaticIpConfiguration getStaticIpConfiguration();
method @Deprecated public boolean hasNoInternetAccess();
method @Deprecated public boolean isEphemeral();
method @Deprecated public static boolean isMetered(@Nullable android.net.wifi.WifiConfiguration, @Nullable android.net.wifi.WifiInfo);
method @Deprecated public boolean isNoInternetAccessExpected();
method @Deprecated public void setIpConfiguration(@Nullable android.net.IpConfiguration);
method @Deprecated public void setNetworkSelectionStatus(@NonNull android.net.wifi.WifiConfiguration.NetworkSelectionStatus);
- method @Deprecated public void setProxy(@NonNull android.net.IpConfiguration.ProxySettings, @NonNull android.net.ProxyInfo);
field @Deprecated public static final int AP_BAND_2GHZ = 0; // 0x0
field @Deprecated public static final int AP_BAND_5GHZ = 1; // 0x1
field @Deprecated public static final int AP_BAND_ANY = -1; // 0xffffffff
@@ -7565,11 +7559,11 @@ package android.net.wifi {
@Deprecated public static class WifiConfiguration.NetworkSelectionStatus {
method @Deprecated public int getDisableReasonCounter(int);
method @Deprecated public long getDisableTime();
- method @Deprecated public boolean getHasEverConnected();
method @Deprecated @Nullable public static String getNetworkDisableReasonString(int);
method @Deprecated public int getNetworkSelectionDisableReason();
method @Deprecated public int getNetworkSelectionStatus();
method @Deprecated @NonNull public String getNetworkStatusString();
+ method @Deprecated public boolean hasEverConnected();
method @Deprecated public boolean isNetworkEnabled();
method @Deprecated public boolean isNetworkPermanentlyDisabled();
field @Deprecated public static final int DISABLED_ASSOCIATION_REJECTION = 1; // 0x1
@@ -7579,10 +7573,10 @@ package android.net.wifi {
field @Deprecated public static final int DISABLED_BY_WIFI_MANAGER = 7; // 0x7
field @Deprecated public static final int DISABLED_BY_WRONG_PASSWORD = 8; // 0x8
field @Deprecated public static final int DISABLED_DHCP_FAILURE = 3; // 0x3
+ field @Deprecated public static final int DISABLED_NONE = 0; // 0x0
field @Deprecated public static final int DISABLED_NO_INTERNET_PERMANENT = 6; // 0x6
field @Deprecated public static final int DISABLED_NO_INTERNET_TEMPORARY = 4; // 0x4
field @Deprecated public static final int NETWORK_SELECTION_DISABLED_MAX = 10; // 0xa
- field @Deprecated public static final int NETWORK_SELECTION_ENABLE = 0; // 0x0
field @Deprecated public static final int NETWORK_SELECTION_ENABLED = 0; // 0x0
field @Deprecated public static final int NETWORK_SELECTION_PERMANENTLY_DISABLED = 2; // 0x2
field @Deprecated public static final int NETWORK_SELECTION_TEMPORARY_DISABLED = 1; // 0x1
@@ -7779,7 +7773,7 @@ package android.net.wifi {
}
public static interface WifiManager.ScoreChangeCallback {
- method public void onStatusChange(int, boolean);
+ method public void onScoreChange(int, @NonNull android.net.NetworkScore);
method public void onTriggerUpdateOfWifiUsabilityStats(int);
}
@@ -8203,6 +8197,7 @@ package android.net.wifi.wificond {
}
public final class NativeScanResult implements android.os.Parcelable {
+ ctor public NativeScanResult();
method public int describeContents();
method @NonNull public byte[] getBssid();
method @NonNull public java.util.BitSet getCapabilities();
@@ -8218,6 +8213,7 @@ package android.net.wifi.wificond {
}
public final class NativeWifiClient implements android.os.Parcelable {
+ ctor public NativeWifiClient(@NonNull byte[]);
method public int describeContents();
method public void writeToParcel(@NonNull android.os.Parcel, int);
field @NonNull public static final android.os.Parcelable.Creator<android.net.wifi.wificond.NativeWifiClient> CREATOR;
@@ -8255,6 +8251,7 @@ package android.net.wifi.wificond {
}
public final class RadioChainInfo implements android.os.Parcelable {
+ ctor public RadioChainInfo(int, int);
method public int describeContents();
method public int getChainId();
method public int getLevelDbm();
@@ -8265,7 +8262,7 @@ package android.net.wifi.wificond {
public class WifiCondManager {
method public void abortScan(@NonNull String);
method public void enableVerboseLogging(boolean);
- method @NonNull public int[] getChannelsMhzForBand(int);
+ method @NonNull public java.util.List<java.lang.Integer> getChannelsMhzForBand(int);
method @Nullable public android.net.wifi.wificond.DeviceWiphyCapabilities getDeviceWiphyCapabilities(@NonNull String);
method @NonNull public java.util.List<android.net.wifi.wificond.NativeScanResult> getScanResults(@NonNull String, int);
method @Nullable public android.net.wifi.wificond.WifiCondManager.TxPacketCounters getTxPacketCounters(@NonNull String);
@@ -8910,13 +8907,13 @@ package android.os {
public class UpdateEngine {
ctor public UpdateEngine();
- method @NonNull public android.os.UpdateEngine.AllocateSpaceResult allocateSpace(@NonNull String, @NonNull String[]);
+ method @NonNull @WorkerThread public android.os.UpdateEngine.AllocateSpaceResult allocateSpace(@NonNull String, @NonNull String[]);
method public void applyPayload(String, long, long, String[]);
method public void applyPayload(@NonNull android.content.res.AssetFileDescriptor, @NonNull String[]);
method public boolean bind(android.os.UpdateEngineCallback, android.os.Handler);
method public boolean bind(android.os.UpdateEngineCallback);
method public void cancel();
- method public int cleanupAppliedPayload();
+ method @WorkerThread public int cleanupAppliedPayload();
method public void resetStatus();
method public void resume();
method public void suspend();
@@ -8925,8 +8922,8 @@ package android.os {
}
public static final class UpdateEngine.AllocateSpaceResult {
- method public int errorCode();
- method public long freeSpaceRequired();
+ method public int getErrorCode();
+ method public long getFreeSpaceRequired();
}
public static final class UpdateEngine.ErrorCodeConstants {
@@ -9223,7 +9220,7 @@ package android.permission {
}
public final class PermissionManager {
- method @IntRange(from=0) @RequiresPermission(android.Manifest.permission.ADJUST_RUNTIME_PERMISSIONS_POLICY) public int getRuntimePermissionsVersion();
+ method @IntRange(from=0) @RequiresPermission(anyOf={android.Manifest.permission.ADJUST_RUNTIME_PERMISSIONS_POLICY, "android.permission.UPGRADE_RUNTIME_PERMISSIONS"}) public int getRuntimePermissionsVersion();
method @NonNull public java.util.List<android.permission.PermissionManager.SplitPermissionInfo> getSplitPermissions();
method @RequiresPermission(android.Manifest.permission.GRANT_RUNTIME_PERMISSIONS_TO_TELEPHONY_DEFAULTS) public void grantDefaultPermissionsToEnabledCarrierApps(@NonNull String[], @NonNull android.os.UserHandle, @NonNull java.util.concurrent.Executor, @NonNull java.util.function.Consumer<java.lang.Boolean>);
method @RequiresPermission(android.Manifest.permission.GRANT_RUNTIME_PERMISSIONS_TO_TELEPHONY_DEFAULTS) public void grantDefaultPermissionsToEnabledImsServices(@NonNull String[], @NonNull android.os.UserHandle, @NonNull java.util.concurrent.Executor, @NonNull java.util.function.Consumer<java.lang.Boolean>);
@@ -9231,7 +9228,7 @@ package android.permission {
method @RequiresPermission(android.Manifest.permission.GRANT_RUNTIME_PERMISSIONS_TO_TELEPHONY_DEFAULTS) public void grantDefaultPermissionsToLuiApp(@NonNull String, @NonNull android.os.UserHandle, @NonNull java.util.concurrent.Executor, @NonNull java.util.function.Consumer<java.lang.Boolean>);
method @RequiresPermission(android.Manifest.permission.GRANT_RUNTIME_PERMISSIONS_TO_TELEPHONY_DEFAULTS) public void revokeDefaultPermissionsFromDisabledTelephonyDataServices(@NonNull String[], @NonNull android.os.UserHandle, @NonNull java.util.concurrent.Executor, @NonNull java.util.function.Consumer<java.lang.Boolean>);
method @RequiresPermission(android.Manifest.permission.GRANT_RUNTIME_PERMISSIONS_TO_TELEPHONY_DEFAULTS) public void revokeDefaultPermissionsFromLuiApps(@NonNull String[], @NonNull android.os.UserHandle, @NonNull java.util.concurrent.Executor, @NonNull java.util.function.Consumer<java.lang.Boolean>);
- method @RequiresPermission(android.Manifest.permission.ADJUST_RUNTIME_PERMISSIONS_POLICY) public void setRuntimePermissionsVersion(@IntRange(from=0) int);
+ method @RequiresPermission(anyOf={android.Manifest.permission.ADJUST_RUNTIME_PERMISSIONS_POLICY, "android.permission.UPGRADE_RUNTIME_PERMISSIONS"}) public void setRuntimePermissionsVersion(@IntRange(from=0) int);
method @RequiresPermission(android.Manifest.permission.MANAGE_ONE_TIME_PERMISSION_SESSIONS) public void startOneTimePermissionSession(@NonNull String, long, int, int);
method @RequiresPermission(android.Manifest.permission.MANAGE_ONE_TIME_PERMISSION_SESSIONS) public void stopOneTimePermissionSession(@NonNull String);
}
diff --git a/api/test-current.txt b/api/test-current.txt
index fda3ab4084a8..9472ce2569d3 100644
--- a/api/test-current.txt
+++ b/api/test-current.txt
@@ -118,8 +118,8 @@ package android.app {
method @RequiresPermission(android.Manifest.permission.MANAGE_ACTIVITY_STACKS) public void resizePinnedStack(int, android.graphics.Rect, boolean);
method @RequiresPermission(android.Manifest.permission.MANAGE_ACTIVITY_STACKS) public void resizeTask(int, android.graphics.Rect);
method @RequiresPermission(android.Manifest.permission.MANAGE_ACTIVITY_STACKS) public void setDisplayToSingleTaskInstance(int);
- method @RequiresPermission(android.Manifest.permission.MANAGE_ACTIVITY_STACKS) public void setTaskWindowingMode(int, int, boolean) throws java.lang.SecurityException;
- method @RequiresPermission(android.Manifest.permission.MANAGE_ACTIVITY_STACKS) public void setTaskWindowingModeSplitScreenPrimary(int, int, boolean, boolean, android.graphics.Rect, boolean) throws java.lang.SecurityException;
+ method @RequiresPermission(android.Manifest.permission.MANAGE_ACTIVITY_STACKS) public boolean setTaskWindowingMode(int, int, boolean) throws java.lang.SecurityException;
+ method @RequiresPermission(android.Manifest.permission.MANAGE_ACTIVITY_STACKS) public boolean setTaskWindowingModeSplitScreenPrimary(int, int, boolean, boolean, android.graphics.Rect, boolean) throws java.lang.SecurityException;
method @RequiresPermission(android.Manifest.permission.MANAGE_ACTIVITY_STACKS) public void startSystemLockTaskMode(int);
method @RequiresPermission(android.Manifest.permission.MANAGE_ACTIVITY_STACKS) public void stopSystemLockTaskMode();
method public static boolean supportsMultiWindow(android.content.Context);
@@ -767,6 +767,7 @@ package android.content {
method public void setAutofillOptions(@Nullable android.content.AutofillOptions);
method public void setContentCaptureOptions(@Nullable android.content.ContentCaptureOptions);
method @RequiresPermission("android.permission.INTERACT_ACROSS_USERS") public void startActivityAsUser(@NonNull @RequiresPermission android.content.Intent, @NonNull android.os.UserHandle);
+ field public static final String APP_INTEGRITY_SERVICE = "app_integrity";
field public static final String BUGREPORT_SERVICE = "bugreport";
field public static final String CONTENT_CAPTURE_MANAGER_SERVICE = "content_capture";
field public static final String DEVICE_IDLE_CONTROLLER = "deviceidle";
@@ -792,6 +793,60 @@ package android.content {
}
+package android.content.integrity {
+
+ public class AppIntegrityManager {
+ method @NonNull public android.content.integrity.RuleSet getCurrentRuleSet();
+ method @NonNull public String getCurrentRuleSetProvider();
+ method @NonNull public String getCurrentRuleSetVersion();
+ method public void updateRuleSet(@NonNull android.content.integrity.RuleSet, @NonNull android.content.IntentSender);
+ field public static final String EXTRA_STATUS = "android.content.integrity.extra.STATUS";
+ field public static final int STATUS_FAILURE = 1; // 0x1
+ field public static final int STATUS_SUCCESS = 0; // 0x0
+ }
+
+ public abstract class IntegrityFormula {
+ method @NonNull public static android.content.integrity.IntegrityFormula all(@NonNull android.content.integrity.IntegrityFormula...);
+ method @NonNull public static android.content.integrity.IntegrityFormula any(@NonNull android.content.integrity.IntegrityFormula...);
+ method @NonNull public android.content.integrity.IntegrityFormula equalTo(@NonNull String);
+ method @NonNull public android.content.integrity.IntegrityFormula equalTo(boolean);
+ method @NonNull public android.content.integrity.IntegrityFormula equalTo(long);
+ method @NonNull public android.content.integrity.IntegrityFormula greaterThan(long);
+ method @NonNull public android.content.integrity.IntegrityFormula greaterThanOrEquals(long);
+ method @NonNull public static android.content.integrity.IntegrityFormula not(@NonNull android.content.integrity.IntegrityFormula);
+ field @NonNull public static final android.content.integrity.IntegrityFormula APP_CERTIFICATE;
+ field @NonNull public static final android.content.integrity.IntegrityFormula INSTALLER_CERTIFICATE;
+ field @NonNull public static final android.content.integrity.IntegrityFormula INSTALLER_NAME;
+ field @NonNull public static final android.content.integrity.IntegrityFormula PACKAGE_NAME;
+ field @NonNull public static final android.content.integrity.IntegrityFormula PRE_INSTALLED;
+ field @NonNull public static final android.content.integrity.IntegrityFormula VERSION_CODE;
+ }
+
+ public final class Rule implements android.os.Parcelable {
+ ctor public Rule(@NonNull android.content.integrity.IntegrityFormula, int);
+ method public int describeContents();
+ method public int getEffect();
+ method @NonNull public android.content.integrity.IntegrityFormula getFormula();
+ method public void writeToParcel(@NonNull android.os.Parcel, int);
+ field @NonNull public static final android.os.Parcelable.Creator<android.content.integrity.Rule> CREATOR;
+ field public static final int DENY = 0; // 0x0
+ field public static final int FORCE_ALLOW = 1; // 0x1
+ }
+
+ public class RuleSet {
+ method @NonNull public java.util.List<android.content.integrity.Rule> getRules();
+ method @NonNull public String getVersion();
+ }
+
+ public static class RuleSet.Builder {
+ ctor public RuleSet.Builder();
+ method @NonNull public android.content.integrity.RuleSet.Builder addRules(@NonNull java.util.List<android.content.integrity.Rule>);
+ method @NonNull public android.content.integrity.RuleSet build();
+ method @NonNull public android.content.integrity.RuleSet.Builder setVersion(@NonNull String);
+ }
+
+}
+
package android.content.pm {
public class ActivityInfo extends android.content.pm.ComponentInfo implements android.os.Parcelable {
@@ -878,6 +933,7 @@ package android.content.pm {
field public static final int PROTECTION_FLAG_DOCUMENTER = 262144; // 0x40000
field public static final int PROTECTION_FLAG_INCIDENT_REPORT_APPROVER = 1048576; // 0x100000
field public static final int PROTECTION_FLAG_OEM = 16384; // 0x4000
+ field public static final int PROTECTION_FLAG_RETAIL_DEMO = 16777216; // 0x1000000
field public static final int PROTECTION_FLAG_SYSTEM_TEXT_CLASSIFIER = 65536; // 0x10000
field public static final int PROTECTION_FLAG_TELEPHONY = 4194304; // 0x400000
field public static final int PROTECTION_FLAG_VENDOR_PRIVILEGED = 32768; // 0x8000
@@ -2713,9 +2769,9 @@ package android.permission {
}
public final class PermissionManager {
- method @IntRange(from=0) @RequiresPermission("android.permission.ADJUST_RUNTIME_PERMISSIONS_POLICY") public int getRuntimePermissionsVersion();
+ method @IntRange(from=0) @RequiresPermission(anyOf={"android.permission.ADJUST_RUNTIME_PERMISSIONS_POLICY", "android.permission.UPGRADE_RUNTIME_PERMISSIONS"}) public int getRuntimePermissionsVersion();
method @NonNull public java.util.List<android.permission.PermissionManager.SplitPermissionInfo> getSplitPermissions();
- method @RequiresPermission("android.permission.ADJUST_RUNTIME_PERMISSIONS_POLICY") public void setRuntimePermissionsVersion(@IntRange(from=0) int);
+ method @RequiresPermission(anyOf={"android.permission.ADJUST_RUNTIME_PERMISSIONS_POLICY", "android.permission.UPGRADE_RUNTIME_PERMISSIONS"}) public void setRuntimePermissionsVersion(@IntRange(from=0) int);
}
public static final class PermissionManager.SplitPermissionInfo {
@@ -4924,6 +4980,11 @@ package android.view {
method public abstract String asyncImpl() default "";
}
+ public class SurfaceControlViewHost {
+ method public void addView(@NonNull android.view.View, android.view.WindowManager.LayoutParams);
+ method public void relayout(android.view.WindowManager.LayoutParams);
+ }
+
@UiThread public class View implements android.view.accessibility.AccessibilityEventSource android.graphics.drawable.Drawable.Callback android.view.KeyEvent.Callback {
method public android.view.View getTooltipView();
method public boolean isAutofilled();
diff --git a/cmds/bootanimation/BootAnimation.cpp b/cmds/bootanimation/BootAnimation.cpp
index 520366f518ab..a1278f358380 100644
--- a/cmds/bootanimation/BootAnimation.cpp
+++ b/cmds/bootanimation/BootAnimation.cpp
@@ -42,10 +42,10 @@
#include <android-base/properties.h>
+#include <ui/DisplayConfig.h>
#include <ui/PixelFormat.h>
#include <ui/Rect.h>
#include <ui/Region.h>
-#include <ui/DisplayInfo.h>
#include <gui/ISurfaceComposer.h>
#include <gui/Surface.h>
@@ -283,16 +283,19 @@ status_t BootAnimation::readyToRun() {
mDisplayToken = SurfaceComposerClient::getInternalDisplayToken();
if (mDisplayToken == nullptr)
- return -1;
+ return NAME_NOT_FOUND;
- DisplayInfo dinfo;
- status_t status = SurfaceComposerClient::getDisplayInfo(mDisplayToken, &dinfo);
- if (status)
- return -1;
+ DisplayConfig displayConfig;
+ const status_t error =
+ SurfaceComposerClient::getActiveDisplayConfig(mDisplayToken, &displayConfig);
+ if (error != NO_ERROR)
+ return error;
+
+ const ui::Size& resolution = displayConfig.resolution;
// create the native surface
sp<SurfaceControl> control = session()->createSurface(String8("BootAnimation"),
- dinfo.w, dinfo.h, PIXEL_FORMAT_RGB_565);
+ resolution.getWidth(), resolution.getHeight(), PIXEL_FORMAT_RGB_565);
SurfaceComposerClient::Transaction t;
diff --git a/cmds/statsd/Android.bp b/cmds/statsd/Android.bp
index 2237bf2b2acb..ebed4ee2b82d 100644
--- a/cmds/statsd/Android.bp
+++ b/cmds/statsd/Android.bp
@@ -110,7 +110,7 @@ cc_defaults {
],
cflags: [
- // "-DNEW_ENCODING_SCHEME",
+ "-DNEW_ENCODING_SCHEME",
],
local_include_dirs: [
diff --git a/cmds/statsd/src/atom_field_options.proto b/cmds/statsd/src/atom_field_options.proto
index 946c55087005..9c875ba7502d 100644
--- a/cmds/statsd/src/atom_field_options.proto
+++ b/cmds/statsd/src/atom_field_options.proto
@@ -24,46 +24,75 @@ option java_outer_classname = "AtomFieldOptions";
import "google/protobuf/descriptor.proto";
enum StateField {
- // Default value for fields that are not primary or exclusive state.
+ // Default value for fields that are not a primary or exclusive state field.
STATE_FIELD_UNSET = 0;
// Fields that represent the key that the state belongs to.
- PRIMARY = 1;
+ // Used on simple proto fields. Do not use on attribution chains.
+ PRIMARY_FIELD = 1;
// The field that represents the state. It's an exclusive state.
- EXCLUSIVE = 2;
-
+ EXCLUSIVE_STATE = 2;
+ // Used on an attribution chain field to indicate that the first uid is the
+ // primary field.
PRIMARY_FIELD_FIRST_UID = 3;
}
-// Used to annotate an atom that reprsents a state change. A state change atom must have exactly ONE
-// exclusive state field, and any number of primary key fields.
-// For example,
-// message UidProcessStateChanged {
-// optional int32 uid = 1 [(state_field_option).option = PRIMARY];
-// optional android.app.ProcessStateEnum state = 2 [(state_field_option).option = EXCLUSIVE];
+// Used to annotate an atom that represents a state change. A state change atom must have exactly
+// ONE exclusive state field, and any number of primary key fields. For example, message
+// UidProcessStateChanged {
+// optional int32 uid = 1 [(state_field_option).option = PRIMARY_FIELD];
+// optional android.app.ProcessStateEnum state = 2 [(state_field_option).option =
+// EXCLUSIVE_STATE];
// }
-// Each of this UidProcessStateChanged atom represents a state change for a specific uid.
+// Each UidProcessStateChanged atom event represents a state change for a specific uid.
// A new state automatically overrides the previous state.
//
-// If the atom has 2 or more primary fields, it means the combination of the primary fields are
-// the primary key.
+// If the atom has 2 or more primary fields, it means the combination of the
+// primary fields are the primary key.
// For example:
// message ThreadStateChanged {
-// optional int32 pid = 1 [(state_field_option).option = PRIMARY];
-// optional int32 tid = 2 [(state_field_option).option = PRIMARY];
-// optional int32 state = 3 [(state_field_option).option = EXCLUSIVE];
+// optional int32 pid = 1 [(state_field_option).option = PRIMARY_FIELD];
+// optional int32 tid = 2 [(state_field_option).option = PRIMARY_FIELD];
+// optional int32 state = 3 [(state_field_option).option = EXCLUSIVE_STATE];
// }
//
// Sometimes, there is no primary key field, when the state is GLOBAL.
// For example,
-//
// message ScreenStateChanged {
-// optional android.view.DisplayStateEnum state = 1 [(state_field_option).option = EXCLUSIVE];
+// optional android.view.DisplayStateEnum state = 1 [(state_field_option).option =
+// EXCLUSIVE_STATE];
// }
//
-// Only fields of primary types can be annotated. AttributionNode cannot be primary keys (and they
-// usually are not).
+// For state atoms with attribution chain, sometimes the primary key is the first uid in the chain.
+// For example:
+// message AudioStateChanged {
+// repeated AttributionNode attribution_node = 1
+// [(stateFieldOption).option = PRIMARY_KEY_FIRST_UID];
+//
+// enum State {
+// OFF = 0;
+// ON = 1;
+// // RESET indicates all audio stopped. Used when it (re)starts (e.g. after it crashes).
+// RESET = 2;
+// }
+// optional State state = 2 [(stateFieldOption).option = EXCLUSIVE_STATE];
+// }
message StateAtomFieldOption {
optional StateField option = 1 [default = STATE_FIELD_UNSET];
+
+ // Note: We cannot annotate directly on the enums because many enums are imported from other
+ // proto files in the platform. proto-lite cc library does not support annotations unfortunately
+
+ // Knowing the default state value allows state trackers to remove entries that become the
+ // default state. If there is no default value specified, the default value is unknown, and all
+ // states will be tracked in memory.
+ optional int32 default_state_value = 2;
+
+ // A reset state signals all states go to default value. For example, BLE reset means all active
+ // BLE scans are to be turned off.
+ optional int32 reset_state_value = 3;
+
+ // If the state change needs to count nesting.
+ optional bool nested = 4 [default = true];
}
// Used to generate StatsLog.write APIs.
diff --git a/cmds/statsd/src/atoms.proto b/cmds/statsd/src/atoms.proto
index 12058cae1a2a..fccefdc07363 100644
--- a/cmds/statsd/src/atoms.proto
+++ b/cmds/statsd/src/atoms.proto
@@ -378,11 +378,11 @@ message Atom {
BootTimeEventUtcTime boot_time_event_utc_time_reported = 241;
BootTimeEventErrorCode boot_time_event_error_code_reported = 242 [(module) = "framework"];
UserspaceRebootReported userspace_reboot_reported = 243;
- NotificationReported notification_reported = 244;
+ NotificationReported notification_reported = 244 [(module) = "framework"];
NotificationPanelReported notification_panel_reported = 245;
NotificationChannelModified notification_panel_modified = 246;
- IntegrityCheckResultReported integrity_check_result_reported = 247;
- IntegrityRulesPushed integrity_rules_pushed = 248;
+ IntegrityCheckResultReported integrity_check_result_reported = 247 [(module) = "framework"];
+ IntegrityRulesPushed integrity_rules_pushed = 248 [(module) = "framework"];
CellBroadcastMessageReported cb_message_reported =
249 [(module) = "cellbroadcast"];
CellBroadcastMessageError cb_message_error =
@@ -466,7 +466,7 @@ message Atom {
DangerousPermissionStateSampled dangerous_permission_state_sampled =
10067 [(module) = "framework"];
GraphicsStats graphics_stats = 10068;
- RuntimeAppOpsAccess runtime_app_ops_access = 10069;
+ RuntimeAppOpAccess runtime_app_op_access = 10069 [(module) = "framework"];
IonHeapSize ion_heap_size = 10070 [(module) = "framework"];
}
@@ -563,7 +563,8 @@ message ThermalThrottlingStateChanged {
*/
message ScreenStateChanged {
// New screen state, from frameworks/base/core/proto/android/view/enums.proto.
- optional android.view.DisplayStateEnum state = 1 [(state_field_option).option = EXCLUSIVE];
+ optional android.view.DisplayStateEnum state = 1
+ [(state_field_option).option = EXCLUSIVE_STATE, (state_field_option).nested = false];
}
/**
@@ -574,10 +575,11 @@ message ScreenStateChanged {
* frameworks/base/services/core/java/com/android/server/am/BatteryStatsService.java
*/
message UidProcessStateChanged {
- optional int32 uid = 1 [(state_field_option).option = PRIMARY, (is_uid) = true];
+ optional int32 uid = 1 [(state_field_option).option = PRIMARY_FIELD, (is_uid) = true];
// The state, from frameworks/base/core/proto/android/app/enums.proto.
- optional android.app.ProcessStateEnum state = 2 [(state_field_option).option = EXCLUSIVE];
+ optional android.app.ProcessStateEnum state = 2
+ [(state_field_option).option = EXCLUSIVE_STATE, (state_field_option).nested = false];
}
/**
@@ -609,7 +611,7 @@ message ActivityManagerSleepStateChanged {
ASLEEP = 1;
AWAKE = 2;
}
- optional State state = 1 [(state_field_option).option = EXCLUSIVE];
+ optional State state = 1 [(state_field_option).option = EXCLUSIVE_STATE];
}
/**
@@ -628,7 +630,7 @@ message MemoryFactorStateChanged {
CRITICAL = 4; // critical memory.
}
- optional State factor = 1 [(state_field_option).option = EXCLUSIVE];
+ optional State factor = 1 [(state_field_option).option = EXCLUSIVE_STATE];
}
/**
@@ -790,7 +792,8 @@ message ProcessLifeCycleStateChanged {
* packages/apps/Bluetooth/src/com/android/bluetooth/gatt/AppScanStats.java
*/
message BleScanStateChanged {
- repeated AttributionNode attribution_node = 1;
+ repeated AttributionNode attribution_node = 1
+ [(state_field_option).option = PRIMARY_FIELD_FIRST_UID];
enum State {
OFF = 0;
@@ -798,14 +801,19 @@ message BleScanStateChanged {
// RESET indicates all ble stopped. Used when it (re)starts (e.g. after it crashes).
RESET = 2;
}
- optional State state = 2;
+ optional State state = 2 [
+ (state_field_option).option = EXCLUSIVE_STATE,
+ (state_field_option).default_state_value = 0 /* State.OFF */,
+ (state_field_option).reset_state_value = 2 /* State.RESET */,
+ (state_field_option).nested = true
+ ];
// Does the scan have a filter.
- optional bool is_filtered = 3;
+ optional bool is_filtered = 3 [(state_field_option).option = PRIMARY_FIELD];
// Whether the scan is a CALLBACK_TYPE_FIRST_MATCH scan. Called 'background' scan internally.
- optional bool is_first_match = 4;
+ optional bool is_first_match = 4 [(state_field_option).option = PRIMARY_FIELD];
// Whether the scan set to piggy-back off the results of other scans (SCAN_MODE_OPPORTUNISTIC).
- optional bool is_opportunistic = 5;
+ optional bool is_opportunistic = 5 [(state_field_option).option = PRIMARY_FIELD];
}
/**
@@ -1029,11 +1037,11 @@ message WakelockStateChanged {
// The type (level) of the wakelock; e.g. a partial wakelock or a full wakelock.
// From frameworks/base/core/proto/android/os/enums.proto.
- optional android.os.WakeLockLevelEnum type = 2 [(state_field_option).option = PRIMARY];
+ optional android.os.WakeLockLevelEnum type = 2 [(state_field_option).option = PRIMARY_FIELD];
;
// The wakelock tag (Called tag in the Java API, sometimes name elsewhere).
- optional string tag = 3 [(state_field_option).option = PRIMARY];
+ optional string tag = 3 [(state_field_option).option = PRIMARY_FIELD];
enum State {
RELEASE = 0;
@@ -1041,7 +1049,11 @@ message WakelockStateChanged {
CHANGE_RELEASE = 2;
CHANGE_ACQUIRE = 3;
}
- optional State state = 4 [(state_field_option).option = EXCLUSIVE];
+ optional State state = 4 [
+ (state_field_option).option = EXCLUSIVE_STATE,
+ (state_field_option).default_state_value = 0,
+ (state_field_option).nested = true
+ ];
}
/**
@@ -3225,9 +3237,9 @@ message PictureInPictureStateChanged {
* services/core/java/com/android/server/wm/Session.java
*/
message OverlayStateChanged {
- optional int32 uid = 1 [(state_field_option).option = PRIMARY, (is_uid) = true];
+ optional int32 uid = 1 [(state_field_option).option = PRIMARY_FIELD, (is_uid) = true];
- optional string package_name = 2 [(state_field_option).option = PRIMARY];
+ optional string package_name = 2 [(state_field_option).option = PRIMARY_FIELD];
optional bool using_alert_window = 3;
@@ -3235,7 +3247,11 @@ message OverlayStateChanged {
ENTERED = 1;
EXITED = 2;
}
- optional State state = 4 [(state_field_option).option = EXCLUSIVE];
+ optional State state = 4 [
+ (state_field_option).option = EXCLUSIVE_STATE,
+ (state_field_option).nested = false,
+ (state_field_option).default_state_value = 2
+ ];
}
/*
@@ -3401,7 +3417,7 @@ message LmkKillOccurred {
*/
message AppDied {
// timestamp(elapsedRealtime) of record creation
- optional uint64 timestamp_millis = 1 [(state_field_option).option = EXCLUSIVE];
+ optional uint64 timestamp_millis = 1 [(state_field_option).option = EXCLUSIVE_STATE];
}
/**
@@ -3921,7 +3937,7 @@ message PrivacyIndicatorsInteracted {
DIALOG_LINE_ITEM = 5;
}
- optional Type type = 1 [(state_field_option).option = EXCLUSIVE];
+ optional Type type = 1 [(state_field_option).option = EXCLUSIVE_STATE];
// Used if the type is LINE_ITEM
optional string package_name = 2;
@@ -4231,6 +4247,9 @@ message BootTimeEventDuration {
// Time since last factory reset.
// Logged from bootstat.
FACTORY_RESET_TIME_SINCE_RESET = 18;
+ // Init's total time spent for completing the 1st stage.
+ // Logged from bootstat.
+ ANDROID_INIT_STAGE_1 = 19;
}
// Type of the event.
@@ -4258,19 +4277,19 @@ message BootTimeEventElapsedTime {
// BOOT_COMPLETE for device with no encryption.
BOOT_COMPLETE_NO_ENCRYPTION = 4;
// Adjusted BOOT_COMPLETE for encrypted device extracting decryption time.
- BOOT_COMPLETE_POST_DESCRYPT = 5;
+ BOOT_COMPLETE_POST_DECRYPT = 5;
// BOOT_COMPLETE after factory reset.
FACTORY_RESET_BOOT_COMPLETE = 6;
// BOOT_COMPLETE_NO_ENCRYPTION after factory reset.
FACTORY_RESET_BOOT_COMPLETE_NO_ENCRYPTION = 7;
- // BOOT_COMPLETE_POST_DESCRYPT after factory reset.
- FACTORY_RESET_BOOT_COMPLETE_POST_DESCRYPT = 8;
+ // BOOT_COMPLETE_POST_DECRYPT after factory reset.
+ FACTORY_RESET_BOOT_COMPLETE_POST_DECRYPT = 8;
// BOOT_COMPLETE after OTA.
OTA_BOOT_COMPLETE = 9;
// BOOT_COMPLETE_NO_ENCRYPTION after OTA.
OTA_BOOT_COMPLETE_NO_ENCRYPTION = 10;
- // BOOT_COMPLETE_POST_DESCRYPT after OTA.
- OTA_BOOT_COMPLETE_POST_DESCRYPT = 11;
+ // BOOT_COMPLETE_POST_DECRYPT after OTA.
+ OTA_BOOT_COMPLETE_POST_DECRYPT = 11;
// Time when the system starts sending LOCKED_BOOT_COMPLETED broadcast.
// Logged from f/b/services/.../UserController.java
FRAMEWORK_LOCKED_BOOT_COMPLETED = 12;
@@ -6463,6 +6482,8 @@ message PermissionGrantRequestResultReported {
USER_DENIED_WITH_PREJUDICE_IN_SETTINGS = 14;
// permission was automatically revoked after one-time permission expired
AUTO_ONE_TIME_PERMISSION_REVOKED = 15;
+ // permission was automatically revoked for unused app
+ AUTO_UNUSED_APP_PERMISSION_REVOKED = 16;
}
// The result of the permission grant
optional Result result = 6;
@@ -7540,6 +7561,28 @@ message AppPermissionFragmentActionReported {
// The result of the permission grant
optional bool permission_granted = 6;
+
+ // State of Permission Flags after grant as per android.content.pm.PermissionFlags
+ optional int32 permission_flags = 7;
+
+ enum Button {
+ UNDEFINED = 0;
+ // Allow button
+ ALLOW = 1;
+ // Deny button
+ DENY = 2;
+ // Ask every time button
+ ASK_EVERY_TIME = 3;
+ // Allow all the time button
+ ALLOW_ALWAYS = 4;
+ // Allow only while using the app button
+ ALLOW_FOREGROUND = 5;
+ // Same is Deny button but shown in while in use dialog
+ DENY_FOREGROUND = 6;
+ }
+
+ // Button pressed in the dialog
+ optional Button button_pressed = 8;
}
/**
@@ -8132,15 +8175,15 @@ message GraphicsStats {
/**
* Message related to dangerous (runtime) app ops access
*/
-message RuntimeAppOpsAccess {
+message RuntimeAppOpAccess {
// Uid of the package accessing app op
optional int32 uid = 1 [(is_uid) = true];
// Name of the package accessing app op
optional string package_name = 2;
- // operation id; maps to the OP_* constants in AppOpsManager.java
- optional int32 op_id = 3;
+ // operation string id per OPSTR_ constants in AppOpsManager.java
+ optional string op = 3;
// feature id; provided by developer when accessing related API, limited at 50 chars by API.
// Features must be provided through manifest using <feature> tag available in R and above.
diff --git a/cmds/statsd/src/external/StatsPullerManager.cpp b/cmds/statsd/src/external/StatsPullerManager.cpp
index 988a53f5d028..15d7e33d63fd 100644
--- a/cmds/statsd/src/external/StatsPullerManager.cpp
+++ b/cmds/statsd/src/external/StatsPullerManager.cpp
@@ -80,7 +80,7 @@ bool StatsPullerManager::PullLocked(int tagId, vector<shared_ptr<LogEvent>>* dat
}
return ret;
} else {
- VLOG("Unknown tagId %d", tagId);
+ ALOGW("StatsPullerManager: Unknown tagId %d", tagId);
return false; // Return early since we don't know what to pull.
}
}
diff --git a/cmds/statsd/src/metrics/GaugeMetricProducer.cpp b/cmds/statsd/src/metrics/GaugeMetricProducer.cpp
index 83983e80d479..5c606bc98235 100644
--- a/cmds/statsd/src/metrics/GaugeMetricProducer.cpp
+++ b/cmds/statsd/src/metrics/GaugeMetricProducer.cpp
@@ -547,14 +547,11 @@ void GaugeMetricProducer::flushIfNeededLocked(const int64_t& eventTimeNs) {
void GaugeMetricProducer::flushCurrentBucketLocked(const int64_t& eventTimeNs,
const int64_t& nextBucketStartTimeNs) {
int64_t fullBucketEndTimeNs = getCurrentBucketEndTimeNs();
+ int64_t bucketEndTime = eventTimeNs < fullBucketEndTimeNs ? eventTimeNs : fullBucketEndTimeNs;
GaugeBucket info;
info.mBucketStartNs = mCurrentBucketStartTimeNs;
- if (eventTimeNs < fullBucketEndTimeNs) {
- info.mBucketEndNs = eventTimeNs;
- } else {
- info.mBucketEndNs = fullBucketEndTimeNs;
- }
+ info.mBucketEndNs = bucketEndTime;
// Add bucket to mPastBuckets if bucket is large enough.
// Otherwise, drop the bucket data and add bucket metadata to mSkippedBuckets.
@@ -569,7 +566,7 @@ void GaugeMetricProducer::flushCurrentBucketLocked(const int64_t& eventTimeNs,
}
} else {
mCurrentSkippedBucket.bucketStartTimeNs = mCurrentBucketStartTimeNs;
- mCurrentSkippedBucket.bucketEndTimeNs = eventTimeNs;
+ mCurrentSkippedBucket.bucketEndTimeNs = bucketEndTime;
if (!maxDropEventsReached()) {
mCurrentSkippedBucket.dropEvents.emplace_back(
buildDropEvent(eventTimeNs, BucketDropReason::BUCKET_TOO_SMALL));
diff --git a/cmds/statsd/src/metrics/ValueMetricProducer.cpp b/cmds/statsd/src/metrics/ValueMetricProducer.cpp
index 2a5b5302b619..dc9b41303894 100644
--- a/cmds/statsd/src/metrics/ValueMetricProducer.cpp
+++ b/cmds/statsd/src/metrics/ValueMetricProducer.cpp
@@ -379,9 +379,6 @@ void ValueMetricProducer::invalidateCurrentBucketWithoutResetBase(const int64_t
if (!mCurrentBucketIsInvalid) {
// Only report to StatsdStats once per invalid bucket.
StatsdStats::getInstance().noteInvalidatedBucket(mMetricId);
-
- mCurrentSkippedBucket.bucketStartTimeNs = mCurrentBucketStartTimeNs;
- mCurrentSkippedBucket.bucketEndTimeNs = getCurrentBucketEndTimeNs();
}
if (!maxDropEventsReached()) {
@@ -955,12 +952,6 @@ void ValueMetricProducer::flushCurrentBucketLocked(const int64_t& eventTimeNs,
int64_t conditionTrueDuration = mConditionTimer.newBucketStart(bucketEndTime);
bool isBucketLargeEnough = bucketEndTime - mCurrentBucketStartTimeNs >= mMinBucketSizeNs;
if (!isBucketLargeEnough) {
- // If the bucket is valid, this is the only drop reason and we need to
- // set the skipped bucket start and end times.
- if (!mCurrentBucketIsInvalid) {
- mCurrentSkippedBucket.bucketStartTimeNs = mCurrentBucketStartTimeNs;
- mCurrentSkippedBucket.bucketEndTimeNs = bucketEndTime;
- }
if (!maxDropEventsReached()) {
mCurrentSkippedBucket.dropEvents.emplace_back(
buildDropEvent(eventTimeNs, BucketDropReason::BUCKET_TOO_SMALL));
@@ -978,6 +969,8 @@ void ValueMetricProducer::flushCurrentBucketLocked(const int64_t& eventTimeNs,
}
}
} else {
+ mCurrentSkippedBucket.bucketStartTimeNs = mCurrentBucketStartTimeNs;
+ mCurrentSkippedBucket.bucketEndTimeNs = bucketEndTime;
mSkippedBuckets.emplace_back(mCurrentSkippedBucket);
}
diff --git a/cmds/statsd/src/state/StateTracker.cpp b/cmds/statsd/src/state/StateTracker.cpp
index 3ad21e0c96ae..ab861275073c 100644
--- a/cmds/statsd/src/state/StateTracker.cpp
+++ b/cmds/statsd/src/state/StateTracker.cpp
@@ -26,7 +26,9 @@ namespace os {
namespace statsd {
StateTracker::StateTracker(const int32_t atomId, const util::StateAtomFieldOptions& stateAtomInfo)
- : mAtomId(atomId), mStateField(getSimpleMatcher(atomId, stateAtomInfo.exclusiveField)) {
+ : mAtomId(atomId),
+ mStateField(getSimpleMatcher(atomId, stateAtomInfo.exclusiveField)),
+ mNested(stateAtomInfo.nested) {
// create matcher for each primary field
for (const auto& primaryField : stateAtomInfo.primaryFields) {
if (primaryField == util::FIRST_UID_IN_CHAIN) {
@@ -38,7 +40,13 @@ StateTracker::StateTracker(const int32_t atomId, const util::StateAtomFieldOptio
}
}
- // TODO(tsaichristine): b/142108433 set default state, reset state, and nesting
+ if (stateAtomInfo.defaultState != util::UNSET_VALUE) {
+ mDefaultState = stateAtomInfo.defaultState;
+ }
+
+ if (stateAtomInfo.resetState != util::UNSET_VALUE) {
+ mResetState = stateAtomInfo.resetState;
+ }
}
void StateTracker::onLogEvent(const LogEvent& event) {
@@ -60,7 +68,6 @@ void StateTracker::onLogEvent(const LogEvent& event) {
// Parse event for state value.
FieldValue stateValue;
- int32_t state;
if (!filterValues(mStateField, event.getValues(), &stateValue) ||
stateValue.mValue.getType() != INT) {
ALOGE("StateTracker error extracting state from log event. Type: %d",
@@ -68,11 +75,12 @@ void StateTracker::onLogEvent(const LogEvent& event) {
handlePartialReset(eventTimeNs, primaryKey);
return;
}
- state = stateValue.mValue.int_value;
+ int32_t state = stateValue.mValue.int_value;
if (state == mResetState) {
VLOG("StateTracker Reset state: %s", stateValue.mValue.toString().c_str());
handleReset(eventTimeNs);
+ return;
}
// Track and update state.
@@ -113,15 +121,17 @@ bool StateTracker::getStateValue(const HashableDimensionKey& queryKey, FieldValu
return true;
}
} else if (queryKey.getValues().size() > mPrimaryFields.size()) {
- ALOGE("StateTracker query key size > primary key size is illegal");
+ ALOGE("StateTracker query key size %zu > primary key size %zu is illegal",
+ queryKey.getValues().size(), mPrimaryFields.size());
} else {
- ALOGE("StateTracker query key size < primary key size is not supported");
+ ALOGE("StateTracker query key size %zu < primary key size %zu is not supported",
+ queryKey.getValues().size(), mPrimaryFields.size());
}
- // Set the state value to unknown if:
+ // Set the state value to default state if:
// - query key size is incorrect
// - query key is not found in state map
- output->mValue = StateTracker::kStateUnknown;
+ output->mValue = mDefaultState;
return false;
}
@@ -164,18 +174,52 @@ void StateTracker::updateState(const HashableDimensionKey& primaryKey, const int
*oldState = mDefaultState;
}
- // update state map
- if (eventState == mDefaultState) {
- // remove (key, state) pair if state returns to default state
- VLOG("\t StateTracker changed to default state")
- mStateMap.erase(primaryKey);
- } else {
- mStateMap[primaryKey].state = eventState;
- mStateMap[primaryKey].count = 1;
+ // Update state map for non-nested counting case.
+ // Every state event triggers a state overwrite.
+ if (!mNested) {
+ if (eventState == mDefaultState) {
+ // remove (key, state) pair if state returns to default state
+ VLOG("\t StateTracker changed to default state")
+ mStateMap.erase(primaryKey);
+ } else {
+ mStateMap[primaryKey].state = eventState;
+ mStateMap[primaryKey].count = 1;
+ }
+ *newState = eventState;
+ return;
}
- *newState = eventState;
- // TODO(tsaichristine): support atoms with nested counting
+ // Update state map for nested counting case.
+ //
+ // Nested counting is only allowed for binary state events such as ON/OFF or
+ // ACQUIRE/RELEASE. For example, WakelockStateChanged might have the state
+ // events: ON, ON, OFF. The state will still be ON until we see the same
+ // number of OFF events as ON events.
+ //
+ // In atoms.proto, a state atom with nested counting enabled
+ // must only have 2 states and one of the states must be the default state.
+ it = mStateMap.find(primaryKey);
+ if (it != mStateMap.end()) {
+ *newState = it->second.state;
+ if (eventState == it->second.state) {
+ it->second.count++;
+ } else if (eventState == mDefaultState) {
+ if ((--it->second.count) == 0) {
+ mStateMap.erase(primaryKey);
+ *newState = mDefaultState;
+ }
+ } else {
+ ALOGE("StateTracker Nest counting state has a third state instead of the binary state "
+ "limit.");
+ return;
+ }
+ } else {
+ if (eventState != mDefaultState) {
+ mStateMap[primaryKey].state = eventState;
+ mStateMap[primaryKey].count = 1;
+ }
+ *newState = eventState;
+ }
}
} // namespace statsd
diff --git a/cmds/statsd/src/state/StateTracker.h b/cmds/statsd/src/state/StateTracker.h
index 70f16274c7f6..aeca2a53bfdc 100644
--- a/cmds/statsd/src/state/StateTracker.h
+++ b/cmds/statsd/src/state/StateTracker.h
@@ -74,6 +74,8 @@ private:
int32_t mResetState = kStateUnknown;
+ const bool mNested;
+
// Maps primary key to state value info
std::unordered_map<HashableDimensionKey, StateValueInfo> mStateMap;
diff --git a/cmds/statsd/tests/e2e/GaugeMetric_e2e_pull_test.cpp b/cmds/statsd/tests/e2e/GaugeMetric_e2e_pull_test.cpp
index c7ba9be3ca5a..5e60abaf7792 100644
--- a/cmds/statsd/tests/e2e/GaugeMetric_e2e_pull_test.cpp
+++ b/cmds/statsd/tests/e2e/GaugeMetric_e2e_pull_test.cpp
@@ -14,12 +14,12 @@
#include <gtest/gtest.h>
+#include <vector>
+
#include "src/StatsLogProcessor.h"
#include "src/stats_log_util.h"
#include "tests/statsd_test_util.h"
-#include <vector>
-
namespace android {
namespace os {
namespace statsd {
@@ -29,12 +29,13 @@ namespace statsd {
namespace {
const int64_t metricId = 123456;
+const int32_t ATOM_TAG = android::util::SUBSYSTEM_SLEEP_STATE;
StatsdConfig CreateStatsdConfig(const GaugeMetric::SamplingType sampling_type,
bool useCondition = true) {
StatsdConfig config;
config.add_allowed_log_source("AID_ROOT"); // LogEvent defaults to UID of root.
- auto atomMatcher = CreateSimpleAtomMatcher("TestMatcher", android::util::SUBSYSTEM_SLEEP_STATE);
+ auto atomMatcher = CreateSimpleAtomMatcher("TestMatcher", ATOM_TAG);
*config.add_atom_matcher() = atomMatcher;
*config.add_atom_matcher() = CreateScreenTurnedOnAtomMatcher();
*config.add_atom_matcher() = CreateScreenTurnedOffAtomMatcher();
@@ -51,7 +52,7 @@ StatsdConfig CreateStatsdConfig(const GaugeMetric::SamplingType sampling_type,
gaugeMetric->set_sampling_type(sampling_type);
gaugeMetric->mutable_gauge_fields_filter()->set_include_all(true);
*gaugeMetric->mutable_dimensions_in_what() =
- CreateDimensions(android::util::SUBSYSTEM_SLEEP_STATE, {1 /* subsystem name */});
+ CreateDimensions(ATOM_TAG, {1 /* subsystem name */});
gaugeMetric->set_bucket(FIVE_MINUTES);
gaugeMetric->set_max_pull_delay_sec(INT_MAX);
config.set_hash_strings_in_metric_report(false);
@@ -69,8 +70,8 @@ TEST(GaugeMetricE2eTest, TestRandomSamplePulledEvents) {
TimeUnitToBucketSizeInMillis(config.gauge_metric(0).bucket()) * 1000000;
ConfigKey cfgKey;
- auto processor = CreateStatsLogProcessor(
- baseTimeNs, configAddedTimeNs, config, cfgKey);
+ auto processor = CreateStatsLogProcessor(baseTimeNs, configAddedTimeNs, config, cfgKey,
+ new FakeSubsystemSleepCallback(), ATOM_TAG);
EXPECT_EQ(processor->mMetricsManagers.size(), 1u);
EXPECT_TRUE(processor->mMetricsManagers.begin()->second->isConfigValid());
processor->mPullerManager->ForceClearPullerCache();
@@ -144,7 +145,7 @@ TEST(GaugeMetricE2eTest, TestRandomSamplePulledEvents) {
EXPECT_GT((int)gaugeMetrics.data_size(), 1);
auto data = gaugeMetrics.data(0);
- EXPECT_EQ(android::util::SUBSYSTEM_SLEEP_STATE, data.dimensions_in_what().field());
+ EXPECT_EQ(ATOM_TAG, data.dimensions_in_what().field());
EXPECT_EQ(1, data.dimensions_in_what().value_tuple().dimensions_value_size());
EXPECT_EQ(1 /* subsystem name field */,
data.dimensions_in_what().value_tuple().dimensions_value(0).field());
@@ -214,8 +215,8 @@ TEST(GaugeMetricE2eTest, TestConditionChangeToTrueSamplePulledEvents) {
TimeUnitToBucketSizeInMillis(config.gauge_metric(0).bucket()) * 1000000;
ConfigKey cfgKey;
- auto processor = CreateStatsLogProcessor(
- baseTimeNs, configAddedTimeNs, config, cfgKey);
+ auto processor = CreateStatsLogProcessor(baseTimeNs, configAddedTimeNs, config, cfgKey,
+ new FakeSubsystemSleepCallback(), ATOM_TAG);
EXPECT_EQ(processor->mMetricsManagers.size(), 1u);
EXPECT_TRUE(processor->mMetricsManagers.begin()->second->isConfigValid());
processor->mPullerManager->ForceClearPullerCache();
@@ -267,7 +268,7 @@ TEST(GaugeMetricE2eTest, TestConditionChangeToTrueSamplePulledEvents) {
EXPECT_GT((int)gaugeMetrics.data_size(), 1);
auto data = gaugeMetrics.data(0);
- EXPECT_EQ(android::util::SUBSYSTEM_SLEEP_STATE, data.dimensions_in_what().field());
+ EXPECT_EQ(ATOM_TAG, data.dimensions_in_what().field());
EXPECT_EQ(1, data.dimensions_in_what().value_tuple().dimensions_value_size());
EXPECT_EQ(1 /* subsystem name field */,
data.dimensions_in_what().value_tuple().dimensions_value(0).field());
@@ -315,8 +316,8 @@ TEST(GaugeMetricE2eTest, TestRandomSamplePulledEvent_LateAlarm) {
TimeUnitToBucketSizeInMillis(config.gauge_metric(0).bucket()) * 1000000;
ConfigKey cfgKey;
- auto processor = CreateStatsLogProcessor(
- baseTimeNs, configAddedTimeNs, config, cfgKey);
+ auto processor = CreateStatsLogProcessor(baseTimeNs, configAddedTimeNs, config, cfgKey,
+ new FakeSubsystemSleepCallback, ATOM_TAG);
EXPECT_EQ(processor->mMetricsManagers.size(), 1u);
EXPECT_TRUE(processor->mMetricsManagers.begin()->second->isConfigValid());
processor->mPullerManager->ForceClearPullerCache();
@@ -371,7 +372,7 @@ TEST(GaugeMetricE2eTest, TestRandomSamplePulledEvent_LateAlarm) {
EXPECT_GT((int)gaugeMetrics.data_size(), 1);
auto data = gaugeMetrics.data(0);
- EXPECT_EQ(android::util::SUBSYSTEM_SLEEP_STATE, data.dimensions_in_what().field());
+ EXPECT_EQ(ATOM_TAG, data.dimensions_in_what().field());
EXPECT_EQ(1, data.dimensions_in_what().value_tuple().dimensions_value_size());
EXPECT_EQ(1 /* subsystem name field */,
data.dimensions_in_what().value_tuple().dimensions_value(0).field());
@@ -424,8 +425,8 @@ TEST(GaugeMetricE2eTest, TestRandomSamplePulledEventsWithActivation) {
event_activation->set_ttl_seconds(ttlNs / 1000000000);
ConfigKey cfgKey;
- auto processor = CreateStatsLogProcessor(
- baseTimeNs, configAddedTimeNs, config, cfgKey);
+ auto processor = CreateStatsLogProcessor(baseTimeNs, configAddedTimeNs, config, cfgKey,
+ new FakeSubsystemSleepCallback(), ATOM_TAG);
EXPECT_EQ(processor->mMetricsManagers.size(), 1u);
EXPECT_TRUE(processor->mMetricsManagers.begin()->second->isConfigValid());
processor->mPullerManager->ForceClearPullerCache();
@@ -493,7 +494,7 @@ TEST(GaugeMetricE2eTest, TestRandomSamplePulledEventsWithActivation) {
EXPECT_GT((int)gaugeMetrics.data_size(), 0);
auto data = gaugeMetrics.data(0);
- EXPECT_EQ(android::util::SUBSYSTEM_SLEEP_STATE, data.dimensions_in_what().field());
+ EXPECT_EQ(ATOM_TAG, data.dimensions_in_what().field());
EXPECT_EQ(1, data.dimensions_in_what().value_tuple().dimensions_value_size());
EXPECT_EQ(1 /* subsystem name field */,
data.dimensions_in_what().value_tuple().dimensions_value(0).field());
@@ -542,8 +543,8 @@ TEST(GaugeMetricE2eTest, TestRandomSamplePulledEventsNoCondition) {
TimeUnitToBucketSizeInMillis(config.gauge_metric(0).bucket()) * 1000000;
ConfigKey cfgKey;
- auto processor = CreateStatsLogProcessor(
- baseTimeNs, configAddedTimeNs, config, cfgKey);
+ auto processor = CreateStatsLogProcessor(baseTimeNs, configAddedTimeNs, config, cfgKey,
+ new FakeSubsystemSleepCallback(), ATOM_TAG);
EXPECT_EQ(processor->mMetricsManagers.size(), 1u);
EXPECT_TRUE(processor->mMetricsManagers.begin()->second->isConfigValid());
processor->mPullerManager->ForceClearPullerCache();
@@ -586,7 +587,7 @@ TEST(GaugeMetricE2eTest, TestRandomSamplePulledEventsNoCondition) {
EXPECT_GT((int)gaugeMetrics.data_size(), 0);
auto data = gaugeMetrics.data(0);
- EXPECT_EQ(android::util::SUBSYSTEM_SLEEP_STATE, data.dimensions_in_what().field());
+ EXPECT_EQ(ATOM_TAG, data.dimensions_in_what().field());
EXPECT_EQ(1, data.dimensions_in_what().value_tuple().dimensions_value_size());
EXPECT_EQ(1 /* subsystem name field */,
data.dimensions_in_what().value_tuple().dimensions_value(0).field());
diff --git a/cmds/statsd/tests/e2e/PartialBucket_e2e_test.cpp b/cmds/statsd/tests/e2e/PartialBucket_e2e_test.cpp
index 16b51d99535b..9d58867f09db 100644
--- a/cmds/statsd/tests/e2e/PartialBucket_e2e_test.cpp
+++ b/cmds/statsd/tests/e2e/PartialBucket_e2e_test.cpp
@@ -12,22 +12,24 @@
// See the License for the specific language governing permissions and
// limitations under the License.
+#include <android/os/BnPullAtomCallback.h>
+#include <android/os/IPullAtomResultReceiver.h>
+#include <binder/IPCThreadState.h>
#include <gtest/gtest.h>
-#include <binder/IPCThreadState.h>
+#include <vector>
+
#include "src/StatsLogProcessor.h"
#include "src/StatsService.h"
#include "src/stats_log_util.h"
#include "tests/statsd_test_util.h"
-#include <vector>
-
namespace android {
namespace os {
namespace statsd {
#ifdef __ANDROID__
-
+namespace {
const string kApp1 = "app1.sharing.1";
const int kConfigKey = 789130123; // Randomly chosen to avoid collisions with existing configs.
const int kCallingUid = 0; // Randomly chosen
@@ -109,6 +111,7 @@ StatsdConfig MakeGaugeMetricConfig(int64_t minTime) {
gaugeMetric->set_min_bucket_size_nanos(minTime);
return config;
}
+} // anonymous namespace
TEST(PartialBucketE2eTest, TestCountMetricWithoutSplit) {
StatsService service(nullptr, nullptr);
@@ -202,6 +205,9 @@ TEST(PartialBucketE2eTest, TestCountMetricSplitOnRemoval) {
TEST(PartialBucketE2eTest, TestValueMetricWithoutMinPartialBucket) {
StatsService service(nullptr, nullptr);
+ service.mPullerManager->RegisterPullAtomCallback(
+ /*uid=*/0, android::util::SUBSYSTEM_SLEEP_STATE, NS_PER_SEC, NS_PER_SEC * 10, {},
+ new FakeSubsystemSleepCallback());
// Partial buckets don't occur when app is first installed.
service.mUidMap->updateApp(1, String16(kApp1.c_str()), 1, 1, String16("v1"), String16(""));
SendConfig(service, MakeValueMetricConfig(0));
@@ -220,6 +226,9 @@ TEST(PartialBucketE2eTest, TestValueMetricWithoutMinPartialBucket) {
TEST(PartialBucketE2eTest, TestValueMetricWithMinPartialBucket) {
StatsService service(nullptr, nullptr);
+ service.mPullerManager->RegisterPullAtomCallback(
+ /*uid=*/0, android::util::SUBSYSTEM_SLEEP_STATE, NS_PER_SEC, NS_PER_SEC * 10, {},
+ new FakeSubsystemSleepCallback());
// Partial buckets don't occur when app is first installed.
service.mUidMap->updateApp(1, String16(kApp1.c_str()), 1, 1, String16("v1"), String16(""));
SendConfig(service, MakeValueMetricConfig(60 * NS_PER_SEC /* One minute */));
diff --git a/cmds/statsd/tests/e2e/ValueMetric_pull_e2e_test.cpp b/cmds/statsd/tests/e2e/ValueMetric_pull_e2e_test.cpp
index e8d2ec514cad..a140af876474 100644
--- a/cmds/statsd/tests/e2e/ValueMetric_pull_e2e_test.cpp
+++ b/cmds/statsd/tests/e2e/ValueMetric_pull_e2e_test.cpp
@@ -69,8 +69,9 @@ TEST(ValueMetricE2eTest, TestPulledEvents) {
TimeUnitToBucketSizeInMillis(config.value_metric(0).bucket()) * 1000000;
ConfigKey cfgKey;
- auto processor = CreateStatsLogProcessor(
- baseTimeNs, configAddedTimeNs, config, cfgKey);
+ auto processor = CreateStatsLogProcessor(baseTimeNs, configAddedTimeNs, config, cfgKey,
+ new FakeSubsystemSleepCallback(),
+ android::util::SUBSYSTEM_SLEEP_STATE);
EXPECT_EQ(processor->mMetricsManagers.size(), 1u);
EXPECT_TRUE(processor->mMetricsManagers.begin()->second->isConfigValid());
processor->mPullerManager->ForceClearPullerCache();
@@ -173,8 +174,9 @@ TEST(ValueMetricE2eTest, TestPulledEvents_LateAlarm) {
TimeUnitToBucketSizeInMillis(config.value_metric(0).bucket()) * 1000000;
ConfigKey cfgKey;
- auto processor = CreateStatsLogProcessor(
- baseTimeNs, configAddedTimeNs, config, cfgKey);
+ auto processor = CreateStatsLogProcessor(baseTimeNs, configAddedTimeNs, config, cfgKey,
+ new FakeSubsystemSleepCallback(),
+ android::util::SUBSYSTEM_SLEEP_STATE);
EXPECT_EQ(processor->mMetricsManagers.size(), 1u);
EXPECT_TRUE(processor->mMetricsManagers.begin()->second->isConfigValid());
processor->mPullerManager->ForceClearPullerCache();
@@ -285,8 +287,9 @@ TEST(ValueMetricE2eTest, TestPulledEvents_WithActivation) {
event_activation->set_ttl_seconds(ttlNs / 1000000000);
ConfigKey cfgKey;
- auto processor = CreateStatsLogProcessor(
- baseTimeNs, configAddedTimeNs, config, cfgKey);
+ auto processor = CreateStatsLogProcessor(baseTimeNs, configAddedTimeNs, config, cfgKey,
+ new FakeSubsystemSleepCallback(),
+ android::util::SUBSYSTEM_SLEEP_STATE);
EXPECT_EQ(processor->mMetricsManagers.size(), 1u);
EXPECT_TRUE(processor->mMetricsManagers.begin()->second->isConfigValid());
processor->mPullerManager->ForceClearPullerCache();
diff --git a/cmds/statsd/tests/metrics/ValueMetricProducer_test.cpp b/cmds/statsd/tests/metrics/ValueMetricProducer_test.cpp
index 92e8241d9ec2..f6245ac8ea9a 100644
--- a/cmds/statsd/tests/metrics/ValueMetricProducer_test.cpp
+++ b/cmds/statsd/tests/metrics/ValueMetricProducer_test.cpp
@@ -3333,7 +3333,7 @@ TEST(ValueMetricProducerTest_BucketDrop, TestInvalidBucketWhenDumpReportRequeste
EXPECT_EQ(NanoToMillis(bucketStartTimeNs),
report.value_metrics().skipped(0).start_bucket_elapsed_millis());
- EXPECT_EQ(NanoToMillis(bucket2StartTimeNs),
+ EXPECT_EQ(NanoToMillis(bucketStartTimeNs + 40),
report.value_metrics().skipped(0).end_bucket_elapsed_millis());
EXPECT_EQ(1, report.value_metrics().skipped(0).drop_event_size());
@@ -3393,7 +3393,7 @@ TEST(ValueMetricProducerTest_BucketDrop, TestInvalidBucketWhenConditionEventWron
EXPECT_EQ(NanoToMillis(bucket2StartTimeNs),
report.value_metrics().skipped(0).start_bucket_elapsed_millis());
- EXPECT_EQ(NanoToMillis(bucket3StartTimeNs),
+ EXPECT_EQ(NanoToMillis(bucket2StartTimeNs + 100),
report.value_metrics().skipped(0).end_bucket_elapsed_millis());
EXPECT_EQ(1, report.value_metrics().skipped(0).drop_event_size());
@@ -3470,7 +3470,7 @@ TEST(ValueMetricProducerTest_BucketDrop, TestInvalidBucketWhenAccumulateEventWro
EXPECT_EQ(NanoToMillis(bucket2StartTimeNs),
report.value_metrics().skipped(0).start_bucket_elapsed_millis());
- EXPECT_EQ(NanoToMillis(bucket3StartTimeNs),
+ EXPECT_EQ(NanoToMillis(bucket2StartTimeNs + 100),
report.value_metrics().skipped(0).end_bucket_elapsed_millis());
EXPECT_EQ(1, report.value_metrics().skipped(0).drop_event_size());
@@ -3519,7 +3519,8 @@ TEST(ValueMetricProducerTest_BucketDrop, TestInvalidBucketWhenConditionUnknown)
// Check dump report.
ProtoOutputStream output;
std::set<string> strSet;
- valueProducer->onDumpReport(bucketStartTimeNs + 100, true /* include recent buckets */, true,
+ int64_t dumpReportTimeNs = bucketStartTimeNs + 10000;
+ valueProducer->onDumpReport(dumpReportTimeNs, true /* include recent buckets */, true,
NO_TIME_CONSTRAINTS /* dumpLatency */, &strSet, &output);
StatsLogReport report = outputStreamToProto(&output);
@@ -3529,13 +3530,13 @@ TEST(ValueMetricProducerTest_BucketDrop, TestInvalidBucketWhenConditionUnknown)
EXPECT_EQ(NanoToMillis(bucketStartTimeNs),
report.value_metrics().skipped(0).start_bucket_elapsed_millis());
- EXPECT_EQ(NanoToMillis(bucket2StartTimeNs),
+ EXPECT_EQ(NanoToMillis(dumpReportTimeNs),
report.value_metrics().skipped(0).end_bucket_elapsed_millis());
EXPECT_EQ(1, report.value_metrics().skipped(0).drop_event_size());
auto dropEvent = report.value_metrics().skipped(0).drop_event(0);
EXPECT_EQ(BucketDropReason::CONDITION_UNKNOWN, dropEvent.drop_reason());
- EXPECT_EQ(NanoToMillis(bucketStartTimeNs + 100), dropEvent.drop_time_millis());
+ EXPECT_EQ(NanoToMillis(dumpReportTimeNs), dropEvent.drop_time_millis());
}
/*
@@ -3569,7 +3570,8 @@ TEST(ValueMetricProducerTest_BucketDrop, TestInvalidBucketWhenPullFailed) {
// Check dump report.
ProtoOutputStream output;
std::set<string> strSet;
- valueProducer->onDumpReport(bucketStartTimeNs + 100, true /* include recent buckets */, true,
+ int64_t dumpReportTimeNs = bucketStartTimeNs + 10000;
+ valueProducer->onDumpReport(dumpReportTimeNs, true /* include recent buckets */, true,
NO_TIME_CONSTRAINTS /* dumpLatency */, &strSet, &output);
StatsLogReport report = outputStreamToProto(&output);
@@ -3579,13 +3581,13 @@ TEST(ValueMetricProducerTest_BucketDrop, TestInvalidBucketWhenPullFailed) {
EXPECT_EQ(NanoToMillis(bucketStartTimeNs),
report.value_metrics().skipped(0).start_bucket_elapsed_millis());
- EXPECT_EQ(NanoToMillis(bucket2StartTimeNs),
+ EXPECT_EQ(NanoToMillis(dumpReportTimeNs),
report.value_metrics().skipped(0).end_bucket_elapsed_millis());
EXPECT_EQ(1, report.value_metrics().skipped(0).drop_event_size());
auto dropEvent = report.value_metrics().skipped(0).drop_event(0);
EXPECT_EQ(BucketDropReason::PULL_FAILED, dropEvent.drop_reason());
- EXPECT_EQ(NanoToMillis(bucketStartTimeNs + 100), dropEvent.drop_time_millis());
+ EXPECT_EQ(NanoToMillis(dumpReportTimeNs), dropEvent.drop_time_millis());
}
/*
@@ -3691,8 +3693,9 @@ TEST(ValueMetricProducerTest_BucketDrop, TestBucketDropWhenBucketTooSmall) {
// Check dump report.
ProtoOutputStream output;
std::set<string> strSet;
- valueProducer->onDumpReport(bucketStartTimeNs + 9000000, true /* include recent buckets */,
- true, NO_TIME_CONSTRAINTS /* dumpLatency */, &strSet, &output);
+ int64_t dumpReportTimeNs = bucketStartTimeNs + 9000000;
+ valueProducer->onDumpReport(dumpReportTimeNs, true /* include recent buckets */, true,
+ NO_TIME_CONSTRAINTS /* dumpLatency */, &strSet, &output);
StatsLogReport report = outputStreamToProto(&output);
EXPECT_TRUE(report.has_value_metrics());
@@ -3701,13 +3704,13 @@ TEST(ValueMetricProducerTest_BucketDrop, TestBucketDropWhenBucketTooSmall) {
EXPECT_EQ(NanoToMillis(bucketStartTimeNs),
report.value_metrics().skipped(0).start_bucket_elapsed_millis());
- EXPECT_EQ(NanoToMillis(bucketStartTimeNs + 9000000),
+ EXPECT_EQ(NanoToMillis(dumpReportTimeNs),
report.value_metrics().skipped(0).end_bucket_elapsed_millis());
EXPECT_EQ(1, report.value_metrics().skipped(0).drop_event_size());
auto dropEvent = report.value_metrics().skipped(0).drop_event(0);
EXPECT_EQ(BucketDropReason::BUCKET_TOO_SMALL, dropEvent.drop_reason());
- EXPECT_EQ(NanoToMillis(bucketStartTimeNs + 9000000), dropEvent.drop_time_millis());
+ EXPECT_EQ(NanoToMillis(dumpReportTimeNs), dropEvent.drop_time_millis());
}
/*
@@ -3739,7 +3742,8 @@ TEST(ValueMetricProducerTest_BucketDrop, TestMultipleBucketDropEvents) {
// Check dump report.
ProtoOutputStream output;
std::set<string> strSet;
- valueProducer->onDumpReport(bucketStartTimeNs + 1000, true /* include recent buckets */, true,
+ int64_t dumpReportTimeNs = bucketStartTimeNs + 1000;
+ valueProducer->onDumpReport(dumpReportTimeNs, true /* include recent buckets */, true,
FAST /* dumpLatency */, &strSet, &output);
StatsLogReport report = outputStreamToProto(&output);
@@ -3749,7 +3753,7 @@ TEST(ValueMetricProducerTest_BucketDrop, TestMultipleBucketDropEvents) {
EXPECT_EQ(NanoToMillis(bucketStartTimeNs),
report.value_metrics().skipped(0).start_bucket_elapsed_millis());
- EXPECT_EQ(NanoToMillis(bucket2StartTimeNs),
+ EXPECT_EQ(NanoToMillis(dumpReportTimeNs),
report.value_metrics().skipped(0).end_bucket_elapsed_millis());
EXPECT_EQ(2, report.value_metrics().skipped(0).drop_event_size());
@@ -3759,7 +3763,7 @@ TEST(ValueMetricProducerTest_BucketDrop, TestMultipleBucketDropEvents) {
dropEvent = report.value_metrics().skipped(0).drop_event(1);
EXPECT_EQ(BucketDropReason::DUMP_REPORT_REQUESTED, dropEvent.drop_reason());
- EXPECT_EQ(NanoToMillis(bucketStartTimeNs + 1000), dropEvent.drop_time_millis());
+ EXPECT_EQ(NanoToMillis(dumpReportTimeNs), dropEvent.drop_time_millis());
}
/*
@@ -3826,6 +3830,7 @@ TEST(ValueMetricProducerTest_BucketDrop, TestMaxBucketDropEvents) {
// Check dump report.
ProtoOutputStream output;
std::set<string> strSet;
+ int64_t dumpReportTimeNs = bucketStartTimeNs + 1000;
// Because we already have 10 dump events in the current bucket,
// this case should not be added to the list of dump events.
valueProducer->onDumpReport(bucketStartTimeNs + 1000, true /* include recent buckets */, true,
@@ -3838,7 +3843,7 @@ TEST(ValueMetricProducerTest_BucketDrop, TestMaxBucketDropEvents) {
EXPECT_EQ(NanoToMillis(bucketStartTimeNs),
report.value_metrics().skipped(0).start_bucket_elapsed_millis());
- EXPECT_EQ(NanoToMillis(bucket2StartTimeNs),
+ EXPECT_EQ(NanoToMillis(dumpReportTimeNs),
report.value_metrics().skipped(0).end_bucket_elapsed_millis());
EXPECT_EQ(10, report.value_metrics().skipped(0).drop_event_size());
diff --git a/cmds/statsd/tests/state/StateTracker_test.cpp b/cmds/statsd/tests/state/StateTracker_test.cpp
index 84aaa54bc5bf..b0acd5ad7452 100644
--- a/cmds/statsd/tests/state/StateTracker_test.cpp
+++ b/cmds/statsd/tests/state/StateTracker_test.cpp
@@ -127,6 +127,23 @@ std::shared_ptr<LogEvent> buildOverlayEventBadStateType(int uid, const std::stri
event->init();
return event;
}
+
+std::shared_ptr<LogEvent> buildBleScanEvent(int uid, bool acquire, bool reset) {
+ std::vector<AttributionNodeInternal> chain;
+ chain.push_back(AttributionNodeInternal());
+ AttributionNodeInternal& attr = chain.back();
+ attr.set_uid(uid);
+
+ std::shared_ptr<LogEvent> event =
+ std::make_shared<LogEvent>(android::util::BLE_SCAN_STATE_CHANGED, 1000);
+ event->write(chain);
+ event->write(reset ? 2 : acquire ? 1 : 0); // PARTIAL_WAKE_LOCK
+ event->write(0); // filtered
+ event->write(0); // first match
+ event->write(0); // opportunistic
+ event->init();
+ return event;
+}
// END: build event functions.
// START: get primary key functions
@@ -277,6 +294,80 @@ TEST(StateTrackerTest, TestUnregisterListener) {
}
/**
+ * Test a binary state atom with nested counting.
+ *
+ * To go from an "ON" state to an "OFF" state with nested counting, we must see
+ * an equal number of "OFF" events as "ON" events.
+ * For example, ACQUIRE, ACQUIRE, RELEASE will still be in the ACQUIRE state.
+ * ACQUIRE, ACQUIRE, RELEASE, RELEASE will be in the RELEASE state.
+ */
+TEST(StateTrackerTest, TestStateChangeNested) {
+ sp<TestStateListener> listener = new TestStateListener();
+ StateManager mgr;
+ mgr.registerListener(android::util::WAKELOCK_STATE_CHANGED, listener);
+
+ std::shared_ptr<LogEvent> event1 =
+ buildPartialWakelockEvent(1000 /* uid */, "tag", true /*acquire*/);
+ mgr.onLogEvent(*event1);
+ EXPECT_EQ(1, listener->updates.size());
+ EXPECT_EQ(1000, listener->updates[0].mKey.getValues()[0].mValue.int_value);
+ EXPECT_EQ(1, listener->updates[0].mState);
+ listener->updates.clear();
+
+ std::shared_ptr<LogEvent> event2 =
+ buildPartialWakelockEvent(1000 /* uid */, "tag", true /*acquire*/);
+ mgr.onLogEvent(*event2);
+ EXPECT_EQ(0, listener->updates.size());
+
+ std::shared_ptr<LogEvent> event3 =
+ buildPartialWakelockEvent(1000 /* uid */, "tag", false /*release*/);
+ mgr.onLogEvent(*event3);
+ EXPECT_EQ(0, listener->updates.size());
+
+ std::shared_ptr<LogEvent> event4 =
+ buildPartialWakelockEvent(1000 /* uid */, "tag", false /*release*/);
+ mgr.onLogEvent(*event4);
+ EXPECT_EQ(1, listener->updates.size());
+ EXPECT_EQ(1000, listener->updates[0].mKey.getValues()[0].mValue.int_value);
+ EXPECT_EQ(0, listener->updates[0].mState);
+}
+
+/**
+ * Test a state atom with a reset state.
+ *
+ * If the reset state value is seen, every state in the map is set to the default
+ * state and every listener is notified.
+ */
+TEST(StateTrackerTest, TestStateChangeReset) {
+ sp<TestStateListener> listener = new TestStateListener();
+ StateManager mgr;
+ mgr.registerListener(android::util::BLE_SCAN_STATE_CHANGED, listener);
+
+ std::shared_ptr<LogEvent> event1 =
+ buildBleScanEvent(1000 /* uid */, true /*acquire*/, false /*reset*/);
+ mgr.onLogEvent(*event1);
+ EXPECT_EQ(1, listener->updates.size());
+ EXPECT_EQ(1000, listener->updates[0].mKey.getValues()[0].mValue.int_value);
+ EXPECT_EQ(BleScanStateChanged::ON, listener->updates[0].mState);
+ listener->updates.clear();
+
+ std::shared_ptr<LogEvent> event2 =
+ buildBleScanEvent(2000 /* uid */, true /*acquire*/, false /*reset*/);
+ mgr.onLogEvent(*event2);
+ EXPECT_EQ(1, listener->updates.size());
+ EXPECT_EQ(2000, listener->updates[0].mKey.getValues()[0].mValue.int_value);
+ EXPECT_EQ(BleScanStateChanged::ON, listener->updates[0].mState);
+ listener->updates.clear();
+
+ std::shared_ptr<LogEvent> event3 =
+ buildBleScanEvent(2000 /* uid */, false /*acquire*/, true /*reset*/);
+ mgr.onLogEvent(*event3);
+ EXPECT_EQ(2, listener->updates.size());
+ EXPECT_EQ(BleScanStateChanged::OFF, listener->updates[0].mState);
+ EXPECT_EQ(BleScanStateChanged::OFF, listener->updates[1].mState);
+}
+
+/**
* Test StateManager's onLogEvent and StateListener's onStateChanged correctly
* updates listener for states without primary keys.
*/
@@ -334,7 +425,7 @@ TEST(StateTrackerTest, TestStateChangePrimaryFieldAttrChain) {
// Log event.
std::shared_ptr<LogEvent> event =
- buildPartialWakelockEvent(1001 /* uid */, "tag1", false /* acquire */);
+ buildPartialWakelockEvent(1001 /* uid */, "tag1", true /* acquire */);
mgr.onLogEvent(*event);
EXPECT_EQ(1, mgr.getStateTrackersCount());
@@ -346,23 +437,25 @@ TEST(StateTrackerTest, TestStateChangePrimaryFieldAttrChain) {
EXPECT_EQ(1001, listener1->updates[0].mKey.getValues()[0].mValue.int_value);
EXPECT_EQ(1, listener1->updates[0].mKey.getValues()[1].mValue.int_value);
EXPECT_EQ("tag1", listener1->updates[0].mKey.getValues()[2].mValue.str_value);
- EXPECT_EQ(WakelockStateChanged::RELEASE, listener1->updates[0].mState);
+ EXPECT_EQ(WakelockStateChanged::ACQUIRE, listener1->updates[0].mState);
// Check StateTracker was updated by querying for state.
HashableDimensionKey queryKey;
getPartialWakelockKey(1001 /* uid */, "tag1", &queryKey);
- EXPECT_EQ(WakelockStateChanged::RELEASE,
+ EXPECT_EQ(WakelockStateChanged::ACQUIRE,
getStateInt(mgr, android::util::WAKELOCK_STATE_CHANGED, queryKey));
// No state stored for this query key.
HashableDimensionKey queryKey2;
getPartialWakelockKey(1002 /* uid */, "tag1", &queryKey2);
- EXPECT_EQ(-1, getStateInt(mgr, android::util::WAKELOCK_STATE_CHANGED, queryKey2));
+ EXPECT_EQ(WakelockStateChanged::RELEASE,
+ getStateInt(mgr, android::util::WAKELOCK_STATE_CHANGED, queryKey2));
// Partial query fails.
HashableDimensionKey queryKey3;
getPartialWakelockKey(1001 /* uid */, &queryKey3);
- EXPECT_EQ(-1, getStateInt(mgr, android::util::WAKELOCK_STATE_CHANGED, queryKey3));
+ EXPECT_EQ(WakelockStateChanged::RELEASE,
+ getStateInt(mgr, android::util::WAKELOCK_STATE_CHANGED, queryKey3));
}
/**
diff --git a/cmds/statsd/tests/statsd_test_util.cpp b/cmds/statsd/tests/statsd_test_util.cpp
index e0aecceac4e3..6e1890ad63a9 100644
--- a/cmds/statsd/tests/statsd_test_util.cpp
+++ b/cmds/statsd/tests/statsd_test_util.cpp
@@ -580,9 +580,15 @@ std::unique_ptr<LogEvent> CreateUidProcessStateChangedEvent(
}
sp<StatsLogProcessor> CreateStatsLogProcessor(const int64_t timeBaseNs, const int64_t currentTimeNs,
- const StatsdConfig& config, const ConfigKey& key) {
+ const StatsdConfig& config, const ConfigKey& key,
+ const sp<IPullAtomCallback>& puller,
+ const int32_t atomTag) {
sp<UidMap> uidMap = new UidMap();
sp<StatsPullerManager> pullerManager = new StatsPullerManager();
+ if (puller != nullptr) {
+ pullerManager->RegisterPullAtomCallback(/*uid=*/0, atomTag, NS_PER_SEC, NS_PER_SEC * 10, {},
+ puller);
+ }
sp<AlarmMonitor> anomalyAlarmMonitor =
new AlarmMonitor(1, [](const sp<IStatsCompanionService>&, int64_t){},
[](const sp<IStatsCompanionService>&){});
@@ -942,6 +948,34 @@ void backfillStartEndTimestamp(ConfigMetricsReportList *config_report_list) {
}
}
+binder::Status FakeSubsystemSleepCallback::onPullAtom(
+ int atomTag, const sp<IPullAtomResultReceiver>& resultReceiver) {
+ // Convert stats_events into StatsEventParcels.
+ std::vector<android::util::StatsEventParcel> parcels;
+ for (int i = 1; i < 3; i++) {
+ stats_event* event = stats_event_obtain();
+ stats_event_set_atom_id(event, atomTag);
+ std::string subsystemName = "subsystem_name_";
+ subsystemName = subsystemName + std::to_string(i);
+ stats_event_write_string8(event, subsystemName.c_str());
+ stats_event_write_string8(event, "subsystem_subname foo");
+ stats_event_write_int64(event, /*count= */ i);
+ stats_event_write_int64(event, /*time_millis= */ i * 100);
+ stats_event_build(event);
+ size_t size;
+ uint8_t* buffer = stats_event_get_buffer(event, &size);
+
+ android::util::StatsEventParcel p;
+ // vector.assign() creates a copy, but this is inevitable unless
+ // stats_event.h/c uses a vector as opposed to a buffer.
+ p.buffer.assign(buffer, buffer + size);
+ parcels.push_back(std::move(p));
+ stats_event_release(event);
+ }
+ resultReceiver->pullFinished(atomTag, /*success=*/true, parcels);
+ return binder::Status::ok();
+}
+
} // namespace statsd
} // namespace os
} // namespace android
diff --git a/cmds/statsd/tests/statsd_test_util.h b/cmds/statsd/tests/statsd_test_util.h
index 9bdfeebe561f..576a4916d60b 100644
--- a/cmds/statsd/tests/statsd_test_util.h
+++ b/cmds/statsd/tests/statsd_test_util.h
@@ -14,12 +14,16 @@
#pragma once
+#include <android/os/BnPullAtomCallback.h>
+#include <android/os/IPullAtomCallback.h>
+#include <android/os/IPullAtomResultReceiver.h>
#include <gtest/gtest.h>
+
#include "frameworks/base/cmds/statsd/src/stats_log.pb.h"
#include "frameworks/base/cmds/statsd/src/statsd_config.pb.h"
#include "src/StatsLogProcessor.h"
-#include "src/logd/LogEvent.h"
#include "src/hash.h"
+#include "src/logd/LogEvent.h"
#include "src/stats_log_util.h"
#include "statslog.h"
@@ -224,9 +228,10 @@ std::unique_ptr<LogEvent> CreateUidProcessStateChangedEvent(
AttributionNodeInternal CreateAttribution(const int& uid, const string& tag);
// Create a statsd log event processor upon the start time in seconds, config and key.
-sp<StatsLogProcessor> CreateStatsLogProcessor(const int64_t timeBaseNs,
- const int64_t currentTimeNs,
- const StatsdConfig& config, const ConfigKey& key);
+sp<StatsLogProcessor> CreateStatsLogProcessor(const int64_t timeBaseNs, const int64_t currentTimeNs,
+ const StatsdConfig& config, const ConfigKey& key,
+ const sp<IPullAtomCallback>& puller = nullptr,
+ const int32_t atomTag = 0 /*for puller only*/);
// Util function to sort the log events by timestamp.
void sortLogEventsByTimestamp(std::vector<std::unique_ptr<LogEvent>> *events);
@@ -278,6 +283,12 @@ bool backfillDimensionPath(const DimensionsValue& path,
const google::protobuf::RepeatedPtrField<DimensionsValue>& leafValues,
DimensionsValue* dimension);
+class FakeSubsystemSleepCallback : public BnPullAtomCallback {
+public:
+ binder::Status onPullAtom(int atomTag,
+ const sp<IPullAtomResultReceiver>& resultReceiver) override;
+};
+
template <typename T>
void backfillDimensionPath(const DimensionsValue& whatPath,
const DimensionsValue& conditionPath,
diff --git a/core/java/android/app/ActivityManagerInternal.java b/core/java/android/app/ActivityManagerInternal.java
index 4e47594b6196..c60f7bd29ce8 100644
--- a/core/java/android/app/ActivityManagerInternal.java
+++ b/core/java/android/app/ActivityManagerInternal.java
@@ -278,7 +278,7 @@ public abstract class ActivityManagerInternal {
String resolvedType, boolean fgRequired, String callingPackage, @UserIdInt int userId,
boolean allowBackgroundActivityStarts) throws TransactionTooLargeException;
- public abstract void disconnectActivityFromServices(Object connectionHolder, Object conns);
+ public abstract void disconnectActivityFromServices(Object connectionHolder);
public abstract void cleanUpServices(@UserIdInt int userId, ComponentName component,
Intent baseIntent);
public abstract ActivityInfo getActivityInfoForUser(ActivityInfo aInfo, @UserIdInt int userId);
diff --git a/core/java/android/app/ActivityTaskManager.java b/core/java/android/app/ActivityTaskManager.java
index 58bff7f4dc04..9ba56cf40161 100644
--- a/core/java/android/app/ActivityTaskManager.java
+++ b/core/java/android/app/ActivityTaskManager.java
@@ -182,12 +182,13 @@ public class ActivityTaskManager {
* @param taskId The id of the task to set the windowing mode for.
* @param windowingMode The windowing mode to set for the task.
* @param toTop If the task should be moved to the top once the windowing mode changes.
+ * @return Whether the task was successfully put into the specified windowing mode.
*/
@RequiresPermission(android.Manifest.permission.MANAGE_ACTIVITY_STACKS)
- public void setTaskWindowingMode(int taskId, int windowingMode, boolean toTop)
+ public boolean setTaskWindowingMode(int taskId, int windowingMode, boolean toTop)
throws SecurityException {
try {
- getService().setTaskWindowingMode(taskId, windowingMode, toTop);
+ return getService().setTaskWindowingMode(taskId, windowingMode, toTop);
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
@@ -208,13 +209,14 @@ public class ActivityTaskManager {
* docked stack. Pass {@code null} to use default bounds.
* @param showRecents If the recents activity should be shown on the other side of the task
* going into split-screen mode.
+ * @return Whether the task was successfully put into splitscreen.
*/
@RequiresPermission(android.Manifest.permission.MANAGE_ACTIVITY_STACKS)
- public void setTaskWindowingModeSplitScreenPrimary(int taskId, int createMode, boolean toTop,
+ public boolean setTaskWindowingModeSplitScreenPrimary(int taskId, int createMode, boolean toTop,
boolean animate, Rect initialBounds, boolean showRecents) throws SecurityException {
try {
- getService().setTaskWindowingModeSplitScreenPrimary(taskId, createMode, toTop, animate,
- initialBounds, showRecents);
+ return getService().setTaskWindowingModeSplitScreenPrimary(taskId, createMode, toTop,
+ animate, initialBounds, showRecents);
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
diff --git a/core/java/android/app/AppOpsManagerInternal.java b/core/java/android/app/AppOpsManagerInternal.java
index 9958c6a31027..c13c5a5ab9c4 100644
--- a/core/java/android/app/AppOpsManagerInternal.java
+++ b/core/java/android/app/AppOpsManagerInternal.java
@@ -21,6 +21,7 @@ import android.annotation.Nullable;
import android.util.SparseArray;
import android.util.SparseIntArray;
+import com.android.internal.app.IAppOpsCallback;
import com.android.internal.util.function.HexFunction;
import com.android.internal.util.function.QuadFunction;
@@ -91,4 +92,16 @@ public abstract class AppOpsManagerInternal {
*/
public abstract void updateAppWidgetVisibility(SparseArray<String> uidPackageNames,
boolean visible);
+
+ /**
+ * Like {@link AppOpsManager#setUidMode}, but allows ignoring a certain callback.
+ */
+ public abstract void setUidModeIgnoringCallback(int code, int uid, int mode,
+ @Nullable IAppOpsCallback callbackToIgnore);
+
+ /**
+ * Like {@link AppOpsManager#setMode}, but allows ignoring a certain callback.
+ */
+ public abstract void setModeIgnoringCallback(int code, int uid, @NonNull String packageName,
+ int mode, @Nullable IAppOpsCallback callbackToIgnore);
}
diff --git a/core/java/android/app/IActivityTaskManager.aidl b/core/java/android/app/IActivityTaskManager.aidl
index 503f5c56c617..be2f144c2fe8 100644
--- a/core/java/android/app/IActivityTaskManager.aidl
+++ b/core/java/android/app/IActivityTaskManager.aidl
@@ -231,8 +231,9 @@ interface IActivityTaskManager {
* @param taskId The id of the task to set the windowing mode for.
* @param windowingMode The windowing mode to set for the task.
* @param toTop If the task should be moved to the top once the windowing mode changes.
+ * @return Whether the task was successfully put into the specified windowing mode.
*/
- void setTaskWindowingMode(int taskId, int windowingMode, boolean toTop);
+ boolean setTaskWindowingMode(int taskId, int windowingMode, boolean toTop);
void moveTaskToStack(int taskId, int stackId, boolean toTop);
/**
* Resizes the input pinned stack to the given bounds with animation.
diff --git a/core/java/android/app/admin/DevicePolicyEventLogger.java b/core/java/android/app/admin/DevicePolicyEventLogger.java
index 4c0e176c519e..bbc0bdfa6d7d 100644
--- a/core/java/android/app/admin/DevicePolicyEventLogger.java
+++ b/core/java/android/app/admin/DevicePolicyEventLogger.java
@@ -19,10 +19,9 @@ package android.app.admin;
import android.annotation.Nullable;
import android.content.ComponentName;
import android.stats.devicepolicy.nano.StringList;
-import android.util.StatsLog;
import com.android.framework.protobuf.nano.MessageNano;
-import com.android.internal.util.Preconditions;
+import com.android.internal.util.FrameworkStatsLog;
import java.util.Arrays;
import java.util.Objects;
@@ -197,8 +196,8 @@ public class DevicePolicyEventLogger {
*/
public void write() {
byte[] bytes = stringArrayValueToBytes(mStringArrayValue);
- StatsLog.write(StatsLog.DEVICE_POLICY_EVENT, mEventId, mAdminPackageName, mIntValue,
- mBooleanValue, mTimePeriodMs, bytes);
+ FrameworkStatsLog.write(FrameworkStatsLog.DEVICE_POLICY_EVENT, mEventId, mAdminPackageName,
+ mIntValue, mBooleanValue, mTimePeriodMs, bytes);
}
/**
diff --git a/core/java/android/app/prediction/AppPredictionContext.java b/core/java/android/app/prediction/AppPredictionContext.java
index d14238bb2672..8fc7e8d256b6 100644
--- a/core/java/android/app/prediction/AppPredictionContext.java
+++ b/core/java/android/app/prediction/AppPredictionContext.java
@@ -100,6 +100,16 @@ public final class AppPredictionContext implements Parcelable {
&& mPackageName.equals(other.mPackageName);
}
+ @NonNull
+ @Override
+ public String toString() {
+ return new StringBuilder(this.getClass().getSimpleName())
+ .append("[mUiSurface=").append(mUiSurface)
+ .append(",mPackageName=").append(mPackageName)
+ .append(",mPredictedTargetCount=").append(mPredictedTargetCount)
+ .append(",mExtras=").append(mExtras.toString()).append("]").toString();
+ }
+
@Override
public int describeContents() {
return 0;
diff --git a/core/java/android/app/timedetector/ManualTimeSuggestion.java b/core/java/android/app/timedetector/ManualTimeSuggestion.java
index 50de73855511..da51ce2a6aff 100644
--- a/core/java/android/app/timedetector/ManualTimeSuggestion.java
+++ b/core/java/android/app/timedetector/ManualTimeSuggestion.java
@@ -29,10 +29,18 @@ import java.util.List;
import java.util.Objects;
/**
- * A time signal from a manual (user provided) source. The value consists of the number of
- * milliseconds elapsed since 1/1/1970 00:00:00 UTC and the time according to the elapsed realtime
- * clock when that number was established. The elapsed realtime clock is considered accurate but
- * volatile, so time signals must not be persisted across device resets.
+ * A time signal from a manual (user provided) source.
+ *
+ * <p>{@code utcTime} is the suggested time. The {@code utcTime.value} is the number of milliseconds
+ * elapsed since 1/1/1970 00:00:00 UTC. The {@code utcTime.referenceTimeMillis} is the value of the
+ * elapsed realtime clock when the {@code utcTime.value} was established.
+ * Note that the elapsed realtime clock is considered accurate but it is volatile, so time
+ * suggestions cannot be persisted across device resets.
+ *
+ * <p>{@code debugInfo} contains debugging metadata associated with the suggestion. This is used to
+ * record why the suggestion exists and how it was entered. This information exists only to aid in
+ * debugging and therefore is used by {@link #toString()}, but it is not for use in detection
+ * logic and is not considered in {@link #hashCode()} or {@link #equals(Object)}.
*
* @hide
*/
@@ -49,10 +57,8 @@ public final class ManualTimeSuggestion implements Parcelable {
}
};
- @NonNull
- private final TimestampedValue<Long> mUtcTime;
- @Nullable
- private ArrayList<String> mDebugInfo;
+ @NonNull private final TimestampedValue<Long> mUtcTime;
+ @Nullable private ArrayList<String> mDebugInfo;
public ManualTimeSuggestion(@NonNull TimestampedValue<Long> utcTime) {
mUtcTime = Objects.requireNonNull(utcTime);
diff --git a/core/java/android/app/timedetector/NetworkTimeSuggestion.java b/core/java/android/app/timedetector/NetworkTimeSuggestion.java
index 17e9c5a79fa5..89fd6f31e042 100644
--- a/core/java/android/app/timedetector/NetworkTimeSuggestion.java
+++ b/core/java/android/app/timedetector/NetworkTimeSuggestion.java
@@ -29,10 +29,18 @@ import java.util.List;
import java.util.Objects;
/**
- * A time signal from a network time source like NTP. The value consists of the number of
- * milliseconds elapsed since 1/1/1970 00:00:00 UTC and the time according to the elapsed realtime
- * clock when that number was established. The elapsed realtime clock is considered accurate but
- * volatile, so time signals must not be persisted across device resets.
+ * A time signal from a network time source like NTP.
+ *
+ * <p>{@code utcTime} contains the suggested time. The {@code utcTime.value} is the number of
+ * milliseconds elapsed since 1/1/1970 00:00:00 UTC. The {@code utcTime.referenceTimeMillis} is the
+ * value of the elapsed realtime clock when the {@code utcTime.value} was established.
+ * Note that the elapsed realtime clock is considered accurate but it is volatile, so time
+ * suggestions cannot be persisted across device resets.
+ *
+ * <p>{@code debugInfo} contains debugging metadata associated with the suggestion. This is used to
+ * record why the suggestion exists and how it was determined. This information exists only to aid
+ * in debugging and therefore is used by {@link #toString()}, but it is not for use in detection
+ * logic and is not considered in {@link #hashCode()} or {@link #equals(Object)}.
*
* @hide
*/
@@ -49,10 +57,8 @@ public final class NetworkTimeSuggestion implements Parcelable {
}
};
- @NonNull
- private final TimestampedValue<Long> mUtcTime;
- @Nullable
- private ArrayList<String> mDebugInfo;
+ @NonNull private final TimestampedValue<Long> mUtcTime;
+ @Nullable private ArrayList<String> mDebugInfo;
public NetworkTimeSuggestion(@NonNull TimestampedValue<Long> utcTime) {
mUtcTime = Objects.requireNonNull(utcTime);
diff --git a/core/java/android/app/timedetector/PhoneTimeSuggestion.java b/core/java/android/app/timedetector/PhoneTimeSuggestion.java
index eab88383fa1d..16288e82d452 100644
--- a/core/java/android/app/timedetector/PhoneTimeSuggestion.java
+++ b/core/java/android/app/timedetector/PhoneTimeSuggestion.java
@@ -32,12 +32,22 @@ import java.util.Objects;
* A time suggestion from an identified telephony source. e.g. from NITZ information from a specific
* radio.
*
- * <p>The time value can be {@code null} to indicate that the telephony source has entered an
- * "un-opinionated" state and any previous suggestions from the source are being withdrawn. When not
- * {@code null}, the value consists of the number of milliseconds elapsed since 1/1/1970 00:00:00
- * UTC and the time according to the elapsed realtime clock when that number was established. The
- * elapsed realtime clock is considered accurate but volatile, so time suggestions must not be
- * persisted across device resets.
+ * <p>{@code slotIndex} identifies the suggestion source. This enables detection logic to identify
+ * suggestions from the same source when there are several in use.
+ *
+ * <p>{@code utcTime}. When not {@code null}, the {@code utcTime.value} is the number of
+ * milliseconds elapsed since 1/1/1970 00:00:00 UTC. The {@code utcTime.referenceTimeMillis} is the
+ * value of the elapsed realtime clock when the {@code utcTime.value} was established.
+ * Note that the elapsed realtime clock is considered accurate but it is volatile, so time
+ * suggestions cannot be persisted across device resets. {@code utcTime} can be {@code null} to
+ * indicate that the telephony source has entered an "un-opinionated" state and any previous
+ * suggestion from the source is being withdrawn.
+ *
+ * <p>{@code debugInfo} contains debugging metadata associated with the suggestion. This is used to
+ * record why the suggestion exists, e.g. what triggered it to be made and what heuristic was used
+ * to determine the time or its absence. This information exists only to aid in debugging and
+ * therefore is used by {@link #toString()}, but it is not for use in detection logic and is not
+ * considered in {@link #hashCode()} or {@link #equals(Object)}.
*
* @hide
*/
@@ -93,16 +103,18 @@ public final class PhoneTimeSuggestion implements Parcelable {
}
/**
- * Returns an identifier for the source of this suggestion. When a device has several sim slots
- * or equivalent, it is used to identify which one the suggestion is from.
+ * Returns an identifier for the source of this suggestion.
+ *
+ * <p>See {@link PhoneTimeSuggestion} for more information about {@code slotIndex}.
*/
public int getSlotIndex() {
return mSlotIndex;
}
/**
- * Returns the suggestion. {@code null} means that the caller is no longer sure what time it
- * is.
+ * Returns the suggested time or {@code null} if there isn't one.
+ *
+ * <p>See {@link PhoneTimeSuggestion} for more information about {@code utcTime}.
*/
@Nullable
public TimestampedValue<Long> getUtcTime() {
@@ -110,8 +122,9 @@ public final class PhoneTimeSuggestion implements Parcelable {
}
/**
- * Returns debug metadata for the suggestion. The information is present in {@link #toString()}
- * but is not considered for {@link #equals(Object)} and {@link #hashCode()}.
+ * Returns debug metadata for the suggestion.
+ *
+ * <p>See {@link PhoneTimeSuggestion} for more information about {@code debugInfo}.
*/
@NonNull
public List<String> getDebugInfo() {
@@ -120,9 +133,9 @@ public final class PhoneTimeSuggestion implements Parcelable {
}
/**
- * Associates information with the instance that can be useful for debugging / logging. The
- * information is present in {@link #toString()} but is not considered for
- * {@link #equals(Object)} and {@link #hashCode()}.
+ * Associates information with the instance that can be useful for debugging / logging.
+ *
+ * <p>See {@link PhoneTimeSuggestion} for more information about {@code debugInfo}.
*/
public void addDebugInfo(@NonNull String debugInfo) {
if (mDebugInfo == null) {
@@ -132,9 +145,9 @@ public final class PhoneTimeSuggestion implements Parcelable {
}
/**
- * Associates information with the instance that can be useful for debugging / logging. The
- * information is present in {@link #toString()} but is not considered for
- * {@link #equals(Object)} and {@link #hashCode()}.
+ * Associates information with the instance that can be useful for debugging / logging.
+ *
+ * <p>See {@link PhoneTimeSuggestion} for more information about {@code debugInfo}.
*/
public void addDebugInfo(@NonNull List<String> debugInfo) {
if (mDebugInfo == null) {
@@ -181,12 +194,20 @@ public final class PhoneTimeSuggestion implements Parcelable {
@Nullable private TimestampedValue<Long> mUtcTime;
@Nullable private List<String> mDebugInfo;
- /** Creates a builder with the specified {@code slotIndex}. */
+ /**
+ * Creates a builder with the specified {@code slotIndex}.
+ *
+ * <p>See {@link PhoneTimeSuggestion} for more information about {@code slotIndex}.
+ */
public Builder(int slotIndex) {
mSlotIndex = slotIndex;
}
- /** Returns the builder for call chaining. */
+ /**
+ * Returns the builder for call chaining.
+ *
+ * <p>See {@link PhoneTimeSuggestion} for more information about {@code utcTime}.
+ */
@NonNull
public Builder setUtcTime(@Nullable TimestampedValue<Long> utcTime) {
if (utcTime != null) {
@@ -198,7 +219,11 @@ public final class PhoneTimeSuggestion implements Parcelable {
return this;
}
- /** Returns the builder for call chaining. */
+ /**
+ * Returns the builder for call chaining.
+ *
+ * <p>See {@link PhoneTimeSuggestion} for more information about {@code debugInfo}.
+ */
@NonNull
public Builder addDebugInfo(@NonNull String debugInfo) {
if (mDebugInfo == null) {
diff --git a/core/java/android/app/timezonedetector/ManualTimeZoneSuggestion.java b/core/java/android/app/timezonedetector/ManualTimeZoneSuggestion.java
index a6b953b42f8f..3a9adc72aab1 100644
--- a/core/java/android/app/timezonedetector/ManualTimeZoneSuggestion.java
+++ b/core/java/android/app/timezonedetector/ManualTimeZoneSuggestion.java
@@ -28,10 +28,14 @@ import java.util.List;
import java.util.Objects;
/**
- * A time signal from a manual (user provided) source. The value consists of the number of
- * milliseconds elapsed since 1/1/1970 00:00:00 UTC and the time according to the elapsed realtime
- * clock when that number was established. The elapsed realtime clock is considered accurate but
- * volatile, so time signals must not be persisted across device resets.
+ * A time signal from a manual (user provided) source.
+ *
+ * <p>{@code zoneId} contains the suggested time zone ID, e.g. "America/Los_Angeles".
+ *
+ * <p>{@code debugInfo} contains debugging metadata associated with the suggestion. This is used to
+ * record why the suggestion exists and how it was entered. This information exists only to aid in
+ * debugging and therefore is used by {@link #toString()}, but it is not for use in detection logic
+ * and is not considered in {@link #hashCode()} or {@link #equals(Object)}.
*
* @hide
*/
@@ -48,10 +52,8 @@ public final class ManualTimeZoneSuggestion implements Parcelable {
}
};
- @NonNull
- private final String mZoneId;
- @Nullable
- private ArrayList<String> mDebugInfo;
+ @NonNull private final String mZoneId;
+ @Nullable private ArrayList<String> mDebugInfo;
public ManualTimeZoneSuggestion(@NonNull String zoneId) {
mZoneId = Objects.requireNonNull(zoneId);
diff --git a/core/java/android/app/timezonedetector/PhoneTimeZoneSuggestion.java b/core/java/android/app/timezonedetector/PhoneTimeZoneSuggestion.java
index ebaf951130ca..0544ccd3f4c5 100644
--- a/core/java/android/app/timezonedetector/PhoneTimeZoneSuggestion.java
+++ b/core/java/android/app/timezonedetector/PhoneTimeZoneSuggestion.java
@@ -34,10 +34,14 @@ import java.util.Objects;
* A time zone suggestion from an identified telephony source, e.g. from MCC and NITZ information
* associated with a specific radio.
*
- * <p>The time zone ID can be {@code null} to indicate that the telephony source has entered an
- * "un-opinionated" state and any previous suggestions from that source are being withdrawn.
- * When not {@code null}, the value consists of a suggested time zone ID and metadata that can be
- * used to judge quality / certainty of the suggestion.
+ * <p>{@code slotIndex} identifies the suggestion source. This enables detection logic to identify
+ * suggestions from the same source when there are several in use.
+ *
+ * <p>{@code zoneId}. When not {@code null}, {@code zoneId} contains the suggested time zone ID,
+ * e.g. "America/Los_Angeles". Suggestion metadata like {@code matchType} and {@code quality} can be
+ * used to judge quality / certainty of the suggestion. {@code zoneId} can be {@code null} to
+ * indicate that the telephony source has entered an "un-opinionated" state and any previous
+ * suggestion from the same source is being withdrawn.
*
* <p>{@code matchType} must be set to {@link #MATCH_TYPE_NA} when {@code zoneId} is {@code null},
* and one of the other {@code MATCH_TYPE_} values when it is not {@code null}.
@@ -45,6 +49,12 @@ import java.util.Objects;
* <p>{@code quality} must be set to {@link #QUALITY_NA} when {@code zoneId} is {@code null},
* and one of the other {@code QUALITY_} values when it is not {@code null}.
*
+ * <p>{@code debugInfo} contains debugging metadata associated with the suggestion. This is used to
+ * record why the suggestion exists, e.g. what triggered it to be made and what heuristic was used
+ * to determine the time zone or its absence. This information exists only to aid in debugging and
+ * therefore is used by {@link #toString()}, but it is not for use in detection logic and is not
+ * considered in {@link #hashCode()} or {@link #equals(Object)}.
+ *
* @hide
*/
@SystemApi(client = SystemApi.Client.MODULE_LIBRARIES)
@@ -131,38 +141,11 @@ public final class PhoneTimeZoneSuggestion implements Parcelable {
*/
public static final int QUALITY_MULTIPLE_ZONES_WITH_DIFFERENT_OFFSETS = 3;
- /**
- * The ID of the phone this suggestion is associated with. For multiple-sim devices this
- * helps to establish source so filtering / stickiness can be implemented.
- */
private final int mSlotIndex;
-
- /**
- * The suggestion. {@code null} means there is no current suggestion and any previous suggestion
- * should be forgotten.
- */
- @Nullable
- private final String mZoneId;
-
- /**
- * The type of "match" used to establish the time zone.
- */
- @MatchType
- private final int mMatchType;
-
- /**
- * A measure of the quality of the time zone suggestion, i.e. how confident one could be in
- * it.
- */
- @Quality
- private final int mQuality;
-
- /**
- * Free-form debug information about how the suggestion was derived. Used for debug only,
- * intentionally not used in equals(), etc.
- */
- @Nullable
- private List<String> mDebugInfo;
+ @Nullable private final String mZoneId;
+ @MatchType private final int mMatchType;
+ @Quality private final int mQuality;
+ @Nullable private List<String> mDebugInfo;
private PhoneTimeZoneSuggestion(Builder builder) {
mSlotIndex = builder.mSlotIndex;
@@ -203,8 +186,9 @@ public final class PhoneTimeZoneSuggestion implements Parcelable {
}
/**
- * Returns an identifier for the source of this suggestion. When a device has several sim slots
- * or equivalent, it is used to identify which one the suggestion is from.
+ * Returns an identifier for the source of this suggestion.
+ *
+ * <p>See {@link PhoneTimeZoneSuggestion} for more information about {@code slotIndex}.
*/
public int getSlotIndex() {
return mSlotIndex;
@@ -212,8 +196,9 @@ public final class PhoneTimeZoneSuggestion implements Parcelable {
/**
* Returns the suggested time zone Olson ID, e.g. "America/Los_Angeles". {@code null} means that
- * the caller is no longer sure what the current time zone is. See
- * {@link PhoneTimeZoneSuggestion} for the associated {@code matchType} / {@code quality} rules.
+ * the caller is no longer sure what the current time zone is.
+ *
+ * <p>See {@link PhoneTimeZoneSuggestion} for more information about {@code zoneId}.
*/
@Nullable
public String getZoneId() {
@@ -222,8 +207,9 @@ public final class PhoneTimeZoneSuggestion implements Parcelable {
/**
* Returns information about how the suggestion was determined which could be used to rank
- * suggestions when several are available from different sources. See
- * {@link PhoneTimeZoneSuggestion} for the associated rules.
+ * suggestions when several are available from different sources.
+ *
+ * <p>See {@link PhoneTimeZoneSuggestion} for more information about {@code matchType}.
*/
@MatchType
public int getMatchType() {
@@ -231,8 +217,9 @@ public final class PhoneTimeZoneSuggestion implements Parcelable {
}
/**
- * Returns information about the likelihood of the suggested zone being correct. See
- * {@link PhoneTimeZoneSuggestion} for the associated rules.
+ * Returns information about the likelihood of the suggested zone being correct.
+ *
+ * <p>See {@link PhoneTimeZoneSuggestion} for more information about {@code quality}.
*/
@Quality
public int getQuality() {
@@ -240,8 +227,9 @@ public final class PhoneTimeZoneSuggestion implements Parcelable {
}
/**
- * Returns debug metadata for the suggestion. The information is present in {@link #toString()}
- * but is not considered for {@link #equals(Object)} and {@link #hashCode()}.
+ * Returns debug metadata for the suggestion.
+ *
+ * <p>See {@link PhoneTimeZoneSuggestion} for more information about {@code debugInfo}.
*/
@NonNull
public List<String> getDebugInfo() {
@@ -250,9 +238,9 @@ public final class PhoneTimeZoneSuggestion implements Parcelable {
}
/**
- * Associates information with the instance that can be useful for debugging / logging. The
- * information is present in {@link #toString()} but is not considered for
- * {@link #equals(Object)} and {@link #hashCode()}.
+ * Associates information with the instance that can be useful for debugging / logging.
+ *
+ * <p>See {@link PhoneTimeZoneSuggestion} for more information about {@code debugInfo}.
*/
public void addDebugInfo(@NonNull String debugInfo) {
if (mDebugInfo == null) {
@@ -262,9 +250,9 @@ public final class PhoneTimeZoneSuggestion implements Parcelable {
}
/**
- * Associates information with the instance that can be useful for debugging / logging. The
- * information is present in {@link #toString()} but is not considered for
- * {@link #equals(Object)} and {@link #hashCode()}.
+ * Associates information with the instance that can be useful for debugging / logging.
+ *
+ * <p>See {@link PhoneTimeZoneSuggestion} for more information about {@code debugInfo}.
*/
public void addDebugInfo(@NonNull List<String> debugInfo) {
if (mDebugInfo == null) {
@@ -317,12 +305,19 @@ public final class PhoneTimeZoneSuggestion implements Parcelable {
@Quality private int mQuality;
@Nullable private List<String> mDebugInfo;
+ /**
+ * Creates a builder with the specified {@code slotIndex}.
+ *
+ * <p>See {@link PhoneTimeZoneSuggestion} for more information about {@code slotIndex}.
+ */
public Builder(int slotIndex) {
mSlotIndex = slotIndex;
}
/**
* Returns the builder for call chaining.
+ *
+ * <p>See {@link PhoneTimeZoneSuggestion} for more information about {@code zoneId}.
*/
@NonNull
public Builder setZoneId(@Nullable String zoneId) {
@@ -330,21 +325,33 @@ public final class PhoneTimeZoneSuggestion implements Parcelable {
return this;
}
- /** Returns the builder for call chaining. */
+ /**
+ * Returns the builder for call chaining.
+ *
+ * <p>See {@link PhoneTimeZoneSuggestion} for more information about {@code matchType}.
+ */
@NonNull
public Builder setMatchType(@MatchType int matchType) {
mMatchType = matchType;
return this;
}
- /** Returns the builder for call chaining. */
+ /**
+ * Returns the builder for call chaining.
+ *
+ * <p>See {@link PhoneTimeZoneSuggestion} for more information about {@code quality}.
+ */
@NonNull
public Builder setQuality(@Quality int quality) {
mQuality = quality;
return this;
}
- /** Returns the builder for call chaining. */
+ /**
+ * Returns the builder for call chaining.
+ *
+ * <p>See {@link PhoneTimeZoneSuggestion} for more information about {@code debugInfo}.
+ */
@NonNull
public Builder addDebugInfo(@NonNull String debugInfo) {
if (mDebugInfo == null) {
diff --git a/core/java/android/content/Context.java b/core/java/android/content/Context.java
index ec40b7e3508c..249e58244870 100644
--- a/core/java/android/content/Context.java
+++ b/core/java/android/content/Context.java
@@ -5099,6 +5099,7 @@ public abstract class Context {
* @hide
*/
@SystemApi
+ @TestApi
public static final String APP_INTEGRITY_SERVICE = "app_integrity";
/**
diff --git a/core/java/android/content/Intent.java b/core/java/android/content/Intent.java
index acffec98b2fc..6f8a99fce897 100644
--- a/core/java/android/content/Intent.java
+++ b/core/java/android/content/Intent.java
@@ -4284,7 +4284,15 @@ public class Intent implements Parcelable, Cloneable {
public static final String ACTION_SERVICE_STATE = "android.intent.action.SERVICE_STATE";
/**
- * Used for looking up a Data Loader Service provider.
+ * Used by {@link services.core.java.com.android.server.pm.DataLoaderManagerService}
+ * for querying Data Loader Service providers. Data loader service providers register this
+ * intent filter in their manifests, so that they can be looked up and bound to by
+ * {@code DataLoaderManagerService}.
+ *
+ * Data loader service providers must be privileged apps.
+ * See {@link com.android.server.pm.PackageManagerShellCommandDataLoader} as an example of such
+ * data loader service provider.
+ *
* @hide
*/
@SystemApi
diff --git a/core/java/android/content/integrity/AppIntegrityManager.java b/core/java/android/content/integrity/AppIntegrityManager.java
index e53ef66020f1..9f95d4d75f6f 100644
--- a/core/java/android/content/integrity/AppIntegrityManager.java
+++ b/core/java/android/content/integrity/AppIntegrityManager.java
@@ -19,6 +19,7 @@ package android.content.integrity;
import android.annotation.NonNull;
import android.annotation.SystemApi;
import android.annotation.SystemService;
+import android.annotation.TestApi;
import android.content.Context;
import android.content.IntentSender;
import android.content.pm.ParceledListSlice;
@@ -33,6 +34,7 @@ import android.os.RemoteException;
*
* @hide
*/
+@TestApi
@SystemApi
@SystemService(Context.APP_INTEGRITY_SERVICE)
public class AppIntegrityManager {
@@ -100,4 +102,23 @@ public class AppIntegrityManager {
throw e.rethrowAsRuntimeException();
}
}
+
+ /**
+ * Get current RuleSet on device.
+ *
+ * <p>Warning: this method is only used for tests.
+ *
+ * @hide
+ */
+ @TestApi
+ @NonNull
+ public RuleSet getCurrentRuleSet() {
+ try {
+ ParceledListSlice<Rule> rules = mManager.getCurrentRules();
+ String version = mManager.getCurrentRuleSetVersion();
+ return new RuleSet.Builder().setVersion(version).addRules(rules.getList()).build();
+ } catch (RemoteException e) {
+ throw e.rethrowAsRuntimeException();
+ }
+ }
}
diff --git a/core/java/android/content/integrity/AtomicFormula.java b/core/java/android/content/integrity/AtomicFormula.java
index 76007e67798d..439d53661b8e 100644
--- a/core/java/android/content/integrity/AtomicFormula.java
+++ b/core/java/android/content/integrity/AtomicFormula.java
@@ -460,14 +460,14 @@ public abstract class AtomicFormula extends IntegrityFormula {
}
private static String hashValue(@Key int key, String value) {
- // Hash the string value unless it is a PACKAGE_NAME or INSTALLER_NAME and the value is
- // less than 33 characters.
- if (value.length() <= 32) {
+ // Hash the string value if it is a PACKAGE_NAME or INSTALLER_NAME and the value is
+ // greater than 32 characters.
+ if (value.length() > 32) {
if (key == PACKAGE_NAME || key == INSTALLER_NAME) {
- return value;
+ return hash(value);
}
}
- return hash(value);
+ return value;
}
private static String hash(String value) {
diff --git a/core/java/android/content/integrity/IAppIntegrityManager.aidl b/core/java/android/content/integrity/IAppIntegrityManager.aidl
index 6b73fd70bb99..4714ad7c7989 100644
--- a/core/java/android/content/integrity/IAppIntegrityManager.aidl
+++ b/core/java/android/content/integrity/IAppIntegrityManager.aidl
@@ -25,4 +25,5 @@ interface IAppIntegrityManager {
void updateRuleSet(String version, in ParceledListSlice<Rule> rules, in IntentSender statusReceiver);
String getCurrentRuleSetVersion();
String getCurrentRuleSetProvider();
+ ParceledListSlice<Rule> getCurrentRules();
}
diff --git a/core/java/android/content/integrity/IntegrityFormula.java b/core/java/android/content/integrity/IntegrityFormula.java
index 8505d32e3f02..a2d937e4df31 100644
--- a/core/java/android/content/integrity/IntegrityFormula.java
+++ b/core/java/android/content/integrity/IntegrityFormula.java
@@ -19,6 +19,7 @@ package android.content.integrity;
import android.annotation.IntDef;
import android.annotation.NonNull;
import android.annotation.SystemApi;
+import android.annotation.TestApi;
import android.content.integrity.AtomicFormula.BooleanAtomicFormula;
import android.content.integrity.AtomicFormula.LongAtomicFormula;
import android.content.integrity.AtomicFormula.StringAtomicFormula;
@@ -37,6 +38,7 @@ import java.util.Arrays;
* @hide
*/
@SystemApi
+@TestApi
@VisibleForTesting
public abstract class IntegrityFormula {
diff --git a/core/java/android/content/integrity/Rule.java b/core/java/android/content/integrity/Rule.java
index c421c4076d16..d29e6df5ec39 100644
--- a/core/java/android/content/integrity/Rule.java
+++ b/core/java/android/content/integrity/Rule.java
@@ -21,6 +21,7 @@ import static com.android.internal.util.Preconditions.checkArgument;
import android.annotation.IntDef;
import android.annotation.NonNull;
import android.annotation.SystemApi;
+import android.annotation.TestApi;
import android.os.Parcel;
import android.os.Parcelable;
@@ -37,6 +38,7 @@ import java.util.Objects;
*
* @hide
*/
+@TestApi
@SystemApi
@VisibleForTesting
public final class Rule implements Parcelable {
diff --git a/core/java/android/content/integrity/RuleSet.java b/core/java/android/content/integrity/RuleSet.java
index b423b54a7d92..e121ff8bbcaa 100644
--- a/core/java/android/content/integrity/RuleSet.java
+++ b/core/java/android/content/integrity/RuleSet.java
@@ -18,6 +18,7 @@ package android.content.integrity;
import android.annotation.NonNull;
import android.annotation.SystemApi;
+import android.annotation.TestApi;
import java.util.ArrayList;
import java.util.Collections;
@@ -29,6 +30,7 @@ import java.util.Objects;
*
* @hide
*/
+@TestApi
@SystemApi
public class RuleSet {
private final String mVersion;
diff --git a/core/java/android/content/om/OverlayManager.java b/core/java/android/content/om/OverlayManager.java
index 33d17763fb24..dbe39547b0ca 100644
--- a/core/java/android/content/om/OverlayManager.java
+++ b/core/java/android/content/om/OverlayManager.java
@@ -44,6 +44,16 @@ public class OverlayManager {
private final IOverlayManager mService;
private final Context mContext;
+ /**
+ * Pre R a {@link java.lang.SecurityException} would only be thrown by setEnabled APIs (e
+ * .g. {@link #setEnabled(String, boolean, UserHandle)}) for a permission error.
+ * Since R this no longer holds true, and {@link java.lang.SecurityException} can be
+ * thrown for any number of reasons, none of which are exposed to the caller.
+ *
+ * <p>To maintain existing API behavior, if a legacy permission failure or actor enforcement
+ * failure occurs for an app not yet targeting R, coerce it into an {@link
+ * java.lang.IllegalStateException}, which existed in the source prior to R.
+ */
@ChangeId
@EnabledAfter(targetSdkVersion = Build.VERSION_CODES.Q)
private static final long THROW_SECURITY_EXCEPTIONS = 147340954;
diff --git a/core/java/android/content/pm/PackageInstaller.java b/core/java/android/content/pm/PackageInstaller.java
index b1b9454aeddd..2acbb97cb20a 100644
--- a/core/java/android/content/pm/PackageInstaller.java
+++ b/core/java/android/content/pm/PackageInstaller.java
@@ -1467,6 +1467,8 @@ public class PackageInstaller {
public DataLoaderParams dataLoaderParams;
/** {@hide} */
public int rollbackDataPolicy = PackageManager.RollbackDataPolicy.RESTORE;
+ /** {@hide} */
+ public boolean forceQueryableOverride;
/**
* Construct parameters for a new package install session.
@@ -1499,6 +1501,7 @@ public class PackageInstaller {
installerPackageName = source.readString();
isMultiPackage = source.readBoolean();
isStaged = source.readBoolean();
+ forceQueryableOverride = source.readBoolean();
requiredInstalledVersionCode = source.readLong();
DataLoaderParamsParcel dataLoaderParamsParcel = source.readParcelable(
DataLoaderParamsParcel.class.getClassLoader());
@@ -1528,6 +1531,7 @@ public class PackageInstaller {
ret.installerPackageName = installerPackageName;
ret.isMultiPackage = isMultiPackage;
ret.isStaged = isStaged;
+ ret.forceQueryableOverride = forceQueryableOverride;
ret.requiredInstalledVersionCode = requiredInstalledVersionCode;
ret.dataLoaderParams = dataLoaderParams;
ret.rollbackDataPolicy = rollbackDataPolicy;
@@ -1904,6 +1908,14 @@ public class PackageInstaller {
this.dataLoaderParams = dataLoaderParams;
}
+ /**
+ *
+ * {@hide}
+ */
+ public void setForceQueryable() {
+ this.forceQueryableOverride = true;
+ }
+
/** {@hide} */
public void dump(IndentingPrintWriter pw) {
pw.printPair("mode", mode);
@@ -1923,6 +1935,7 @@ public class PackageInstaller {
pw.printPair("installerPackageName", installerPackageName);
pw.printPair("isMultiPackage", isMultiPackage);
pw.printPair("isStaged", isStaged);
+ pw.printPair("forceQueryable", forceQueryableOverride);
pw.printPair("requiredInstalledVersionCode", requiredInstalledVersionCode);
pw.printPair("dataLoaderParams", dataLoaderParams);
pw.printPair("rollbackDataPolicy", rollbackDataPolicy);
@@ -1954,6 +1967,7 @@ public class PackageInstaller {
dest.writeString(installerPackageName);
dest.writeBoolean(isMultiPackage);
dest.writeBoolean(isStaged);
+ dest.writeBoolean(forceQueryableOverride);
dest.writeLong(requiredInstalledVersionCode);
if (dataLoaderParams != null) {
dest.writeParcelable(dataLoaderParams.getData(), flags);
@@ -2078,6 +2092,8 @@ public class PackageInstaller {
/** {@hide} */
public boolean isStaged;
/** {@hide} */
+ public boolean forceQueryable;
+ /** {@hide} */
public int parentSessionId = INVALID_ID;
/** {@hide} */
public int[] childSessionIds = NO_SESSIONS;
@@ -2135,6 +2151,7 @@ public class PackageInstaller {
installFlags = source.readInt();
isMultiPackage = source.readBoolean();
isStaged = source.readBoolean();
+ forceQueryable = source.readBoolean();
parentSessionId = source.readInt();
childSessionIds = source.createIntArray();
if (childSessionIds == null) {
@@ -2476,6 +2493,14 @@ public class PackageInstaller {
}
/**
+ * Returns true if this session is marked as forceQueryable
+ * {@hide}
+ */
+ public boolean isForceQueryable() {
+ return forceQueryable;
+ }
+
+ /**
* Returns {@code true} if this session is an active staged session.
*
* We consider a session active if it has been committed and it is either pending
@@ -2636,6 +2661,7 @@ public class PackageInstaller {
dest.writeInt(installFlags);
dest.writeBoolean(isMultiPackage);
dest.writeBoolean(isStaged);
+ dest.writeBoolean(forceQueryable);
dest.writeInt(parentSessionId);
dest.writeIntArray(childSessionIds);
dest.writeBoolean(isStagedSessionApplied);
diff --git a/core/java/android/content/pm/PackageManager.java b/core/java/android/content/pm/PackageManager.java
index b64c001ea6e2..6d5e8fb0240e 100644
--- a/core/java/android/content/pm/PackageManager.java
+++ b/core/java/android/content/pm/PackageManager.java
@@ -1506,6 +1506,15 @@ public abstract class PackageManager {
*/
public static final int INSTALL_FAILED_WRONG_INSTALLED_VERSION = -121;
+ /**
+ * Installation return code: this is passed in the {@link PackageInstaller#EXTRA_LEGACY_STATUS}
+ * if the new package failed because it contains a request to use a process that was not
+ * explicitly defined as part of its &lt;processes&gt; tag.
+ *
+ * @hide
+ */
+ public static final int INSTALL_FAILED_PROCESS_NOT_DEFINED = -122;
+
/** @hide */
@IntDef(flag = true, prefix = { "DELETE_" }, value = {
DELETE_KEEP_DATA,
@@ -7237,6 +7246,7 @@ public abstract class PackageManager {
case INSTALL_FAILED_MISSING_SPLIT: return "INSTALL_FAILED_MISSING_SPLIT";
case INSTALL_FAILED_BAD_SIGNATURE: return "INSTALL_FAILED_BAD_SIGNATURE";
case INSTALL_FAILED_WRONG_INSTALLED_VERSION: return "INSTALL_FAILED_WRONG_INSTALLED_VERSION";
+ case INSTALL_FAILED_PROCESS_NOT_DEFINED: return "INSTALL_FAILED_PROCESS_NOT_DEFINED";
default: return Integer.toString(status);
}
}
diff --git a/core/java/android/content/pm/PackageParser.java b/core/java/android/content/pm/PackageParser.java
index dfe9fe64657d..b6b27b6e514a 100644
--- a/core/java/android/content/pm/PackageParser.java
+++ b/core/java/android/content/pm/PackageParser.java
@@ -954,6 +954,10 @@ public class PackageParser {
throw new PackageParserException(INSTALL_PARSE_FAILED_NOT_APK,
"No packages found in split");
}
+ // Apk directory is directly nested under the current directory
+ if (files.length == 1 && files[0].isDirectory()) {
+ return parseClusterPackageLite(files[0], flags);
+ }
String packageName = null;
int versionCode = 0;
@@ -1327,12 +1331,9 @@ public class PackageParser {
}
}
- pkg.setCodePath(packageDir.getCanonicalPath());
+ pkg.setCodePath(lite.codePath);
pkg.setUse32bitAbi(lite.use32bitAbi);
return pkg;
- } catch (IOException e) {
- throw new PackageParserException(INSTALL_PARSE_FAILED_UNEXPECTED_EXCEPTION,
- "Failed to get path: " + lite.baseCodePath, e);
} finally {
IoUtils.closeQuietly(assetLoader);
}
diff --git a/core/java/android/content/pm/PermissionInfo.java b/core/java/android/content/pm/PermissionInfo.java
index a0f089b2df41..3aa1a6d3f8ff 100644
--- a/core/java/android/content/pm/PermissionInfo.java
+++ b/core/java/android/content/pm/PermissionInfo.java
@@ -259,6 +259,17 @@ public class PermissionInfo extends PackageItemInfo implements Parcelable {
@TestApi
public static final int PROTECTION_FLAG_COMPANION = 0x800000;
+ /**
+ * Additional flag for {@link #protectionLevel}, corresponding
+ * to the <code>retailDemo</code> value of
+ * {@link android.R.attr#protectionLevel}.
+ *
+ * @hide
+ */
+ @SystemApi
+ @TestApi
+ public static final int PROTECTION_FLAG_RETAIL_DEMO = 0x1000000;
+
/** @hide */
@IntDef(flag = true, prefix = { "PROTECTION_FLAG_" }, value = {
PROTECTION_FLAG_PRIVILEGED,
@@ -282,6 +293,7 @@ public class PermissionInfo extends PackageItemInfo implements Parcelable {
PROTECTION_FLAG_APP_PREDICTOR,
PROTECTION_FLAG_TELEPHONY,
PROTECTION_FLAG_COMPANION,
+ PROTECTION_FLAG_RETAIL_DEMO,
})
@Retention(RetentionPolicy.SOURCE)
public @interface ProtectionFlags {}
@@ -528,6 +540,9 @@ public class PermissionInfo extends PackageItemInfo implements Parcelable {
if ((level & PermissionInfo.PROTECTION_FLAG_TELEPHONY) != 0) {
protLevel += "|telephony";
}
+ if ((level & PermissionInfo.PROTECTION_FLAG_RETAIL_DEMO) != 0) {
+ protLevel += "|retailDemo";
+ }
return protLevel;
}
diff --git a/core/java/android/content/pm/parsing/ApkLiteParseUtils.java b/core/java/android/content/pm/parsing/ApkLiteParseUtils.java
index 90a332ccb430..5be9c910744d 100644
--- a/core/java/android/content/pm/parsing/ApkLiteParseUtils.java
+++ b/core/java/android/content/pm/parsing/ApkLiteParseUtils.java
@@ -97,6 +97,10 @@ public class ApkLiteParseUtils {
throw new PackageParser.PackageParserException(
PackageManager.INSTALL_PARSE_FAILED_NOT_APK, "No packages found in split");
}
+ // Apk directory is directly nested under the current directory
+ if (files.length == 1 && files[0].isDirectory()) {
+ return parseClusterPackageLite(files[0], flags);
+ }
String packageName = null;
int versionCode = 0;
diff --git a/core/java/android/content/pm/parsing/ApkParseUtils.java b/core/java/android/content/pm/parsing/ApkParseUtils.java
index 7506412f72cc..5c8c9a41a520 100644
--- a/core/java/android/content/pm/parsing/ApkParseUtils.java
+++ b/core/java/android/content/pm/parsing/ApkParseUtils.java
@@ -205,11 +205,8 @@ public class ApkParseUtils {
}
}
- return parsingPackage.setCodePath(packageDir.getCanonicalPath())
+ return parsingPackage.setCodePath(lite.codePath)
.setUse32BitAbi(lite.use32bitAbi);
- } catch (IOException e) {
- throw new PackageParserException(INSTALL_PARSE_FAILED_UNEXPECTED_EXCEPTION,
- "Failed to get path: " + lite.baseCodePath, e);
} finally {
IoUtils.closeQuietly(assetLoader);
}
diff --git a/core/java/android/content/pm/parsing/ComponentParseUtils.java b/core/java/android/content/pm/parsing/ComponentParseUtils.java
index 9a0a6d54da50..a0f58120e8a1 100644
--- a/core/java/android/content/pm/parsing/ComponentParseUtils.java
+++ b/core/java/android/content/pm/parsing/ComponentParseUtils.java
@@ -3414,16 +3414,12 @@ public class ComponentParseUtils {
proc.name = sa.getNonConfigurationString(
R.styleable.AndroidManifestProcess_process,0);
proc.name = PackageParser.buildProcessName(parsingPackage.getPackageName(),
- null, proc.name, flags, separateProcesses, outError);
-
- if (proc.name == null || proc.name.length() <= 0) {
- outError[0] = "<process> does not specify android:process";
+ parsingPackage.getPackageName(), proc.name, flags, separateProcesses, outError);
+ if (outError[0] != null) {
return null;
}
- proc.name = PackageParser.buildProcessName(parsingPackage.getPackageName(),
- parsingPackage.getPackageName(), proc.name,
- flags, separateProcesses, outError);
- if (outError[0] != null) {
+ if (proc.name == null || proc.name.length() <= 0) {
+ outError[0] = "<process> does not specify android:process";
return null;
}
} finally {
diff --git a/core/java/android/hardware/biometrics/BiometricManager.java b/core/java/android/hardware/biometrics/BiometricManager.java
index 4c114fdc5f10..125b676e14f7 100644
--- a/core/java/android/hardware/biometrics/BiometricManager.java
+++ b/core/java/android/hardware/biometrics/BiometricManager.java
@@ -18,6 +18,7 @@ package android.hardware.biometrics;
import static android.Manifest.permission.USE_BIOMETRIC;
import static android.Manifest.permission.USE_BIOMETRIC_INTERNAL;
+import static android.Manifest.permission.WRITE_DEVICE_CONFIG;
import android.annotation.IntDef;
import android.annotation.RequiresPermission;
@@ -87,9 +88,15 @@ public class BiometricManager {
/**
* Empty set with no authenticators specified.
+ *
+ * <p>This constant is intended for use by {@link android.provider.DeviceConfig} to adjust
+ * the reported strength of a biometric sensor. It is not a valid parameter for any of the
+ * public {@link android.hardware.biometrics} APIs.
+ *
* @hide
*/
@SystemApi
+ @RequiresPermission(WRITE_DEVICE_CONFIG)
int EMPTY_SET = 0x0000;
/**
@@ -115,12 +122,16 @@ public class BiometricManager {
/**
* Any biometric (e.g. fingerprint, iris, or face) on the device that meets or exceeds the
- * requirements for <strong>Convenience</strong>, as defined by the Android CDD. This
- * is not a valid parameter to any of the {@link android.hardware.biometrics} APIs, since
- * the CDD allows only {@link #BIOMETRIC_WEAK} and stronger authenticators to participate.
+ * requirements for <strong>Convenience</strong>, as defined by the Android CDD.
+ *
+ * <p>This constant is intended for use by {@link android.provider.DeviceConfig} to adjust
+ * the reported strength of a biometric sensor. It is not a valid parameter for any of the
+ * public {@link android.hardware.biometrics} APIs.
+ *
* @hide
*/
@SystemApi
+ @RequiresPermission(WRITE_DEVICE_CONFIG)
int BIOMETRIC_CONVENIENCE = 0x0FFF;
/**
diff --git a/core/java/android/net/IConnectivityManager.aidl b/core/java/android/net/IConnectivityManager.aidl
index 0dc66b5052c5..1089a197ff59 100644
--- a/core/java/android/net/IConnectivityManager.aidl
+++ b/core/java/android/net/IConnectivityManager.aidl
@@ -117,10 +117,18 @@ interface IConnectivityManager
boolean prepareVpn(String oldPackage, String newPackage, int userId);
- void setVpnPackageAuthorization(String packageName, int userId, boolean authorized);
+ void setVpnPackageAuthorization(String packageName, int userId, int vpnType);
ParcelFileDescriptor establishVpn(in VpnConfig config);
+ boolean provisionVpnProfile(in VpnProfile profile, String packageName);
+
+ void deleteVpnProfile(String packageName);
+
+ void startVpnProfile(String packageName);
+
+ void stopVpnProfile(String packageName);
+
VpnConfig getVpnConfig(int userId);
@UnsupportedAppUsage
diff --git a/core/java/android/net/VpnManager.java b/core/java/android/net/VpnManager.java
index f95807a14f00..f19ba0f5ef51 100644
--- a/core/java/android/net/VpnManager.java
+++ b/core/java/android/net/VpnManager.java
@@ -18,10 +18,22 @@ package android.net;
import static com.android.internal.util.Preconditions.checkNotNull;
+import android.annotation.IntDef;
import android.annotation.NonNull;
import android.annotation.Nullable;
+import android.app.Activity;
+import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
+import android.content.res.Resources;
+import android.os.RemoteException;
+
+import com.android.internal.net.VpnProfile;
+
+import java.io.IOException;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.security.GeneralSecurityException;
/**
* This class provides an interface for apps to manage platform VPN profiles
@@ -38,9 +50,30 @@ import android.content.Intent;
* @see Ikev2VpnProfile
*/
public class VpnManager {
+ /** Type representing a lack of VPN @hide */
+ public static final int TYPE_VPN_NONE = -1;
+ /** VPN service type code @hide */
+ public static final int TYPE_VPN_SERVICE = 1;
+ /** Platform VPN type code @hide */
+ public static final int TYPE_VPN_PLATFORM = 2;
+
+ /** @hide */
+ @IntDef(value = {TYPE_VPN_NONE, TYPE_VPN_SERVICE, TYPE_VPN_PLATFORM})
+ @Retention(RetentionPolicy.SOURCE)
+ public @interface VpnType {}
+
@NonNull private final Context mContext;
@NonNull private final IConnectivityManager mService;
+ private static Intent getIntentForConfirmation() {
+ final Intent intent = new Intent();
+ final ComponentName componentName = ComponentName.unflattenFromString(
+ Resources.getSystem().getString(
+ com.android.internal.R.string.config_platformVpnConfirmDialogComponent));
+ intent.setComponent(componentName);
+ return intent;
+ }
+
/**
* Create an instance of the VpnManger with the given context.
*
@@ -57,18 +90,49 @@ public class VpnManager {
/**
* Install a VpnProfile configuration keyed on the calling app's package name.
*
- * @param profile the PlatformVpnProfile provided by this package. Will override any previous
- * PlatformVpnProfile stored for this package.
- * @return an intent to request user consent if needed (null otherwise).
+ * <p>This method returns {@code null} if user consent has already been granted, or an {@link
+ * Intent} to a system activity. If an intent is returned, the application should launch the
+ * activity using {@link Activity#startActivityForResult} to request user consent. The activity
+ * may pop up a dialog to require user action, and the result will come back via its {@link
+ * Activity#onActivityResult}. If the result is {@link Activity#RESULT_OK}, the user has
+ * consented, and the VPN profile can be started.
+ *
+ * @param profile the VpnProfile provided by this package. Will override any previous VpnProfile
+ * stored for this package.
+ * @return an Intent requesting user consent to start the VPN, or null if consent is not
+ * required based on privileges or previous user consent.
*/
@Nullable
public Intent provisionVpnProfile(@NonNull PlatformVpnProfile profile) {
- throw new UnsupportedOperationException("Not yet implemented");
+ final VpnProfile internalProfile;
+
+ try {
+ internalProfile = profile.toVpnProfile();
+ } catch (GeneralSecurityException | IOException e) {
+ // Conversion to VpnProfile failed; this is an invalid profile. Both of these exceptions
+ // indicate a failure to convert a PrivateKey or X509Certificate to a Base64 encoded
+ // string as required by the VpnProfile.
+ throw new IllegalArgumentException("Failed to serialize PlatformVpnProfile", e);
+ }
+
+ try {
+ // Profile can never be null; it either gets set, or an exception is thrown.
+ if (mService.provisionVpnProfile(internalProfile, mContext.getOpPackageName())) {
+ return null;
+ }
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ return getIntentForConfirmation();
}
/** Delete the VPN profile configuration that was provisioned by the calling app */
public void deleteProvisionedVpnProfile() {
- throw new UnsupportedOperationException("Not yet implemented");
+ try {
+ mService.deleteVpnProfile(mContext.getOpPackageName());
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
}
/**
@@ -78,11 +142,19 @@ public class VpnManager {
* setup, or if user consent has not been granted
*/
public void startProvisionedVpnProfile() {
- throw new UnsupportedOperationException("Not yet implemented");
+ try {
+ mService.startVpnProfile(mContext.getOpPackageName());
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
}
/** Tear down the VPN provided by the calling app (if any) */
public void stopProvisionedVpnProfile() {
- throw new UnsupportedOperationException("Not yet implemented");
+ try {
+ mService.stopVpnProfile(mContext.getOpPackageName());
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
}
}
diff --git a/core/java/android/net/VpnService.java b/core/java/android/net/VpnService.java
index 4b804b097d59..63e510733907 100644
--- a/core/java/android/net/VpnService.java
+++ b/core/java/android/net/VpnService.java
@@ -234,7 +234,7 @@ public class VpnService extends Service {
if (!cm.prepareVpn(packageName, null, userId)) {
cm.prepareVpn(null, packageName, userId);
}
- cm.setVpnPackageAuthorization(packageName, userId, true);
+ cm.setVpnPackageAuthorization(packageName, userId, VpnManager.TYPE_VPN_SERVICE);
} catch (RemoteException e) {
// ignore
}
diff --git a/core/java/android/os/Binder.java b/core/java/android/os/Binder.java
index a90ab8564100..fe7c7c921b67 100644
--- a/core/java/android/os/Binder.java
+++ b/core/java/android/os/Binder.java
@@ -996,6 +996,18 @@ public class Binder implements IBinder {
return 0;
}
+ /** @hide */
+ @Override
+ public final native @Nullable IBinder getExtension();
+
+ /**
+ * Set the binder extension.
+ * This should be called immediately when the object is created.
+ *
+ * @hide
+ */
+ public final native void setExtension(@Nullable IBinder extension);
+
/**
* Default implementation rewinds the parcels and calls onTransact. On
* the remote side, transact calls into the binder to do the IPC.
diff --git a/core/java/android/os/BinderProxy.java b/core/java/android/os/BinderProxy.java
index ac70b523bcc1..be307ab4737d 100644
--- a/core/java/android/os/BinderProxy.java
+++ b/core/java/android/os/BinderProxy.java
@@ -455,6 +455,10 @@ public final class BinderProxy implements IBinder {
return null;
}
+ /** @hide */
+ @Override
+ public native @Nullable IBinder getExtension() throws RemoteException;
+
/**
* Perform a binder transaction on a proxy.
*
diff --git a/core/java/android/os/IBinder.java b/core/java/android/os/IBinder.java
index f5fe9c3334bd..486ec2ab2b88 100644
--- a/core/java/android/os/IBinder.java
+++ b/core/java/android/os/IBinder.java
@@ -257,6 +257,18 @@ public interface IBinder {
@NonNull ResultReceiver resultReceiver) throws RemoteException;
/**
+ * Get the binder extension of this binder interface.
+ * This allows one to customize an interface without having to modify the original interface.
+ *
+ * @return null if don't have binder extension
+ * @throws RemoteException
+ * @hide
+ */
+ public default @Nullable IBinder getExtension() throws RemoteException {
+ throw new IllegalStateException("Method is not implemented");
+ }
+
+ /**
* Perform a generic operation with the object.
*
* @param code The action to perform. This should
diff --git a/core/java/android/os/Process.java b/core/java/android/os/Process.java
index 8daf6e8cc1e9..8d0946156044 100644
--- a/core/java/android/os/Process.java
+++ b/core/java/android/os/Process.java
@@ -113,6 +113,12 @@ public class Process {
public static final int KEYSTORE_UID = 1017;
/**
+ * Defines the UID/GID for credstore.
+ * @hide
+ */
+ public static final int CREDSTORE_UID = 1076;
+
+ /**
* Defines the UID/GID for the NFC service process.
* @hide
*/
diff --git a/core/java/android/os/UpdateEngine.java b/core/java/android/os/UpdateEngine.java
index 73e1adf134f2..223f92054f79 100644
--- a/core/java/android/os/UpdateEngine.java
+++ b/core/java/android/os/UpdateEngine.java
@@ -19,6 +19,7 @@ package android.os;
import android.annotation.IntDef;
import android.annotation.NonNull;
import android.annotation.SystemApi;
+import android.annotation.WorkerThread;
import android.content.res.AssetFileDescriptor;
import android.os.IUpdateEngine;
import android.os.IUpdateEngineCallback;
@@ -478,7 +479,7 @@ public class UpdateEngine {
* </ul>
*/
@ErrorCode
- public int errorCode() {
+ public int getErrorCode() {
return mErrorCode;
}
@@ -492,14 +493,15 @@ public class UpdateEngine {
*
* @return The following values:
* <ul>
- * <li>zero if {@link #errorCode} returns {@link ErrorCodeConstants#SUCCESS}</li>
- * <li>non-zero if {@link #errorCode} returns {@link ErrorCodeConstants#NOT_ENOUGH_SPACE}.
+ * <li>zero if {@link #getErrorCode} returns {@link ErrorCodeConstants#SUCCESS}</li>
+ * <li>non-zero if {@link #getErrorCode} returns
+ * {@link ErrorCodeConstants#NOT_ENOUGH_SPACE}.
* Value is the estimated total space required on userdata partition.</li>
* </ul>
- * @throws IllegalStateException if {@link #errorCode} is not one of the above.
+ * @throws IllegalStateException if {@link #getErrorCode} is not one of the above.
*
*/
- public long freeSpaceRequired() {
+ public long getFreeSpaceRequired() {
if (mErrorCode == ErrorCodeConstants.SUCCESS) {
return 0;
}
@@ -507,7 +509,7 @@ public class UpdateEngine {
return mFreeSpaceRequired;
}
throw new IllegalStateException(String.format(
- "freeSpaceRequired() is not available when error code is %d", mErrorCode));
+ "getFreeSpaceRequired() is not available when error code is %d", mErrorCode));
}
}
@@ -531,8 +533,10 @@ public class UpdateEngine {
*
* @param payloadMetadataFilename See {@link #verifyPayloadMetadata}.
* @param headerKeyValuePairs See {@link #applyPayload}.
- * @return See {@link AllocateSpaceResult}.
+ * @return See {@link AllocateSpaceResult#getErrorCode} and
+ * {@link AllocateSpaceResult#getFreeSpaceRequired}.
*/
+ @WorkerThread
@NonNull
public AllocateSpaceResult allocateSpace(
@NonNull String payloadMetadataFilename,
@@ -583,6 +587,7 @@ public class UpdateEngine {
* @throws ServiceSpecificException if other transient errors has occurred.
* A reboot may or may not help resolving the issue.
*/
+ @WorkerThread
@ErrorCode
public int cleanupAppliedPayload() {
try {
diff --git a/core/java/android/os/incremental/IncrementalFileStorages.java b/core/java/android/os/incremental/IncrementalFileStorages.java
index 4a668791aeb9..987a53e337a0 100644
--- a/core/java/android/os/incremental/IncrementalFileStorages.java
+++ b/core/java/android/os/incremental/IncrementalFileStorages.java
@@ -17,24 +17,24 @@
package android.os.incremental;
/**
- * Set up files and directories used in an installation session.
- * Currently only used by Incremental Installation.
- * For Incremental installation, the expected outcome of this function is:
- * 0) All the files are in defaultStorage
- * 1) All APK files are in the same directory, bound to mApkStorage, and bound to the
- * InstallerSession's stage dir. The files are linked from mApkStorage to defaultStorage.
- * 2) All lib files are in the sub directories as their names suggest, and in the same parent
- * directory as the APK files. The files are linked from mApkStorage to defaultStorage.
- * 3) OBB files are in another directory that is different from APK files and lib files, bound
- * to mObbStorage. The files are linked from mObbStorage to defaultStorage.
+ * Set up files and directories used in an installation session. Currently only used by Incremental
+ * Installation. For Incremental installation, the expected outcome of this function is: 0) All the
+ * files are in defaultStorage 1) All APK files are in the same directory, bound to mApkStorage, and
+ * bound to the InstallerSession's stage dir. The files are linked from mApkStorage to
+ * defaultStorage. 2) All lib files are in the sub directories as their names suggest, and in the
+ * same parent directory as the APK files. The files are linked from mApkStorage to defaultStorage.
+ * 3) OBB files are in another directory that is different from APK files and lib files, bound to
+ * mObbStorage. The files are linked from mObbStorage to defaultStorage.
*
* @throws IllegalStateException the session is not an Incremental installation session.
*/
import android.annotation.NonNull;
import android.annotation.Nullable;
+import android.content.Context;
import android.content.pm.DataLoaderParams;
import android.content.pm.InstallationFile;
+import android.text.TextUtils;
import android.util.Slog;
import java.io.File;
@@ -42,6 +42,8 @@ import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
+import java.util.List;
+import java.util.Objects;
import java.util.Random;
/**
@@ -50,6 +52,10 @@ import java.util.Random;
*/
public final class IncrementalFileStorages {
private static final String TAG = "IncrementalFileStorages";
+
+ private static final String TMP_DIR_ROOT = "/data/incremental/tmp";
+ private static final Random TMP_DIR_RANDOM = new Random();
+
private @Nullable IncrementalStorage mDefaultStorage;
private @Nullable String mDefaultDir;
private @NonNull IncrementalManager mIncrementalManager;
@@ -61,41 +67,77 @@ public final class IncrementalFileStorages {
* TODO(b/133435829): code clean up
*
* @throws IllegalStateException the session is not an Incremental installation session.
+ * @throws IOException if fails to setup files or directories.
*/
- public IncrementalFileStorages(@NonNull String packageName,
+ public static IncrementalFileStorages initialize(Context context,
@NonNull File stageDir,
+ @NonNull DataLoaderParams dataLoaderParams,
+ List<InstallationFile> addedFiles) throws IOException {
+ // TODO(b/136132412): sanity check if session should not be incremental
+ IncrementalManager incrementalManager = (IncrementalManager) context.getSystemService(
+ Context.INCREMENTAL_SERVICE);
+ if (incrementalManager == null) {
+ // TODO(b/146080380): add incremental-specific error code
+ throw new IOException("Failed to obtain incrementalManager.");
+ }
+
+ IncrementalFileStorages result = null;
+ try {
+ result = new IncrementalFileStorages(stageDir, incrementalManager, dataLoaderParams);
+ for (InstallationFile file : addedFiles) {
+ if (file.getFileType() == InstallationFile.FILE_TYPE_APK) {
+ try {
+ result.addApkFile(file);
+ } catch (IOException e) {
+ // TODO(b/146080380): add incremental-specific error code
+ throw new IOException(
+ "Failed to add and configure Incremental File: " + file.getName(),
+ e);
+ }
+ } else {
+ throw new IOException("Unknown file type: " + file.getFileType());
+ }
+ }
+
+ if (!result.mDefaultStorage.startLoading()) {
+ // TODO(b/146080380): add incremental-specific error code
+ throw new IOException("Failed to start loading data for Incremental installation.");
+ }
+
+ return result;
+ } catch (IOException e) {
+ if (result != null) {
+ result.cleanUp();
+ }
+ throw e;
+ }
+ }
+
+ private IncrementalFileStorages(@NonNull File stageDir,
@NonNull IncrementalManager incrementalManager,
- @NonNull DataLoaderParams dataLoaderParams) {
+ @NonNull DataLoaderParams dataLoaderParams) throws IOException {
mStageDir = stageDir;
mIncrementalManager = incrementalManager;
if (dataLoaderParams.getComponentName().getPackageName().equals("local")) {
final String incrementalPath = dataLoaderParams.getArguments();
- mDefaultStorage = mIncrementalManager.openStorage(incrementalPath);
mDefaultDir = incrementalPath;
- return;
- }
- mDefaultDir = getTempDir();
- if (mDefaultDir == null) {
- return;
+ if (TextUtils.isEmpty(mDefaultDir)) {
+ throw new IOException("Failed to create storage: incrementalPath is empty");
+ }
+ mDefaultStorage = mIncrementalManager.openStorage(incrementalPath);
+ } else {
+ mDefaultDir = getTempDir();
+ if (mDefaultDir == null) {
+ throw new IOException("Failed to create storage: tempDir is empty");
+ }
+ mDefaultStorage = mIncrementalManager.createStorage(mDefaultDir,
+ dataLoaderParams,
+ IncrementalManager.CREATE_MODE_CREATE
+ | IncrementalManager.CREATE_MODE_TEMPORARY_BIND, false);
}
- mDefaultStorage = mIncrementalManager.createStorage(mDefaultDir,
- dataLoaderParams,
- IncrementalManager.CREATE_MODE_CREATE
- | IncrementalManager.CREATE_MODE_TEMPORARY_BIND, false);
- }
- /**
- * Adds a file into the installation session. Makes sure it will be placed inside
- * a proper storage instance, based on its file type.
- */
- public void addFile(@NonNull InstallationFile file) throws IOException {
if (mDefaultStorage == null) {
- throw new IOException("Cannot add file because default storage does not exist");
- }
- if (file.getFileType() == InstallationFile.FILE_TYPE_APK) {
- addApkFile(file);
- } else {
- throw new IOException("Unknown file type: " + file.getFileType());
+ throw new IOException("Failed to create storage");
}
}
@@ -108,26 +150,6 @@ public final class IncrementalFileStorages {
mDefaultStorage.makeFile(apkName, apk.getSize(), null,
apk.getMetadata(), 0, null, null, null);
}
- if (targetFile.exists()) {
- Slog.i(TAG, "!!! created: " + targetFile.getAbsolutePath());
- }
- }
-
- /**
- * Starts loading data for default storage.
- * TODO(b/136132412): update the implementation with latest API design.
- */
- public boolean startLoading() {
- if (mDefaultStorage == null) {
- return false;
- }
- return mDefaultStorage.startLoading();
- }
-
- /**
- * Sets up obb storage directory and create bindings.
- */
- public void finishSetUp() {
}
/**
@@ -135,22 +157,21 @@ public final class IncrementalFileStorages {
* TODO(b/136132412): make sure unnecessary binds are removed but useful storages are kept
*/
public void cleanUp() {
- if (mDefaultStorage != null && mDefaultDir != null) {
- try {
- mDefaultStorage.unBind(mDefaultDir);
- mDefaultStorage.unBind(mStageDir.getAbsolutePath());
- } catch (IOException ignored) {
- }
- mDefaultDir = null;
- mDefaultStorage = null;
+ Objects.requireNonNull(mDefaultStorage);
+
+ try {
+ mDefaultStorage.unBind(mDefaultDir);
+ mDefaultStorage.unBind(mStageDir.getAbsolutePath());
+ } catch (IOException ignored) {
}
+
+ mDefaultDir = null;
+ mDefaultStorage = null;
}
- private String getTempDir() {
- final String tmpDirRoot = "/data/incremental/tmp";
- final Random random = new Random();
- final Path tmpDir =
- Paths.get(tmpDirRoot, String.valueOf(random.nextInt(Integer.MAX_VALUE - 1)));
+ private static String getTempDir() {
+ final Path tmpDir = Paths.get(TMP_DIR_ROOT,
+ String.valueOf(TMP_DIR_RANDOM.nextInt(Integer.MAX_VALUE - 1)));
try {
Files.createDirectories(tmpDir);
} catch (Exception ex) {
diff --git a/core/java/android/permission/PermissionManager.java b/core/java/android/permission/PermissionManager.java
index 4812ea98b569..d945b65c8522 100644
--- a/core/java/android/permission/PermissionManager.java
+++ b/core/java/android/permission/PermissionManager.java
@@ -93,7 +93,10 @@ public final class PermissionManager {
*/
@TestApi
@SystemApi
- @RequiresPermission(Manifest.permission.ADJUST_RUNTIME_PERMISSIONS_POLICY)
+ @RequiresPermission(anyOf = {
+ Manifest.permission.ADJUST_RUNTIME_PERMISSIONS_POLICY,
+ Manifest.permission.UPGRADE_RUNTIME_PERMISSIONS
+ })
public @IntRange(from = 0) int getRuntimePermissionsVersion() {
try {
return mPackageManager.getRuntimePermissionsVersion(mContext.getUserId());
@@ -111,7 +114,10 @@ public final class PermissionManager {
*/
@TestApi
@SystemApi
- @RequiresPermission(Manifest.permission.ADJUST_RUNTIME_PERMISSIONS_POLICY)
+ @RequiresPermission(anyOf = {
+ Manifest.permission.ADJUST_RUNTIME_PERMISSIONS_POLICY,
+ Manifest.permission.UPGRADE_RUNTIME_PERMISSIONS
+ })
public void setRuntimePermissionsVersion(@IntRange(from = 0) int version) {
try {
mPackageManager.setRuntimePermissionsVersion(version, mContext.getUserId());
diff --git a/core/java/android/provider/ContactsContract.java b/core/java/android/provider/ContactsContract.java
index e383a371eb41..146387e761d4 100644
--- a/core/java/android/provider/ContactsContract.java
+++ b/core/java/android/provider/ContactsContract.java
@@ -2381,11 +2381,7 @@ public final class ContactsContract {
* This id is provided by its own data source, and can be used to backup metadata
* to the server.
* This should be unique within each set of account_name/account_type/data_set
- *
- * @deprecated This column is no longer supported as of Android version
- * {@link android.os.Build.VERSION_CODES#R}.
*/
- @Deprecated
public static final String BACKUP_ID = "backup_id";
/**
@@ -2450,8 +2446,8 @@ public final class ContactsContract {
* needs to be synchronized by the server.
* <P>Type: INTEGER (boolean)</P>
*
- * @deprecated This column is no longer supported as of Android version
- * {@link android.os.Build.VERSION_CODES#R}.
+ * @deprecated This column never actually worked since added. It will not supported as
+ * of Android version {@link android.os.Build.VERSION_CODES#R}.
*/
@Deprecated
public static final String METADATA_DIRTY = "metadata_dirty";
@@ -4196,8 +4192,8 @@ public final class ContactsContract {
* Hash id on the data fields, used for backup and restore.
*
* @hide
- * @deprecated This column is no longer supported as of Android version
- * {@link android.os.Build.VERSION_CODES#R}.
+ * @deprecated This column was never public since added. It will not be supported
+ * as of Android version {@link android.os.Build.VERSION_CODES#R}.
*/
@Deprecated
public static final String HASH_ID = "hash_id";
@@ -9506,8 +9502,8 @@ public final class ContactsContract {
/**
* @hide
- * @deprecated These columns are no longer supported as of Android version
- * {@link android.os.Build.VERSION_CODES#R}.
+ * @deprecated These columns were never public since added. They will not be supported
+ * as of Android version {@link android.os.Build.VERSION_CODES#R}.
*/
@Deprecated
@SystemApi
@@ -9616,8 +9612,8 @@ public final class ContactsContract {
* from server before it is merged into other CP2 tables.
*
* @hide
- * @deprecated These columns are no longer supported as of Android version
- * {@link android.os.Build.VERSION_CODES#R}.
+ * @deprecated These columns were never public since added. They will not be supported
+ * as of Android version {@link android.os.Build.VERSION_CODES#R}.
*/
@Deprecated
@SystemApi
diff --git a/core/java/android/provider/DeviceConfig.java b/core/java/android/provider/DeviceConfig.java
index 091d78e88c98..505d4ca4b399 100644
--- a/core/java/android/provider/DeviceConfig.java
+++ b/core/java/android/provider/DeviceConfig.java
@@ -383,6 +383,14 @@ public final class DeviceConfig {
*/
public static final String NAMESPACE_WIDGET = "widget";
+ /**
+ * Namespace for connectivity thermal power manager features.
+ *
+ * @hide
+ */
+ public static final String NAMESPACE_CONNECTIVITY_THERMAL_POWER_MANAGER =
+ "connectivity_thermal_power_manager";
+
private static final Object sLock = new Object();
@GuardedBy("sLock")
private static ArrayMap<OnPropertiesChangedListener, Pair<String, Executor>> sListeners =
diff --git a/core/java/android/service/autofill/Dataset.java b/core/java/android/service/autofill/Dataset.java
index 262d9896df87..886b433d4ade 100644
--- a/core/java/android/service/autofill/Dataset.java
+++ b/core/java/android/service/autofill/Dataset.java
@@ -322,6 +322,9 @@ public final class Dataset implements Parcelable {
* platform needs to fill in the authentication arguments.
*
* @param authentication Intent to an activity with your authentication flow.
+ *
+ * @throws IllegalStateException if {@link #build()} was already called.
+ *
* @return this builder.
*
* @see android.app.PendingIntent
@@ -349,6 +352,8 @@ public final class Dataset implements Parcelable {
*
* @param id id for this dataset or {@code null} to unset.
*
+ * @throws IllegalStateException if {@link #build()} was already called.
+ *
* @return this builder.
*/
public @NonNull Builder setId(@Nullable String id) {
@@ -378,6 +383,9 @@ public final class Dataset implements Parcelable {
* @param value value to be autofilled. Pass {@code null} if you do not have the value
* but the target view is a logical part of the dataset. For example, if
* the dataset needs authentication and you have no access to the value.
+ *
+ * @throws IllegalStateException if {@link #build()} was already called.
+ *
* @return this builder.
*/
public @NonNull Builder setValue(@NonNull AutofillId id, @Nullable AutofillValue value) {
@@ -403,8 +411,10 @@ public final class Dataset implements Parcelable {
* but the target view is a logical part of the dataset. For example, if
* the dataset needs authentication and you have no access to the value.
* @param presentation the presentation used to visualize this field.
- * @return this builder.
*
+ * @throws IllegalStateException if {@link #build()} was already called.
+ *
+ * @return this builder.
*/
public @NonNull Builder setValue(@NonNull AutofillId id, @Nullable AutofillValue value,
@NonNull RemoteViews presentation) {
@@ -439,7 +449,7 @@ public final class Dataset implements Parcelable {
*
* @return this builder.
* @throws IllegalStateException if the builder was constructed without a
- * {@link RemoteViews presentation}.
+ * {@link RemoteViews presentation} or {@link #build()} was already called.
*/
public @NonNull Builder setValue(@NonNull AutofillId id, @Nullable AutofillValue value,
@Nullable Pattern filter) {
@@ -475,6 +485,8 @@ public final class Dataset implements Parcelable {
* such as passwords).
* @param presentation the presentation used to visualize this field.
*
+ * @throws IllegalStateException if {@link #build()} was already called.
+ *
* @return this builder.
*/
public @NonNull Builder setValue(@NonNull AutofillId id, @Nullable AutofillValue value,
@@ -504,6 +516,8 @@ public final class Dataset implements Parcelable {
* as inline suggestions. If the dataset supports inline suggestions,
* this should not be null.
*
+ * @throws IllegalStateException if {@link #build()} was already called.
+ *
* @return this builder.
*/
public @NonNull Builder setValue(@NonNull AutofillId id, @Nullable AutofillValue value,
@@ -544,6 +558,8 @@ public final class Dataset implements Parcelable {
* as inline suggestions. If the dataset supports inline suggestions, this
* should not be null.
*
+ * @throws IllegalStateException if {@link #build()} was already called.
+ *
* @return this builder.
*/
public @NonNull Builder setValue(@NonNull AutofillId id, @Nullable AutofillValue value,
@@ -576,6 +592,8 @@ public final class Dataset implements Parcelable {
* as inline suggestions. If the dataset supports inline suggestions, this
* should not be null.
*
+ * @throws IllegalStateException if {@link #build()} was already called.
+ *
* @return this builder.
*
* @hide
@@ -628,7 +646,8 @@ public final class Dataset implements Parcelable {
* @throws IllegalStateException if no field was set (through
* {@link #setValue(AutofillId, AutofillValue)} or
* {@link #setValue(AutofillId, AutofillValue, RemoteViews)} or
- * {@link #setValue(AutofillId, AutofillValue, RemoteViews, InlinePresentation)}).
+ * {@link #setValue(AutofillId, AutofillValue, RemoteViews, InlinePresentation)}),
+ * or if {@link #build()} was already called.
*
* @return The built dataset.
*/
diff --git a/core/java/android/service/chooser/ChooserTarget.java b/core/java/android/service/chooser/ChooserTarget.java
index 5d2d667b438a..ec3dfe11259f 100644
--- a/core/java/android/service/chooser/ChooserTarget.java
+++ b/core/java/android/service/chooser/ChooserTarget.java
@@ -39,7 +39,12 @@ import android.os.Parcelable;
* <p>Creators of chooser targets should consult the relevant design guidelines for the type
* of target they are presenting. For example, targets involving people should be presented
* with a circular icon.</p>
+ *
+ * @deprecated For publishing direct share targets, please follow the instructions in
+ * https://developer.android.com/training/sharing/receive.html#providing-direct-share-targets
+ * instead.
*/
+@Deprecated
public final class ChooserTarget implements Parcelable {
private static final String TAG = "ChooserTarget";
diff --git a/core/java/android/service/chooser/ChooserTargetService.java b/core/java/android/service/chooser/ChooserTargetService.java
index a1afa60646b0..ec560643a2bb 100644
--- a/core/java/android/service/chooser/ChooserTargetService.java
+++ b/core/java/android/service/chooser/ChooserTargetService.java
@@ -72,7 +72,13 @@ import java.util.List;
* android:value=".MyChooserTargetService" />
* &lt;/activity>
* </pre>
+ *
+ * @deprecated For publishing direct share targets, please follow the instructions in
+ * https://developer.android.com/training/sharing/receive.html#providing-direct-share-targets
+ * instead.
*/
+
+@Deprecated
public abstract class ChooserTargetService extends Service {
// TAG = "ChooserTargetService[MySubclass]";
private final String TAG = ChooserTargetService.class.getSimpleName()
diff --git a/core/java/android/service/contentcapture/ContentCaptureService.java b/core/java/android/service/contentcapture/ContentCaptureService.java
index 5a8521fead87..707426a22889 100644
--- a/core/java/android/service/contentcapture/ContentCaptureService.java
+++ b/core/java/android/service/contentcapture/ContentCaptureService.java
@@ -42,7 +42,6 @@ import android.os.RemoteException;
import android.util.Log;
import android.util.Slog;
import android.util.SparseIntArray;
-import android.util.StatsLog;
import android.view.contentcapture.ContentCaptureCondition;
import android.view.contentcapture.ContentCaptureContext;
import android.view.contentcapture.ContentCaptureEvent;
@@ -55,6 +54,7 @@ import android.view.contentcapture.IContentCaptureDirectManager;
import android.view.contentcapture.MainContentCaptureSession;
import com.android.internal.os.IResultReceiver;
+import com.android.internal.util.FrameworkStatsLog;
import com.android.internal.util.Preconditions;
import java.io.FileDescriptor;
@@ -594,7 +594,7 @@ public abstract class ContentCaptureService extends Service {
+ rightUid);
long now = System.currentTimeMillis();
if (now - mLastCallerMismatchLog > mCallerMismatchTimeout) {
- StatsLog.write(StatsLog.CONTENT_CAPTURE_CALLER_MISMATCH_REPORTED,
+ FrameworkStatsLog.write(FrameworkStatsLog.CONTENT_CAPTURE_CALLER_MISMATCH_REPORTED,
getPackageManager().getNameForUid(rightUid),
getPackageManager().getNameForUid(uid));
mLastCallerMismatchLog = now;
diff --git a/core/java/android/timezone/CountryTimeZones.java b/core/java/android/timezone/CountryTimeZones.java
index 5875761bb1c9..970acd0a5b3f 100644
--- a/core/java/android/timezone/CountryTimeZones.java
+++ b/core/java/android/timezone/CountryTimeZones.java
@@ -51,8 +51,10 @@ public final class CountryTimeZones {
}
/**
- * Returns the ID for this mapping. See also {@link #getTimeZone()} which handles when the
- * ID is unrecognized.
+ * Returns the ID for this mapping. The ID is a tzdb time zone identifier like
+ * "America/Los_Angeles" that can be used with methods such as {@link
+ * TimeZone#getFrozenTimeZone(String)}. See {@link #getTimeZone()} which returns a frozen
+ * {@link TimeZone} object.
*/
@NonNull
public String getTimeZoneId() {
@@ -60,10 +62,9 @@ public final class CountryTimeZones {
}
/**
- * Returns a {@link TimeZone} object for this mapping, or {@code null} if the ID is
- * unrecognized.
+ * Returns a frozen {@link TimeZone} object for this mapping.
*/
- @Nullable
+ @NonNull
public TimeZone getTimeZone() {
return mDelegate.getTimeZone();
}
@@ -158,9 +159,10 @@ public final class CountryTimeZones {
}
/**
- * Returns true if the ISO code for the country is a match for the one specified.
+ * Returns true if the ISO code for the country is a case-insensitive match for the one
+ * supplied.
*/
- public boolean isForCountryCode(@NonNull String countryIso) {
+ public boolean matchesCountryCode(@NonNull String countryIso) {
return mDelegate.isForCountryCode(countryIso);
}
@@ -183,15 +185,25 @@ public final class CountryTimeZones {
}
/**
- * Qualifier for a country's default time zone. {@code true} indicates whether the default
- * would be a good choice <em>generally</em> when there's no other information available.
+ * Qualifier for a country's default time zone. {@code true} indicates that the country's
+ * default time zone would be a good choice <em>generally</em> when there's no UTC offset
+ * information available. This will only be {@code true} in countries with multiple zones where
+ * a large majority of the population is covered by only one of them.
*/
public boolean isDefaultTimeZoneBoosted() {
return mDelegate.isDefaultTimeZoneBoosted();
}
/**
- * Returns true if the country has at least one zone that is the same as UTC at the given time.
+ * Returns {@code true} if the country has at least one time zone that uses UTC at the given
+ * time. This is an efficient check when trying to validate received UTC offset information.
+ * For example, there are situations when a detected zero UTC offset cannot be distinguished
+ * from "no information available" or a corrupted signal. This method is useful because checking
+ * offset information for large countries is relatively expensive but it is generally only the
+ * countries close to the prime meridian that use UTC at <em>any</em> time of the year.
+ *
+ * @param whenMillis the time the offset information is for in milliseconds since the beginning
+ * of the Unix epoch
*/
public boolean hasUtcZone(long whenMillis) {
return mDelegate.hasUtcZone(whenMillis);
diff --git a/core/java/android/util/RotationUtils.java b/core/java/android/util/RotationUtils.java
new file mode 100644
index 000000000000..a44ed59c14d4
--- /dev/null
+++ b/core/java/android/util/RotationUtils.java
@@ -0,0 +1,72 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.util;
+
+import static android.view.Surface.ROTATION_0;
+import static android.view.Surface.ROTATION_180;
+import static android.view.Surface.ROTATION_270;
+import static android.view.Surface.ROTATION_90;
+
+import android.graphics.Insets;
+import android.view.Surface.Rotation;
+
+/**
+ * A class containing utility methods related to rotation.
+ *
+ * @hide
+ */
+public class RotationUtils {
+
+ /**
+ * Rotates an Insets according to the given rotation.
+ */
+ public static Insets rotateInsets(Insets insets, @Rotation int rotation) {
+ if (insets == null || insets == Insets.NONE) {
+ return insets;
+ }
+ Insets rotated;
+ switch (rotation) {
+ case ROTATION_0:
+ rotated = insets;
+ break;
+ case ROTATION_90:
+ rotated = Insets.of(
+ insets.top,
+ insets.right,
+ insets.bottom,
+ insets.left);
+ break;
+ case ROTATION_180:
+ rotated = Insets.of(
+ insets.right,
+ insets.bottom,
+ insets.left,
+ insets.top);
+ break;
+ case ROTATION_270:
+ rotated = Insets.of(
+ insets.bottom,
+ insets.left,
+ insets.top,
+ insets.right);
+ break;
+ default:
+ throw new IllegalArgumentException("unknown rotation: " + rotation);
+ }
+ return rotated;
+ }
+}
diff --git a/core/java/android/util/StatsLog.java b/core/java/android/util/StatsLog.java
index 8635340397b4..feeff6c47972 100644
--- a/core/java/android/util/StatsLog.java
+++ b/core/java/android/util/StatsLog.java
@@ -28,11 +28,13 @@ import android.os.IStatsd;
import android.os.RemoteException;
import android.os.ServiceManager;
+import com.android.internal.util.FrameworkStatsLog;
+
/**
* StatsLog provides an API for developers to send events to statsd. The events can be used to
* define custom metrics inside statsd.
*/
-public final class StatsLog extends StatsLogInternal {
+public final class StatsLog {
private static final String TAG = "StatsLog";
private static final boolean DEBUG = false;
@@ -60,7 +62,7 @@ public final class StatsLog extends StatsLogInternal {
return false;
}
service.sendAppBreadcrumbAtom(label,
- StatsLog.APP_BREADCRUMB_REPORTED__STATE__START);
+ FrameworkStatsLog.APP_BREADCRUMB_REPORTED__STATE__START);
return true;
} catch (RemoteException e) {
sService = null;
@@ -88,7 +90,8 @@ public final class StatsLog extends StatsLogInternal {
}
return false;
}
- service.sendAppBreadcrumbAtom(label, StatsLog.APP_BREADCRUMB_REPORTED__STATE__STOP);
+ service.sendAppBreadcrumbAtom(
+ label, FrameworkStatsLog.APP_BREADCRUMB_REPORTED__STATE__STOP);
return true;
} catch (RemoteException e) {
sService = null;
@@ -117,7 +120,7 @@ public final class StatsLog extends StatsLogInternal {
return false;
}
service.sendAppBreadcrumbAtom(
- label, StatsLog.APP_BREADCRUMB_REPORTED__STATE__UNSPECIFIED);
+ label, FrameworkStatsLog.APP_BREADCRUMB_REPORTED__STATE__UNSPECIFIED);
return true;
} catch (RemoteException e) {
sService = null;
diff --git a/core/java/android/view/GestureDetector.java b/core/java/android/view/GestureDetector.java
index 19793b945ffd..f6c72c4eefbc 100644
--- a/core/java/android/view/GestureDetector.java
+++ b/core/java/android/view/GestureDetector.java
@@ -16,12 +16,12 @@
package android.view;
-import static android.util.StatsLog.TOUCH_GESTURE_CLASSIFIED__CLASSIFICATION__DEEP_PRESS;
-import static android.util.StatsLog.TOUCH_GESTURE_CLASSIFIED__CLASSIFICATION__DOUBLE_TAP;
-import static android.util.StatsLog.TOUCH_GESTURE_CLASSIFIED__CLASSIFICATION__LONG_PRESS;
-import static android.util.StatsLog.TOUCH_GESTURE_CLASSIFIED__CLASSIFICATION__SCROLL;
-import static android.util.StatsLog.TOUCH_GESTURE_CLASSIFIED__CLASSIFICATION__SINGLE_TAP;
-import static android.util.StatsLog.TOUCH_GESTURE_CLASSIFIED__CLASSIFICATION__UNKNOWN_CLASSIFICATION;
+import static com.android.internal.util.FrameworkStatsLog.TOUCH_GESTURE_CLASSIFIED__CLASSIFICATION__DEEP_PRESS;
+import static com.android.internal.util.FrameworkStatsLog.TOUCH_GESTURE_CLASSIFIED__CLASSIFICATION__DOUBLE_TAP;
+import static com.android.internal.util.FrameworkStatsLog.TOUCH_GESTURE_CLASSIFIED__CLASSIFICATION__LONG_PRESS;
+import static com.android.internal.util.FrameworkStatsLog.TOUCH_GESTURE_CLASSIFIED__CLASSIFICATION__SCROLL;
+import static com.android.internal.util.FrameworkStatsLog.TOUCH_GESTURE_CLASSIFIED__CLASSIFICATION__SINGLE_TAP;
+import static com.android.internal.util.FrameworkStatsLog.TOUCH_GESTURE_CLASSIFIED__CLASSIFICATION__UNKNOWN_CLASSIFICATION;
import android.compat.annotation.UnsupportedAppUsage;
import android.content.Context;
@@ -29,7 +29,8 @@ import android.os.Build;
import android.os.Handler;
import android.os.Message;
import android.os.SystemClock;
-import android.util.StatsLog;
+
+import com.android.internal.util.FrameworkStatsLog;
/**
* Detects various gestures and events using the supplied {@link MotionEvent}s.
@@ -887,8 +888,8 @@ public class GestureDetector {
mHasRecordedClassification = true;
return;
}
- StatsLog.write(
- StatsLog.TOUCH_GESTURE_CLASSIFIED,
+ FrameworkStatsLog.write(
+ FrameworkStatsLog.TOUCH_GESTURE_CLASSIFIED,
getClass().getName(),
classification,
(int) (SystemClock.uptimeMillis() - mCurrentMotionEvent.getDownTime()),
diff --git a/core/java/android/view/IWindowSession.aidl b/core/java/android/view/IWindowSession.aidl
index 1677357dedbe..91000a93b17c 100644
--- a/core/java/android/view/IWindowSession.aidl
+++ b/core/java/android/view/IWindowSession.aidl
@@ -318,5 +318,11 @@ interface IWindowSession {
* an input channel where the client can receive input.
*/
void grantInputChannel(int displayId, in SurfaceControl surface, in IWindow window,
- in IBinder hostInputToken, out InputChannel outInputChannel);
+ in IBinder hostInputToken, int flags, out InputChannel outInputChannel);
+
+ /**
+ * Update the flags on an input channel associated with a particular surface.
+ */
+ void updateInputChannel(in IBinder channelToken, int displayId, in SurfaceControl surface,
+ int flags);
}
diff --git a/core/java/android/view/ImeFocusController.java b/core/java/android/view/ImeFocusController.java
index 8d58ee83cd67..67e88a5a1831 100644
--- a/core/java/android/view/ImeFocusController.java
+++ b/core/java/android/view/ImeFocusController.java
@@ -253,4 +253,12 @@ public final class ImeFocusController {
public void setNextServedView(View view) {
mNextServedView = view;
}
+
+ /**
+ * Indicates whether the view's window has IME focused.
+ */
+ @UiThread
+ boolean hasImeFocus() {
+ return mHasImeFocus;
+ }
}
diff --git a/core/java/android/view/SurfaceControl.java b/core/java/android/view/SurfaceControl.java
index bee05a9a57d0..fe9e36e53cd8 100644
--- a/core/java/android/view/SurfaceControl.java
+++ b/core/java/android/view/SurfaceControl.java
@@ -152,7 +152,8 @@ public final class SurfaceControl implements Parcelable {
int L, int T, int R, int B);
private static native void nativeSetDisplaySize(long transactionObj, IBinder displayToken,
int width, int height);
- private static native SurfaceControl.PhysicalDisplayInfo[] nativeGetDisplayConfigs(
+ private static native SurfaceControl.DisplayInfo nativeGetDisplayInfo(IBinder displayToken);
+ private static native SurfaceControl.DisplayConfig[] nativeGetDisplayConfigs(
IBinder displayToken);
private static native DisplayedContentSamplingAttributes
nativeGetDisplayedContentSamplingAttributes(IBinder displayToken);
@@ -1278,140 +1279,45 @@ public final class SurfaceControl implements Parcelable {
Integer.toHexString(System.identityHashCode(this));
}
- /*
- * set display parameters.
- * needs to be inside open/closeTransaction block
+ /**
+ * Immutable information about physical display.
+ *
+ * @hide
*/
+ public static final class DisplayInfo {
+ public float density;
+ public boolean secure;
+
+ @Override
+ public String toString() {
+ return "DisplayInfo{density=" + density + ", secure=" + secure + "}";
+ }
+ }
/**
- * Describes the properties of a physical display known to surface flinger.
+ * Configuration supported by physical display.
+ *
* @hide
*/
- public static final class PhysicalDisplayInfo {
- /**
- * @hide
- */
- @UnsupportedAppUsage
+ public static final class DisplayConfig {
public int width;
-
- /**
- * @hide
- */
- @UnsupportedAppUsage
public int height;
-
- /**
- * @hide
- */
- @UnsupportedAppUsage
- public float refreshRate;
-
- /**
- * @hide
- */
- @UnsupportedAppUsage
- public float density;
-
- /**
- * @hide
- */
- @UnsupportedAppUsage
public float xDpi;
-
- /**
- * @hide
- */
- @UnsupportedAppUsage
public float yDpi;
- /**
- * @hide
- */
- @UnsupportedAppUsage
- public boolean secure;
-
- /**
- * @hide
- */
- @UnsupportedAppUsage
+ public float refreshRate;
public long appVsyncOffsetNanos;
-
- /**
- * @hide
- */
- @UnsupportedAppUsage
public long presentationDeadlineNanos;
- /**
- * @hide
- */
- @UnsupportedAppUsage
- public PhysicalDisplayInfo() {
- }
-
- /**
- * @hide
- */
- public PhysicalDisplayInfo(PhysicalDisplayInfo other) {
- copyFrom(other);
- }
-
- /**
- * @hide
- */
- @Override
- public boolean equals(Object o) {
- return o instanceof PhysicalDisplayInfo && equals((PhysicalDisplayInfo)o);
- }
-
- /**
- * @hide
- */
- public boolean equals(PhysicalDisplayInfo other) {
- return other != null
- && width == other.width
- && height == other.height
- && refreshRate == other.refreshRate
- && density == other.density
- && xDpi == other.xDpi
- && yDpi == other.yDpi
- && secure == other.secure
- && appVsyncOffsetNanos == other.appVsyncOffsetNanos
- && presentationDeadlineNanos == other.presentationDeadlineNanos;
- }
-
- /**
- * @hide
- */
- @Override
- public int hashCode() {
- return 0; // don't care
- }
-
- /**
- * @hide
- */
- public void copyFrom(PhysicalDisplayInfo other) {
- width = other.width;
- height = other.height;
- refreshRate = other.refreshRate;
- density = other.density;
- xDpi = other.xDpi;
- yDpi = other.yDpi;
- secure = other.secure;
- appVsyncOffsetNanos = other.appVsyncOffsetNanos;
- presentationDeadlineNanos = other.presentationDeadlineNanos;
- }
-
- /**
- * @hide
- */
@Override
public String toString() {
- return "PhysicalDisplayInfo{" + width + " x " + height + ", " + refreshRate + " fps, "
- + "density " + density + ", " + xDpi + " x " + yDpi + " dpi, secure " + secure
- + ", appVsyncOffset " + appVsyncOffsetNanos
- + ", bufferDeadline " + presentationDeadlineNanos + "}";
+ return "DisplayConfig{width=" + width
+ + ", height=" + height
+ + ", xDpi=" + xDpi
+ + ", yDpi=" + yDpi
+ + ", refreshRate=" + refreshRate
+ + ", appVsyncOffsetNanos=" + appVsyncOffsetNanos
+ + ", presentationDeadlineNanos=" + presentationDeadlineNanos + "}";
}
}
@@ -1428,8 +1334,17 @@ public final class SurfaceControl implements Parcelable {
/**
* @hide
*/
- @UnsupportedAppUsage
- public static SurfaceControl.PhysicalDisplayInfo[] getDisplayConfigs(IBinder displayToken) {
+ public static SurfaceControl.DisplayInfo getDisplayInfo(IBinder displayToken) {
+ if (displayToken == null) {
+ throw new IllegalArgumentException("displayToken must not be null");
+ }
+ return nativeGetDisplayInfo(displayToken);
+ }
+
+ /**
+ * @hide
+ */
+ public static SurfaceControl.DisplayConfig[] getDisplayConfigs(IBinder displayToken) {
if (displayToken == null) {
throw new IllegalArgumentException("displayToken must not be null");
}
@@ -1825,7 +1740,7 @@ public final class SurfaceControl implements Parcelable {
}
/**
- * TODO(116025192): Remove this stopgap once framework is display-agnostic.
+ * TODO(b/116025192): Remove this stopgap once framework is display-agnostic.
*
* @hide
*/
diff --git a/core/java/android/view/SurfaceControlViewHost.java b/core/java/android/view/SurfaceControlViewHost.java
index 71cf051a4e08..bf848196454d 100644
--- a/core/java/android/view/SurfaceControlViewHost.java
+++ b/core/java/android/view/SurfaceControlViewHost.java
@@ -138,6 +138,7 @@ public class SurfaceControlViewHost {
/**
* @hide
*/
+ @TestApi
public void addView(@NonNull View view, WindowManager.LayoutParams attrs) {
mViewRoot.setView(view, attrs, null);
}
@@ -161,6 +162,7 @@ public class SurfaceControlViewHost {
/**
* @hide
*/
+ @TestApi
public void relayout(WindowManager.LayoutParams attrs) {
mViewRoot.setLayoutParams(attrs, false);
mViewRoot.setReportNextDraw();
diff --git a/core/java/android/view/View.java b/core/java/android/view/View.java
index ee7f6fb11de6..a407bd8f001e 100644
--- a/core/java/android/view/View.java
+++ b/core/java/android/view/View.java
@@ -17,13 +17,14 @@
package android.view;
import static android.content.res.Resources.ID_NULL;
-import static android.util.StatsLog.TOUCH_GESTURE_CLASSIFIED__CLASSIFICATION__DEEP_PRESS;
-import static android.util.StatsLog.TOUCH_GESTURE_CLASSIFIED__CLASSIFICATION__LONG_PRESS;
-import static android.util.StatsLog.TOUCH_GESTURE_CLASSIFIED__CLASSIFICATION__SINGLE_TAP;
-import static android.util.StatsLog.TOUCH_GESTURE_CLASSIFIED__CLASSIFICATION__UNKNOWN_CLASSIFICATION;
import static android.view.ViewRootImpl.NEW_INSETS_MODE_FULL;
import static android.view.accessibility.AccessibilityEvent.CONTENT_CHANGE_TYPE_UNDEFINED;
+import static com.android.internal.util.FrameworkStatsLog.TOUCH_GESTURE_CLASSIFIED__CLASSIFICATION__DEEP_PRESS;
+import static com.android.internal.util.FrameworkStatsLog.TOUCH_GESTURE_CLASSIFIED__CLASSIFICATION__LONG_PRESS;
+import static com.android.internal.util.FrameworkStatsLog.TOUCH_GESTURE_CLASSIFIED__CLASSIFICATION__SINGLE_TAP;
+import static com.android.internal.util.FrameworkStatsLog.TOUCH_GESTURE_CLASSIFIED__CLASSIFICATION__UNKNOWN_CLASSIFICATION;
+
import static java.lang.Math.max;
import android.animation.AnimatorInflater;
@@ -102,7 +103,6 @@ import android.util.Property;
import android.util.SparseArray;
import android.util.SparseIntArray;
import android.util.StateSet;
-import android.util.StatsLog;
import android.util.SuperNotCalledException;
import android.util.TypedValue;
import android.view.AccessibilityIterators.CharacterTextSegmentIterator;
@@ -142,6 +142,7 @@ import android.widget.FrameLayout;
import android.widget.ScrollBarDrawable;
import com.android.internal.R;
+import com.android.internal.util.FrameworkStatsLog;
import com.android.internal.view.TooltipPopup;
import com.android.internal.view.menu.MenuBuilder;
import com.android.internal.widget.ScrollBarUtils;
@@ -14469,7 +14470,7 @@ public class View implements Drawable.Callback, KeyEvent.Callback,
* @hide
*/
public boolean hasImeFocus() {
- return mAttachInfo != null && mAttachInfo.mHasImeFocus;
+ return getViewRootImpl() != null && getViewRootImpl().getImeFocusController().hasImeFocus();
}
/**
@@ -28059,7 +28060,7 @@ public class View implements Drawable.Callback, KeyEvent.Callback,
private boolean mOriginalPressedState;
/**
* The classification of the long click being checked: one of the
- * StatsLog.TOUCH_GESTURE_CLASSIFIED__CLASSIFICATION__* constants.
+ * FrameworkStatsLog.TOUCH_GESTURE_CLASSIFIED__CLASSIFICATION__* constants.
*/
private int mClassification;
@@ -28125,7 +28126,8 @@ public class View implements Drawable.Callback, KeyEvent.Callback,
}
// To avoid negatively impacting View performance, the latency and displacement metrics
// are omitted.
- StatsLog.write(StatsLog.TOUCH_GESTURE_CLASSIFIED, getClass().getName(), classification);
+ FrameworkStatsLog.write(FrameworkStatsLog.TOUCH_GESTURE_CLASSIFIED, getClass().getName(),
+ classification);
}
/**
@@ -28723,11 +28725,6 @@ public class View implements Drawable.Callback, KeyEvent.Callback,
boolean mHasWindowFocus;
/**
- * Indicates whether the view's window has IME focused.
- */
- boolean mHasImeFocus;
-
- /**
* The current visibility of the window.
*/
int mWindowVisibility;
diff --git a/core/java/android/view/ViewRootImpl.java b/core/java/android/view/ViewRootImpl.java
index 2b4b71f01aa5..159b93eb12dd 100644
--- a/core/java/android/view/ViewRootImpl.java
+++ b/core/java/android/view/ViewRootImpl.java
@@ -1691,7 +1691,7 @@ public final class ViewRootImpl implements ViewParent,
.build();
setBoundsLayerCrop();
mTransaction.show(mBoundsLayer).apply();
- }
+ }
return mBoundsLayer;
}
@@ -1699,7 +1699,9 @@ public final class ViewRootImpl implements ViewParent,
if (mSurfaceControl == null || !mSurfaceControl.isValid()) {
return null;
}
- if ((mBlastBufferQueue != null) && mBlastSurfaceControl.isValid()) {
+ if ((mBlastSurfaceControl != null)
+ && (mBlastBufferQueue == null)
+ && mBlastSurfaceControl.isValid()) {
mBlastBufferQueue = new BLASTBufferQueue(
mBlastSurfaceControl, width, height);
}
@@ -1742,12 +1744,9 @@ public final class ViewRootImpl implements ViewParent,
mSurface.release();
mSurfaceControl.release();
- if (mBlastBufferQueue != null) {
- mTransaction.remove(mBlastSurfaceControl).apply();
- mBlastSurfaceControl = null;
- // We should probably add an explicit dispose.
- mBlastBufferQueue = null;
- }
+ mBlastSurfaceControl.release();
+ // We should probably add an explicit dispose.
+ mBlastBufferQueue = null;
}
/**
@@ -3143,8 +3142,7 @@ public final class ViewRootImpl implements ViewParent,
}
mAttachInfo.mHasWindowFocus = hasWindowFocus;
- mAttachInfo.mHasImeFocus = mImeFocusController.updateImeFocusable(
- mWindowAttributes, true /* force */);
+ mImeFocusController.updateImeFocusable(mWindowAttributes, true /* force */);
mImeFocusController.onPreWindowFocus(hasWindowFocus, mWindowAttributes);
if (mView != null) {
@@ -3348,7 +3346,6 @@ public final class ViewRootImpl implements ViewParent,
private void performLayout(WindowManager.LayoutParams lp, int desiredWindowWidth,
int desiredWindowHeight) {
- mLayoutRequested = false;
mScrollMayChange = true;
mInLayout = true;
diff --git a/core/java/android/view/WindowlessWindowManager.java b/core/java/android/view/WindowlessWindowManager.java
index 91778aaf51fd..62f3fa4f6ed4 100644
--- a/core/java/android/view/WindowlessWindowManager.java
+++ b/core/java/android/view/WindowlessWindowManager.java
@@ -41,9 +41,14 @@ public class WindowlessWindowManager implements IWindowSession {
private class State {
SurfaceControl mSurfaceControl;
WindowManager.LayoutParams mParams = new WindowManager.LayoutParams();
- State(SurfaceControl sc, WindowManager.LayoutParams p) {
+ int mDisplayId;
+ IBinder mInputChannelToken;
+ State(SurfaceControl sc, WindowManager.LayoutParams p, int displayId,
+ IBinder inputChannelToken) {
mSurfaceControl = sc;
mParams.copyFrom(p);
+ mDisplayId = displayId;
+ mInputChannelToken = inputChannelToken;
}
};
@@ -105,19 +110,23 @@ public class WindowlessWindowManager implements IWindowSession {
.setFormat(attrs.format)
.setName(attrs.getTitle().toString());
final SurfaceControl sc = b.build();
- synchronized (this) {
- mStateForWindow.put(window.asBinder(), new State(sc, attrs));
- }
if (((attrs.inputFeatures &
WindowManager.LayoutParams.INPUT_FEATURE_NO_INPUT_CHANNEL) == 0)) {
try {
- mRealWm.grantInputChannel(displayId, sc, window, mHostInputToken, outInputChannel);
+ mRealWm.grantInputChannel(displayId, sc, window, mHostInputToken, attrs.flags,
+ outInputChannel);
} catch (RemoteException e) {
- Log.e(TAG, "Failed to bless surface: " + e);
+ Log.e(TAG, "Failed to grant input to surface: ", e);
}
}
+ final State state = new State(sc, attrs, displayId,
+ outInputChannel != null ? outInputChannel.getToken() : null);
+ synchronized (this) {
+ mStateForWindow.put(window.asBinder(), state);
+ }
+
return WindowManagerGlobal.ADD_OKAY | WindowManagerGlobal.ADD_FLAG_APP_VISIBLE;
}
@@ -162,7 +171,7 @@ public class WindowlessWindowManager implements IWindowSession {
DisplayCutout.ParcelableWrapper cutout, MergedConfiguration mergedConfiguration,
SurfaceControl outSurfaceControl, InsetsState outInsetsState,
Point outSurfaceSize, SurfaceControl outBLASTSurfaceControl) {
- State state = null;
+ final State state;
synchronized (this) {
state = mStateForWindow.get(window.asBinder());
}
@@ -173,8 +182,9 @@ public class WindowlessWindowManager implements IWindowSession {
SurfaceControl sc = state.mSurfaceControl;
SurfaceControl.Transaction t = new SurfaceControl.Transaction();
+ int attrChanges = 0;
if (inAttrs != null) {
- state.mParams.copyFrom(inAttrs);
+ attrChanges = state.mParams.copyFrom(inAttrs);
}
WindowManager.LayoutParams attrs = state.mParams;
@@ -197,6 +207,16 @@ public class WindowlessWindowManager implements IWindowSession {
mergedConfiguration.setConfiguration(mConfiguration, mConfiguration);
+ if ((attrChanges & WindowManager.LayoutParams.FLAGS_CHANGED) != 0
+ && state.mInputChannelToken != null) {
+ try {
+ mRealWm.updateInputChannel(state.mInputChannelToken, state.mDisplayId, sc,
+ attrs.flags);
+ } catch (RemoteException e) {
+ Log.e(TAG, "Failed to update surface input channel: ", e);
+ }
+ }
+
return 0;
}
@@ -353,7 +373,12 @@ public class WindowlessWindowManager implements IWindowSession {
@Override
public void grantInputChannel(int displayId, SurfaceControl surface, IWindow window,
- IBinder hostInputToken, InputChannel outInputChannel) {
+ IBinder hostInputToken, int flags, InputChannel outInputChannel) {
+ }
+
+ @Override
+ public void updateInputChannel(IBinder channelToken, int displayId, SurfaceControl surface,
+ int flags) {
}
@Override
diff --git a/core/java/android/view/inputmethod/InputMethodManager.java b/core/java/android/view/inputmethod/InputMethodManager.java
index 307abd2f5f65..f0c16aadf12f 100644
--- a/core/java/android/view/inputmethod/InputMethodManager.java
+++ b/core/java/android/view/inputmethod/InputMethodManager.java
@@ -671,6 +671,11 @@ public final class InputMethodManager {
mWindowFocusGainFuture = null;
}
synchronized (mH) {
+ if (mCurRootView != null) {
+ // Reset the last served view and restart window focus state of the root view.
+ mCurRootView.getImeFocusController().setServedView(null);
+ mRestartOnNextWindowFocus = true;
+ }
mCurRootView = rootView;
}
}
diff --git a/core/java/android/widget/Magnifier.java b/core/java/android/widget/Magnifier.java
index 57b63a7a9f0d..50da6ce19f44 100644
--- a/core/java/android/widget/Magnifier.java
+++ b/core/java/android/widget/Magnifier.java
@@ -854,6 +854,8 @@ public final class Magnifier {
// The z of the magnifier surface, defining its z order in the list of
// siblings having the same parent surface (usually the main app surface).
private static final int SURFACE_Z = 5;
+ // The width of the ramp region in pixels on the left & right sides of the fish-eye effect.
+ private static final int FISHEYE_RAMP_WIDTH = 30;
// Display associated to the view the magnifier is attached to.
private final Display mDisplay;
@@ -906,7 +908,8 @@ public final class Magnifier {
// Whether is in the new magnifier style.
private boolean mIsFishEyeStyle;
// The mesh matrix for the fish-eye effect.
- private float[] mMesh;
+ private float[] mMeshLeft;
+ private float[] mMeshRight;
private int mMeshWidth;
private int mMeshHeight;
@@ -986,29 +989,29 @@ public final class Magnifier {
}
private void createMeshMatrixForFishEyeEffect() {
- mMeshWidth = mZoom < 1.5f ? 5 : 4;
+ mMeshWidth = 1;
mMeshHeight = 6;
final float w = mContentWidth;
final float h = mContentHeight;
- final float dx = (w - mZoom * w * (mMeshWidth - 2) / mMeshWidth) / 2;
- mMesh = new float[2 * (mMeshWidth + 1) * (mMeshHeight + 1)];
+ final float h0 = h / mZoom;
+ final float dh = h - h0;
+ final float ramp = FISHEYE_RAMP_WIDTH;
+ mMeshLeft = new float[2 * (mMeshWidth + 1) * (mMeshHeight + 1)];
+ mMeshRight = new float[2 * (mMeshWidth + 1) * (mMeshHeight + 1)];
for (int i = 0; i < 2 * (mMeshWidth + 1) * (mMeshHeight + 1); i += 2) {
// Calculates X value.
final int colIndex = i % (2 * (mMeshWidth + 1)) / 2;
- if (colIndex == 0) {
- mMesh[i] = 0;
- } else if (colIndex == mMeshWidth) {
- mMesh[i] = w;
- } else {
- mMesh[i] = (colIndex - 1) * (w - 2 * dx) / (mMeshWidth - 2) + dx;
- }
+ mMeshLeft[i] = (float) colIndex * ramp / mMeshWidth;
+ mMeshRight[i] = w - ramp + colIndex * ramp / mMeshWidth;
+
// Calculates Y value.
final int rowIndex = i / 2 / (mMeshWidth + 1);
- final float y0 = colIndex == 0 || colIndex == mMeshWidth
- ? (h - h / mZoom) / 2 : 0;
- final float dy = colIndex == 0 || colIndex == mMeshWidth
- ? h / mZoom / mMeshHeight : h / mMeshHeight;
- mMesh[i + 1] = y0 + rowIndex * dy;
+ final float hl = h0 + dh * colIndex / mMeshWidth;
+ final float yl = (h - hl) / 2;
+ mMeshLeft[i + 1] = yl + hl * rowIndex / mMeshHeight;
+ final float hr = h - dh * colIndex / mMeshWidth;
+ final float yr = (h - hr) / 2;
+ mMeshRight[i + 1] = yr + hr * rowIndex / mMeshHeight;
}
}
@@ -1166,14 +1169,31 @@ public final class Magnifier {
final RecordingCanvas canvas =
mBitmapRenderNode.beginRecording(mContentWidth, mContentHeight);
try {
+ final int w = mBitmap.getWidth();
+ final int h = mBitmap.getHeight();
+ final Paint paint = new Paint();
+ paint.setFilterBitmap(true);
if (mIsFishEyeStyle) {
+ final int ramp = FISHEYE_RAMP_WIDTH;
+ final int margin =
+ (int)((mContentWidth - (mContentWidth - 2 * ramp) / mZoom) / 2);
+
+ // Draws the middle part.
+ final Rect srcRect = new Rect(margin, 0, w - margin, h);
+ final Rect dstRect = new Rect(
+ ramp, 0, mContentWidth - ramp, mContentHeight);
+ canvas.drawBitmap(mBitmap, srcRect, dstRect, paint);
+
+ // Draws the left/right parts with mesh matrixes.
+ canvas.drawBitmapMesh(
+ Bitmap.createBitmap(mBitmap, 0, 0, margin, h),
+ mMeshWidth, mMeshHeight, mMeshLeft, 0, null, 0, paint);
canvas.drawBitmapMesh(
- mBitmap, mMeshWidth, mMeshHeight, mMesh, 0, null, 0, null);
+ Bitmap.createBitmap(mBitmap, w - margin, 0, margin, h),
+ mMeshWidth, mMeshHeight, mMeshRight, 0, null, 0, paint);
} else {
- final Rect srcRect = new Rect(0, 0, mBitmap.getWidth(), mBitmap.getHeight());
+ final Rect srcRect = new Rect(0, 0, w, h);
final Rect dstRect = new Rect(0, 0, mContentWidth, mContentHeight);
- final Paint paint = new Paint();
- paint.setFilterBitmap(true);
canvas.drawBitmap(mBitmap, srcRect, dstRect, paint);
}
} finally {
diff --git a/core/java/com/android/ims/internal/uce/common/CapInfo.java b/core/java/com/android/ims/internal/uce/common/CapInfo.java
index 2bb3f1fed927..a7a90f6912c2 100644
--- a/core/java/com/android/ims/internal/uce/common/CapInfo.java
+++ b/core/java/com/android/ims/internal/uce/common/CapInfo.java
@@ -78,6 +78,10 @@ public class CapInfo implements Parcelable {
private boolean mChatbotSupported = false;
/** Chatbot role support. */
private boolean mChatbotRoleSupported = false;
+ /** Standalone Chatbot communication support. */
+ private boolean mSmChatbotSupported = false;
+ /** MMtel based call composer support. */
+ private boolean mMmtelCallComposerSupported = false;
/** List of supported extensions. */
private String[] mExts = new String[10];
/** Time used to compute when to query again. */
@@ -498,6 +502,34 @@ public class CapInfo implements Parcelable {
this.mChatbotRoleSupported = chatbotRoleSupported;
}
+ /**
+ * Checks whether standalone chatbot communication is supported.
+ */
+ public boolean isSmChatbotSupported() {
+ return mSmChatbotSupported;
+ }
+
+ /**
+ * Sets standalone chatbot communication as supported or not supported.
+ */
+ public void setSmChatbotSupported(boolean smChatbotSupported) {
+ this.mSmChatbotSupported = smChatbotSupported;
+ }
+
+ /**
+ * Checks whether Mmtel based call composer is supported.
+ */
+ public boolean isMmtelCallComposerSupported() {
+ return mMmtelCallComposerSupported;
+ }
+
+ /**
+ * Sets Mmtel based call composer as supported or not supported.
+ */
+ public void setMmtelCallComposerSupported(boolean mmtelCallComposerSupported) {
+ this.mMmtelCallComposerSupported = mmtelCallComposerSupported;
+ }
+
/** Gets the list of supported extensions. */
public String[] getExts() {
return mExts;
@@ -553,6 +585,8 @@ public class CapInfo implements Parcelable {
dest.writeInt(mSharedSketchSupported ? 1 : 0);
dest.writeInt(mChatbotSupported ? 1 : 0);
dest.writeInt(mChatbotRoleSupported ? 1 : 0);
+ dest.writeInt(mSmChatbotSupported ? 1 : 0);
+ dest.writeInt(mMmtelCallComposerSupported ? 1 : 0);
dest.writeInt(mRcsIpVoiceCallSupported ? 1 : 0);
dest.writeInt(mRcsIpVideoCallSupported ? 1 : 0);
@@ -602,6 +636,8 @@ public class CapInfo implements Parcelable {
mSharedSketchSupported = (source.readInt() == 0) ? false : true;
mChatbotSupported = (source.readInt() == 0) ? false : true;
mChatbotRoleSupported = (source.readInt() == 0) ? false : true;
+ mSmChatbotSupported = (source.readInt() == 0) ? false : true;
+ mMmtelCallComposerSupported = (source.readInt() == 0) ? false : true;
mRcsIpVoiceCallSupported = (source.readInt() == 0) ? false : true;
mRcsIpVideoCallSupported = (source.readInt() == 0) ? false : true;
diff --git a/core/java/com/android/internal/app/AccessibilityButtonChooserActivity.java b/core/java/com/android/internal/app/AccessibilityButtonChooserActivity.java
index 384275f64e3d..6a6a60d6617c 100644
--- a/core/java/com/android/internal/app/AccessibilityButtonChooserActivity.java
+++ b/core/java/com/android/internal/app/AccessibilityButtonChooserActivity.java
@@ -39,6 +39,8 @@ import android.content.ComponentName;
import android.content.Context;
import android.content.DialogInterface;
import android.content.res.TypedArray;
+import android.graphics.ColorMatrix;
+import android.graphics.ColorMatrixColorFilter;
import android.graphics.drawable.Drawable;
import android.os.Build;
import android.os.Bundle;
@@ -74,6 +76,8 @@ import java.util.StringJoiner;
*/
public class AccessibilityButtonChooserActivity extends Activity {
private static final char SERVICES_SEPARATOR = ':';
+ private static final float DISABLED_ALPHA = 0.5f;
+ private static final float ENABLED_ALPHA = 1.0f;
private static final TextUtils.SimpleStringSplitter sStringColonSplitter =
new TextUtils.SimpleStringSplitter(SERVICES_SEPARATOR);
@ShortcutType
@@ -452,9 +456,16 @@ public class AccessibilityButtonChooserActivity extends Activity {
final boolean isLaunchMenuMode = (mShortcutMenuMode == ShortcutMenuMode.LAUNCH);
final boolean isHardwareButtonTriggered =
(mShortcutButtonType == ACCESSIBILITY_SHORTCUT_KEY);
-
- holder.mLabelView.setEnabled(isLaunchMenuMode || isHardwareButtonTriggered);
- holder.mViewItem.setEnabled(isLaunchMenuMode || isHardwareButtonTriggered);
+ final boolean enabledState = (isLaunchMenuMode || isHardwareButtonTriggered);
+ final ColorMatrix grayScaleMatrix = new ColorMatrix();
+ grayScaleMatrix.setSaturation(/* grayScale */0);
+
+ holder.mIconView.setColorFilter(enabledState
+ ? null : new ColorMatrixColorFilter(grayScaleMatrix));
+ holder.mIconView.setAlpha(enabledState
+ ? ENABLED_ALPHA : DISABLED_ALPHA);
+ holder.mLabelView.setEnabled(enabledState);
+ holder.mViewItem.setEnabled(enabledState);
holder.mViewItem.setImageDrawable(context.getDrawable(R.drawable.ic_delete_item));
holder.mViewItem.setVisibility(View.VISIBLE);
holder.mSwitchItem.setVisibility(View.GONE);
@@ -463,12 +474,15 @@ public class AccessibilityButtonChooserActivity extends Activity {
private void updateInvisibleActionItemVisibility(@NonNull Context context,
@NonNull ViewHolder holder) {
- final boolean isEditMenuMode = (mShortcutMenuMode == ShortcutMenuMode.EDIT);
-
+ holder.mIconView.setColorFilter(null);
+ holder.mIconView.setAlpha(ENABLED_ALPHA);
+ holder.mLabelView.setEnabled(true);
+ holder.mViewItem.setEnabled(true);
holder.mViewItem.setImageDrawable(context.getDrawable(R.drawable.ic_delete_item));
holder.mViewItem.setVisibility(View.VISIBLE);
holder.mSwitchItem.setVisibility(View.GONE);
- holder.mItemContainer.setVisibility(isEditMenuMode ? View.VISIBLE : View.GONE);
+ holder.mItemContainer.setVisibility((mShortcutMenuMode == ShortcutMenuMode.EDIT)
+ ? View.VISIBLE : View.GONE);
}
private void updateIntuitiveActionItemVisibility(@NonNull Context context,
@@ -478,6 +492,10 @@ public class AccessibilityButtonChooserActivity extends Activity {
? isWhiteListingServiceEnabled(context, target)
: isAccessibilityServiceEnabled(context, target);
+ holder.mIconView.setColorFilter(null);
+ holder.mIconView.setAlpha(ENABLED_ALPHA);
+ holder.mLabelView.setEnabled(true);
+ holder.mViewItem.setEnabled(true);
holder.mViewItem.setImageDrawable(context.getDrawable(R.drawable.ic_delete_item));
holder.mViewItem.setVisibility(isEditMenuMode ? View.VISIBLE : View.GONE);
holder.mSwitchItem.setVisibility(isEditMenuMode ? View.GONE : View.VISIBLE);
@@ -487,12 +505,13 @@ public class AccessibilityButtonChooserActivity extends Activity {
private void updateBounceActionItemVisibility(@NonNull Context context,
@NonNull ViewHolder holder) {
- final boolean isEditMenuMode = (mShortcutMenuMode == ShortcutMenuMode.EDIT);
-
- holder.mViewItem.setImageDrawable(
- isEditMenuMode ? context.getDrawable(R.drawable.ic_delete_item)
- : context.getDrawable(R.drawable.ic_open_in_new));
- holder.mViewItem.setVisibility(isEditMenuMode ? View.VISIBLE : View.GONE);
+ holder.mIconView.setColorFilter(null);
+ holder.mIconView.setAlpha(ENABLED_ALPHA);
+ holder.mLabelView.setEnabled(true);
+ holder.mViewItem.setEnabled(true);
+ holder.mViewItem.setImageDrawable(context.getDrawable(R.drawable.ic_delete_item));
+ holder.mViewItem.setVisibility((mShortcutMenuMode == ShortcutMenuMode.EDIT)
+ ? View.VISIBLE : View.GONE);
holder.mSwitchItem.setVisibility(View.GONE);
holder.mItemContainer.setVisibility(View.VISIBLE);
}
diff --git a/core/java/com/android/internal/config/sysui/SystemUiDeviceConfigFlags.java b/core/java/com/android/internal/config/sysui/SystemUiDeviceConfigFlags.java
index b232efc8f46c..3322834c9ed1 100644
--- a/core/java/com/android/internal/config/sysui/SystemUiDeviceConfigFlags.java
+++ b/core/java/com/android/internal/config/sysui/SystemUiDeviceConfigFlags.java
@@ -253,6 +253,13 @@ public final class SystemUiDeviceConfigFlags {
public static final String NOTIFICATIONS_USE_PEOPLE_FILTERING =
"notifications_use_people_filtering";
+ /**
+ * (boolean) Whether or not to enable user dismissing of foreground service notifications
+ * into a new section at the bottom of the notification shade.
+ */
+ public static final String NOTIFICATIONS_ALLOW_FGS_DISMISSAL =
+ "notifications_allow_fgs_dismissal";
+
// Flags related to brightline falsing
/**
diff --git a/core/java/com/android/internal/logging/MetricsLogger.java b/core/java/com/android/internal/logging/MetricsLogger.java
index ed04fd83a515..140c410e8de6 100644
--- a/core/java/com/android/internal/logging/MetricsLogger.java
+++ b/core/java/com/android/internal/logging/MetricsLogger.java
@@ -19,10 +19,10 @@ import android.compat.annotation.UnsupportedAppUsage;
import android.content.Context;
import android.metrics.LogMaker;
import android.os.Build;
-import android.util.StatsLog;
import android.view.View;
import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
+import com.android.internal.util.FrameworkStatsLog;
/**
* Writes sysui_multi_event records to the system event log.
@@ -55,8 +55,8 @@ public class MetricsLogger {
protected void saveLog(LogMaker log) {
// TODO(b/116684537): Flag guard logging to event log and statsd socket.
EventLogTags.writeSysuiMultiAction(log.serialize());
- StatsLog.write(StatsLog.KEY_VALUE_PAIRS_ATOM, /* UID is retrieved from statsd side */ 0,
- log.getEntries());
+ FrameworkStatsLog.write(FrameworkStatsLog.KEY_VALUE_PAIRS_ATOM,
+ /* UID is retrieved from statsd side */ 0, log.getEntries());
}
public static final int VIEW_UNKNOWN = MetricsEvent.VIEW_UNKNOWN;
diff --git a/core/java/com/android/internal/logging/UiEventLoggerImpl.java b/core/java/com/android/internal/logging/UiEventLoggerImpl.java
index fe758a8e6cb7..785b2edf2e1b 100644
--- a/core/java/com/android/internal/logging/UiEventLoggerImpl.java
+++ b/core/java/com/android/internal/logging/UiEventLoggerImpl.java
@@ -16,10 +16,10 @@
package com.android.internal.logging;
-import android.util.StatsLog;
+import com.android.internal.util.FrameworkStatsLog;
/**
- * Standard implementation of UiEventLogger, writing to StatsLog.
+ * Standard implementation of UiEventLogger, writing to FrameworkStatsLog.
*
* See UiEventReported atom in atoms.proto for more context.
*/
@@ -33,7 +33,7 @@ public class UiEventLoggerImpl implements UiEventLogger {
public void log(UiEventEnum event, int uid, String packageName) {
final int eventID = event.getId();
if (eventID > 0) {
- StatsLog.write(StatsLog.UI_EVENT_REPORTED, eventID, uid, packageName);
+ FrameworkStatsLog.write(FrameworkStatsLog.UI_EVENT_REPORTED, eventID, uid, packageName);
}
}
@@ -42,7 +42,7 @@ public class UiEventLoggerImpl implements UiEventLogger {
InstanceId instance) {
final int eventID = event.getId();
if (eventID > 0) {
- StatsLog.write(StatsLog.UI_EVENT_REPORTED, eventID, uid, packageName,
+ FrameworkStatsLog.write(FrameworkStatsLog.UI_EVENT_REPORTED, eventID, uid, packageName,
instance.getId());
}
}
diff --git a/core/java/com/android/internal/os/BatteryStatsImpl.java b/core/java/com/android/internal/os/BatteryStatsImpl.java
index 580c1f00d788..18066dce287e 100644
--- a/core/java/com/android/internal/os/BatteryStatsImpl.java
+++ b/core/java/com/android/internal/os/BatteryStatsImpl.java
@@ -85,7 +85,6 @@ import android.util.Slog;
import android.util.SparseArray;
import android.util.SparseIntArray;
import android.util.SparseLongArray;
-import android.util.StatsLog;
import android.util.TimeUtils;
import android.util.Xml;
import android.view.Display;
@@ -100,6 +99,7 @@ import com.android.internal.os.KernelCpuUidTimeReader.KernelCpuUidUserSysTimeRea
import com.android.internal.util.ArrayUtils;
import com.android.internal.util.FastPrintWriter;
import com.android.internal.util.FastXmlSerializer;
+import com.android.internal.util.FrameworkStatsLog;
import com.android.internal.util.XmlUtils;
import libcore.util.EmptyArray;
@@ -4263,13 +4263,13 @@ public class BatteryStatsImpl extends BatteryStats {
getUidStatsLocked(uid).noteStartWakeLocked(pid, name, type, elapsedRealtime);
if (wc != null) {
- StatsLog.write(StatsLog.WAKELOCK_STATE_CHANGED, wc.getUids(), wc.getTags(),
- getPowerManagerWakeLockLevel(type), name,
- StatsLog.WAKELOCK_STATE_CHANGED__STATE__ACQUIRE);
+ FrameworkStatsLog.write(FrameworkStatsLog.WAKELOCK_STATE_CHANGED, wc.getUids(),
+ wc.getTags(), getPowerManagerWakeLockLevel(type), name,
+ FrameworkStatsLog.WAKELOCK_STATE_CHANGED__STATE__ACQUIRE);
} else {
- StatsLog.write_non_chained(StatsLog.WAKELOCK_STATE_CHANGED, uid, null,
- getPowerManagerWakeLockLevel(type), name,
- StatsLog.WAKELOCK_STATE_CHANGED__STATE__ACQUIRE);
+ FrameworkStatsLog.write_non_chained(FrameworkStatsLog.WAKELOCK_STATE_CHANGED, uid,
+ null, getPowerManagerWakeLockLevel(type), name,
+ FrameworkStatsLog.WAKELOCK_STATE_CHANGED__STATE__ACQUIRE);
}
}
}
@@ -4308,13 +4308,13 @@ public class BatteryStatsImpl extends BatteryStats {
getUidStatsLocked(uid).noteStopWakeLocked(pid, name, type, elapsedRealtime);
if (wc != null) {
- StatsLog.write(StatsLog.WAKELOCK_STATE_CHANGED, wc.getUids(), wc.getTags(),
- getPowerManagerWakeLockLevel(type), name,
- StatsLog.WAKELOCK_STATE_CHANGED__STATE__RELEASE);
+ FrameworkStatsLog.write(FrameworkStatsLog.WAKELOCK_STATE_CHANGED, wc.getUids(),
+ wc.getTags(), getPowerManagerWakeLockLevel(type), name,
+ FrameworkStatsLog.WAKELOCK_STATE_CHANGED__STATE__RELEASE);
} else {
- StatsLog.write_non_chained(StatsLog.WAKELOCK_STATE_CHANGED, uid, null,
- getPowerManagerWakeLockLevel(type), name,
- StatsLog.WAKELOCK_STATE_CHANGED__STATE__RELEASE);
+ FrameworkStatsLog.write_non_chained(FrameworkStatsLog.WAKELOCK_STATE_CHANGED, uid,
+ null, getPowerManagerWakeLockLevel(type), name,
+ FrameworkStatsLog.WAKELOCK_STATE_CHANGED__STATE__RELEASE);
}
}
}
@@ -4323,7 +4323,8 @@ public class BatteryStatsImpl extends BatteryStats {
* Converts BatteryStats wakelock types back into PowerManager wakelock levels.
* This is the inverse map of Notifier.getBatteryStatsWakeLockMonitorType().
* These are estimations, since batterystats loses some of the original data.
- * TODO: Delete this. Instead, StatsLog.write should be called from PowerManager's Notifier.
+ * TODO: Delete this. Instead, FrameworkStatsLog.write should be called from
+ * PowerManager's Notifier.
*/
private int getPowerManagerWakeLockLevel(int battertStatsWakelockType) {
switch (battertStatsWakelockType) {
@@ -4516,7 +4517,7 @@ public class BatteryStatsImpl extends BatteryStats {
long deltaUptime = uptimeMs - mLastWakeupUptimeMs;
SamplingTimer timer = getWakeupReasonTimerLocked(mLastWakeupReason);
timer.add(deltaUptime * 1000, 1); // time in in microseconds
- StatsLog.write(StatsLog.KERNEL_WAKEUP_REPORTED, mLastWakeupReason,
+ FrameworkStatsLog.write(FrameworkStatsLog.KERNEL_WAKEUP_REPORTED, mLastWakeupReason,
/* duration_usec */ deltaUptime * 1000);
mLastWakeupReason = null;
}
@@ -4658,12 +4659,12 @@ public class BatteryStatsImpl extends BatteryStats {
mGpsNesting++;
if (workChain == null) {
- StatsLog.write_non_chained(StatsLog.GPS_SCAN_STATE_CHANGED, uid, null,
- StatsLog.GPS_SCAN_STATE_CHANGED__STATE__ON);
+ FrameworkStatsLog.write_non_chained(FrameworkStatsLog.GPS_SCAN_STATE_CHANGED, uid, null,
+ FrameworkStatsLog.GPS_SCAN_STATE_CHANGED__STATE__ON);
} else {
- StatsLog.write(StatsLog.GPS_SCAN_STATE_CHANGED,
+ FrameworkStatsLog.write(FrameworkStatsLog.GPS_SCAN_STATE_CHANGED,
workChain.getUids(), workChain.getTags(),
- StatsLog.GPS_SCAN_STATE_CHANGED__STATE__ON);
+ FrameworkStatsLog.GPS_SCAN_STATE_CHANGED__STATE__ON);
}
getUidStatsLocked(uid).noteStartGps(elapsedRealtime);
@@ -4684,11 +4685,11 @@ public class BatteryStatsImpl extends BatteryStats {
}
if (workChain == null) {
- StatsLog.write_non_chained(StatsLog.GPS_SCAN_STATE_CHANGED, uid, null,
- StatsLog.GPS_SCAN_STATE_CHANGED__STATE__OFF);
+ FrameworkStatsLog.write_non_chained(FrameworkStatsLog.GPS_SCAN_STATE_CHANGED, uid, null,
+ FrameworkStatsLog.GPS_SCAN_STATE_CHANGED__STATE__OFF);
} else {
- StatsLog.write(StatsLog.GPS_SCAN_STATE_CHANGED, workChain.getUids(),
- workChain.getTags(), StatsLog.GPS_SCAN_STATE_CHANGED__STATE__OFF);
+ FrameworkStatsLog.write(FrameworkStatsLog.GPS_SCAN_STATE_CHANGED, workChain.getUids(),
+ workChain.getTags(), FrameworkStatsLog.GPS_SCAN_STATE_CHANGED__STATE__OFF);
}
getUidStatsLocked(uid).noteStopGps(elapsedRealtime);
@@ -4947,9 +4948,10 @@ public class BatteryStatsImpl extends BatteryStats {
mPowerSaveModeEnabledTimer.stopRunningLocked(elapsedRealtime);
}
addHistoryRecordLocked(elapsedRealtime, uptime);
- StatsLog.write(StatsLog.BATTERY_SAVER_MODE_STATE_CHANGED, enabled ?
- StatsLog.BATTERY_SAVER_MODE_STATE_CHANGED__STATE__ON :
- StatsLog.BATTERY_SAVER_MODE_STATE_CHANGED__STATE__OFF);
+ FrameworkStatsLog.write(FrameworkStatsLog.BATTERY_SAVER_MODE_STATE_CHANGED,
+ enabled
+ ? FrameworkStatsLog.BATTERY_SAVER_MODE_STATE_CHANGED__STATE__ON
+ : FrameworkStatsLog.BATTERY_SAVER_MODE_STATE_CHANGED__STATE__OFF);
}
}
@@ -4977,7 +4979,7 @@ public class BatteryStatsImpl extends BatteryStats {
if (nowIdling) statsmode = DEVICE_IDLE_MODE_DEEP;
else if (nowLightIdling) statsmode = DEVICE_IDLE_MODE_LIGHT;
else statsmode = DEVICE_IDLE_MODE_OFF;
- StatsLog.write(StatsLog.DEVICE_IDLING_MODE_STATE_CHANGED, statsmode);
+ FrameworkStatsLog.write(FrameworkStatsLog.DEVICE_IDLING_MODE_STATE_CHANGED, statsmode);
}
if (mDeviceIdling != nowIdling) {
mDeviceIdling = nowIdling;
@@ -5023,7 +5025,7 @@ public class BatteryStatsImpl extends BatteryStats {
mDeviceIdleModeFullTimer.startRunningLocked(elapsedRealtime);
}
mDeviceIdleMode = mode;
- StatsLog.write(StatsLog.DEVICE_IDLE_MODE_STATE_CHANGED, mode);
+ FrameworkStatsLog.write(FrameworkStatsLog.DEVICE_IDLE_MODE_STATE_CHANGED, mode);
}
}
@@ -5197,7 +5199,8 @@ public class BatteryStatsImpl extends BatteryStats {
if (DEBUG_HISTORY) Slog.v(TAG, "Phone started scanning to: "
+ Integer.toHexString(mHistoryCur.states));
mPhoneSignalScanningTimer.startRunningLocked(elapsedRealtime);
- StatsLog.write(StatsLog.PHONE_SERVICE_STATE_CHANGED, state, simState, strengthBin);
+ FrameworkStatsLog.write(FrameworkStatsLog.PHONE_SERVICE_STATE_CHANGED, state,
+ simState, strengthBin);
}
}
@@ -5209,7 +5212,8 @@ public class BatteryStatsImpl extends BatteryStats {
+ Integer.toHexString(mHistoryCur.states));
newHistory = true;
mPhoneSignalScanningTimer.stopRunningLocked(elapsedRealtime);
- StatsLog.write(StatsLog.PHONE_SERVICE_STATE_CHANGED, state, simState, strengthBin);
+ FrameworkStatsLog.write(FrameworkStatsLog.PHONE_SERVICE_STATE_CHANGED, state,
+ simState, strengthBin);
}
}
@@ -5236,7 +5240,8 @@ public class BatteryStatsImpl extends BatteryStats {
if (DEBUG_HISTORY) Slog.v(TAG, "Signal strength " + strengthBin + " to: "
+ Integer.toHexString(mHistoryCur.states));
newHistory = true;
- StatsLog.write(StatsLog.PHONE_SIGNAL_STRENGTH_CHANGED, strengthBin);
+ FrameworkStatsLog.write(
+ FrameworkStatsLog.PHONE_SIGNAL_STRENGTH_CHANGED, strengthBin);
} else {
stopAllPhoneSignalStrengthTimersLocked(-1);
}
@@ -12436,13 +12441,13 @@ public class BatteryStatsImpl extends BatteryStats {
final int status, final int plugType, final int level) {
if (recentPast == null || recentPast.batteryStatus != status) {
- StatsLog.write(StatsLog.CHARGING_STATE_CHANGED, status);
+ FrameworkStatsLog.write(FrameworkStatsLog.CHARGING_STATE_CHANGED, status);
}
if (recentPast == null || recentPast.batteryPlugType != plugType) {
- StatsLog.write(StatsLog.PLUGGED_STATE_CHANGED, plugType);
+ FrameworkStatsLog.write(FrameworkStatsLog.PLUGGED_STATE_CHANGED, plugType);
}
if (recentPast == null || recentPast.batteryLevel != level) {
- StatsLog.write(StatsLog.BATTERY_LEVEL_CHANGED, level);
+ FrameworkStatsLog.write(FrameworkStatsLog.BATTERY_LEVEL_CHANGED, level);
}
}
diff --git a/core/java/com/android/internal/os/StatsdHiddenApiUsageLogger.java b/core/java/com/android/internal/os/StatsdHiddenApiUsageLogger.java
index c169b2239554..89773b3feac7 100644
--- a/core/java/com/android/internal/os/StatsdHiddenApiUsageLogger.java
+++ b/core/java/com/android/internal/os/StatsdHiddenApiUsageLogger.java
@@ -18,10 +18,10 @@ package com.android.internal.os;
import android.metrics.LogMaker;
import android.os.Process;
-import android.util.StatsLog;
import com.android.internal.logging.MetricsLogger;
import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
+import com.android.internal.util.FrameworkStatsLog;
import dalvik.system.VMRuntime.HiddenApiUsageLogger;
@@ -84,23 +84,24 @@ class StatsdHiddenApiUsageLogger implements HiddenApiUsageLogger {
}
private void newLogUsage(String signature, int accessMethod, boolean accessDenied) {
- int accessMethodProto = StatsLog.HIDDEN_API_USED__ACCESS_METHOD__NONE;
+ int accessMethodProto = FrameworkStatsLog.HIDDEN_API_USED__ACCESS_METHOD__NONE;
switch(accessMethod) {
case HiddenApiUsageLogger.ACCESS_METHOD_NONE:
- accessMethodProto = StatsLog.HIDDEN_API_USED__ACCESS_METHOD__NONE;
+ accessMethodProto = FrameworkStatsLog.HIDDEN_API_USED__ACCESS_METHOD__NONE;
break;
case HiddenApiUsageLogger.ACCESS_METHOD_REFLECTION:
- accessMethodProto = StatsLog.HIDDEN_API_USED__ACCESS_METHOD__REFLECTION;
+ accessMethodProto = FrameworkStatsLog.HIDDEN_API_USED__ACCESS_METHOD__REFLECTION;
break;
case HiddenApiUsageLogger.ACCESS_METHOD_JNI:
- accessMethodProto = StatsLog.HIDDEN_API_USED__ACCESS_METHOD__JNI;
+ accessMethodProto = FrameworkStatsLog.HIDDEN_API_USED__ACCESS_METHOD__JNI;
break;
case HiddenApiUsageLogger.ACCESS_METHOD_LINKING:
- accessMethodProto = StatsLog.HIDDEN_API_USED__ACCESS_METHOD__LINKING;
+ accessMethodProto = FrameworkStatsLog.HIDDEN_API_USED__ACCESS_METHOD__LINKING;
break;
}
int uid = Process.myUid();
- StatsLog.write(StatsLog.HIDDEN_API_USED, uid, signature, accessMethodProto, accessDenied);
+ FrameworkStatsLog.write(FrameworkStatsLog.HIDDEN_API_USED, uid, signature,
+ accessMethodProto, accessDenied);
}
}
diff --git a/core/java/com/android/internal/os/logging/MetricsLoggerWrapper.java b/core/java/com/android/internal/os/logging/MetricsLoggerWrapper.java
index 8bb1e48201bb..ba60fa590792 100644
--- a/core/java/com/android/internal/os/logging/MetricsLoggerWrapper.java
+++ b/core/java/com/android/internal/os/logging/MetricsLoggerWrapper.java
@@ -20,11 +20,11 @@ import android.content.ComponentName;
import android.content.Context;
import android.content.pm.PackageManager.NameNotFoundException;
import android.util.Pair;
-import android.util.StatsLog;
import android.view.WindowManager.LayoutParams;
import com.android.internal.logging.MetricsLogger;
import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
+import com.android.internal.util.FrameworkStatsLog;
/**
* Used to wrap different logging calls in one, so that client side code base is clean and more
@@ -39,10 +39,10 @@ public class MetricsLoggerWrapper {
Pair<ComponentName, Integer> topActivityInfo) {
MetricsLogger.action(context, MetricsEvent.ACTION_PICTURE_IN_PICTURE_DISMISSED,
METRIC_VALUE_DISMISSED_BY_TAP);
- StatsLog.write(StatsLog.PICTURE_IN_PICTURE_STATE_CHANGED,
+ FrameworkStatsLog.write(FrameworkStatsLog.PICTURE_IN_PICTURE_STATE_CHANGED,
getUid(context, topActivityInfo.first, topActivityInfo.second),
topActivityInfo.first.flattenToString(),
- StatsLog.PICTURE_IN_PICTURE_STATE_CHANGED__STATE__DISMISSED);
+ FrameworkStatsLog.PICTURE_IN_PICTURE_STATE_CHANGED__STATE__DISMISSED);
}
public static void logPictureInPictureDismissByDrag(Context context,
@@ -50,20 +50,20 @@ public class MetricsLoggerWrapper {
MetricsLogger.action(context,
MetricsEvent.ACTION_PICTURE_IN_PICTURE_DISMISSED,
METRIC_VALUE_DISMISSED_BY_DRAG);
- StatsLog.write(StatsLog.PICTURE_IN_PICTURE_STATE_CHANGED,
+ FrameworkStatsLog.write(FrameworkStatsLog.PICTURE_IN_PICTURE_STATE_CHANGED,
getUid(context, topActivityInfo.first, topActivityInfo.second),
topActivityInfo.first.flattenToString(),
- StatsLog.PICTURE_IN_PICTURE_STATE_CHANGED__STATE__DISMISSED);
+ FrameworkStatsLog.PICTURE_IN_PICTURE_STATE_CHANGED__STATE__DISMISSED);
}
public static void logPictureInPictureMinimize(Context context, boolean isMinimized,
Pair<ComponentName, Integer> topActivityInfo) {
MetricsLogger.action(context, MetricsEvent.ACTION_PICTURE_IN_PICTURE_MINIMIZED,
isMinimized);
- StatsLog.write(StatsLog.PICTURE_IN_PICTURE_STATE_CHANGED,
+ FrameworkStatsLog.write(FrameworkStatsLog.PICTURE_IN_PICTURE_STATE_CHANGED,
getUid(context, topActivityInfo.first, topActivityInfo.second),
topActivityInfo.first.flattenToString(),
- StatsLog.PICTURE_IN_PICTURE_STATE_CHANGED__STATE__MINIMIZED);
+ FrameworkStatsLog.PICTURE_IN_PICTURE_STATE_CHANGED__STATE__MINIMIZED);
}
/**
@@ -92,29 +92,29 @@ public class MetricsLoggerWrapper {
int uid, String shortComponentName, boolean supportsEnterPipOnTaskSwitch) {
MetricsLogger.action(context, MetricsEvent.ACTION_PICTURE_IN_PICTURE_ENTERED,
supportsEnterPipOnTaskSwitch);
- StatsLog.write(StatsLog.PICTURE_IN_PICTURE_STATE_CHANGED, uid,
+ FrameworkStatsLog.write(FrameworkStatsLog.PICTURE_IN_PICTURE_STATE_CHANGED, uid,
shortComponentName,
- StatsLog.PICTURE_IN_PICTURE_STATE_CHANGED__STATE__ENTERED);
+ FrameworkStatsLog.PICTURE_IN_PICTURE_STATE_CHANGED__STATE__ENTERED);
}
public static void logPictureInPictureFullScreen(Context context, int uid,
String shortComponentName) {
MetricsLogger.action(context,
MetricsEvent.ACTION_PICTURE_IN_PICTURE_EXPANDED_TO_FULLSCREEN);
- StatsLog.write(StatsLog.PICTURE_IN_PICTURE_STATE_CHANGED,
+ FrameworkStatsLog.write(FrameworkStatsLog.PICTURE_IN_PICTURE_STATE_CHANGED,
uid,
shortComponentName,
- StatsLog.PICTURE_IN_PICTURE_STATE_CHANGED__STATE__EXPANDED_TO_FULL_SCREEN);
+ FrameworkStatsLog.PICTURE_IN_PICTURE_STATE_CHANGED__STATE__EXPANDED_TO_FULL_SCREEN);
}
public static void logAppOverlayEnter(int uid, String packageName, boolean changed, int type, boolean usingAlertWindow) {
if (changed) {
if (type != LayoutParams.TYPE_APPLICATION_OVERLAY) {
- StatsLog.write(StatsLog.OVERLAY_STATE_CHANGED, uid, packageName, true,
- StatsLog.OVERLAY_STATE_CHANGED__STATE__ENTERED);
+ FrameworkStatsLog.write(FrameworkStatsLog.OVERLAY_STATE_CHANGED, uid, packageName,
+ true, FrameworkStatsLog.OVERLAY_STATE_CHANGED__STATE__ENTERED);
} else if (!usingAlertWindow){
- StatsLog.write(StatsLog.OVERLAY_STATE_CHANGED, uid, packageName, false,
- StatsLog.OVERLAY_STATE_CHANGED__STATE__ENTERED);
+ FrameworkStatsLog.write(FrameworkStatsLog.OVERLAY_STATE_CHANGED, uid, packageName,
+ false, FrameworkStatsLog.OVERLAY_STATE_CHANGED__STATE__ENTERED);
}
}
}
@@ -122,11 +122,11 @@ public class MetricsLoggerWrapper {
public static void logAppOverlayExit(int uid, String packageName, boolean changed, int type, boolean usingAlertWindow) {
if (changed) {
if (type != LayoutParams.TYPE_APPLICATION_OVERLAY) {
- StatsLog.write(StatsLog.OVERLAY_STATE_CHANGED, uid, packageName, true,
- StatsLog.OVERLAY_STATE_CHANGED__STATE__EXITED);
+ FrameworkStatsLog.write(FrameworkStatsLog.OVERLAY_STATE_CHANGED, uid, packageName,
+ true, FrameworkStatsLog.OVERLAY_STATE_CHANGED__STATE__EXITED);
} else if (!usingAlertWindow){
- StatsLog.write(StatsLog.OVERLAY_STATE_CHANGED, uid, packageName, false,
- StatsLog.OVERLAY_STATE_CHANGED__STATE__EXITED);
+ FrameworkStatsLog.write(FrameworkStatsLog.OVERLAY_STATE_CHANGED, uid, packageName,
+ false, FrameworkStatsLog.OVERLAY_STATE_CHANGED__STATE__EXITED);
}
}
}
diff --git a/core/java/com/android/internal/util/ObjectUtils.java b/core/java/com/android/internal/util/ObjectUtils.java
index 5568d91011dc..0e7b93d406f5 100644
--- a/core/java/com/android/internal/util/ObjectUtils.java
+++ b/core/java/com/android/internal/util/ObjectUtils.java
@@ -46,4 +46,12 @@ public class ObjectUtils {
return (b != null) ? -1 : 0;
}
}
+
+ /**
+ * Returns its first argument if non-null, and the second otherwise.
+ */
+ @Nullable
+ public static <T> T getOrElse(@Nullable final T object, @Nullable final T otherwise) {
+ return null != object ? object : otherwise;
+ }
}
diff --git a/core/java/com/android/server/BootReceiver.java b/core/java/com/android/server/BootReceiver.java
index 31b5e491d0e5..ac2361dff560 100644
--- a/core/java/com/android/server/BootReceiver.java
+++ b/core/java/com/android/server/BootReceiver.java
@@ -35,14 +35,13 @@ import android.text.TextUtils;
import android.util.AtomicFile;
import android.util.EventLog;
import android.util.Slog;
-import android.util.StatsLog;
import android.util.Xml;
import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.logging.MetricsLogger;
import com.android.internal.util.FastXmlSerializer;
+import com.android.internal.util.FrameworkStatsLog;
import com.android.internal.util.XmlUtils;
-import com.android.server.DropboxLogTags;
import org.xmlpull.v1.XmlPullParser;
import org.xmlpull.v1.XmlPullParserException;
@@ -330,7 +329,7 @@ public class BootReceiver extends BroadcastReceiver {
addTextToDropBox(db, "system_server_native_crash", text, filename, maxSize);
}
if (tag.equals(TAG_TOMBSTONE)) {
- StatsLog.write(StatsLog.TOMB_STONE_OCCURRED);
+ FrameworkStatsLog.write(FrameworkStatsLog.TOMB_STONE_OCCURRED);
}
addTextToDropBox(db, tag, text, filename, maxSize);
}
@@ -427,20 +426,25 @@ public class BootReceiver extends BroadcastReceiver {
int eventType;
switch (propPostfix) {
case "early":
- eventType = StatsLog.BOOT_TIME_EVENT_DURATION__EVENT__MOUNT_EARLY_DURATION;
+ eventType =
+ FrameworkStatsLog
+ .BOOT_TIME_EVENT_DURATION__EVENT__MOUNT_EARLY_DURATION;
break;
case "default":
eventType =
- StatsLog.BOOT_TIME_EVENT_DURATION__EVENT__MOUNT_DEFAULT_DURATION;
+ FrameworkStatsLog
+ .BOOT_TIME_EVENT_DURATION__EVENT__MOUNT_DEFAULT_DURATION;
break;
case "late":
- eventType = StatsLog.BOOT_TIME_EVENT_DURATION__EVENT__MOUNT_LATE_DURATION;
+ eventType =
+ FrameworkStatsLog
+ .BOOT_TIME_EVENT_DURATION__EVENT__MOUNT_LATE_DURATION;
break;
default:
continue;
}
- StatsLog.write(StatsLog.BOOT_TIME_EVENT_DURATION_REPORTED, eventType,
- duration);
+ FrameworkStatsLog.write(FrameworkStatsLog.BOOT_TIME_EVENT_DURATION_REPORTED,
+ eventType, duration);
}
}
}
@@ -544,7 +548,8 @@ public class BootReceiver extends BroadcastReceiver {
Slog.e(TAG, "No value received for shutdown duration");
}
- StatsLog.write(StatsLog.SHUTDOWN_SEQUENCE_REPORTED, reboot, reason, start, duration);
+ FrameworkStatsLog.write(FrameworkStatsLog.SHUTDOWN_SEQUENCE_REPORTED, reboot, reason, start,
+ duration);
}
private static void logFsShutdownTime() {
@@ -571,19 +576,19 @@ public class BootReceiver extends BroadcastReceiver {
Pattern pattern = Pattern.compile(LAST_SHUTDOWN_TIME_PATTERN, Pattern.MULTILINE);
Matcher matcher = pattern.matcher(lines);
if (matcher.find()) {
- StatsLog.write(StatsLog.BOOT_TIME_EVENT_DURATION_REPORTED,
- StatsLog.BOOT_TIME_EVENT_DURATION__EVENT__SHUTDOWN_DURATION,
+ FrameworkStatsLog.write(FrameworkStatsLog.BOOT_TIME_EVENT_DURATION_REPORTED,
+ FrameworkStatsLog.BOOT_TIME_EVENT_DURATION__EVENT__SHUTDOWN_DURATION,
Integer.parseInt(matcher.group(1)));
- StatsLog.write(StatsLog.BOOT_TIME_EVENT_ERROR_CODE_REPORTED,
- StatsLog.BOOT_TIME_EVENT_ERROR_CODE__EVENT__SHUTDOWN_UMOUNT_STAT,
+ FrameworkStatsLog.write(FrameworkStatsLog.BOOT_TIME_EVENT_ERROR_CODE_REPORTED,
+ FrameworkStatsLog.BOOT_TIME_EVENT_ERROR_CODE__EVENT__SHUTDOWN_UMOUNT_STAT,
Integer.parseInt(matcher.group(2)));
Slog.i(TAG, "boot_fs_shutdown," + matcher.group(1) + "," + matcher.group(2));
} else { // not found
// This can happen when a device has too much kernel log after file system unmount
// ,exceeding maxReadSize. And having that much kernel logging can affect overall
// performance as well. So it is better to fix the kernel to reduce the amount of log.
- StatsLog.write(StatsLog.BOOT_TIME_EVENT_ERROR_CODE_REPORTED,
- StatsLog.BOOT_TIME_EVENT_ERROR_CODE__EVENT__SHUTDOWN_UMOUNT_STAT,
+ FrameworkStatsLog.write(FrameworkStatsLog.BOOT_TIME_EVENT_ERROR_CODE_REPORTED,
+ FrameworkStatsLog.BOOT_TIME_EVENT_ERROR_CODE__EVENT__SHUTDOWN_UMOUNT_STAT,
UMOUNT_STATUS_NOT_AVAILABLE);
Slog.w(TAG, "boot_fs_shutdown, string not found");
}
@@ -694,8 +699,9 @@ public class BootReceiver extends BroadcastReceiver {
}
stat = fixFsckFsStat(partition, stat, lines, startLineNumber, endLineNumber);
if ("userdata".equals(partition) || "data".equals(partition)) {
- StatsLog.write(StatsLog.BOOT_TIME_EVENT_ERROR_CODE_REPORTED,
- StatsLog.BOOT_TIME_EVENT_ERROR_CODE__EVENT__FS_MGR_FS_STAT_DATA_PARTITION,
+ FrameworkStatsLog.write(FrameworkStatsLog.BOOT_TIME_EVENT_ERROR_CODE_REPORTED,
+ FrameworkStatsLog
+ .BOOT_TIME_EVENT_ERROR_CODE__EVENT__FS_MGR_FS_STAT_DATA_PARTITION,
stat);
}
Slog.i(TAG, "fs_stat, partition:" + partition + " stat:0x" + Integer.toHexString(stat));
diff --git a/core/jni/android_util_Binder.cpp b/core/jni/android_util_Binder.cpp
index e77c25efb1d4..c269d1cf4295 100644
--- a/core/jni/android_util_Binder.cpp
+++ b/core/jni/android_util_Binder.cpp
@@ -463,6 +463,9 @@ public:
if (mVintf) {
::android::internal::Stability::markVintf(b.get());
}
+ if (mExtension != nullptr) {
+ b.get()->setExtension(mExtension);
+ }
mBinder = b;
ALOGV("Creating JavaBinder %p (refs %p) for Object %p, weakCount=%" PRId32 "\n",
b.get(), b->getWeakRefs(), obj, b->getWeakRefs()->getWeakCount());
@@ -481,6 +484,24 @@ public:
mVintf = true;
}
+ sp<IBinder> getExtension() {
+ AutoMutex _l(mLock);
+ sp<JavaBBinder> b = mBinder.promote();
+ if (b != nullptr) {
+ return b.get()->getExtension();
+ }
+ return mExtension;
+ }
+
+ void setExtension(const sp<IBinder>& extension) {
+ AutoMutex _l(mLock);
+ mExtension = extension;
+ sp<JavaBBinder> b = mBinder.promote();
+ if (b != nullptr) {
+ b.get()->setExtension(mExtension);
+ }
+ }
+
private:
Mutex mLock;
wp<JavaBBinder> mBinder;
@@ -489,6 +510,8 @@ private:
// is too much binder state here, we can think about making JavaBBinder an
// sp here (avoid recreating it)
bool mVintf = false;
+
+ sp<IBinder> mExtension;
};
// ----------------------------------------------------------------------------
@@ -1037,6 +1060,17 @@ static jobject android_os_Binder_waitForService(
return javaObjectForIBinder(env, service);
}
+static jobject android_os_Binder_getExtension(JNIEnv* env, jobject obj) {
+ JavaBBinderHolder* jbh = (JavaBBinderHolder*) env->GetLongField(obj, gBinderOffsets.mObject);
+ return javaObjectForIBinder(env, jbh->getExtension());
+}
+
+static void android_os_Binder_setExtension(JNIEnv* env, jobject obj, jobject extensionObject) {
+ JavaBBinderHolder* jbh = (JavaBBinderHolder*) env->GetLongField(obj, gBinderOffsets.mObject);
+ sp<IBinder> extension = ibinderForJavaObject(env, extensionObject);
+ jbh->setExtension(extension);
+}
+
// ----------------------------------------------------------------------------
static const JNINativeMethod gBinderMethods[] = {
@@ -1066,7 +1100,9 @@ static const JNINativeMethod gBinderMethods[] = {
{ "getNativeBBinderHolder", "()J", (void*)android_os_Binder_getNativeBBinderHolder },
{ "getNativeFinalizer", "()J", (void*)android_os_Binder_getNativeFinalizer },
{ "blockUntilThreadAvailable", "()V", (void*)android_os_Binder_blockUntilThreadAvailable },
- { "waitForService", "(Ljava/lang/String;)Landroid/os/IBinder;", (void*)android_os_Binder_waitForService }
+ { "waitForService", "(Ljava/lang/String;)Landroid/os/IBinder;", (void*)android_os_Binder_waitForService },
+ { "getExtension", "()Landroid/os/IBinder;", (void*)android_os_Binder_getExtension },
+ { "setExtension", "(Landroid/os/IBinder;)V", (void*)android_os_Binder_setExtension },
};
const char* const kBinderPathName = "android/os/Binder";
@@ -1506,6 +1542,21 @@ JNIEXPORT jlong JNICALL android_os_BinderProxy_getNativeFinalizer(JNIEnv*, jclas
return (jlong) BinderProxy_destroy;
}
+static jobject android_os_BinderProxy_getExtension(JNIEnv* env, jobject obj) {
+ IBinder* binder = getBPNativeData(env, obj)->mObject.get();
+ if (binder == nullptr) {
+ jniThrowException(env, "java/lang/IllegalStateException", "Native IBinder is null");
+ return nullptr;
+ }
+ sp<IBinder> extension;
+ status_t err = binder->getExtension(&extension);
+ if (err != OK) {
+ signalExceptionForError(env, obj, err, true /* canThrowRemoteException */);
+ return nullptr;
+ }
+ return javaObjectForIBinder(env, extension);
+}
+
// ----------------------------------------------------------------------------
static const JNINativeMethod gBinderProxyMethods[] = {
@@ -1517,6 +1568,7 @@ static const JNINativeMethod gBinderProxyMethods[] = {
{"linkToDeath", "(Landroid/os/IBinder$DeathRecipient;I)V", (void*)android_os_BinderProxy_linkToDeath},
{"unlinkToDeath", "(Landroid/os/IBinder$DeathRecipient;I)Z", (void*)android_os_BinderProxy_unlinkToDeath},
{"getNativeFinalizer", "()J", (void*)android_os_BinderProxy_getNativeFinalizer},
+ {"getExtension", "()Landroid/os/IBinder;", (void*)android_os_BinderProxy_getExtension},
};
const char* const kBinderProxyPathName = "android/os/BinderProxy";
diff --git a/core/jni/android_view_SurfaceControl.cpp b/core/jni/android_view_SurfaceControl.cpp
index 2b9d45431582..f564d7558832 100644
--- a/core/jni/android_view_SurfaceControl.cpp
+++ b/core/jni/android_view_SurfaceControl.cpp
@@ -22,20 +22,22 @@
#include "android_hardware_input_InputWindowHandle.h"
#include "core_jni_helpers.h"
+#include <memory>
+
+#include <android-base/chrono_utils.h>
#include <android/graphics/region.h>
#include <android_runtime/AndroidRuntime.h>
-#include <android-base/chrono_utils.h>
-#include <nativehelper/JNIHelp.h>
-#include <nativehelper/ScopedUtfChars.h>
#include <android_runtime/android_view_Surface.h>
#include <android_runtime/android_view_SurfaceSession.h>
#include <gui/Surface.h>
#include <gui/SurfaceComposerClient.h>
#include <jni.h>
-#include <memory>
+#include <nativehelper/JNIHelp.h>
+#include <nativehelper/ScopedUtfChars.h>
#include <stdio.h>
#include <system/graphics.h>
#include <ui/ConfigStoreTypes.h>
+#include <ui/DisplayConfig.h>
#include <ui/DisplayInfo.h>
#include <ui/DisplayedFrameStats.h>
#include <ui/FrameStats.h>
@@ -63,16 +65,21 @@ static const char* const OutOfResourcesException =
static struct {
jclass clazz;
jmethodID ctor;
+ jfieldID density;
+ jfieldID secure;
+} gDisplayInfoClassInfo;
+
+static struct {
+ jclass clazz;
+ jmethodID ctor;
jfieldID width;
jfieldID height;
- jfieldID refreshRate;
- jfieldID density;
jfieldID xDpi;
jfieldID yDpi;
- jfieldID secure;
+ jfieldID refreshRate;
jfieldID appVsyncOffsetNanos;
jfieldID presentationDeadlineNanos;
-} gPhysicalDisplayInfoClassInfo;
+} gDisplayConfigClassInfo;
static struct {
jfieldID bottom;
@@ -766,37 +773,46 @@ static void nativeSetDisplaySize(JNIEnv* env, jclass clazz,
}
}
-static jobjectArray nativeGetDisplayConfigs(JNIEnv* env, jclass clazz,
- jobject tokenObj) {
- sp<IBinder> token(ibinderForJavaObject(env, tokenObj));
- if (token == NULL) return NULL;
+static jobject nativeGetDisplayInfo(JNIEnv* env, jclass clazz, jobject tokenObj) {
+ DisplayInfo info;
+ if (const auto token = ibinderForJavaObject(env, tokenObj);
+ !token || SurfaceComposerClient::getDisplayInfo(token, &info) != NO_ERROR) {
+ return nullptr;
+ }
- Vector<DisplayInfo> configs;
- if (SurfaceComposerClient::getDisplayConfigs(token, &configs) != NO_ERROR ||
- configs.size() == 0) {
- return NULL;
+ jobject object = env->NewObject(gDisplayInfoClassInfo.clazz, gDisplayInfoClassInfo.ctor);
+ env->SetFloatField(object, gDisplayInfoClassInfo.density, info.density);
+ env->SetBooleanField(object, gDisplayInfoClassInfo.secure, info.secure);
+ return object;
+}
+
+static jobjectArray nativeGetDisplayConfigs(JNIEnv* env, jclass clazz, jobject tokenObj) {
+ Vector<DisplayConfig> configs;
+ if (const auto token = ibinderForJavaObject(env, tokenObj); !token ||
+ SurfaceComposerClient::getDisplayConfigs(token, &configs) != NO_ERROR ||
+ configs.isEmpty()) {
+ return nullptr;
}
- jobjectArray configArray = env->NewObjectArray(configs.size(),
- gPhysicalDisplayInfoClassInfo.clazz, NULL);
+ jobjectArray configArray =
+ env->NewObjectArray(configs.size(), gDisplayConfigClassInfo.clazz, nullptr);
for (size_t c = 0; c < configs.size(); ++c) {
- const DisplayInfo& info = configs[c];
- jobject infoObj = env->NewObject(gPhysicalDisplayInfoClassInfo.clazz,
- gPhysicalDisplayInfoClassInfo.ctor);
- env->SetIntField(infoObj, gPhysicalDisplayInfoClassInfo.width, info.w);
- env->SetIntField(infoObj, gPhysicalDisplayInfoClassInfo.height, info.h);
- env->SetFloatField(infoObj, gPhysicalDisplayInfoClassInfo.refreshRate, info.fps);
- env->SetFloatField(infoObj, gPhysicalDisplayInfoClassInfo.density, info.density);
- env->SetFloatField(infoObj, gPhysicalDisplayInfoClassInfo.xDpi, info.xdpi);
- env->SetFloatField(infoObj, gPhysicalDisplayInfoClassInfo.yDpi, info.ydpi);
- env->SetBooleanField(infoObj, gPhysicalDisplayInfoClassInfo.secure, info.secure);
- env->SetLongField(infoObj, gPhysicalDisplayInfoClassInfo.appVsyncOffsetNanos,
- info.appVsyncOffset);
- env->SetLongField(infoObj, gPhysicalDisplayInfoClassInfo.presentationDeadlineNanos,
- info.presentationDeadline);
- env->SetObjectArrayElement(configArray, static_cast<jsize>(c), infoObj);
- env->DeleteLocalRef(infoObj);
+ const DisplayConfig& config = configs[c];
+ jobject object =
+ env->NewObject(gDisplayConfigClassInfo.clazz, gDisplayConfigClassInfo.ctor);
+ env->SetIntField(object, gDisplayConfigClassInfo.width, config.resolution.getWidth());
+ env->SetIntField(object, gDisplayConfigClassInfo.height, config.resolution.getHeight());
+ env->SetFloatField(object, gDisplayConfigClassInfo.xDpi, config.xDpi);
+ env->SetFloatField(object, gDisplayConfigClassInfo.yDpi, config.yDpi);
+
+ env->SetFloatField(object, gDisplayConfigClassInfo.refreshRate, config.refreshRate);
+ env->SetLongField(object, gDisplayConfigClassInfo.appVsyncOffsetNanos,
+ config.appVsyncOffset);
+ env->SetLongField(object, gDisplayConfigClassInfo.presentationDeadlineNanos,
+ config.presentationDeadline);
+ env->SetObjectArrayElement(configArray, static_cast<jsize>(c), object);
+ env->DeleteLocalRef(object);
}
return configArray;
@@ -1409,7 +1425,9 @@ static const JNINativeMethod sSurfaceControlMethods[] = {
(void*)nativeSetDisplayProjection },
{"nativeSetDisplaySize", "(JLandroid/os/IBinder;II)V",
(void*)nativeSetDisplaySize },
- {"nativeGetDisplayConfigs", "(Landroid/os/IBinder;)[Landroid/view/SurfaceControl$PhysicalDisplayInfo;",
+ {"nativeGetDisplayInfo", "(Landroid/os/IBinder;)Landroid/view/SurfaceControl$DisplayInfo;",
+ (void*)nativeGetDisplayInfo },
+ {"nativeGetDisplayConfigs", "(Landroid/os/IBinder;)[Landroid/view/SurfaceControl$DisplayConfig;",
(void*)nativeGetDisplayConfigs },
{"nativeGetActiveConfig", "(Landroid/os/IBinder;)I",
(void*)nativeGetActiveConfig },
@@ -1507,21 +1525,24 @@ int register_android_view_SurfaceControl(JNIEnv* env)
int err = RegisterMethodsOrDie(env, "android/view/SurfaceControl",
sSurfaceControlMethods, NELEM(sSurfaceControlMethods));
- jclass clazz = FindClassOrDie(env, "android/view/SurfaceControl$PhysicalDisplayInfo");
- gPhysicalDisplayInfoClassInfo.clazz = MakeGlobalRefOrDie(env, clazz);
- gPhysicalDisplayInfoClassInfo.ctor = GetMethodIDOrDie(env,
- gPhysicalDisplayInfoClassInfo.clazz, "<init>", "()V");
- gPhysicalDisplayInfoClassInfo.width = GetFieldIDOrDie(env, clazz, "width", "I");
- gPhysicalDisplayInfoClassInfo.height = GetFieldIDOrDie(env, clazz, "height", "I");
- gPhysicalDisplayInfoClassInfo.refreshRate = GetFieldIDOrDie(env, clazz, "refreshRate", "F");
- gPhysicalDisplayInfoClassInfo.density = GetFieldIDOrDie(env, clazz, "density", "F");
- gPhysicalDisplayInfoClassInfo.xDpi = GetFieldIDOrDie(env, clazz, "xDpi", "F");
- gPhysicalDisplayInfoClassInfo.yDpi = GetFieldIDOrDie(env, clazz, "yDpi", "F");
- gPhysicalDisplayInfoClassInfo.secure = GetFieldIDOrDie(env, clazz, "secure", "Z");
- gPhysicalDisplayInfoClassInfo.appVsyncOffsetNanos = GetFieldIDOrDie(env,
- clazz, "appVsyncOffsetNanos", "J");
- gPhysicalDisplayInfoClassInfo.presentationDeadlineNanos = GetFieldIDOrDie(env,
- clazz, "presentationDeadlineNanos", "J");
+ jclass infoClazz = FindClassOrDie(env, "android/view/SurfaceControl$DisplayInfo");
+ gDisplayInfoClassInfo.clazz = MakeGlobalRefOrDie(env, infoClazz);
+ gDisplayInfoClassInfo.ctor = GetMethodIDOrDie(env, infoClazz, "<init>", "()V");
+ gDisplayInfoClassInfo.density = GetFieldIDOrDie(env, infoClazz, "density", "F");
+ gDisplayInfoClassInfo.secure = GetFieldIDOrDie(env, infoClazz, "secure", "Z");
+
+ jclass configClazz = FindClassOrDie(env, "android/view/SurfaceControl$DisplayConfig");
+ gDisplayConfigClassInfo.clazz = MakeGlobalRefOrDie(env, configClazz);
+ gDisplayConfigClassInfo.ctor = GetMethodIDOrDie(env, configClazz, "<init>", "()V");
+ gDisplayConfigClassInfo.width = GetFieldIDOrDie(env, configClazz, "width", "I");
+ gDisplayConfigClassInfo.height = GetFieldIDOrDie(env, configClazz, "height", "I");
+ gDisplayConfigClassInfo.xDpi = GetFieldIDOrDie(env, configClazz, "xDpi", "F");
+ gDisplayConfigClassInfo.yDpi = GetFieldIDOrDie(env, configClazz, "yDpi", "F");
+ gDisplayConfigClassInfo.refreshRate = GetFieldIDOrDie(env, configClazz, "refreshRate", "F");
+ gDisplayConfigClassInfo.appVsyncOffsetNanos =
+ GetFieldIDOrDie(env, configClazz, "appVsyncOffsetNanos", "J");
+ gDisplayConfigClassInfo.presentationDeadlineNanos =
+ GetFieldIDOrDie(env, configClazz, "presentationDeadlineNanos", "J");
jclass rectClazz = FindClassOrDie(env, "android/graphics/Rect");
gRectClassInfo.bottom = GetFieldIDOrDie(env, rectClazz, "bottom", "I");
diff --git a/core/jni/com_android_internal_os_Zygote.cpp b/core/jni/com_android_internal_os_Zygote.cpp
index 1ea7d6144356..92941b8cb22b 100644
--- a/core/jni/com_android_internal_os_Zygote.cpp
+++ b/core/jni/com_android_internal_os_Zygote.cpp
@@ -820,6 +820,7 @@ static void MountEmulatedStorage(uid_t uid, jint mount_mode,
if (isFuse) {
if (mount_mode == MOUNT_EXTERNAL_PASS_THROUGH) {
const std::string pass_through_source = StringPrintf("/mnt/pass_through/%d", user_id);
+ PrepareDir(pass_through_source, 0710, AID_ROOT, AID_MEDIA_RW, fail_fn);
BindMount(pass_through_source, "/storage", fail_fn);
} else if (mount_mode == MOUNT_EXTERNAL_INSTALLER) {
const std::string installer_source = StringPrintf("/mnt/installer/%d", user_id);
diff --git a/core/proto/android/app/settings_enums.proto b/core/proto/android/app/settings_enums.proto
index ce03727314d1..a6e08d2cecc1 100644
--- a/core/proto/android/app/settings_enums.proto
+++ b/core/proto/android/app/settings_enums.proto
@@ -2568,4 +2568,9 @@ enum PageId {
// OPEN: Settings > Display > Dark theme > Set end time dialog
DIALOG_DARK_THEME_SET_END_TIME = 1826;
+
+ // OPEN: Settings -> Sound -> Vibrate for calls
+ // CATEGORY: SETTINGS
+ // OS: R
+ VIBRATE_FOR_CALLS = 1827;
}
diff --git a/core/proto/android/server/connectivity/data_stall_event.proto b/core/proto/android/server/connectivity/data_stall_event.proto
index a82326f67d74..23fcf6ebc2cc 100644
--- a/core/proto/android/server/connectivity/data_stall_event.proto
+++ b/core/proto/android/server/connectivity/data_stall_event.proto
@@ -34,7 +34,7 @@ enum ApBand {
AP_BAND_5GHZ = 2;
}
-// Refer to definition in ServiceState.java.
+// Refer to definition in TelephonyManager.java.
enum RadioTech {
RADIO_TECHNOLOGY_UNKNOWN = 0;
RADIO_TECHNOLOGY_GPRS = 1;
@@ -49,8 +49,8 @@ enum RadioTech {
RADIO_TECHNOLOGY_HSUPA = 10;
RADIO_TECHNOLOGY_HSPA = 11;
RADIO_TECHNOLOGY_EVDO_B = 12;
- RADIO_TECHNOLOGY_EHRPD = 13;
- RADIO_TECHNOLOGY_LTE = 14;
+ RADIO_TECHNOLOGY_LTE = 13;
+ RADIO_TECHNOLOGY_EHRPD = 14;
RADIO_TECHNOLOGY_HSPAP = 15;
RADIO_TECHNOLOGY_GSM = 16;
RADIO_TECHNOLOGY_TD_SCDMA = 17;
diff --git a/core/proto/android/server/vibratorservice.proto b/core/proto/android/server/vibratorservice.proto
new file mode 100644
index 000000000000..281a25e55dc2
--- /dev/null
+++ b/core/proto/android/server/vibratorservice.proto
@@ -0,0 +1,66 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+syntax = "proto2";
+package com.android.server;
+
+option java_multiple_files = true;
+
+import "frameworks/base/core/proto/android/privacy.proto";
+
+message WaveformProto {
+ option (.android.msg_privacy).dest = DEST_AUTOMATIC;
+ repeated int32 timings = 1;
+ repeated int32 amplitudes = 2;
+ required bool repeat = 3;
+}
+
+message PrebakedProto {
+ option (.android.msg_privacy).dest = DEST_AUTOMATIC;
+ optional int32 effect_id = 1;
+ optional int32 effect_strength = 2;
+ optional int32 fallback = 3;
+}
+
+// A com.android.os.VibrationEffect object.
+message VibrationEffectProto {
+ option (.android.msg_privacy).dest = DEST_AUTOMATIC;
+ optional WaveformProto waveform = 1;
+ optional PrebakedProto prebaked = 2;
+}
+
+message VibrationProto {
+ option (.android.msg_privacy).dest = DEST_AUTOMATIC;
+ optional int64 start_time = 1;
+ optional VibrationEffectProto effect = 2;
+ optional VibrationEffectProto origin_effect = 3;
+}
+
+message VibratorServiceDumpProto {
+ option (.android.msg_privacy).dest = DEST_AUTOMATIC;
+ optional VibrationProto current_vibration = 1;
+ optional bool is_vibrating = 2;
+ optional VibrationProto current_external_vibration = 3;
+ optional bool vibrator_under_external_control = 4;
+ optional bool low_power_mode = 5;
+ optional int32 haptic_feedback_intensity = 6;
+ optional int32 notification_intensity = 7;
+ optional int32 ring_intensity = 8;
+ repeated VibrationProto previous_ring_vibrations = 9;
+ repeated VibrationProto previous_notification_vibrations = 10;
+ repeated VibrationProto previous_alarm_vibrations = 11;
+ repeated VibrationProto previous_vibrations = 12;
+} \ No newline at end of file
diff --git a/core/res/AndroidManifest.xml b/core/res/AndroidManifest.xml
index 3a1b63d56b5f..753d64a3359a 100644
--- a/core/res/AndroidManifest.xml
+++ b/core/res/AndroidManifest.xml
@@ -377,7 +377,7 @@
<protected-broadcast android:name="android.net.wifi.action.PASSPOINT_SUBSCRIPTION_REMEDIATION" />
<protected-broadcast android:name="android.net.wifi.action.PASSPOINT_LAUNCH_OSU_VIEW" />
<protected-broadcast android:name="android.net.wifi.action.WIFI_NETWORK_SUGGESTION_POST_CONNECTION" />
- <protected-broadcast android:name="android.net.wifi.action.WIFI_SCAN_AVAILABLE" />
+ <protected-broadcast android:name="android.net.wifi.action.WIFI_SCAN_AVAILABILITY_CHANGED" />
<protected-broadcast android:name="android.net.wifi.supplicant.CONNECTION_CHANGE" />
<protected-broadcast android:name="android.net.wifi.supplicant.STATE_CHANGE" />
<protected-broadcast android:name="android.net.wifi.p2p.STATE_CHANGED" />
@@ -2088,7 +2088,6 @@
Allows reading of detailed information about phone state for special-use applications
such as dialers, carrier applications, or ims applications. -->
<permission android:name="android.permission.READ_PRECISE_PHONE_STATE"
- android:permissionGroup="android.permission-group.UNDEFINED"
android:protectionLevel="signature|privileged" />
<!-- @SystemApi Allows read access to privileged phone state.
@@ -3441,8 +3440,8 @@
<permission android:name="android.permission.NOTIFY_TV_INPUTS"
android:protectionLevel="signature|privileged" />
- <!-- @SystemApi Allows an application to interact with tuner resources through
- Tuner Resource Manager.
+ <!-- This permission is required among systems services when accessing
+ tuner resource management related APIs or information.
<p>Protection level: signature|privileged
<p>Not for use by third-party applications.
@hide -->
@@ -3672,6 +3671,10 @@
<permission android:name="android.permission.ADJUST_RUNTIME_PERMISSIONS_POLICY"
android:protectionLevel="signature|installer" />
+ <!-- @hide Allows an application to upgrade runtime permissions. -->
+ <permission android:name="android.permission.UPGRADE_RUNTIME_PERMISSIONS"
+ android:protectionLevel="signature" />
+
<!-- @SystemApi Allows an application to whitelist restricted permissions
on any of the whitelists.
@hide -->
@@ -4014,9 +4017,9 @@
statistics
<p>Declaring the permission implies intention to use the API and the user of the
device can grant permission through the Settings application.
- <p>Protection level: signature|privileged|development|appop -->
+ <p>Protection level: signature|privileged|development|appop|retailDemo -->
<permission android:name="android.permission.PACKAGE_USAGE_STATS"
- android:protectionLevel="signature|privileged|development|appop" />
+ android:protectionLevel="signature|privileged|development|appop|retailDemo" />
<uses-permission android:name="android.permission.PACKAGE_USAGE_STATS" />
<!-- @hide @SystemApi Allows an application to observe usage time of apps. The app can register
@@ -4234,7 +4237,7 @@
<!-- @SystemApi @TestApi @hide Allows managing apk level rollbacks. -->
<permission android:name="android.permission.MANAGE_ROLLBACKS"
- android:protectionLevel="signature|verifier" />
+ android:protectionLevel="signature|privileged" />
<!-- @TestApi @hide Allows testing apk level rollbacks. -->
<permission android:name="android.permission.TEST_MANAGE_ROLLBACKS"
@@ -4403,6 +4406,10 @@
android.service.chooser.ChooserTargetService}, to ensure that
only the system can bind to it.
<p>Protection level: signature
+
+ @deprecated For publishing direct share targets, please follow the instructions in
+ https://developer.android.com/training/sharing/receive.html#providing-direct-share-targets
+ instead.
-->
<permission android:name="android.permission.BIND_CHOOSER_TARGET_SERVICE"
android:protectionLevel="signature" />
diff --git a/core/res/res/drawable/ic_open_in_new.xml b/core/res/res/drawable/ic_open_in_new.xml
deleted file mode 100644
index 67378c8d4b8a..000000000000
--- a/core/res/res/drawable/ic_open_in_new.xml
+++ /dev/null
@@ -1,26 +0,0 @@
-<!--
- Copyright (C) 2020 The Android Open Source Project
-
- Licensed under the Apache License, Version 2.0 (the "License");
- you may not use this file except in compliance with the License.
- You may obtain a copy of the License at
-
- http://www.apache.org/licenses/LICENSE-2.0
-
- Unless required by applicable law or agreed to in writing, software
- distributed under the License is distributed on an "AS IS" BASIS,
- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- See the License for the specific language governing permissions and
- limitations under the License.
- -->
-
-<vector xmlns:android="http://schemas.android.com/apk/res/android"
- android:width="24dp"
- android:height="24dp"
- android:viewportWidth="24"
- android:viewportHeight="24"
- android:tint="?attr/colorControlNormal">
- <path
- android:pathData="M19,19H5V5h7V3H5c-1.11,0 -2,0.9 -2,2v14c0,1.1 0.89,2 2,2h14c1.1,0 2,-0.9 2,-2v-7h-2v7zM14,3v2h3.59l-9.83,9.83 1.41,1.41L19,6.41V10h2V3h-7z"
- android:fillColor="#FFFFFF"/>
-</vector>
diff --git a/core/res/res/drawable/media_seamless_background.xml b/core/res/res/drawable/media_seamless_background.xml
index aec89e0e8980..9b470cbe096f 100644
--- a/core/res/res/drawable/media_seamless_background.xml
+++ b/core/res/res/drawable/media_seamless_background.xml
@@ -20,7 +20,7 @@
<item android:id="@android:id/background">
<shape android:shape="rectangle">
<stroke android:width="1dp" android:color="#1f000000"/>
- <corners android:radius="20dp"/>
+ <corners android:radius="24dp"/>
</shape>
</item>
</ripple>
diff --git a/core/res/res/values-mcc334/config.xml b/core/res/res/values-mcc334/config.xml
new file mode 100644
index 000000000000..e99c9a028178
--- /dev/null
+++ b/core/res/res/values-mcc334/config.xml
@@ -0,0 +1,23 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+/*
+** Copyright 2020, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License");
+** you may not use this file except in compliance with the License.
+** You may obtain a copy of the License at
+**
+** http://www.apache.org/licenses/LICENSE-2.0
+**
+** Unless required by applicable law or agreed to in writing, software
+** distributed under the License is distributed on an "AS IS" BASIS,
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+** See the License for the specific language governing permissions and
+** limitations under the License.
+*/
+-->
+
+<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <!-- Show area update info settings in CellBroadcastReceiver and information in SIM status in Settings app -->
+ <bool name="config_showAreaUpdateInfoSettings">true</bool>
+</resources>
diff --git a/core/res/res/values-mcc732/config.xml b/core/res/res/values-mcc732/config.xml
new file mode 100644
index 000000000000..e99c9a028178
--- /dev/null
+++ b/core/res/res/values-mcc732/config.xml
@@ -0,0 +1,23 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+/*
+** Copyright 2020, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License");
+** you may not use this file except in compliance with the License.
+** You may obtain a copy of the License at
+**
+** http://www.apache.org/licenses/LICENSE-2.0
+**
+** Unless required by applicable law or agreed to in writing, software
+** distributed under the License is distributed on an "AS IS" BASIS,
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+** See the License for the specific language governing permissions and
+** limitations under the License.
+*/
+-->
+
+<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <!-- Show area update info settings in CellBroadcastReceiver and information in SIM status in Settings app -->
+ <bool name="config_showAreaUpdateInfoSettings">true</bool>
+</resources>
diff --git a/core/res/res/values-mcc740/config.xml b/core/res/res/values-mcc740/config.xml
new file mode 100644
index 000000000000..e99c9a028178
--- /dev/null
+++ b/core/res/res/values-mcc740/config.xml
@@ -0,0 +1,23 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+/*
+** Copyright 2020, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License");
+** you may not use this file except in compliance with the License.
+** You may obtain a copy of the License at
+**
+** http://www.apache.org/licenses/LICENSE-2.0
+**
+** Unless required by applicable law or agreed to in writing, software
+** distributed under the License is distributed on an "AS IS" BASIS,
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+** See the License for the specific language governing permissions and
+** limitations under the License.
+*/
+-->
+
+<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <!-- Show area update info settings in CellBroadcastReceiver and information in SIM status in Settings app -->
+ <bool name="config_showAreaUpdateInfoSettings">true</bool>
+</resources>
diff --git a/core/res/res/values/attrs_manifest.xml b/core/res/res/values/attrs_manifest.xml
index 059bc443a609..b22e1867f257 100644
--- a/core/res/res/values/attrs_manifest.xml
+++ b/core/res/res/values/attrs_manifest.xml
@@ -301,6 +301,9 @@
<!-- Additional flag from base permission type: this permission can be automatically
granted to the system companion device manager service -->
<flag name="companion" value="0x800000" />
+ <!-- Additional flag from base permission type: this permission will be granted to the
+ retail demo app, as defined by the OEM. -->
+ <flag name="retailDemo" value="0x1000000" />
</attr>
<!-- Flags indicating more context for a permission group. -->
diff --git a/core/res/res/values/config.xml b/core/res/res/values/config.xml
index 52b92d2660da..3d7b1e18be7b 100644
--- a/core/res/res/values/config.xml
+++ b/core/res/res/values/config.xml
@@ -2565,7 +2565,11 @@
<string name="config_usbResolverActivity" translatable="false"
>com.android.systemui/com.android.systemui.usb.UsbResolverActivity</string>
- <!-- Name of the dialog that is used to request the user's consent to VPN connection -->
+ <!-- Name of the dialog that is used to request the user's consent for a Platform VPN -->
+ <string name="config_platformVpnConfirmDialogComponent" translatable="false"
+ >com.android.vpndialogs/com.android.vpndialogs.PlatformVpnConfirmDialog</string>
+
+ <!-- Name of the dialog that is used to request the user's consent for a VpnService VPN -->
<string name="config_customVpnConfirmDialogComponent" translatable="false"
>com.android.vpndialogs/com.android.vpndialogs.ConfirmDialog</string>
@@ -3590,7 +3594,6 @@
-->
<string name="config_defaultWellbeingPackage" translatable="false"></string>
-
<!-- The package name for the system telephony apps.
This package must be trusted, as it will be granted with permissions with special telephony
protection level. Note, framework by default support multiple telephony apps, each package
@@ -3658,6 +3661,19 @@
-->
<string name="config_defaultContentSuggestionsService" translatable="false"></string>
+ <!-- The package name for the default retail demo app.
+ This package must be trusted, as it has the permissions to query the usage stats on the
+ device.
+ Example: "com.google.android.retaildemo"
+ -->
+ <string name="config_retailDemoPackage" translatable="false"></string>
+
+ <!-- The package signature hash for the default retail demo app.
+ This package must be trusted, as it has the permissions to query the usage stats on the
+ device.
+ -->
+ <string name="config_retailDemoPackageSignature" translatable="false"></string>
+
<!-- Whether the device uses the default focus highlight when focus state isn't specified. -->
<bool name="config_useDefaultFocusHighlight">true</bool>
@@ -4331,4 +4347,9 @@
<!-- Boolean indicating whether frameworks needs to reset cell broadcast geo-fencing
check after reboot or airplane mode toggling -->
<bool translatable="false" name="reset_geo_fencing_check_after_boot_or_apm">false</bool>
+
+ <!-- Screen Wake Keys
+ Determines whether the specified key groups can be used to wake up the device. -->
+ <bool name="config_wakeOnDpadKeyPress">true</bool>
+ <bool name="config_wakeOnAssistKeyPress">true</bool>
</resources>
diff --git a/core/res/res/values/dimens.xml b/core/res/res/values/dimens.xml
index 0b5082cdaf94..22abedcac2a7 100644
--- a/core/res/res/values/dimens.xml
+++ b/core/res/res/values/dimens.xml
@@ -33,9 +33,11 @@
<dimen name="toast_y_offset">24dp</dimen>
<!-- Height of the status bar -->
<dimen name="status_bar_height">@dimen/status_bar_height_portrait</dimen>
- <!-- Height of the status bar in portrait -->
+ <!-- Height of the status bar in portrait. The height should be
+ Max((status bar content height + waterfall top size), top cutout size) -->
<dimen name="status_bar_height_portrait">24dp</dimen>
- <!-- Height of the status bar in landscape -->
+ <!-- Height of the status bar in landscape. The height should be
+ Max((status bar content height + waterfall top size), top cutout size) -->
<dimen name="status_bar_height_landscape">@dimen/status_bar_height_portrait</dimen>
<!-- Height of area above QQS where battery/time go -->
<dimen name="quick_qs_offset_height">48dp</dimen>
diff --git a/core/res/res/values/symbols.xml b/core/res/res/values/symbols.xml
index 21d1d3cf9c89..36296a850dcb 100644
--- a/core/res/res/values/symbols.xml
+++ b/core/res/res/values/symbols.xml
@@ -2143,6 +2143,7 @@
<java-symbol type="string" name="config_customAdbPublicKeyConfirmationSecondaryUserComponent" />
<java-symbol type="string" name="config_customVpnConfirmDialogComponent" />
<java-symbol type="string" name="config_customVpnAlwaysOnDisconnectedDialogComponent" />
+ <java-symbol type="string" name="config_platformVpnConfirmDialogComponent" />
<java-symbol type="string" name="config_carrierAppInstallDialogComponent" />
<java-symbol type="string" name="config_defaultNetworkScorerPackageName" />
<java-symbol type="string" name="config_persistentDataPackageName" />
@@ -3027,6 +3028,10 @@
<java-symbol type="id" name="notification_action_list_margin_target" />
<java-symbol type="dimen" name="notification_action_disabled_alpha" />
+ <!-- Override Wake Key Behavior When Screen is Off -->
+ <java-symbol type="bool" name="config_wakeOnDpadKeyPress" />
+ <java-symbol type="bool" name="config_wakeOnAssistKeyPress" />
+
<!-- Pinner Service -->
<java-symbol type="array" name="config_defaultPinnerServiceFiles" />
<java-symbol type="bool" name="config_pinnerCameraApp" />
@@ -3226,7 +3231,6 @@
<java-symbol type="drawable" name="ic_accessibility_magnification" />
<java-symbol type="drawable" name="ic_delete_item" />
- <java-symbol type="drawable" name="ic_open_in_new" />
<!-- com.android.internal.widget.RecyclerView -->
<java-symbol type="id" name="item_touch_helper_previous_elevation"/>
@@ -3396,6 +3400,8 @@
<java-symbol type="string" name="config_defaultAttentionService" />
<java-symbol type="string" name="config_defaultSystemCaptionsService" />
<java-symbol type="string" name="config_defaultSystemCaptionsManagerService" />
+ <java-symbol type="string" name="config_retailDemoPackage" />
+ <java-symbol type="string" name="config_retailDemoPackageSignature" />
<java-symbol type="string" name="notification_channel_foreground_service" />
<java-symbol type="string" name="foreground_service_app_in_background" />
diff --git a/core/tests/coretests/AndroidManifest.xml b/core/tests/coretests/AndroidManifest.xml
index 23f9f90248df..ee93b397bedf 100644
--- a/core/tests/coretests/AndroidManifest.xml
+++ b/core/tests/coretests/AndroidManifest.xml
@@ -1341,6 +1341,10 @@
<service android:name="android.os.BinderWorkSourceNestedService"
android:process=":BinderWorkSourceNestedService" />
+ <!-- Used by BinderProxyTest -->
+ <service android:name="android.os.BinderProxyService"
+ android:process=":BinderProxyService" />
+
<!-- Application components used for search manager tests -->
<activity android:name="android.app.activity.SearchableActivity"
diff --git a/core/tests/coretests/src/android/os/BinderProxyService.java b/core/tests/coretests/src/android/os/BinderProxyService.java
new file mode 100644
index 000000000000..bf1fbc555d8b
--- /dev/null
+++ b/core/tests/coretests/src/android/os/BinderProxyService.java
@@ -0,0 +1,35 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.os;
+
+import android.app.Service;
+import android.content.Intent;
+
+public class BinderProxyService extends Service {
+ private final Binder mBinder = new Binder();
+
+ @Override
+ public void onCreate() {
+ super.onCreate();
+ mBinder.setExtension(new Binder());
+ }
+
+ @Override
+ public IBinder onBind(Intent intent) {
+ return mBinder;
+ }
+}
diff --git a/core/tests/coretests/src/android/os/BinderProxyTest.java b/core/tests/coretests/src/android/os/BinderProxyTest.java
index aceda2d0524b..3567d17ea874 100644
--- a/core/tests/coretests/src/android/os/BinderProxyTest.java
+++ b/core/tests/coretests/src/android/os/BinderProxyTest.java
@@ -17,11 +17,17 @@
package android.os;
import android.annotation.Nullable;
+import android.content.ComponentName;
import android.content.Context;
+import android.content.Intent;
+import android.content.ServiceConnection;
import android.test.AndroidTestCase;
import androidx.test.filters.MediumTest;
+import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.TimeUnit;
+
public class BinderProxyTest extends AndroidTestCase {
private static class CountingListener implements Binder.ProxyTransactListener {
int mStartedCount;
@@ -86,4 +92,41 @@ public class BinderProxyTest extends AndroidTestCase {
// Check it does not throw..
mPowerManager.isInteractive();
}
+
+ private IBinder mRemoteBinder = null;
+
+ @MediumTest
+ public void testGetExtension() throws Exception {
+ final CountDownLatch bindLatch = new CountDownLatch(1);
+ ServiceConnection connection =
+ new ServiceConnection() {
+ @Override
+ public void onServiceConnected(ComponentName name, IBinder service) {
+ mRemoteBinder = service;
+ bindLatch.countDown();
+ }
+
+ @Override
+ public void onServiceDisconnected(ComponentName name) {}
+ };
+ try {
+ mContext.bindService(
+ new Intent(mContext, BinderProxyService.class),
+ connection,
+ Context.BIND_AUTO_CREATE);
+ if (!bindLatch.await(500, TimeUnit.MILLISECONDS)) {
+ fail(
+ "Timed out while binding service: "
+ + BinderProxyService.class.getSimpleName());
+ }
+ assertTrue(mRemoteBinder instanceof BinderProxy);
+ assertNotNull(mRemoteBinder);
+
+ IBinder extension = mRemoteBinder.getExtension();
+ assertNotNull(extension);
+ assertTrue(extension.pingBinder());
+ } finally {
+ mContext.unbindService(connection);
+ }
+ }
}
diff --git a/core/tests/coretests/src/android/os/BinderTest.java b/core/tests/coretests/src/android/os/BinderTest.java
index a354195c75a3..99dbe6445662 100644
--- a/core/tests/coretests/src/android/os/BinderTest.java
+++ b/core/tests/coretests/src/android/os/BinderTest.java
@@ -52,4 +52,18 @@ public class BinderTest extends TestCase {
} catch (IllegalStateException expected) {
}
}
+
+ @SmallTest
+ public void testGetExtension() throws Exception {
+ Binder binder = new Binder();
+ assertNull(binder.getExtension());
+
+ IBinder extension = new Binder();
+ binder.setExtension(extension);
+ assertNotNull(binder.getExtension());
+ assertSame(binder.getExtension(), extension);
+
+ binder.setExtension(null);
+ assertNull(binder.getExtension());
+ }
}
diff --git a/core/tests/coretests/src/com/android/internal/os/BatteryStatsHelperTest.java b/core/tests/coretests/src/com/android/internal/os/BatteryStatsHelperTest.java
index 355601ca05ed..fbe16f2c39d1 100644
--- a/core/tests/coretests/src/com/android/internal/os/BatteryStatsHelperTest.java
+++ b/core/tests/coretests/src/com/android/internal/os/BatteryStatsHelperTest.java
@@ -37,12 +37,13 @@ import android.content.pm.PackageManager;
import android.os.BatteryStats;
import android.os.Process;
import android.text.format.DateUtils;
-import android.util.StatsLog;
import androidx.test.InstrumentationRegistry;
import androidx.test.filters.SmallTest;
import androidx.test.runner.AndroidJUnit4;
+import com.android.internal.util.FrameworkStatsLog;
+
import junit.framework.TestCase;
import org.junit.Before;
@@ -262,31 +263,31 @@ public class BatteryStatsHelperTest extends TestCase {
@Test
public void testDrainTypesSyncedWithProto() {
assertEquals(BatterySipper.DrainType.AMBIENT_DISPLAY.ordinal(),
- StatsLog.DEVICE_CALCULATED_POWER_BLAME_OTHER__DRAIN_TYPE__AMBIENT_DISPLAY);
+ FrameworkStatsLog.DEVICE_CALCULATED_POWER_BLAME_OTHER__DRAIN_TYPE__AMBIENT_DISPLAY);
// AtomsProto has no "APP"
assertEquals(BatterySipper.DrainType.BLUETOOTH.ordinal(),
- StatsLog.DEVICE_CALCULATED_POWER_BLAME_OTHER__DRAIN_TYPE__BLUETOOTH);
+ FrameworkStatsLog.DEVICE_CALCULATED_POWER_BLAME_OTHER__DRAIN_TYPE__BLUETOOTH);
assertEquals(BatterySipper.DrainType.CAMERA.ordinal(),
- StatsLog.DEVICE_CALCULATED_POWER_BLAME_OTHER__DRAIN_TYPE__CAMERA);
+ FrameworkStatsLog.DEVICE_CALCULATED_POWER_BLAME_OTHER__DRAIN_TYPE__CAMERA);
assertEquals(BatterySipper.DrainType.CELL.ordinal(),
- StatsLog.DEVICE_CALCULATED_POWER_BLAME_OTHER__DRAIN_TYPE__CELL);
+ FrameworkStatsLog.DEVICE_CALCULATED_POWER_BLAME_OTHER__DRAIN_TYPE__CELL);
assertEquals(BatterySipper.DrainType.FLASHLIGHT.ordinal(),
- StatsLog.DEVICE_CALCULATED_POWER_BLAME_OTHER__DRAIN_TYPE__FLASHLIGHT);
+ FrameworkStatsLog.DEVICE_CALCULATED_POWER_BLAME_OTHER__DRAIN_TYPE__FLASHLIGHT);
assertEquals(BatterySipper.DrainType.IDLE.ordinal(),
- StatsLog.DEVICE_CALCULATED_POWER_BLAME_OTHER__DRAIN_TYPE__IDLE);
+ FrameworkStatsLog.DEVICE_CALCULATED_POWER_BLAME_OTHER__DRAIN_TYPE__IDLE);
assertEquals(BatterySipper.DrainType.MEMORY.ordinal(),
- StatsLog.DEVICE_CALCULATED_POWER_BLAME_OTHER__DRAIN_TYPE__MEMORY);
+ FrameworkStatsLog.DEVICE_CALCULATED_POWER_BLAME_OTHER__DRAIN_TYPE__MEMORY);
assertEquals(BatterySipper.DrainType.OVERCOUNTED.ordinal(),
- StatsLog.DEVICE_CALCULATED_POWER_BLAME_OTHER__DRAIN_TYPE__OVERCOUNTED);
+ FrameworkStatsLog.DEVICE_CALCULATED_POWER_BLAME_OTHER__DRAIN_TYPE__OVERCOUNTED);
assertEquals(BatterySipper.DrainType.PHONE.ordinal(),
- StatsLog.DEVICE_CALCULATED_POWER_BLAME_OTHER__DRAIN_TYPE__PHONE);
+ FrameworkStatsLog.DEVICE_CALCULATED_POWER_BLAME_OTHER__DRAIN_TYPE__PHONE);
assertEquals(BatterySipper.DrainType.SCREEN.ordinal(),
- StatsLog.DEVICE_CALCULATED_POWER_BLAME_OTHER__DRAIN_TYPE__SCREEN);
+ FrameworkStatsLog.DEVICE_CALCULATED_POWER_BLAME_OTHER__DRAIN_TYPE__SCREEN);
assertEquals(BatterySipper.DrainType.UNACCOUNTED.ordinal(),
- StatsLog.DEVICE_CALCULATED_POWER_BLAME_OTHER__DRAIN_TYPE__UNACCOUNTED);
+ FrameworkStatsLog.DEVICE_CALCULATED_POWER_BLAME_OTHER__DRAIN_TYPE__UNACCOUNTED);
// AtomsProto has no "USER"
assertEquals(BatterySipper.DrainType.WIFI.ordinal(),
- StatsLog.DEVICE_CALCULATED_POWER_BLAME_OTHER__DRAIN_TYPE__WIFI);
+ FrameworkStatsLog.DEVICE_CALCULATED_POWER_BLAME_OTHER__DRAIN_TYPE__WIFI);
}
private BatterySipper createTestSmearBatterySipper(long activityTime, double totalPowerMah,
diff --git a/data/etc/privapp-permissions-platform.xml b/data/etc/privapp-permissions-platform.xml
index 816bb8789667..b5eba090691f 100644
--- a/data/etc/privapp-permissions-platform.xml
+++ b/data/etc/privapp-permissions-platform.xml
@@ -132,6 +132,7 @@ applications that come with the platform
<permission name="android.permission.APPROVE_INCIDENT_REPORTS"/>
<permission name="android.permission.READ_PRIVILEGED_PHONE_STATE" />
<permission name="android.permission.SUBSTITUTE_NOTIFICATION_APP_NAME" />
+ <permission name="android.permission.PACKAGE_USAGE_STATS" />
</privapp-permissions>
<privapp-permissions package="com.android.phone">
@@ -306,6 +307,7 @@ applications that come with the platform
<permission name="android.permission.LOCAL_MAC_ADDRESS"/>
<permission name="android.permission.MANAGE_ACCESSIBILITY"/>
<permission name="android.permission.MANAGE_DEVICE_ADMINS"/>
+ <permission name="android.permission.MANAGE_ROLLBACKS"/>
<permission name="android.permission.MANAGE_USB"/>
<permission name="android.permission.MODIFY_APPWIDGET_BIND_PERMISSIONS"/>
<permission name="android.permission.MODIFY_PHONE_STATE"/>
@@ -371,6 +373,7 @@ applications that come with the platform
<permission name="android.permission.CONTROL_DEVICE_LIGHTS" />
<!-- Permission required for ShortcutManagerUsageTest CTS test. -->
<permission name="android.permission.ACCESS_SHORTCUTS"/>
+ <permission name="android.permission.REBOOT"/>
</privapp-permissions>
<privapp-permissions package="com.android.statementservice">
@@ -398,5 +401,6 @@ applications that come with the platform
</privapp-permissions>
<privapp-permissions package="com.android.settings">
<permission name="android.permission.INSTALL_DYNAMIC_SYSTEM"/>
+ <permission name="android.permission.BIND_CELL_BROADCAST_SERVICE"/>
</privapp-permissions>
</permissions>
diff --git a/libs/hwui/pipeline/skia/SkiaPipeline.cpp b/libs/hwui/pipeline/skia/SkiaPipeline.cpp
index b940cff04713..6f4af3d26b3a 100644
--- a/libs/hwui/pipeline/skia/SkiaPipeline.cpp
+++ b/libs/hwui/pipeline/skia/SkiaPipeline.cpp
@@ -24,6 +24,7 @@
#include <SkOverdrawColorFilter.h>
#include <SkPicture.h>
#include <SkPictureRecorder.h>
+#include <SkTypeface.h>
#include <SkSerialProcs.h>
#include "LightingInfo.h"
#include "VectorDrawable.h"
@@ -264,6 +265,9 @@ bool SkiaPipeline::setupMultiFrameCapture() {
SkSerialProcs procs;
procs.fImageProc = SkSharingSerialContext::serializeImage;
procs.fImageCtx = mSerialContext.get();
+ procs.fTypefaceProc = [](SkTypeface* tf, void* ctx){
+ return tf->serialize(SkTypeface::SerializeBehavior::kDoIncludeData);
+ };
// SkDocuments don't take owership of the streams they write.
// we need to keep it until after mMultiPic.close()
// procs is passed as a pointer, but just as a method of having an optional default.
@@ -405,6 +409,10 @@ void SkiaPipeline::endCapture(SkSurface* surface) {
std::invoke(mPictureCapturedCallback, std::move(picture));
} else {
// single frame skp to file
+ SkSerialProcs procs;
+ procs.fTypefaceProc = [](SkTypeface* tf, void* ctx){
+ return tf->serialize(SkTypeface::SerializeBehavior::kDoIncludeData);
+ };
auto data = picture->serialize();
savePictureAsync(data, mCapturedFile);
mCaptureSequence = 0;
diff --git a/libs/hwui/renderthread/CanvasContext.cpp b/libs/hwui/renderthread/CanvasContext.cpp
index 699b96a685c9..c1435d1ea2d5 100644
--- a/libs/hwui/renderthread/CanvasContext.cpp
+++ b/libs/hwui/renderthread/CanvasContext.cpp
@@ -700,7 +700,7 @@ void CanvasContext::enqueueFrameWork(std::function<void()>&& func) {
int64_t CanvasContext::getFrameNumber() {
// mFrameNumber is reset to -1 when the surface changes or we swap buffers
if (mFrameNumber == -1 && mNativeSurface.get()) {
- mFrameNumber = static_cast<int64_t>(mNativeSurface->getNextFrameNumber());
+ mFrameNumber = ANativeWindow_getNextFrameId(mNativeSurface->getNativeWindow());
}
return mFrameNumber;
}
diff --git a/libs/hwui/renderthread/ReliableSurface.h b/libs/hwui/renderthread/ReliableSurface.h
index 32472539f616..da5097ce33f0 100644
--- a/libs/hwui/renderthread/ReliableSurface.h
+++ b/libs/hwui/renderthread/ReliableSurface.h
@@ -43,8 +43,6 @@ public:
int query(int what, int* value) const { return mSurface->query(what, value); }
- uint64_t getNextFrameNumber() const { return mSurface->getNextFrameNumber(); }
-
int getAndClearError() {
int ret = mBufferQueueState;
mBufferQueueState = OK;
diff --git a/libs/hwui/tests/common/TestContext.cpp b/libs/hwui/tests/common/TestContext.cpp
index e075d806126b..06f158f25fc5 100644
--- a/libs/hwui/tests/common/TestContext.cpp
+++ b/libs/hwui/tests/common/TestContext.cpp
@@ -22,43 +22,50 @@ namespace android {
namespace uirenderer {
namespace test {
-static const int IDENT_DISPLAYEVENT = 1;
-
-static android::DisplayInfo DUMMY_DISPLAY{
- 1080, // w
- 1920, // h
- 320.0, // xdpi
- 320.0, // ydpi
- 60.0, // fps
- 2.0, // density
- ui::ROTATION_0, // orientation
- false, // secure?
- 0, // appVsyncOffset
- 0, // presentationDeadline
-};
-
-DisplayInfo getInternalDisplay() {
-#if !HWUI_NULL_GPU
- DisplayInfo display;
- const sp<IBinder> token = SurfaceComposerClient::getInternalDisplayToken();
- LOG_ALWAYS_FATAL_IF(token == nullptr,
- "Failed to get display info because internal display is disconnected\n");
- status_t status = SurfaceComposerClient::getDisplayInfo(token, &display);
- LOG_ALWAYS_FATAL_IF(status, "Failed to get display info\n");
- return display;
+const DisplayInfo& getDisplayInfo() {
+ static DisplayInfo info = [] {
+ DisplayInfo info;
+#if HWUI_NULL_GPU
+ info.density = 2.f;
#else
- return DUMMY_DISPLAY;
+ const sp<IBinder> token = SurfaceComposerClient::getInternalDisplayToken();
+ LOG_ALWAYS_FATAL_IF(!token, "%s: No internal display", __FUNCTION__);
+
+ const status_t status = SurfaceComposerClient::getDisplayInfo(token, &info);
+ LOG_ALWAYS_FATAL_IF(status, "%s: Failed to get display info", __FUNCTION__);
#endif
+ return info;
+ }();
+
+ return info;
}
-// Initialize to a dummy default
-android::DisplayInfo gDisplay = DUMMY_DISPLAY;
+const DisplayConfig& getActiveDisplayConfig() {
+ static DisplayConfig config = [] {
+ DisplayConfig config;
+#if HWUI_NULL_GPU
+ config.resolution = ui::Size(1080, 1920);
+ config.xDpi = config.yDpi = 320.f;
+ config.refreshRate = 60.f;
+#else
+ const sp<IBinder> token = SurfaceComposerClient::getInternalDisplayToken();
+ LOG_ALWAYS_FATAL_IF(!token, "%s: No internal display", __FUNCTION__);
+
+ const status_t status = SurfaceComposerClient::getActiveDisplayConfig(token, &config);
+ LOG_ALWAYS_FATAL_IF(status, "%s: Failed to get active display config", __FUNCTION__);
+#endif
+ return config;
+ }();
+
+ return config;
+}
TestContext::TestContext() {
mLooper = new Looper(true);
mSurfaceComposerClient = new SurfaceComposerClient();
- mLooper->addFd(mDisplayEventReceiver.getFd(), IDENT_DISPLAYEVENT, Looper::EVENT_INPUT, nullptr,
- nullptr);
+
+ constexpr int EVENT_ID = 1;
+ mLooper->addFd(mDisplayEventReceiver.getFd(), EVENT_ID, Looper::EVENT_INPUT, nullptr, nullptr);
}
TestContext::~TestContext() {}
@@ -79,8 +86,10 @@ void TestContext::createSurface() {
}
void TestContext::createWindowSurface() {
- mSurfaceControl = mSurfaceComposerClient->createSurface(String8("HwuiTest"), gDisplay.w,
- gDisplay.h, PIXEL_FORMAT_RGBX_8888);
+ const ui::Size& resolution = getActiveDisplayResolution();
+ mSurfaceControl =
+ mSurfaceComposerClient->createSurface(String8("HwuiTest"), resolution.getWidth(),
+ resolution.getHeight(), PIXEL_FORMAT_RGBX_8888);
SurfaceComposerClient::Transaction t;
t.setLayer(mSurfaceControl, 0x7FFFFFF).show(mSurfaceControl).apply();
@@ -94,7 +103,8 @@ void TestContext::createOffscreenSurface() {
producer->setMaxDequeuedBufferCount(3);
producer->setAsyncMode(true);
mConsumer = new BufferItemConsumer(consumer, GRALLOC_USAGE_HW_COMPOSER, 4);
- mConsumer->setDefaultBufferSize(gDisplay.w, gDisplay.h);
+ const ui::Size& resolution = getActiveDisplayResolution();
+ mConsumer->setDefaultBufferSize(resolution.getWidth(), resolution.getHeight());
mSurface = new Surface(producer);
}
diff --git a/libs/hwui/tests/common/TestContext.h b/libs/hwui/tests/common/TestContext.h
index 116d4de8090a..a012ecb1a1d3 100644
--- a/libs/hwui/tests/common/TestContext.h
+++ b/libs/hwui/tests/common/TestContext.h
@@ -23,20 +23,25 @@
#include <gui/Surface.h>
#include <gui/SurfaceComposerClient.h>
#include <gui/SurfaceControl.h>
+#include <ui/DisplayConfig.h>
#include <ui/DisplayInfo.h>
#include <utils/Looper.h>
#include <atomic>
#include <thread>
+#define dp(x) ((x) * android::uirenderer::test::getDisplayInfo().density)
+
namespace android {
namespace uirenderer {
namespace test {
-extern DisplayInfo gDisplay;
-#define dp(x) ((x)*android::uirenderer::test::gDisplay.density)
+const DisplayInfo& getDisplayInfo();
+const DisplayConfig& getActiveDisplayConfig();
-DisplayInfo getInternalDisplay();
+inline const ui::Size& getActiveDisplayResolution() {
+ return getActiveDisplayConfig().resolution;
+}
class TestContext {
public:
diff --git a/libs/hwui/tests/macrobench/TestSceneRunner.cpp b/libs/hwui/tests/macrobench/TestSceneRunner.cpp
index 22d5abbd3dbc..3b6baa70db9a 100644
--- a/libs/hwui/tests/macrobench/TestSceneRunner.cpp
+++ b/libs/hwui/tests/macrobench/TestSceneRunner.cpp
@@ -109,16 +109,14 @@ void outputBenchmarkReport(const TestScene::Info& info, const TestScene::Options
void run(const TestScene::Info& info, const TestScene::Options& opts,
benchmark::BenchmarkReporter* reporter) {
- // Switch to the real display
- gDisplay = getInternalDisplay();
-
Properties::forceDrawFrame = true;
TestContext testContext;
testContext.setRenderOffscreen(opts.renderOffscreen);
// create the native surface
- const int width = gDisplay.w;
- const int height = gDisplay.h;
+ const ui::Size& resolution = getActiveDisplayResolution();
+ const int width = resolution.getWidth();
+ const int height = resolution.getHeight();
sp<Surface> surface = testContext.surface();
std::unique_ptr<TestScene> scene(info.createScene(opts));
diff --git a/location/java/com/android/internal/location/gnssmetrics/GnssMetrics.java b/location/java/com/android/internal/location/gnssmetrics/GnssMetrics.java
index 127d00c0afe2..d57148092754 100644
--- a/location/java/com/android/internal/location/gnssmetrics/GnssMetrics.java
+++ b/location/java/com/android/internal/location/gnssmetrics/GnssMetrics.java
@@ -24,12 +24,12 @@ import android.server.location.ServerLocationProtoEnums;
import android.text.format.DateUtils;
import android.util.Base64;
import android.util.Log;
-import android.util.StatsLog;
import android.util.TimeUtils;
import com.android.internal.app.IBatteryStats;
import com.android.internal.location.nano.GnssLogsProto.GnssLog;
import com.android.internal.location.nano.GnssLogsProto.PowerMetrics;
+import com.android.internal.util.FrameworkStatsLog;
import java.util.ArrayList;
import java.util.Arrays;
@@ -540,7 +540,7 @@ public class GnssMetrics {
}
int signalLevel = getSignalLevel(avgCn0);
if (signalLevel != mLastSignalLevel) {
- StatsLog.write(StatsLog.GPS_SIGNAL_QUALITY_CHANGED, signalLevel);
+ FrameworkStatsLog.write(FrameworkStatsLog.GPS_SIGNAL_QUALITY_CHANGED, signalLevel);
mLastSignalLevel = signalLevel;
}
try {
diff --git a/media/java/android/media/session/MediaSessionManager.java b/media/java/android/media/session/MediaSessionManager.java
index 5ef466dfd0e9..69be8b307950 100644
--- a/media/java/android/media/session/MediaSessionManager.java
+++ b/media/java/android/media/session/MediaSessionManager.java
@@ -61,6 +61,7 @@ import java.util.concurrent.Executor;
* @see MediaSession
* @see MediaController
*/
+// TODO: (jinpark) Add API for getting and setting session policies from MediaSessionService.
@SystemService(Context.MEDIA_SESSION_SERVICE)
public final class MediaSessionManager {
private static final String TAG = "SessionManager";
diff --git a/media/java/android/media/tv/TvInputManager.java b/media/java/android/media/tv/TvInputManager.java
index 2589521ca370..6bf9e19ae079 100644
--- a/media/java/android/media/tv/TvInputManager.java
+++ b/media/java/android/media/tv/TvInputManager.java
@@ -358,7 +358,6 @@ public final class TvInputManager {
*
* @hide
*/
- @SystemApi
public static final int UNKNOWN_CLIENT_PID = -1;
/**
@@ -1588,7 +1587,6 @@ public final class TvInputManager {
*
* @hide
*/
- @SystemApi
@RequiresPermission(android.Manifest.permission.TUNER_RESOURCE_ACCESS)
public int getClientPid(@NonNull String sessionId) {
return getClientPidInternal(sessionId);
diff --git a/media/java/android/media/tv/tuner/frontend/Atsc3FrontendSettings.java b/media/java/android/media/tv/tuner/frontend/Atsc3FrontendSettings.java
index 85f3f72020d0..b40ab00afb30 100644
--- a/media/java/android/media/tv/tuner/frontend/Atsc3FrontendSettings.java
+++ b/media/java/android/media/tv/tuner/frontend/Atsc3FrontendSettings.java
@@ -327,7 +327,7 @@ public class Atsc3FrontendSettings extends FrontendSettings {
*/
public static class Builder extends FrontendSettings.Builder<Builder> {
private int mBandwidth;
- private byte mDemodOutputFormat;
+ private int mDemodOutputFormat;
private Atsc3PlpSettings[] mPlpSettings;
private Builder() {
@@ -345,7 +345,7 @@ public class Atsc3FrontendSettings extends FrontendSettings {
* Sets Demod Output Format.
*/
@NonNull
- public Builder setDemodOutputFormat(byte demodOutputFormat) {
+ public Builder setDemodOutputFormat(@DemodOutputFormat int demodOutputFormat) {
mDemodOutputFormat = demodOutputFormat;
return this;
}
diff --git a/media/java/android/media/tv/tuner/frontend/DvbcFrontendSettings.java b/media/java/android/media/tv/tuner/frontend/DvbcFrontendSettings.java
index bfa4f3f027a4..705d5207cc5f 100644
--- a/media/java/android/media/tv/tuner/frontend/DvbcFrontendSettings.java
+++ b/media/java/android/media/tv/tuner/frontend/DvbcFrontendSettings.java
@@ -147,11 +147,11 @@ public class DvbcFrontendSettings extends FrontendSettings {
private final long mFec;
private final int mSymbolRate;
private final int mOuterFec;
- private final byte mAnnex;
+ private final int mAnnex;
private final int mSpectralInversion;
private DvbcFrontendSettings(int frequency, int modulation, long fec, int symbolRate,
- int outerFec, byte annex, int spectralInversion) {
+ int outerFec, int annex, int spectralInversion) {
super(frequency);
mModulation = modulation;
mFec = fec;
@@ -192,7 +192,7 @@ public class DvbcFrontendSettings extends FrontendSettings {
* Gets Annex.
*/
@Annex
- public byte getAnnex() {
+ public int getAnnex() {
return mAnnex;
}
/**
@@ -223,7 +223,7 @@ public class DvbcFrontendSettings extends FrontendSettings {
private long mFec;
private int mSymbolRate;
private int mOuterFec;
- private byte mAnnex;
+ private int mAnnex;
private int mSpectralInversion;
private Builder() {
@@ -265,7 +265,7 @@ public class DvbcFrontendSettings extends FrontendSettings {
* Sets Annex.
*/
@NonNull
- public Builder setAnnex(@Annex byte annex) {
+ public Builder setAnnex(@Annex int annex) {
mAnnex = annex;
return this;
}
diff --git a/media/tests/MediaRouteProvider/Android.bp b/media/tests/MediaRouteProvider/Android.bp
deleted file mode 100644
index da4282495ac2..000000000000
--- a/media/tests/MediaRouteProvider/Android.bp
+++ /dev/null
@@ -1,18 +0,0 @@
-android_test {
- name: "mediarouteprovider",
-
- srcs: ["**/*.java"],
-
- libs: [
- "android.test.runner",
- "android.test.base",
- ],
-
- static_libs: [
- "android-support-test",
- "mockito-target-minus-junit4",
- ],
-
- platform_apis: true,
- certificate: "platform",
-} \ No newline at end of file
diff --git a/media/tests/MediaRouteProvider/AndroidManifest.xml b/media/tests/MediaRouteProvider/AndroidManifest.xml
deleted file mode 100644
index 489a6214ecbd..000000000000
--- a/media/tests/MediaRouteProvider/AndroidManifest.xml
+++ /dev/null
@@ -1,30 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!-- Copyright 2019 The Android Open Source Project
-
- Licensed under the Apache License, Version 2.0 (the "License");
- you may not use this file except in compliance with the License.
- You may obtain a copy of the License at
-
- http://www.apache.org/licenses/LICENSE-2.0
-
- Unless required by applicable law or agreed to in writing, software
- distributed under the License is distributed on an "AS IS" BASIS,
- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- See the License for the specific language governing permissions and
- limitations under the License.
--->
-
-<manifest xmlns:android="http://schemas.android.com/apk/res/android"
- package="com.android.mediarouteprovider.example">
-
- <application android:label="@string/app_name">
- <uses-library android:name="android.test.runner" />
- <service android:name=".SampleMediaRoute2ProviderService"
- android:label="@string/app_name"
- android:exported="true">
- <intent-filter>
- <action android:name="android.media.MediaRoute2ProviderService" />
- </intent-filter>
- </service>
- </application>
-</manifest>
diff --git a/media/tests/MediaRouteProvider/res/values/strings.xml b/media/tests/MediaRouteProvider/res/values/strings.xml
deleted file mode 100644
index bb970641ffda..000000000000
--- a/media/tests/MediaRouteProvider/res/values/strings.xml
+++ /dev/null
@@ -1,5 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<resources>
- <!-- name of the app [CHAR LIMIT=25]-->
- <string name="app_name">SampleMediaRouteProvider</string>
-</resources> \ No newline at end of file
diff --git a/media/tests/MediaRouter/AndroidManifest.xml b/media/tests/MediaRouter/AndroidManifest.xml
index a34a264e1247..95465007d5ea 100644
--- a/media/tests/MediaRouter/AndroidManifest.xml
+++ b/media/tests/MediaRouter/AndroidManifest.xml
@@ -17,10 +17,14 @@
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.android.mediaroutertest">
- <uses-permission android:name="android.permission.CONTROL_MEDIA_ROUTE" />
-
<application android:label="@string/app_name">
<uses-library android:name="android.test.runner" />
+ <service android:name=".SampleMediaRoute2ProviderService"
+ android:exported="true">
+ <intent-filter>
+ <action android:name="android.media.MediaRoute2ProviderService" />
+ </intent-filter>
+ </service>
</application>
<instrumentation android:name="android.support.test.runner.AndroidJUnitRunner"
diff --git a/media/tests/MediaRouter/src/com/android/mediaroutertest/MediaRouterManagerTest.java b/media/tests/MediaRouter/src/com/android/mediaroutertest/MediaRouterManagerTest.java
index 16259ab45792..4316e42bb0e1 100644
--- a/media/tests/MediaRouter/src/com/android/mediaroutertest/MediaRouterManagerTest.java
+++ b/media/tests/MediaRouter/src/com/android/mediaroutertest/MediaRouterManagerTest.java
@@ -16,9 +16,23 @@
package com.android.mediaroutertest;
+import static android.media.MediaRoute2Info.FEATURE_LIVE_AUDIO;
import static android.media.MediaRoute2Info.PLAYBACK_VOLUME_FIXED;
import static android.media.MediaRoute2Info.PLAYBACK_VOLUME_VARIABLE;
+import static com.android.mediaroutertest.SampleMediaRoute2ProviderService.ACTION_REMOVE_ROUTE;
+import static com.android.mediaroutertest.SampleMediaRoute2ProviderService.FEATURE_SAMPLE;
+import static com.android.mediaroutertest.SampleMediaRoute2ProviderService.FEATURE_SPECIAL;
+import static com.android.mediaroutertest.SampleMediaRoute2ProviderService.ROUTE_ID1;
+import static com.android.mediaroutertest.SampleMediaRoute2ProviderService.ROUTE_ID2;
+import static com.android.mediaroutertest.SampleMediaRoute2ProviderService.ROUTE_ID5_TO_TRANSFER_TO;
+import static com.android.mediaroutertest.SampleMediaRoute2ProviderService.ROUTE_ID_FIXED_VOLUME;
+import static com.android.mediaroutertest.SampleMediaRoute2ProviderService.ROUTE_ID_SPECIAL_FEATURE;
+import static com.android.mediaroutertest.SampleMediaRoute2ProviderService.ROUTE_ID_VARIABLE_VOLUME;
+import static com.android.mediaroutertest.SampleMediaRoute2ProviderService.ROUTE_NAME1;
+import static com.android.mediaroutertest.SampleMediaRoute2ProviderService.ROUTE_NAME2;
+import static com.android.mediaroutertest.SampleMediaRoute2ProviderService.VOLUME_MAX;
+
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertTrue;
@@ -56,50 +70,6 @@ import java.util.function.Predicate;
@SmallTest
public class MediaRouterManagerTest {
private static final String TAG = "MediaRouterManagerTest";
-
- public static final String SAMPLE_PROVIDER_ROUTES_ID_PREFIX =
- "com.android.mediarouteprovider.example/.SampleMediaRoute2ProviderService:";
-
- // Must be the same as SampleMediaRoute2ProviderService except the prefix of IDs.
- public static final String ROUTE_ID1 = SAMPLE_PROVIDER_ROUTES_ID_PREFIX + "route_id1";
- public static final String ROUTE_NAME1 = "Sample Route 1";
- public static final String ROUTE_ID2 = SAMPLE_PROVIDER_ROUTES_ID_PREFIX + "route_id2";
- public static final String ROUTE_NAME2 = "Sample Route 2";
- public static final String ROUTE_ID3_SESSION_CREATION_FAILED =
- SAMPLE_PROVIDER_ROUTES_ID_PREFIX + "route_id3_session_creation_failed";
- public static final String ROUTE_NAME3 = "Sample Route 3 - Session creation failed";
- public static final String ROUTE_ID4_TO_SELECT_AND_DESELECT =
- SAMPLE_PROVIDER_ROUTES_ID_PREFIX + "route_id4_to_select_and_deselect";
- public static final String ROUTE_NAME4 = "Sample Route 4 - Route to select and deselect";
- public static final String ROUTE_ID5_TO_TRANSFER_TO =
- SAMPLE_PROVIDER_ROUTES_ID_PREFIX + "route_id5_to_transfer_to";
- public static final String ROUTE_NAME5 = "Sample Route 5 - Route to transfer to";
-
- public static final String ROUTE_ID_SPECIAL_FEATURE =
- SAMPLE_PROVIDER_ROUTES_ID_PREFIX + "route_special_feature";
- public static final String ROUTE_NAME_SPECIAL_FEATURE = "Special Feature Route";
-
- public static final String SYSTEM_PROVIDER_ID =
- "com.android.server.media/.SystemMediaRoute2Provider";
-
- public static final int VOLUME_MAX = 100;
- public static final String ROUTE_ID_FIXED_VOLUME =
- SAMPLE_PROVIDER_ROUTES_ID_PREFIX + "route_fixed_volume";
- public static final String ROUTE_NAME_FIXED_VOLUME = "Fixed Volume Route";
- public static final String ROUTE_ID_VARIABLE_VOLUME =
- SAMPLE_PROVIDER_ROUTES_ID_PREFIX + "route_variable_volume";
- public static final String ROUTE_NAME_VARIABLE_VOLUME = "Variable Volume Route";
-
- public static final String ACTION_REMOVE_ROUTE =
- "com.android.mediarouteprovider.action_remove_route";
-
- public static final String FEATURE_SAMPLE =
- "com.android.mediarouteprovider.FEATURE_SAMPLE";
- public static final String FEATURE_SPECIAL =
- "com.android.mediarouteprovider.FEATURE_SPECIAL";
-
- private static final String FEATURE_LIVE_AUDIO = "android.media.intent.route.LIVE_AUDIO";
-
private static final int TIMEOUT_MS = 5000;
private Context mContext;
@@ -155,7 +125,8 @@ public class MediaRouterManagerTest {
public void onRoutesAdded(List<MediaRoute2Info> routes) {
assertTrue(routes.size() > 0);
for (MediaRoute2Info route : routes) {
- if (route.getId().equals(ROUTE_ID1) && route.getName().equals(ROUTE_NAME1)) {
+ if (route.getOriginalId().equals(ROUTE_ID1)
+ && route.getName().equals(ROUTE_NAME1)) {
latch.countDown();
}
}
@@ -176,7 +147,8 @@ public class MediaRouterManagerTest {
public void onRoutesRemoved(List<MediaRoute2Info> routes) {
assertTrue(routes.size() > 0);
for (MediaRoute2Info route : routes) {
- if (route.getId().equals(ROUTE_ID2) && route.getName().equals(ROUTE_NAME2)) {
+ if (route.getOriginalId().equals(ROUTE_ID2)
+ && route.getName().equals(ROUTE_NAME2)) {
latch.countDown();
}
}
@@ -352,8 +324,7 @@ public class MediaRouterManagerTest {
@Override
public void onRoutesAdded(List<MediaRoute2Info> routes) {
for (int i = 0; i < routes.size(); i++) {
- //TODO: use isSystem() or similar method when it's ready
- if (!TextUtils.equals(routes.get(i).getProviderId(), SYSTEM_PROVIDER_ID)) {
+ if (!routes.get(i).isSystemRoute()) {
latch.countDown();
break;
}
@@ -406,7 +377,7 @@ public class MediaRouterManagerTest {
static Map<String, MediaRoute2Info> createRouteMap(List<MediaRoute2Info> routes) {
Map<String, MediaRoute2Info> routeMap = new HashMap<>();
for (MediaRoute2Info route : routes) {
- routeMap.put(route.getId(), route);
+ routeMap.put(route.getOriginalId(), route);
}
return routeMap;
}
diff --git a/media/tests/MediaRouteProvider/src/com/android/mediarouteprovider/example/SampleMediaRoute2ProviderService.java b/media/tests/MediaRouter/src/com/android/mediaroutertest/SampleMediaRoute2ProviderService.java
index 6595cae3c028..e29b3239fcaa 100644
--- a/media/tests/MediaRouteProvider/src/com/android/mediarouteprovider/example/SampleMediaRoute2ProviderService.java
+++ b/media/tests/MediaRouter/src/com/android/mediaroutertest/SampleMediaRoute2ProviderService.java
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package com.android.mediarouteprovider.example;
+package com.android.mediaroutertest;
import static android.media.MediaRoute2Info.DEVICE_TYPE_REMOTE_SPEAKER;
import static android.media.MediaRoute2Info.DEVICE_TYPE_REMOTE_TV;
diff --git a/packages/CarrierDefaultApp/res/values-ky/strings.xml b/packages/CarrierDefaultApp/res/values-ky/strings.xml
index 066e8f6bcbaf..199476f47be0 100644
--- a/packages/CarrierDefaultApp/res/values-ky/strings.xml
+++ b/packages/CarrierDefaultApp/res/values-ky/strings.xml
@@ -12,6 +12,6 @@
<string name="mobile_data_status_notification_channel_name" msgid="833999690121305708">"Мобилдик Интернеттин абалы"</string>
<string name="action_bar_label" msgid="4290345990334377177">"Мобилдик тармакка кирүү"</string>
<string name="ssl_error_warning" msgid="3127935140338254180">"Кошулайын деген тармагыңызда коопсуздук көйгөйлөрү бар."</string>
- <string name="ssl_error_example" msgid="6188711843183058764">"Мисалы, каттоо эсебине кирүү баракчасы көрсөтүлгөн уюмга таандык эмес болушу мүмкүн."</string>
+ <string name="ssl_error_example" msgid="6188711843183058764">"Мисалы, аккаунтка кирүү баракчасы көрсөтүлгөн уюмга таандык эмес болушу мүмкүн."</string>
<string name="ssl_error_continue" msgid="1138548463994095584">"Баары бир серепчи аркылуу улантуу"</string>
</resources>
diff --git a/packages/SettingsLib/src/com/android/settingslib/bluetooth/A2dpProfile.java b/packages/SettingsLib/src/com/android/settingslib/bluetooth/A2dpProfile.java
index a784e04ee6a0..ddb7341b7366 100644
--- a/packages/SettingsLib/src/com/android/settingslib/bluetooth/A2dpProfile.java
+++ b/packages/SettingsLib/src/com/android/settingslib/bluetooth/A2dpProfile.java
@@ -16,6 +16,7 @@
package com.android.settingslib.bluetooth;
+import static android.bluetooth.BluetoothAdapter.ACTIVE_DEVICE_AUDIO;
import static android.bluetooth.BluetoothProfile.CONNECTION_POLICY_ALLOWED;
import static android.bluetooth.BluetoothProfile.CONNECTION_POLICY_FORBIDDEN;
@@ -45,6 +46,7 @@ public class A2dpProfile implements LocalBluetoothProfile {
private boolean mIsProfileReady;
private final CachedBluetoothDeviceManager mDeviceManager;
+ private final BluetoothAdapter mBluetoothAdapter;
static final ParcelUuid[] SINK_UUIDS = {
BluetoothUuid.A2DP_SINK,
@@ -99,7 +101,8 @@ public class A2dpProfile implements LocalBluetoothProfile {
mContext = context;
mDeviceManager = deviceManager;
mProfileManager = profileManager;
- BluetoothAdapter.getDefaultAdapter().getProfileProxy(context, new A2dpServiceListener(),
+ mBluetoothAdapter = BluetoothAdapter.getDefaultAdapter();
+ mBluetoothAdapter.getProfileProxy(context, new A2dpServiceListener(),
BluetoothProfile.A2DP);
}
@@ -173,8 +176,10 @@ public class A2dpProfile implements LocalBluetoothProfile {
}
public boolean setActiveDevice(BluetoothDevice device) {
- if (mService == null) return false;
- return mService.setActiveDevice(device);
+ if (mBluetoothAdapter == null) {
+ return false;
+ }
+ return mBluetoothAdapter.setActiveDevice(device, ACTIVE_DEVICE_AUDIO);
}
public BluetoothDevice getActiveDevice() {
diff --git a/packages/SettingsLib/src/com/android/settingslib/bluetooth/HeadsetProfile.java b/packages/SettingsLib/src/com/android/settingslib/bluetooth/HeadsetProfile.java
index d65b5da22056..218d0b2dc2c0 100644
--- a/packages/SettingsLib/src/com/android/settingslib/bluetooth/HeadsetProfile.java
+++ b/packages/SettingsLib/src/com/android/settingslib/bluetooth/HeadsetProfile.java
@@ -16,6 +16,7 @@
package com.android.settingslib.bluetooth;
+import static android.bluetooth.BluetoothAdapter.ACTIVE_DEVICE_PHONE_CALL;
import static android.bluetooth.BluetoothProfile.CONNECTION_POLICY_ALLOWED;
import static android.bluetooth.BluetoothProfile.CONNECTION_POLICY_FORBIDDEN;
@@ -45,6 +46,7 @@ public class HeadsetProfile implements LocalBluetoothProfile {
private final CachedBluetoothDeviceManager mDeviceManager;
private final LocalBluetoothProfileManager mProfileManager;
+ private final BluetoothAdapter mBluetoothAdapter;
static final ParcelUuid[] UUIDS = {
BluetoothUuid.HSP,
@@ -99,7 +101,8 @@ public class HeadsetProfile implements LocalBluetoothProfile {
LocalBluetoothProfileManager profileManager) {
mDeviceManager = deviceManager;
mProfileManager = profileManager;
- BluetoothAdapter.getDefaultAdapter().getProfileProxy(context, new HeadsetServiceListener(),
+ mBluetoothAdapter = BluetoothAdapter.getDefaultAdapter();
+ mBluetoothAdapter.getProfileProxy(context, new HeadsetServiceListener(),
BluetoothProfile.HEADSET);
}
@@ -134,10 +137,10 @@ public class HeadsetProfile implements LocalBluetoothProfile {
}
public boolean setActiveDevice(BluetoothDevice device) {
- if (mService == null) {
+ if (mBluetoothAdapter == null) {
return false;
}
- return mService.setActiveDevice(device);
+ return mBluetoothAdapter.setActiveDevice(device, ACTIVE_DEVICE_PHONE_CALL);
}
public BluetoothDevice getActiveDevice() {
diff --git a/packages/SettingsLib/src/com/android/settingslib/bluetooth/HearingAidProfile.java b/packages/SettingsLib/src/com/android/settingslib/bluetooth/HearingAidProfile.java
index 9f1af669c708..b82fb37a770f 100644
--- a/packages/SettingsLib/src/com/android/settingslib/bluetooth/HearingAidProfile.java
+++ b/packages/SettingsLib/src/com/android/settingslib/bluetooth/HearingAidProfile.java
@@ -16,6 +16,7 @@
package com.android.settingslib.bluetooth;
+import static android.bluetooth.BluetoothAdapter.ACTIVE_DEVICE_ALL;
import static android.bluetooth.BluetoothProfile.CONNECTION_POLICY_ALLOWED;
import static android.bluetooth.BluetoothProfile.CONNECTION_POLICY_FORBIDDEN;
@@ -45,6 +46,7 @@ public class HearingAidProfile implements LocalBluetoothProfile {
static final String NAME = "HearingAid";
private final LocalBluetoothProfileManager mProfileManager;
+ private final BluetoothAdapter mBluetoothAdapter;
// Order of this profile in device profiles list
private static final int ORDINAL = 1;
@@ -97,7 +99,8 @@ public class HearingAidProfile implements LocalBluetoothProfile {
mContext = context;
mDeviceManager = deviceManager;
mProfileManager = profileManager;
- BluetoothAdapter.getDefaultAdapter().getProfileProxy(context,
+ mBluetoothAdapter = BluetoothAdapter.getDefaultAdapter();
+ mBluetoothAdapter.getProfileProxy(context,
new HearingAidServiceListener(), BluetoothProfile.HEARING_AID);
}
@@ -171,8 +174,10 @@ public class HearingAidProfile implements LocalBluetoothProfile {
}
public boolean setActiveDevice(BluetoothDevice device) {
- if (mService == null) return false;
- return mService.setActiveDevice(device);
+ if (mBluetoothAdapter == null) {
+ return false;
+ }
+ return mBluetoothAdapter.setActiveDevice(device, ACTIVE_DEVICE_ALL);
}
public List<BluetoothDevice> getActiveDevices() {
diff --git a/packages/SettingsLib/src/com/android/settingslib/license/LicenseHtmlGeneratorFromXml.java b/packages/SettingsLib/src/com/android/settingslib/license/LicenseHtmlGeneratorFromXml.java
index 9db4a35c1d78..b4c95e6ee2df 100644
--- a/packages/SettingsLib/src/com/android/settingslib/license/LicenseHtmlGeneratorFromXml.java
+++ b/packages/SettingsLib/src/com/android/settingslib/license/LicenseHtmlGeneratorFromXml.java
@@ -35,8 +35,10 @@ import java.io.PrintWriter;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
+import java.util.HashSet;
import java.util.List;
import java.util.Map;
+import java.util.Set;
import java.util.zip.GZIPInputStream;
/**
@@ -84,7 +86,7 @@ class LicenseHtmlGeneratorFromXml {
* "9645f39e9db895a4aa6e02cb57294595". Here "9645f39e9db895a4aa6e02cb57294595" is a MD5 sum
* of the content of packages/services/Telephony/MODULE_LICENSE_APACHE2.
*/
- private final Map<String, String> mFileNameToContentIdMap = new HashMap();
+ private final Map<String, Set<String>> mFileNameToContentIdMap = new HashMap();
/*
* A map from a content id (MD5 sum of file content) to a license file content.
@@ -186,10 +188,10 @@ class LicenseHtmlGeneratorFromXml {
* </licenses>
*/
@VisibleForTesting
- static void parse(InputStreamReader in, Map<String, String> outFileNameToContentIdMap,
+ static void parse(InputStreamReader in, Map<String, Set<String>> outFileNameToContentIdMap,
Map<String, String> outContentIdToFileContentMap)
throws XmlPullParserException, IOException {
- Map<String, String> fileNameToContentIdMap = new HashMap<String, String>();
+ Map<String, Set<String>> fileNameToContentIdMap = new HashMap<String, Set<String>>();
Map<String, String> contentIdToFileContentMap = new HashMap<String, String>();
XmlPullParser parser = Xml.newPullParser();
@@ -206,7 +208,10 @@ class LicenseHtmlGeneratorFromXml {
if (!TextUtils.isEmpty(contentId)) {
String fileName = readText(parser).trim();
if (!TextUtils.isEmpty(fileName)) {
- fileNameToContentIdMap.put(fileName, contentId);
+ Set<String> contentIds =
+ fileNameToContentIdMap.computeIfAbsent(
+ fileName, k -> new HashSet<>());
+ contentIds.add(contentId);
}
}
} else if (TAG_FILE_CONTENT.equals(parser.getName())) {
@@ -224,7 +229,13 @@ class LicenseHtmlGeneratorFromXml {
state = parser.next();
}
- outFileNameToContentIdMap.putAll(fileNameToContentIdMap);
+ for (Map.Entry<String, Set<String>> entry : fileNameToContentIdMap.entrySet()) {
+ outFileNameToContentIdMap.merge(
+ entry.getKey(), entry.getValue(), (s1, s2) -> {
+ s1.addAll(s2);
+ return s1;
+ });
+ }
outContentIdToFileContentMap.putAll(contentIdToFileContentMap);
}
@@ -240,7 +251,7 @@ class LicenseHtmlGeneratorFromXml {
}
@VisibleForTesting
- static void generateHtml(Map<String, String> fileNameToContentIdMap,
+ static void generateHtml(Map<String, Set<String>> fileNameToContentIdMap,
Map<String, String> contentIdToFileContentMap, PrintWriter writer,
String noticeHeader) {
List<String> fileNameList = new ArrayList();
@@ -259,19 +270,20 @@ class LicenseHtmlGeneratorFromXml {
// Prints all the file list with a link to its license file content.
for (String fileName : fileNameList) {
- String contentId = fileNameToContentIdMap.get(fileName);
- // Assigns an id to a newly referred license file content.
- if (!contentIdToOrderMap.containsKey(contentId)) {
- contentIdToOrderMap.put(contentId, count);
-
- // An index in contentIdAndFileNamesList is the order of each element.
- contentIdAndFileNamesList.add(new ContentIdAndFileNames(contentId));
- count++;
- }
+ for (String contentId : fileNameToContentIdMap.get(fileName)) {
+ // Assigns an id to a newly referred license file content.
+ if (!contentIdToOrderMap.containsKey(contentId)) {
+ contentIdToOrderMap.put(contentId, count);
+
+ // An index in contentIdAndFileNamesList is the order of each element.
+ contentIdAndFileNamesList.add(new ContentIdAndFileNames(contentId));
+ count++;
+ }
- int id = contentIdToOrderMap.get(contentId);
- contentIdAndFileNamesList.get(id).mFileNameList.add(fileName);
- writer.format("<li><a href=\"#id%d\">%s</a></li>\n", id, fileName);
+ int id = contentIdToOrderMap.get(contentId);
+ contentIdAndFileNamesList.get(id).mFileNameList.add(fileName);
+ writer.format("<li><a href=\"#id%d\">%s</a></li>\n", id, fileName);
+ }
}
writer.println(HTML_MIDDLE_STRING);
diff --git a/packages/SettingsLib/src/com/android/settingslib/media/InfoMediaDevice.java b/packages/SettingsLib/src/com/android/settingslib/media/InfoMediaDevice.java
index 99c7dcf52818..d287f95e504a 100644
--- a/packages/SettingsLib/src/com/android/settingslib/media/InfoMediaDevice.java
+++ b/packages/SettingsLib/src/com/android/settingslib/media/InfoMediaDevice.java
@@ -74,6 +74,21 @@ public class InfoMediaDevice extends MediaDevice {
}
@Override
+ public void requestSetVolume(int volume) {
+ mRouterManager.requestSetVolume(mRouteInfo, volume);
+ }
+
+ @Override
+ public int getMaxVolume() {
+ return mRouteInfo.getVolumeMax();
+ }
+
+ @Override
+ public int getCurrentVolume() {
+ return mRouteInfo.getVolume();
+ }
+
+ @Override
public void disconnect() {
//TODO(b/144535188): disconnected last select device
}
diff --git a/packages/SettingsLib/src/com/android/settingslib/media/MediaDevice.java b/packages/SettingsLib/src/com/android/settingslib/media/MediaDevice.java
index 53a852069478..839d528406cd 100644
--- a/packages/SettingsLib/src/com/android/settingslib/media/MediaDevice.java
+++ b/packages/SettingsLib/src/com/android/settingslib/media/MediaDevice.java
@@ -109,6 +109,59 @@ public abstract class MediaDevice implements Comparable<MediaDevice> {
public abstract boolean isConnected();
/**
+ * Request to set volume.
+ *
+ * @param volume is the new value.
+ */
+ public void requestSetVolume(int volume) {
+ }
+
+ /**
+ * Get max volume from MediaDevice.
+ *
+ * @return max volume.
+ */
+ public int getMaxVolume() {
+ return 100;
+ }
+
+ /**
+ * Get current volume from MediaDevice.
+ *
+ * @return current volume.
+ */
+ public int getCurrentVolume() {
+ return 0;
+ }
+
+ /**
+ * Get application package name.
+ *
+ * @return package name.
+ */
+ public String getClientPackageName() {
+ return null;
+ }
+
+ /**
+ * Get application label from MediaDevice.
+ *
+ * @return application label.
+ */
+ public String getClientAppLabel() {
+ return null;
+ }
+
+ /**
+ * Get application label from MediaDevice.
+ *
+ * @return application label.
+ */
+ public int getDeviceType() {
+ return mType;
+ }
+
+ /**
* Rules:
* 1. If there is one of the connected devices identified as a carkit, this carkit will
* be always on the top of the device list. Rule 2 and Rule 3 can’t overrule this rule.
diff --git a/packages/SettingsLib/src/com/android/settingslib/wifi/WifiUtils.java b/packages/SettingsLib/src/com/android/settingslib/wifi/WifiUtils.java
index 78ccba02fb04..d4e0510e15a7 100644
--- a/packages/SettingsLib/src/com/android/settingslib/wifi/WifiUtils.java
+++ b/packages/SettingsLib/src/com/android/settingslib/wifi/WifiUtils.java
@@ -60,7 +60,7 @@ public class WifiUtils {
if (config != null) {
WifiConfiguration.NetworkSelectionStatus networkStatus =
config.getNetworkSelectionStatus();
- for (int index = WifiConfiguration.NetworkSelectionStatus.NETWORK_SELECTION_ENABLE;
+ for (int index = WifiConfiguration.NetworkSelectionStatus.DISABLED_NONE;
index < WifiConfiguration.NetworkSelectionStatus
.NETWORK_SELECTION_DISABLED_MAX; index++) {
if (networkStatus.getDisableReasonCounter(index) != 0) {
diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/license/LicenseHtmlGeneratorFromXmlTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/license/LicenseHtmlGeneratorFromXmlTest.java
index 4b5e9097b3fe..e87461f85762 100644
--- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/license/LicenseHtmlGeneratorFromXmlTest.java
+++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/license/LicenseHtmlGeneratorFromXmlTest.java
@@ -28,8 +28,11 @@ import java.io.IOException;
import java.io.InputStreamReader;
import java.io.PrintWriter;
import java.io.StringWriter;
+import java.util.Arrays;
import java.util.HashMap;
+import java.util.HashSet;
import java.util.Map;
+import java.util.Set;
@RunWith(RobolectricTestRunner.class)
public class LicenseHtmlGeneratorFromXmlTest {
@@ -68,6 +71,7 @@ public class LicenseHtmlGeneratorFromXmlTest {
private static final String HTML_BODY_STRING =
"<li><a href=\"#id0\">/file0</a></li>\n"
+ + "<li><a href=\"#id1\">/file0</a></li>\n"
+ "<li><a href=\"#id0\">/file1</a></li>\n"
+ "</ul>\n"
+ "</div><!-- table of contents -->\n"
@@ -82,6 +86,15 @@ public class LicenseHtmlGeneratorFromXmlTest {
+ "license content #0\n"
+ "</pre><!-- license-text -->\n"
+ "</td></tr><!-- same-license -->\n"
+ + "<tr id=\"id1\"><td class=\"same-license\">\n"
+ + "<div class=\"label\">Notices for file(s):</div>\n"
+ + "<div class=\"file-list\">\n"
+ + "/file0 <br/>\n"
+ + "</div><!-- file-list -->\n"
+ + "<pre class=\"license-text\">\n"
+ + "license content #1\n"
+ + "</pre><!-- license-text -->\n"
+ + "</td></tr><!-- same-license -->\n"
+ "</table></body></html>\n";
private static final String EXPECTED_HTML_STRING = HTML_HEAD_STRING + HTML_BODY_STRING;
@@ -91,22 +104,22 @@ public class LicenseHtmlGeneratorFromXmlTest {
@Test
public void testParseValidXmlStream() throws XmlPullParserException, IOException {
- Map<String, String> fileNameToContentIdMap = new HashMap<>();
+ Map<String, Set<String>> fileNameToContentIdMap = new HashMap<>();
Map<String, String> contentIdToFileContentMap = new HashMap<>();
LicenseHtmlGeneratorFromXml.parse(
new InputStreamReader(new ByteArrayInputStream(VALILD_XML_STRING.getBytes())),
fileNameToContentIdMap, contentIdToFileContentMap);
assertThat(fileNameToContentIdMap.size()).isEqualTo(2);
- assertThat(fileNameToContentIdMap.get("/file0")).isEqualTo("0");
- assertThat(fileNameToContentIdMap.get("/file1")).isEqualTo("0");
+ assertThat(fileNameToContentIdMap.get("/file0")).containsExactly("0");
+ assertThat(fileNameToContentIdMap.get("/file1")).containsExactly("0");
assertThat(contentIdToFileContentMap.size()).isEqualTo(1);
assertThat(contentIdToFileContentMap.get("0")).isEqualTo("license content #0");
}
@Test(expected = XmlPullParserException.class)
public void testParseInvalidXmlStream() throws XmlPullParserException, IOException {
- Map<String, String> fileNameToContentIdMap = new HashMap<>();
+ Map<String, Set<String>> fileNameToContentIdMap = new HashMap<>();
Map<String, String> contentIdToFileContentMap = new HashMap<>();
LicenseHtmlGeneratorFromXml.parse(
@@ -116,12 +129,13 @@ public class LicenseHtmlGeneratorFromXmlTest {
@Test
public void testGenerateHtml() {
- Map<String, String> fileNameToContentIdMap = new HashMap<>();
+ Map<String, Set<String>> fileNameToContentIdMap = new HashMap<>();
Map<String, String> contentIdToFileContentMap = new HashMap<>();
- fileNameToContentIdMap.put("/file0", "0");
- fileNameToContentIdMap.put("/file1", "0");
+ fileNameToContentIdMap.put("/file0", new HashSet<String>(Arrays.asList("0", "1")));
+ fileNameToContentIdMap.put("/file1", new HashSet<String>(Arrays.asList("0")));
contentIdToFileContentMap.put("0", "license content #0");
+ contentIdToFileContentMap.put("1", "license content #1");
StringWriter output = new StringWriter();
LicenseHtmlGeneratorFromXml.generateHtml(
@@ -131,12 +145,13 @@ public class LicenseHtmlGeneratorFromXmlTest {
@Test
public void testGenerateHtmlWithCustomHeading() {
- Map<String, String> fileNameToContentIdMap = new HashMap<>();
+ Map<String, Set<String>> fileNameToContentIdMap = new HashMap<>();
Map<String, String> contentIdToFileContentMap = new HashMap<>();
- fileNameToContentIdMap.put("/file0", "0");
- fileNameToContentIdMap.put("/file1", "0");
+ fileNameToContentIdMap.put("/file0", new HashSet<String>(Arrays.asList("0", "1")));
+ fileNameToContentIdMap.put("/file1", new HashSet<String>(Arrays.asList("0")));
contentIdToFileContentMap.put("0", "license content #0");
+ contentIdToFileContentMap.put("1", "license content #1");
StringWriter output = new StringWriter();
LicenseHtmlGeneratorFromXml.generateHtml(
diff --git a/packages/SettingsProvider/src/com/android/providers/settings/SettingsBackupAgent.java b/packages/SettingsProvider/src/com/android/providers/settings/SettingsBackupAgent.java
index 3c52f543c81b..44864a61ade6 100644
--- a/packages/SettingsProvider/src/com/android/providers/settings/SettingsBackupAgent.java
+++ b/packages/SettingsProvider/src/com/android/providers/settings/SettingsBackupAgent.java
@@ -626,7 +626,7 @@ public class SettingsBackupAgent extends BackupAgentHelper {
* Get names of the settings for which the current value should be preserved during restore.
*/
private Set<String> getSettingsToPreserveInRestore(Uri settingsUri) {
- if (!FeatureFlagUtils.isEnabled(getApplicationContext(),
+ if (!FeatureFlagUtils.isEnabled(getBaseContext(),
FeatureFlagUtils.SETTINGS_DO_NOT_RESTORE_PRESERVED)) {
return Collections.emptySet();
}
diff --git a/packages/SettingsProvider/src/com/android/providers/settings/SettingsProtoDumpUtil.java b/packages/SettingsProvider/src/com/android/providers/settings/SettingsProtoDumpUtil.java
index 1bec826897c0..78b9f16ef355 100644
--- a/packages/SettingsProvider/src/com/android/providers/settings/SettingsProtoDumpUtil.java
+++ b/packages/SettingsProvider/src/com/android/providers/settings/SettingsProtoDumpUtil.java
@@ -2185,13 +2185,14 @@ class SettingsProtoDumpUtil {
Settings.Secure.NAVIGATION_MODE,
SecureSettingsProto.NAVIGATION_MODE);
+ final long gestureNavToken = p.start(SecureSettingsProto.GESTURE_NAVIGATION);
dumpSetting(s, p,
Settings.Secure.BACK_GESTURE_INSET_SCALE_LEFT,
SecureSettingsProto.GestureNavigation.BACK_GESTURE_INSET_SCALE_LEFT);
-
dumpSetting(s, p,
Settings.Secure.BACK_GESTURE_INSET_SCALE_RIGHT,
SecureSettingsProto.GestureNavigation.BACK_GESTURE_INSET_SCALE_RIGHT);
+ p.end(gestureNavToken);
final long nfcPaymentToken = p.start(SecureSettingsProto.NFC_PAYMENT);
dumpSetting(s, p,
diff --git a/packages/Shell/AndroidManifest.xml b/packages/Shell/AndroidManifest.xml
index 84d9bb673d1f..6a89b71be897 100644
--- a/packages/Shell/AndroidManifest.xml
+++ b/packages/Shell/AndroidManifest.xml
@@ -91,6 +91,7 @@
<uses-permission android:name="android.permission.TEST_MANAGE_ROLLBACKS" />
<uses-permission android:name="android.permission.ACCESS_SURFACE_FLINGER" />
<uses-permission android:name="android.permission.READ_FRAME_BUFFER" />
+ <uses-permission android:name="android.permission.REBOOT" />
<uses-permission android:name="android.permission.DEVICE_POWER" />
<uses-permission android:name="android.permission.POWER_SAVER" />
<uses-permission android:name="android.permission.INSTALL_LOCATION_PROVIDER" />
@@ -253,6 +254,9 @@
<!-- Permission required for CTS test - CtsLightsManagerTest -->
<uses-permission android:name="android.permission.CONTROL_DEVICE_LIGHTS" />
+ <!-- Permission needed to test mainline permission module rollback -->
+ <uses-permission android:name="android.permission.UPGRADE_RUNTIME_PERMISSIONS" />
+
<application android:label="@string/app_label"
android:theme="@android:style/Theme.DeviceDefault.DayNight"
android:defaultToDeviceProtectedStorage="true"
diff --git a/packages/SystemUI/AndroidManifest.xml b/packages/SystemUI/AndroidManifest.xml
index 149eaf47b97d..139a8c3f7411 100644
--- a/packages/SystemUI/AndroidManifest.xml
+++ b/packages/SystemUI/AndroidManifest.xml
@@ -644,7 +644,7 @@
<activity android:name=".controls.management.ControlsProviderSelectorActivity"
android:label="Controls Providers"
- android:theme="@style/Theme.SystemUI"
+ android:theme="@style/Theme.ControlsManagement"
android:exported="true"
android:showForAllUsers="true"
android:excludeFromRecents="true"
@@ -654,7 +654,7 @@
<activity android:name=".controls.management.ControlsFavoritingActivity"
android:parentActivityName=".controls.management.ControlsProviderSelectorActivity"
- android:theme="@style/Theme.SystemUI"
+ android:theme="@style/Theme.ControlsManagement"
android:excludeFromRecents="true"
android:showForAllUsers="true"
android:configChanges="screenSize|smallestScreenSize|screenLayout|orientation|keyboard|keyboardHidden"
diff --git a/packages/SystemUI/plugin/src/com/android/systemui/plugins/qs/QSTile.java b/packages/SystemUI/plugin/src/com/android/systemui/plugins/qs/QSTile.java
index 6518924ca0c2..01811e9cdced 100644
--- a/packages/SystemUI/plugin/src/com/android/systemui/plugins/qs/QSTile.java
+++ b/packages/SystemUI/plugin/src/com/android/systemui/plugins/qs/QSTile.java
@@ -14,9 +14,6 @@
package com.android.systemui.plugins.qs;
-import static java.lang.annotation.RetentionPolicy.SOURCE;
-
-import android.annotation.IntDef;
import android.content.Context;
import android.graphics.drawable.Drawable;
import android.metrics.LogMaker;
@@ -28,7 +25,6 @@ import com.android.systemui.plugins.qs.QSTile.Callback;
import com.android.systemui.plugins.qs.QSTile.Icon;
import com.android.systemui.plugins.qs.QSTile.State;
-import java.lang.annotation.Retention;
import java.util.Objects;
import java.util.function.Supplier;
@@ -84,17 +80,6 @@ public interface QSTile {
return logMaker;
}
- @Retention(SOURCE)
- @IntDef({COLOR_TILE_ACCENT, COLOR_TILE_RED, COLOR_TILE_BLUE, COLOR_TILE_YELLOW,
- COLOR_TILE_GREEN})
- @interface ColorTile {}
- int COLOR_TILE_ACCENT = 0;
- int COLOR_TILE_RED = 1;
- int COLOR_TILE_BLUE = 2;
- int COLOR_TILE_YELLOW = 3;
- int COLOR_TILE_GREEN = 4;
- default void setColor(@ColorTile int color) {}
-
@ProvidesInterface(version = Callback.VERSION)
public interface Callback {
public static final int VERSION = 1;
@@ -141,7 +126,6 @@ public interface QSTile {
public SlashState slash;
public boolean handlesLongClick = true;
public boolean showRippleEffect = true;
- public int colorActive = -1;
public boolean copyTo(State other) {
if (other == null) throw new IllegalArgumentException();
@@ -161,8 +145,7 @@ public interface QSTile {
|| !Objects.equals(other.dualTarget, dualTarget)
|| !Objects.equals(other.slash, slash)
|| !Objects.equals(other.handlesLongClick, handlesLongClick)
- || !Objects.equals(other.showRippleEffect, showRippleEffect)
- || !Objects.equals(other.colorActive, colorActive);
+ || !Objects.equals(other.showRippleEffect, showRippleEffect);
other.icon = icon;
other.iconSupplier = iconSupplier;
other.label = label;
@@ -177,7 +160,6 @@ public interface QSTile {
other.slash = slash != null ? slash.copy() : null;
other.handlesLongClick = handlesLongClick;
other.showRippleEffect = showRippleEffect;
- other.colorActive = colorActive;
return changed;
}
diff --git a/packages/SystemUI/proguard.flags b/packages/SystemUI/proguard.flags
index 3e74970ee725..2a2ba1b0ccaa 100644
--- a/packages/SystemUI/proguard.flags
+++ b/packages/SystemUI/proguard.flags
@@ -16,6 +16,7 @@
-keep class com.android.systemui.statusbar.tv.TvStatusBar
-keep class com.android.systemui.car.CarSystemUIFactory
-keep class com.android.systemui.SystemUIFactory
+-keep class com.android.systemui.tv.TvSystemUIFactory
-keep class * extends com.android.systemui.SystemUI
-keep class * implements com.android.systemui.SystemUI$Injector
diff --git a/packages/SystemUI/res-keyguard/layout/controls_management.xml b/packages/SystemUI/res-keyguard/layout/controls_management.xml
new file mode 100644
index 000000000000..8330258e2456
--- /dev/null
+++ b/packages/SystemUI/res-keyguard/layout/controls_management.xml
@@ -0,0 +1,34 @@
+<?xml version="1.0" encoding="utf-8"?>
+
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ android:orientation="vertical"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:gravity="center_horizontal"
+ android:paddingTop="@dimen/controls_management_top_padding"
+ android:paddingStart="@dimen/controls_management_side_padding"
+ android:paddingEnd="@dimen/controls_management_side_padding" >
+
+ <TextView
+ android:id="@+id/title"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:textAppearance="?android:attr/textAppearanceLarge"
+ android:textSize="@dimen/controls_title_size"
+ android:textAlignment="center" />
+
+ <TextView
+ android:id="@+id/subtitle"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_marginTop="@dimen/controls_management_titles_margin"
+ android:textAppearance="?android:attr/textAppearanceSmall"
+ android:textAlignment="center" />
+
+ <androidx.recyclerview.widget.RecyclerView
+ android:id="@+id/list"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:layout_marginTop="@dimen/controls_management_list_margin" />
+
+</LinearLayout> \ No newline at end of file
diff --git a/packages/SystemUI/res/color/lock_background.xml b/packages/SystemUI/res/color/control_background.xml
index 646fe5dfe712..646fe5dfe712 100644
--- a/packages/SystemUI/res/color/lock_background.xml
+++ b/packages/SystemUI/res/color/control_background.xml
diff --git a/packages/SystemUI/res/color/unknown_foreground.xml b/packages/SystemUI/res/color/control_foreground.xml
index bf028f18a7de..bf028f18a7de 100644
--- a/packages/SystemUI/res/color/unknown_foreground.xml
+++ b/packages/SystemUI/res/color/control_foreground.xml
diff --git a/packages/SystemUI/res/color/thermo_cool_background.xml b/packages/SystemUI/res/color/thermo_cool_background.xml
new file mode 100644
index 000000000000..646fe5dfe712
--- /dev/null
+++ b/packages/SystemUI/res/color/thermo_cool_background.xml
@@ -0,0 +1,6 @@
+<?xml version="1.0" encoding="utf-8"?>
+<selector xmlns:android="http://schemas.android.com/apk/res/android">
+ <item android:state_enabled="false"
+ android:color="@color/control_default_background" />
+ <item android:color="@color/GM2_blue_50" />
+</selector>
diff --git a/packages/SystemUI/res/color/thermo_cool_foreground.xml b/packages/SystemUI/res/color/thermo_cool_foreground.xml
new file mode 100644
index 000000000000..bf028f18a7de
--- /dev/null
+++ b/packages/SystemUI/res/color/thermo_cool_foreground.xml
@@ -0,0 +1,6 @@
+<?xml version="1.0" encoding="utf-8"?>
+<selector xmlns:android="http://schemas.android.com/apk/res/android">
+ <item android:state_enabled="false"
+ android:color="@color/control_default_foreground" />
+ <item android:color="@color/GM2_blue_700" />
+ </selector>
diff --git a/packages/SystemUI/res/color/thermo_heat_background.xml b/packages/SystemUI/res/color/thermo_heat_background.xml
new file mode 100644
index 000000000000..6f29ed5f60ac
--- /dev/null
+++ b/packages/SystemUI/res/color/thermo_heat_background.xml
@@ -0,0 +1,6 @@
+<?xml version="1.0" encoding="utf-8"?>
+<selector xmlns:android="http://schemas.android.com/apk/res/android">
+ <item android:state_enabled="false"
+ android:color="@color/control_default_background" />
+ <item android:color="@color/GM2_red_50" />
+</selector>
diff --git a/packages/SystemUI/res/color/lock_foreground.xml b/packages/SystemUI/res/color/thermo_heat_foreground.xml
index 3e05653bce92..72f4b8d13458 100644
--- a/packages/SystemUI/res/color/lock_foreground.xml
+++ b/packages/SystemUI/res/color/thermo_heat_foreground.xml
@@ -2,5 +2,5 @@
<selector xmlns:android="http://schemas.android.com/apk/res/android">
<item android:state_enabled="false"
android:color="@color/control_default_foreground" />
- <item android:color="@color/GM2_blue_700" />
+ <item android:color="@color/GM2_red_700" />
</selector>
diff --git a/packages/SystemUI/res/drawable/auth_dialog_enterprise.xml b/packages/SystemUI/res/drawable/auth_dialog_enterprise.xml
new file mode 100644
index 000000000000..c547c52a4077
--- /dev/null
+++ b/packages/SystemUI/res/drawable/auth_dialog_enterprise.xml
@@ -0,0 +1,25 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ ~ Copyright (C) 2020 The Android Open Source Project
+ ~
+ ~ Licensed under the Apache License, Version 2.0 (the "License");
+ ~ you may not use this file except in compliance with the License.
+ ~ You may obtain a copy of the License at
+ ~
+ ~ http://www.apache.org/licenses/LICENSE-2.0
+ ~
+ ~ Unless required by applicable law or agreed to in writing, software
+ ~ distributed under the License is distributed on an "AS IS" BASIS,
+ ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ ~ See the License for the specific language governing permissions and
+ ~ limitations under the License.
+ -->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="24dp"
+ android:height="24dp"
+ android:viewportWidth="24.0"
+ android:viewportHeight="24.0">
+ <path
+ android:pathData="M20,6h-4L16,4c0,-1.11 -0.89,-2 -2,-2h-4c-1.11,0 -2,0.89 -2,2v2L4,6c-1.11,0 -1.99,0.89 -1.99,2L2,19c0,1.11 0.89,2 2,2h16c1.11,0 2,-0.89 2,-2L22,8c0,-1.11 -0.89,-2 -2,-2zM12,15c-1.1,0 -2,-0.9 -2,-2s0.9,-2 2,-2 2,0.9 2,2 -0.9,2 -2,2zM14,6h-4L10,4h4v2z"
+ android:fillColor="?android:attr/colorAccent"/>
+</vector> \ No newline at end of file
diff --git a/packages/SystemUI/res/drawable/notif_dungeon_bg_gradient.xml b/packages/SystemUI/res/drawable/notif_dungeon_bg_gradient.xml
new file mode 100644
index 000000000000..e456e2965d21
--- /dev/null
+++ b/packages/SystemUI/res/drawable/notif_dungeon_bg_gradient.xml
@@ -0,0 +1,25 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ ~ Copyright (C) 2020 The Android Open Source Project
+ ~
+ ~ Licensed under the Apache License, Version 2.0 (the "License");
+ ~ you may not use this file except in compliance with the License.
+ ~ You may obtain a copy of the License at
+ ~
+ ~ http://www.apache.org/licenses/LICENSE-2.0
+ ~
+ ~ Unless required by applicable law or agreed to in writing, software
+ ~ distributed under the License is distributed on an "AS IS" BASIS,
+ ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ ~ See the License for the specific language governing permissions and
+ ~ limitations under the License.
+ -->
+<shape
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ android:shape="rectangle">
+ <gradient
+ android:angle="90"
+ android:startColor="#ff000000"
+ android:endColor="#00000000"
+ android:type="linear" />
+</shape>
diff --git a/packages/SystemUI/res/drawable/qs_media_background.xml b/packages/SystemUI/res/drawable/qs_media_background.xml
new file mode 100644
index 000000000000..2821e4c28bab
--- /dev/null
+++ b/packages/SystemUI/res/drawable/qs_media_background.xml
@@ -0,0 +1,26 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ ~ Copyright (C) 2020 The Android Open Source Project
+ ~
+ ~ Licensed under the Apache License, Version 2.0 (the "License");
+ ~ you may not use this file except in compliance with the License.
+ ~ You may obtain a copy of the License at
+ ~
+ ~ http://www.apache.org/licenses/LICENSE-2.0
+ ~
+ ~ Unless required by applicable law or agreed to in writing, software
+ ~ distributed under the License is distributed on an "AS IS" BASIS,
+ ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ ~ See the License for the specific language governing permissions and
+ ~ limitations under the License
+ -->
+<shape xmlns:android="http://schemas.android.com/apk/res/android"
+ android:shape="rectangle">
+ <solid android:color="?android:attr/colorBackgroundFloating" />
+ <corners
+ android:bottomLeftRadius="@dimen/qs_media_corner_radius"
+ android:topLeftRadius="@dimen/qs_media_corner_radius"
+ android:bottomRightRadius="@dimen/qs_media_corner_radius"
+ android:topRightRadius="@dimen/qs_media_corner_radius"
+ />
+</shape> \ No newline at end of file
diff --git a/packages/SystemUI/res/layout-land/auth_credential_pattern_view.xml b/packages/SystemUI/res/layout-land/auth_credential_pattern_view.xml
index c3fa39e5a87f..c40e47df9a55 100644
--- a/packages/SystemUI/res/layout-land/auth_credential_pattern_view.xml
+++ b/packages/SystemUI/res/layout-land/auth_credential_pattern_view.xml
@@ -34,20 +34,15 @@
android:layout_weight="1"/>
<ImageView
- android:layout_width="32dp"
- android:layout_height="32dp"
- android:background="@drawable/auth_dialog_lock"/>
+ android:id="@+id/icon"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"/>
<TextView
android:id="@+id/title"
- android:fontFamily="@*android:string/config_headlineFontFamilyMedium"
android:layout_width="match_parent"
android:layout_height="wrap_content"
- android:layout_marginHorizontal="24dp"
- android:layout_marginTop="12dp"
- android:textSize="20sp"
- android:gravity="center"
- android:textColor="?android:attr/textColorPrimary"/>
+ style="@style/TextAppearance.AuthCredential.Title"/>
<TextView
android:id="@+id/subtitle"
@@ -63,17 +58,41 @@
android:id="@+id/description"
android:layout_width="match_parent"
android:layout_height="wrap_content"
- android:layout_marginHorizontal="24dp"
- android:layout_marginTop="8dp"
- android:gravity="center"
- android:textSize="16sp"
- android:textColor="?android:attr/textColorPrimary"/>
+ style="@style/TextAppearance.AuthCredential.Description"/>
<Space
android:layout_width="0dp"
android:layout_height="0dp"
android:layout_weight="1"/>
+ </LinearLayout>
+
+ <LinearLayout
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:orientation="vertical"
+ android:gravity="center"
+ android:paddingLeft="0dp"
+ android:paddingRight="0dp"
+ android:paddingTop="0dp"
+ android:paddingBottom="16dp"
+ android:clipToPadding="false">
+
+ <FrameLayout
+ android:layout_width="wrap_content"
+ android:layout_height="0dp"
+ android:layout_weight="1"
+ style="@style/LockPatternContainerStyle">
+
+ <com.android.internal.widget.LockPatternView
+ android:id="@+id/lockPattern"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:layout_gravity="center"
+ style="@style/LockPatternStyleBiometricPrompt"/>
+
+ </FrameLayout>
+
<TextView
android:id="@+id/error"
android:layout_width="match_parent"
@@ -90,24 +109,4 @@
</LinearLayout>
- <LinearLayout
- android:layout_width="0dp"
- android:layout_height="match_parent"
- android:layout_weight="1"
- android:orientation="vertical"
- android:gravity="center">
-
- <com.android.internal.widget.LockPatternView
- android:id="@+id/lockPattern"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:layout_marginLeft="40dp"
- android:layout_marginRight="40dp"
- android:layout_gravity="center"
- android:clipChildren="false"
- android:clipToPadding="false"
- style="@style/LockPatternStyleBiometricPrompt"/>
-
- </LinearLayout>
-
</com.android.systemui.biometrics.AuthCredentialPatternView> \ No newline at end of file
diff --git a/packages/SystemUI/res/layout/app_item.xml b/packages/SystemUI/res/layout/app_item.xml
deleted file mode 100644
index 83e788731442..000000000000
--- a/packages/SystemUI/res/layout/app_item.xml
+++ /dev/null
@@ -1,70 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
- Copyright (C) 2019 The Android Open Source Project
-
- Licensed under the Apache License, Version 2.0 (the "License");
- you may not use this file except in compliance with the License.
- You may obtain a copy of the License at
-
- http://www.apache.org/licenses/LICENSE-2.0
-
- Unless required by applicable law or agreed to in writing, software
- distributed under the License is distributed on an "AS IS" BASIS,
- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- See the License for the specific language governing permissions and
- limitations under the License.
--->
-<LinearLayout
- xmlns:android="http://schemas.android.com/apk/res/android"
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:background="?android:attr/selectableItemBackground"
- android:gravity="center_vertical"
- android:minHeight="?android:attr/listPreferredItemHeightSmall"
- android:paddingStart="?android:attr/listPreferredItemPaddingStart"
- android:paddingEnd="?android:attr/listPreferredItemPaddingEnd">
-
- <LinearLayout
- android:id="@+id/icon_frame"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:gravity="start|center_vertical"
- android:minWidth="56dp"
- android:orientation="horizontal"
- android:paddingEnd="8dp"
- android:paddingTop="4dp"
- android:paddingBottom="4dp">
- <ImageView
- android:id="@android:id/icon"
- android:layout_width="@dimen/app_icon_size"
- android:layout_height="@dimen/app_icon_size"/>
- </LinearLayout>
-
- <LinearLayout
- android:layout_width="0dp"
- android:layout_height="wrap_content"
- android:layout_weight="1"
- android:orientation="vertical"
- android:paddingTop="16dp"
- android:paddingBottom="16dp">
-
- <TextView
- android:id="@android:id/title"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:ellipsize="marquee"
- android:fadingEdge="horizontal"
- android:singleLine="true"
- android:textAppearance="?android:attr/textAppearanceListItem"/>
-
- </LinearLayout>
-
- <LinearLayout
- android:id="@android:id/widget_frame"
- android:layout_width="wrap_content"
- android:layout_height="match_parent"
- android:gravity="center_vertical|end"
- android:minWidth="64dp"
- android:orientation="vertical"/>
-
-</LinearLayout> \ No newline at end of file
diff --git a/packages/SystemUI/res/layout/auth_credential_password_view.xml b/packages/SystemUI/res/layout/auth_credential_password_view.xml
index 4aed0333e9ca..a1c593fa455c 100644
--- a/packages/SystemUI/res/layout/auth_credential_password_view.xml
+++ b/packages/SystemUI/res/layout/auth_credential_password_view.xml
@@ -28,20 +28,15 @@
android:layout_weight="1"/>
<ImageView
- android:layout_width="32dp"
- android:layout_height="32dp"
- android:background="@drawable/auth_dialog_lock"/>
+ android:id="@+id/icon"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"/>
<TextView
android:id="@+id/title"
- android:fontFamily="@*android:string/config_headlineFontFamilyMedium"
android:layout_width="match_parent"
android:layout_height="wrap_content"
- android:layout_marginHorizontal="24dp"
- android:layout_marginTop="12dp"
- android:textSize="20sp"
- android:gravity="center"
- android:textColor="?android:attr/textColorPrimary"/>
+ style="@style/TextAppearance.AuthCredential.Title"/>
<TextView
android:id="@+id/subtitle"
@@ -57,11 +52,7 @@
android:id="@+id/description"
android:layout_width="match_parent"
android:layout_height="wrap_content"
- android:layout_marginHorizontal="24dp"
- android:layout_marginTop="8dp"
- android:gravity="center"
- android:textSize="16sp"
- android:textColor="?android:attr/textColorPrimary"/>
+ style="@style/TextAppearance.AuthCredential.Description"/>
<Space
android:layout_width="0dp"
@@ -79,19 +70,14 @@
<EditText
android:id="@+id/lockPassword"
- android:layout_marginBottom="20dp"
- android:layout_marginLeft="100dp"
- android:layout_marginRight="100dp"
android:layout_width="208dp"
android:layout_height="wrap_content"
- android:layout_gravity="center"
+ android:layout_gravity="center_horizontal"
android:gravity="center"
android:inputType="textPassword"
android:maxLength="500"
- android:textSize="16sp"
- android:textAppearance="?android:attr/textAppearanceMedium"
- android:imeOptions="flagForceAscii"
- style="@style/LockPatternStyleBiometricPrompt"/>
+ android:imeOptions="actionNext|flagNoFullscreen|flagForceAscii"
+ style="@style/TextAppearance.AuthCredential.PasswordEntry"/>
<Space
android:layout_width="0dp"
diff --git a/packages/SystemUI/res/layout/auth_credential_pattern_view.xml b/packages/SystemUI/res/layout/auth_credential_pattern_view.xml
index c9edcd606277..eda5ecbf7f8c 100644
--- a/packages/SystemUI/res/layout/auth_credential_pattern_view.xml
+++ b/packages/SystemUI/res/layout/auth_credential_pattern_view.xml
@@ -28,20 +28,15 @@
android:layout_weight="1"/>
<ImageView
- android:layout_width="32dp"
- android:layout_height="32dp"
- android:background="@drawable/auth_dialog_lock"/>
+ android:id="@+id/icon"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"/>
<TextView
android:id="@+id/title"
- android:fontFamily="@*android:string/config_headlineFontFamilyMedium"
android:layout_width="match_parent"
android:layout_height="wrap_content"
- android:layout_marginHorizontal="24dp"
- android:layout_marginTop="12dp"
- android:textSize="20sp"
- android:gravity="center"
- android:textColor="?android:attr/textColorPrimary"/>
+ style="@style/TextAppearance.AuthCredential.Title"/>
<TextView
android:id="@+id/subtitle"
@@ -57,37 +52,49 @@
android:id="@+id/description"
android:layout_width="match_parent"
android:layout_height="wrap_content"
- android:layout_marginHorizontal="24dp"
- android:layout_marginTop="8dp"
- android:gravity="center"
- android:textSize="16sp"
- android:textColor="?android:attr/textColorPrimary"/>
+ style="@style/TextAppearance.AuthCredential.Description"/>
<Space
android:layout_width="0dp"
android:layout_height="0dp"
- android:layout_weight="3"/>
+ android:layout_weight="1"/>
- <TextView
- android:id="@+id/error"
+ <LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
- android:layout_marginHorizontal="24dp"
- android:textSize="16sp"
+ android:orientation="vertical"
android:gravity="center"
- android:textColor="?android:attr/colorError"/>
+ android:paddingLeft="0dp"
+ android:paddingRight="0dp"
+ android:paddingTop="0dp"
+ android:paddingBottom="16dp"
+ android:clipToPadding="false">
- <com.android.internal.widget.LockPatternView
- android:id="@+id/lockPattern"
- android:layout_marginBottom="20dp"
- android:layout_marginLeft="40dp"
- android:layout_marginRight="40dp"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:layout_gravity="center"
- android:clipChildren="false"
- android:clipToPadding="false"
- style="@style/LockPatternStyleBiometricPrompt"/>
+ <FrameLayout
+ android:layout_width="wrap_content"
+ android:layout_height="0dp"
+ android:layout_weight="1"
+ style="@style/LockPatternContainerStyle">
+
+ <com.android.internal.widget.LockPatternView
+ android:id="@+id/lockPattern"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:layout_gravity="center"
+ style="@style/LockPatternStyleBiometricPrompt"/>
+
+ </FrameLayout>
+
+ <TextView
+ android:id="@+id/error"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:layout_marginHorizontal="24dp"
+ android:textSize="16sp"
+ android:gravity="center"
+ android:textColor="?android:attr/colorError"/>
+
+ </LinearLayout>
<Space
android:layout_width="0dp"
diff --git a/packages/SystemUI/res/layout/control_item.xml b/packages/SystemUI/res/layout/control_item.xml
deleted file mode 100644
index 85701aaca41d..000000000000
--- a/packages/SystemUI/res/layout/control_item.xml
+++ /dev/null
@@ -1,72 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
- Copyright (C) 2020 The Android Open Source Project
-
- Licensed under the Apache License, Version 2.0 (the "License");
- you may not use this file except in compliance with the License.
- You may obtain a copy of the License at
-
- http://www.apache.org/licenses/LICENSE-2.0
-
- Unless required by applicable law or agreed to in writing, software
- distributed under the License is distributed on an "AS IS" BASIS,
- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- See the License for the specific language governing permissions and
- limitations under the License.
--->
-<androidx.constraintlayout.widget.ConstraintLayout
- xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:app="http://schemas.android.com/apk/res-auto"
- android:layout_width="match_parent"
- android:layout_height="100dp"
- android:padding="15dp"
- android:clickable="true"
- android:focusable="true">
-
- <ImageView
- android:id="@+id/icon"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- app:layout_constraintStart_toStartOf="parent"
- app:layout_constraintTop_toTopOf="parent" />
-
- <TextView
- android:id="@+id/status"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:textSize="12sp"
- android:textColor="?android:attr/textColorPrimary"
- android:fontFamily="@*android:string/config_bodyFontFamily"
- android:paddingLeft="3dp"
- app:layout_constraintBottom_toBottomOf="@+id/icon"
- app:layout_constraintStart_toEndOf="@+id/icon" />
-
- <TextView
- android:id="@+id/title"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:textSize="18sp"
- android:textColor="?android:attr/textColorPrimary"
- android:fontFamily="@*android:string/config_headlineFontFamily"
- app:layout_constraintBottom_toTopOf="@+id/subtitle"
- app:layout_constraintStart_toStartOf="parent"
- app:layout_constraintTop_toBottomOf="@+id/icon" />
-
- <TextView
- android:id="@+id/subtitle"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:textSize="16sp"
- android:textColor="?android:attr/textColorSecondary"
- android:fontFamily="@*android:string/config_headlineFontFamily"
- app:layout_constraintBottom_toBottomOf="parent"
- app:layout_constraintStart_toStartOf="parent" />
-
- <CheckBox
- android:id="@+id/favorite"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- app:layout_constraintEnd_toEndOf="parent"
- app:layout_constraintTop_toTopOf="parent"
- app:layout_constraintBottom_toBottomOf="parent"/>
-</androidx.constraintlayout.widget.ConstraintLayout>
diff --git a/packages/SystemUI/res/layout/controls_app_item.xml b/packages/SystemUI/res/layout/controls_app_item.xml
new file mode 100644
index 000000000000..d54cd6db867a
--- /dev/null
+++ b/packages/SystemUI/res/layout/controls_app_item.xml
@@ -0,0 +1,87 @@
+<?xml version="1.0" encoding="utf-8"?><!--
+ Copyright (C) 2019 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<FrameLayout
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:background="?android:attr/selectableItemBackground">
+
+ <LinearLayout
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:layout_gravity="start|top"
+ android:gravity="center_vertical"
+ android:minHeight="?android:attr/listPreferredItemHeightSmall"
+ android:paddingStart="?android:attr/listPreferredItemPaddingStart"
+ android:paddingEnd="?android:attr/listPreferredItemPaddingEnd"
+ android:layout_marginBottom="@dimen/controls_app_bottom_margin">
+
+ <FrameLayout
+ android:id="@+id/icon_frame"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:gravity="start|center_vertical"
+ android:minWidth="56dp"
+ android:orientation="horizontal"
+ android:paddingTop="@dimen/controls_app_icon_frame_top_padding"
+ android:paddingBottom="@dimen/controls_app_icon_frame_top_padding"
+ android:paddingEnd="@dimen/controls_app_icon_frame_side_padding"
+ android:paddingStart="@dimen/controls_app_icon_frame_side_padding" >
+
+ <ImageView
+ android:id="@android:id/icon"
+ android:layout_width="@dimen/controls_app_icon_size"
+ android:layout_height="@dimen/controls_app_icon_size" />
+ </FrameLayout>
+
+ <LinearLayout
+ android:layout_width="0dp"
+ android:layout_height="wrap_content"
+ android:layout_weight="1"
+ android:orientation="vertical"
+ android:paddingTop="@dimen/controls_app_text_padding"
+ android:paddingBottom="@dimen/controls_app_text_padding">
+
+ <TextView
+ android:id="@android:id/title"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:ellipsize="end"
+ android:fadingEdge="horizontal"
+ android:singleLine="true"
+ android:textAppearance="?android:attr/textAppearanceMedium"
+ android:textColor="?android:attr/textColorPrimary"/>
+
+ <TextView
+ android:id="@+id/favorites"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:ellipsize="end"
+ android:fadingEdge="horizontal"
+ android:singleLine="true"
+ android:textAppearance="?android:attr/textAppearanceSmall" />
+
+ </LinearLayout>
+
+ </LinearLayout>
+ <View
+ android:layout_width="match_parent"
+ android:layout_height="@dimen/controls_app_divider_height"
+ android:layout_gravity="center_horizontal|bottom"
+ android:layout_marginStart="@dimen/controls_app_divider_side_margin"
+ android:layout_marginEnd="@dimen/controls_app_divider_side_margin"
+ android:background="?android:attr/listDivider" />
+</FrameLayout> \ No newline at end of file
diff --git a/packages/SystemUI/res/layout/controls_base_item.xml b/packages/SystemUI/res/layout/controls_base_item.xml
index cd7ec5eb5c81..68c824698b2d 100644
--- a/packages/SystemUI/res/layout/controls_base_item.xml
+++ b/packages/SystemUI/res/layout/controls_base_item.xml
@@ -23,8 +23,8 @@
android:padding="@dimen/control_padding"
android:clickable="true"
android:focusable="true"
- android:layout_marginLeft="2dp"
- android:layout_marginRight="2dp"
+ android:layout_marginLeft="@dimen/control_base_item_margin"
+ android:layout_marginRight="@dimen/control_base_item_margin"
android:background="@drawable/control_background">
<ImageView
@@ -38,10 +38,8 @@
android:id="@+id/status"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
- android:textSize="@dimen/control_status_normal"
- android:textColor="?android:attr/textColorPrimary"
- android:fontFamily="@*android:string/config_bodyFontFamily"
- android:paddingLeft="3dp"
+ android:textAppearance="@style/TextAppearance.Control.Status"
+ android:paddingStart="@dimen/control_status_padding"
app:layout_constraintBottom_toBottomOf="@+id/icon"
app:layout_constraintStart_toEndOf="@+id/icon" />
@@ -49,10 +47,8 @@
android:id="@+id/status_extra"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
- android:textSize="@dimen/control_status_normal"
- android:textColor="?android:attr/textColorPrimary"
- android:fontFamily="@*android:string/config_bodyFontFamily"
- android:paddingLeft="3dp"
+ android:textAppearance="@style/TextAppearance.Control.Status"
+ android:paddingStart="@dimen/control_status_padding"
app:layout_constraintBottom_toBottomOf="@+id/icon"
app:layout_constraintStart_toEndOf="@+id/status" />
@@ -60,9 +56,7 @@
android:id="@+id/title"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
- android:textSize="14sp"
- android:textColor="?android:attr/textColorPrimary"
- android:textAppearance="?android:attr/textAppearanceSmall"
+ android:textAppearance="@style/TextAppearance.Control.Title"
app:layout_constraintBottom_toTopOf="@+id/subtitle"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@+id/icon" />
@@ -71,9 +65,15 @@
android:id="@+id/subtitle"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
- android:textSize="12sp"
- android:textColor="?android:attr/textColorSecondary"
- android:textAppearance="?android:attr/textAppearanceSmall"
+ android:textAppearance="@style/TextAppearance.Control.Subtitle"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintStart_toStartOf="parent" />
+
+ <CheckBox
+ android:id="@+id/favorite"
+ android:visibility="gone"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ app:layout_constraintEnd_toEndOf="parent"
+ app:layout_constraintBottom_toBottomOf="parent"/>
</androidx.constraintlayout.widget.ConstraintLayout>
diff --git a/packages/SystemUI/res/layout/controls_row.xml b/packages/SystemUI/res/layout/controls_row.xml
index 13a6b36accd3..4cc461a28187 100644
--- a/packages/SystemUI/res/layout/controls_row.xml
+++ b/packages/SystemUI/res/layout/controls_row.xml
@@ -16,7 +16,7 @@
-->
<LinearLayout
xmlns:android="http://schemas.android.com/apk/res/android"
- orientation="horizontal"
+ android:orientation="horizontal"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginBottom="@dimen/control_spacing" />
diff --git a/packages/SystemUI/res/layout/controls_with_favorites.xml b/packages/SystemUI/res/layout/controls_with_favorites.xml
index 6a3e95d34358..40b2476941c2 100644
--- a/packages/SystemUI/res/layout/controls_with_favorites.xml
+++ b/packages/SystemUI/res/layout/controls_with_favorites.xml
@@ -13,7 +13,7 @@
android:layout_height="wrap_content"
android:singleLine="true"
android:gravity="center"
- android:textSize="25dp"
+ android:textSize="25sp"
android:textColor="@*android:color/foreground_material_dark"
android:fontFamily="@*android:string/config_headlineFontFamily"
app:layout_constraintBottom_toBottomOf="parent"
diff --git a/packages/SystemUI/res/layout/foreground_service_dungeon.xml b/packages/SystemUI/res/layout/foreground_service_dungeon.xml
new file mode 100644
index 000000000000..d4e98e217213
--- /dev/null
+++ b/packages/SystemUI/res/layout/foreground_service_dungeon.xml
@@ -0,0 +1,61 @@
+<!--
+ ~ Copyright (C) 2020 The Android Open Source Project
+ ~
+ ~ Licensed under the Apache License, Version 2.0 (the "License");
+ ~ you may not use this file except in compliance with the License.
+ ~ You may obtain a copy of the License at
+ ~
+ ~ http://www.apache.org/licenses/LICENSE-2.0
+ ~
+ ~ Unless required by applicable law or agreed to in writing, software
+ ~ distributed under the License is distributed on an "AS IS" BASIS,
+ ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ ~ See the License for the specific language governing permissions and
+ ~ limitations under the License.
+ -->
+
+<com.android.systemui.statusbar.notification.row.ForegroundServiceDungeonView
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ android:id="@+id/foreground_service_dungeon"
+ android:layout_width="@dimen/qs_panel_width"
+ android:layout_height="wrap_content"
+ android:layout_gravity="center_horizontal|bottom"
+ android:visibility="visible"
+>
+ <LinearLayout
+ android:layout_height="wrap_content"
+ android:layout_width="match_parent"
+ android:orientation="vertical"
+ android:gravity="bottom"
+ android:visibility="visible"
+ android:background="@drawable/notif_dungeon_bg_gradient"
+ >
+
+ <!-- divider view -->
+ <View
+ android:layout_width="match_parent"
+ android:layout_height="1dp"
+ android:background="@color/GM2_grey_200"
+ android:visibility="visible"
+ />
+
+ <TextView
+ android:id="@+id/dungeon_title"
+ android:layout_height="48dp"
+ android:layout_width="match_parent"
+ android:padding="8dp"
+ android:text="Apps active in background"
+ android:textColor="@color/GM2_grey_200"
+ />
+
+ <!-- List containing the actual foreground service notifications -->
+ <LinearLayout
+ android:id="@+id/entry_list"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:gravity="bottom"
+ android:orientation="vertical" >
+ </LinearLayout>
+
+ </LinearLayout>
+</com.android.systemui.statusbar.notification.row.ForegroundServiceDungeonView>
diff --git a/packages/SystemUI/res/layout/foreground_service_dungeon_row.xml b/packages/SystemUI/res/layout/foreground_service_dungeon_row.xml
new file mode 100644
index 000000000000..a6f1638a1d89
--- /dev/null
+++ b/packages/SystemUI/res/layout/foreground_service_dungeon_row.xml
@@ -0,0 +1,43 @@
+<!--
+ ~ Copyright (C) 2020 The Android Open Source Project
+ ~
+ ~ Licensed under the Apache License, Version 2.0 (the "License");
+ ~ you may not use this file except in compliance with the License.
+ ~ You may obtain a copy of the License at
+ ~
+ ~ http://www.apache.org/licenses/LICENSE-2.0
+ ~
+ ~ Unless required by applicable law or agreed to in writing, software
+ ~ distributed under the License is distributed on an "AS IS" BASIS,
+ ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ ~ See the License for the specific language governing permissions and
+ ~ limitations under the License.
+ -->
+
+<com.android.systemui.statusbar.notification.row.DungeonRow
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ android:id="@+id/foreground_service_dungeon_row"
+ android:layout_width="match_parent"
+ android:layout_height="48dp"
+ android:padding="8dp"
+ android:clickable="true"
+ android:orientation="horizontal" >
+
+ <com.android.systemui.statusbar.StatusBarIconView
+ android:id="@+id/icon"
+ android:layout_width="24dp"
+ android:layout_height="24dp"
+ android:padding="4dp" />
+
+ <TextView
+ android:id="@+id/app_name"
+ android:layout_width="0dp"
+ android:layout_weight="1"
+ android:layout_height="wrap_content"
+ android:paddingStart="4dp"
+ android:gravity="center_vertical"
+ android:layout_gravity="center_vertical"
+ android:textColor="@color/GM2_grey_200"
+ />
+
+</com.android.systemui.statusbar.notification.row.DungeonRow>
diff --git a/packages/SystemUI/res/layout/media_carousel.xml b/packages/SystemUI/res/layout/media_carousel.xml
index e91f840fe238..149446c55fc5 100644
--- a/packages/SystemUI/res/layout/media_carousel.xml
+++ b/packages/SystemUI/res/layout/media_carousel.xml
@@ -19,7 +19,7 @@
<HorizontalScrollView
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
- android:layout_height="@dimen/qs_media_height"
+ android:layout_height="wrap_content"
android:padding="@dimen/qs_media_padding"
android:scrollbars="none"
android:visibility="gone"
diff --git a/packages/SystemUI/res/layout/qqs_media_panel.xml b/packages/SystemUI/res/layout/qqs_media_panel.xml
index 1189371fc7f1..403b5dc3a427 100644
--- a/packages/SystemUI/res/layout/qqs_media_panel.xml
+++ b/packages/SystemUI/res/layout/qqs_media_panel.xml
@@ -23,20 +23,25 @@
android:layout_height="match_parent"
android:orientation="vertical"
android:gravity="center"
- android:padding="10dp"
+ android:paddingTop="16dp"
+ android:paddingLeft="16dp"
+ android:paddingRight="16dp"
+ android:paddingBottom="12dp"
+ android:background="@drawable/qs_media_background"
>
- <!-- Top line: icon + artist name -->
+ <!-- Top line: icon + song name -->
<LinearLayout
android:orientation="horizontal"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:clipChildren="false"
android:gravity="center"
+ android:layout_marginBottom="12dp"
>
<com.android.internal.widget.CachingIconView
android:id="@+id/icon"
- android:layout_width="15dp"
- android:layout_height="15dp"
+ android:layout_width="14dp"
+ android:layout_height="14dp"
android:layout_marginEnd="5dp"
/>
<TextView
@@ -48,15 +53,6 @@
/>
</LinearLayout>
- <!-- Second line: song name -->
- <TextView
- android:id="@+id/header_text"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:singleLine="true"
- android:fontFamily="@*android:string/config_bodyFontFamily"
- android:gravity="center"/>
-
<!-- Bottom section: controls -->
<LinearLayout
android:id="@+id/media_actions"
@@ -70,8 +66,6 @@
style="@android:style/Widget.Material.Button.Borderless.Small"
android:layout_width="48dp"
android:layout_height="48dp"
- android:padding="8dp"
- android:layout_marginEnd="2dp"
android:gravity="center"
android:visibility="gone"
android:id="@+id/action0"
@@ -80,8 +74,6 @@
style="@android:style/Widget.Material.Button.Borderless.Small"
android:layout_width="48dp"
android:layout_height="48dp"
- android:padding="8dp"
- android:layout_marginEnd="2dp"
android:gravity="center"
android:visibility="gone"
android:id="@+id/action1"
@@ -90,8 +82,6 @@
style="@android:style/Widget.Material.Button.Borderless.Small"
android:layout_width="48dp"
android:layout_height="48dp"
- android:padding="8dp"
- android:layout_marginEnd="2dp"
android:gravity="center"
android:visibility="gone"
android:id="@+id/action2"
diff --git a/packages/SystemUI/res/layout/qs_footer_impl.xml b/packages/SystemUI/res/layout/qs_footer_impl.xml
index a02962e5e1e6..0c9ce3938420 100644
--- a/packages/SystemUI/res/layout/qs_footer_impl.xml
+++ b/packages/SystemUI/res/layout/qs_footer_impl.xml
@@ -130,9 +130,10 @@
</LinearLayout>
<View
android:id="@+id/qs_drag_handle_view"
- android:layout_width="24dp"
+ android:layout_width="48dp"
android:layout_height="4dp"
- android:layout_marginBottom="16dp"
+ android:layout_marginTop="8dp"
+ android:layout_marginBottom="8dp"
android:layout_gravity="center_horizontal|bottom"
android:background="@drawable/qs_footer_drag_handle" />
diff --git a/packages/SystemUI/res/layout/qs_media_panel.xml b/packages/SystemUI/res/layout/qs_media_panel.xml
index dd422766c153..22303dc1d8d2 100644
--- a/packages/SystemUI/res/layout/qs_media_panel.xml
+++ b/packages/SystemUI/res/layout/qs_media_panel.xml
@@ -23,50 +23,116 @@
android:layout_height="match_parent"
android:orientation="vertical"
android:gravity="center_horizontal|fill_vertical"
- android:padding="10dp"
+ android:padding="16dp"
+ android:background="@drawable/qs_media_background"
>
- <!-- placeholder for notification header -->
+ <!-- Header section -->
<LinearLayout
android:orientation="horizontal"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:id="@+id/header"
- android:padding="3dp"
- android:layout_marginEnd="-12dp"
+ android:layout_marginBottom="16dp"
+ >
+
+ <ImageView
+ android:id="@+id/album_art"
+ android:layout_width="@dimen/qs_media_album_size"
+ android:layout_height="@dimen/qs_media_album_size"
+ android:layout_marginRight="16dp"
+ android:layout_weight="0"
/>
- <!-- Top line: artist name -->
- <LinearLayout
- android:orientation="horizontal"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:gravity="center"
+ <LinearLayout
+ android:orientation="vertical"
+ android:layout_width="0dp"
+ android:layout_height="@dimen/qs_media_album_size"
+ android:layout_weight="1"
>
- <TextView
- android:id="@+id/header_title"
- android:layout_width="wrap_content"
+ <LinearLayout
+ android:orientation="horizontal"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:gravity="center"
+ >
+ <com.android.internal.widget.CachingIconView
+ android:id="@+id/icon"
+ android:layout_width="16dp"
+ android:layout_height="16dp"
+ android:layout_marginEnd="5dp"
+ />
+ <TextView
+ android:id="@+id/app_name"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:textSize="14sp"
+ android:singleLine="true"
+ />
+ </LinearLayout>
+
+ <!-- Song name -->
+ <TextView
+ android:id="@+id/header_text"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:singleLine="true"
+ android:fontFamily="@*android:string/config_headlineFontFamilyMedium"
+ android:textSize="18sp"
+ android:paddingBottom="6dp"
+ android:gravity="center"/>
+
+ <!-- Artist name -->
+ <TextView
+ android:id="@+id/header_artist"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:fontFamily="@*android:string/config_bodyFontFamily"
+ android:textSize="14sp"
+ android:singleLine="true"
+ />
+ </LinearLayout>
+
+ <!-- Output chip -->
+ <LinearLayout
+ android:layout_width="0dp"
android:layout_height="wrap_content"
- android:fontFamily="@*android:string/config_headlineFontFamilyMedium"
- android:singleLine="true"
- />
+ android:orientation="horizontal"
+ android:visibility="gone"
+ android:paddingTop="6dp"
+ android:paddingBottom="6dp"
+ android:paddingLeft="12dp"
+ android:paddingRight="12dp"
+ android:gravity="center"
+ android:id="@+id/media_seamless"
+ android:background="@*android:drawable/media_seamless_background"
+ android:layout_weight="1"
+ >
+ <ImageView
+ android:layout_width="@dimen/qs_seamless_icon_size"
+ android:layout_height="@dimen/qs_seamless_icon_size"
+ android:src="@*android:drawable/ic_media_seamless"
+ android:layout_marginRight="8dp"
+ android:id="@+id/media_seamless_image"
+ />
+ <TextView
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:fontFamily="@*android:string/config_bodyFontFamily"
+ android:text="@*android:string/ext_media_seamless_action"
+ android:textSize="14sp"
+ android:id="@+id/media_seamless_text"
+ android:singleLine="true"
+ />
+ </LinearLayout>
</LinearLayout>
- <!-- Second line: song name -->
- <TextView
- android:id="@+id/header_text"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:singleLine="true"
- android:fontFamily="@*android:string/config_bodyFontFamily"
- android:gravity="center"/>
-
- <!-- Bottom section: controls -->
+ <!-- Controls -->
<LinearLayout
android:id="@+id/media_actions"
android:orientation="horizontal"
android:layoutDirection="ltr"
- android:layout_width="wrap_content"
+ android:layout_width="match_parent"
android:layout_height="wrap_content"
android:gravity="center"
>
@@ -74,8 +140,8 @@
style="@android:style/Widget.Material.Button.Borderless.Small"
android:layout_width="48dp"
android:layout_height="48dp"
- android:padding="8dp"
- android:layout_marginEnd="2dp"
+ android:layout_marginStart="8dp"
+ android:layout_marginEnd="8dp"
android:gravity="center"
android:visibility="gone"
android:id="@+id/action0"
@@ -84,18 +150,18 @@
style="@android:style/Widget.Material.Button.Borderless.Small"
android:layout_width="48dp"
android:layout_height="48dp"
- android:padding="8dp"
- android:layout_marginEnd="2dp"
+ android:layout_marginStart="8dp"
+ android:layout_marginEnd="8dp"
android:gravity="center"
android:visibility="gone"
android:id="@+id/action1"
/>
<ImageButton
style="@android:style/Widget.Material.Button.Borderless.Small"
- android:layout_width="48dp"
- android:layout_height="48dp"
- android:padding="8dp"
- android:layout_marginEnd="2dp"
+ android:layout_width="52dp"
+ android:layout_height="52dp"
+ android:layout_marginStart="8dp"
+ android:layout_marginEnd="8dp"
android:gravity="center"
android:visibility="gone"
android:id="@+id/action2"
@@ -104,8 +170,8 @@
style="@android:style/Widget.Material.Button.Borderless.Small"
android:layout_width="48dp"
android:layout_height="48dp"
- android:padding="8dp"
- android:layout_marginEnd="2dp"
+ android:layout_marginStart="8dp"
+ android:layout_marginEnd="8dp"
android:gravity="center"
android:visibility="gone"
android:id="@+id/action3"
@@ -114,8 +180,8 @@
style="@android:style/Widget.Material.Button.Borderless.Small"
android:layout_width="48dp"
android:layout_height="48dp"
- android:padding="8dp"
- android:layout_marginEnd="2dp"
+ android:layout_marginStart="8dp"
+ android:layout_marginEnd="8dp"
android:gravity="center"
android:visibility="gone"
android:id="@+id/action4"
diff --git a/packages/SystemUI/res/values-af/strings.xml b/packages/SystemUI/res/values-af/strings.xml
index 823685e15807..478a93de1427 100644
--- a/packages/SystemUI/res/values-af/strings.xml
+++ b/packages/SystemUI/res/values-af/strings.xml
@@ -964,4 +964,5 @@
<string name="magnification_overlay_title" msgid="6584179429612427958">"Vergrotingoorleggervenster"</string>
<string name="magnification_window_title" msgid="4863914360847258333">"Vergrotingvenster"</string>
<string name="magnification_controls_title" msgid="8421106606708891519">"Vergrotingvensterkontroles"</string>
+ <string name="quick_controls_title" msgid="525285759614231333">"Vinnige kontroles"</string>
</resources>
diff --git a/packages/SystemUI/res/values-am/strings.xml b/packages/SystemUI/res/values-am/strings.xml
index c3248249ebb6..08a655a67faa 100644
--- a/packages/SystemUI/res/values-am/strings.xml
+++ b/packages/SystemUI/res/values-am/strings.xml
@@ -964,4 +964,5 @@
<string name="magnification_overlay_title" msgid="6584179429612427958">"የማጉያ ንብርብር መስኮት"</string>
<string name="magnification_window_title" msgid="4863914360847258333">"የማጉያ መስኮት"</string>
<string name="magnification_controls_title" msgid="8421106606708891519">"የማጉያ መስኮት መቆጣጠሪያዎች"</string>
+ <string name="quick_controls_title" msgid="525285759614231333">"ፈጣን መቆጣጠሪያዎች"</string>
</resources>
diff --git a/packages/SystemUI/res/values-ar/strings.xml b/packages/SystemUI/res/values-ar/strings.xml
index 113d705d7f31..6ceb6cdca3bc 100644
--- a/packages/SystemUI/res/values-ar/strings.xml
+++ b/packages/SystemUI/res/values-ar/strings.xml
@@ -984,4 +984,6 @@
<string name="magnification_overlay_title" msgid="6584179429612427958">"نافذة تراكب التكبير"</string>
<string name="magnification_window_title" msgid="4863914360847258333">"نافذة التكبير"</string>
<string name="magnification_controls_title" msgid="8421106606708891519">"عناصر التحكم في نافذة التكبير"</string>
+ <!-- no translation found for quick_controls_title (525285759614231333) -->
+ <skip />
</resources>
diff --git a/packages/SystemUI/res/values-as/strings.xml b/packages/SystemUI/res/values-as/strings.xml
index 975d639c2ea4..ffefb48438c5 100644
--- a/packages/SystemUI/res/values-as/strings.xml
+++ b/packages/SystemUI/res/values-as/strings.xml
@@ -964,4 +964,6 @@
<string name="magnification_overlay_title" msgid="6584179429612427958">"বিবৰ্ধন অ’ভাৰলে’ৰ ৱিণ্ড’"</string>
<string name="magnification_window_title" msgid="4863914360847258333">"বিবৰ্ধন ৱিণ্ড’"</string>
<string name="magnification_controls_title" msgid="8421106606708891519">"বিবৰ্ধন ৱিণ্ড’ৰ নিয়ন্ত্ৰণসমূহ"</string>
+ <!-- no translation found for quick_controls_title (525285759614231333) -->
+ <skip />
</resources>
diff --git a/packages/SystemUI/res/values-az/strings.xml b/packages/SystemUI/res/values-az/strings.xml
index d98d939da47a..2caffffc7ca1 100644
--- a/packages/SystemUI/res/values-az/strings.xml
+++ b/packages/SystemUI/res/values-az/strings.xml
@@ -964,4 +964,6 @@
<string name="magnification_overlay_title" msgid="6584179429612427958">"Böyütmə Üst-üstə Düşən Pəncərəsi"</string>
<string name="magnification_window_title" msgid="4863914360847258333">"Böyütmə Pəncərəsi"</string>
<string name="magnification_controls_title" msgid="8421106606708891519">"Böyütmə Pəncərəsi Kontrolları"</string>
+ <!-- no translation found for quick_controls_title (525285759614231333) -->
+ <skip />
</resources>
diff --git a/packages/SystemUI/res/values-b+sr+Latn/strings.xml b/packages/SystemUI/res/values-b+sr+Latn/strings.xml
index ff03bb9726ae..aaa35ffd7058 100644
--- a/packages/SystemUI/res/values-b+sr+Latn/strings.xml
+++ b/packages/SystemUI/res/values-b+sr+Latn/strings.xml
@@ -969,4 +969,5 @@
<string name="magnification_overlay_title" msgid="6584179429612427958">"Preklopni prozor za uvećanje"</string>
<string name="magnification_window_title" msgid="4863914360847258333">"Prozor za uvećanje"</string>
<string name="magnification_controls_title" msgid="8421106606708891519">"Kontrole prozora za uvećanje"</string>
+ <string name="quick_controls_title" msgid="525285759614231333">"Brze kontrole"</string>
</resources>
diff --git a/packages/SystemUI/res/values-be/strings.xml b/packages/SystemUI/res/values-be/strings.xml
index 1ebbb5ea100c..619fd1c79a26 100644
--- a/packages/SystemUI/res/values-be/strings.xml
+++ b/packages/SystemUI/res/values-be/strings.xml
@@ -976,4 +976,6 @@
<string name="magnification_overlay_title" msgid="6584179429612427958">"Акно-накладка з павелічэннем"</string>
<string name="magnification_window_title" msgid="4863914360847258333">"Акно павелічэння"</string>
<string name="magnification_controls_title" msgid="8421106606708891519">"Налады акна павелічэння"</string>
+ <!-- no translation found for quick_controls_title (525285759614231333) -->
+ <skip />
</resources>
diff --git a/packages/SystemUI/res/values-bg/strings.xml b/packages/SystemUI/res/values-bg/strings.xml
index 5cfea60f901f..99c1fecef526 100644
--- a/packages/SystemUI/res/values-bg/strings.xml
+++ b/packages/SystemUI/res/values-bg/strings.xml
@@ -964,4 +964,5 @@
<string name="magnification_overlay_title" msgid="6584179429612427958">"Прозорец с наслагване за ниво на мащаба"</string>
<string name="magnification_window_title" msgid="4863914360847258333">"Прозорец за ниво на мащаба"</string>
<string name="magnification_controls_title" msgid="8421106606708891519">"Контроли за прозореца за ниво на мащаба"</string>
+ <string name="quick_controls_title" msgid="525285759614231333">"Бързи контроли"</string>
</resources>
diff --git a/packages/SystemUI/res/values-bn/strings.xml b/packages/SystemUI/res/values-bn/strings.xml
index f1e49ce57ce8..1327cf40fa55 100644
--- a/packages/SystemUI/res/values-bn/strings.xml
+++ b/packages/SystemUI/res/values-bn/strings.xml
@@ -964,4 +964,6 @@
<string name="magnification_overlay_title" msgid="6584179429612427958">"ওভারলে উইন্ডো বড় করে দেখা"</string>
<string name="magnification_window_title" msgid="4863914360847258333">"উইন্ডো বড় করে দেখা"</string>
<string name="magnification_controls_title" msgid="8421106606708891519">"উইন্ডো কন্ট্রোল বড় করে দেখা"</string>
+ <!-- no translation found for quick_controls_title (525285759614231333) -->
+ <skip />
</resources>
diff --git a/packages/SystemUI/res/values-bs/strings.xml b/packages/SystemUI/res/values-bs/strings.xml
index cabe87f4a0b0..7bce70a92485 100644
--- a/packages/SystemUI/res/values-bs/strings.xml
+++ b/packages/SystemUI/res/values-bs/strings.xml
@@ -971,4 +971,5 @@
<string name="magnification_overlay_title" msgid="6584179429612427958">"Preklopni prozor za uvećavanje"</string>
<string name="magnification_window_title" msgid="4863914360847258333">"Prozor za uvećavanje"</string>
<string name="magnification_controls_title" msgid="8421106606708891519">"Kontrole prozora za uvećavanje"</string>
+ <string name="quick_controls_title" msgid="525285759614231333">"Brze kontrole"</string>
</resources>
diff --git a/packages/SystemUI/res/values-ca/strings.xml b/packages/SystemUI/res/values-ca/strings.xml
index 3e257952050a..5597f51066e2 100644
--- a/packages/SystemUI/res/values-ca/strings.xml
+++ b/packages/SystemUI/res/values-ca/strings.xml
@@ -964,4 +964,5 @@
<string name="magnification_overlay_title" msgid="6584179429612427958">"Finestra superposada d\'ampliació"</string>
<string name="magnification_window_title" msgid="4863914360847258333">"Finestra d\'ampliació"</string>
<string name="magnification_controls_title" msgid="8421106606708891519">"Finestra de controls d\'ampliació"</string>
+ <string name="quick_controls_title" msgid="525285759614231333">"Controls ràpids"</string>
</resources>
diff --git a/packages/SystemUI/res/values-cs/strings.xml b/packages/SystemUI/res/values-cs/strings.xml
index 3c85880eaaf8..a608c5b77735 100644
--- a/packages/SystemUI/res/values-cs/strings.xml
+++ b/packages/SystemUI/res/values-cs/strings.xml
@@ -974,4 +974,5 @@
<string name="magnification_overlay_title" msgid="6584179429612427958">"Překryvné zvětšovací okno"</string>
<string name="magnification_window_title" msgid="4863914360847258333">"Zvětšovací okno"</string>
<string name="magnification_controls_title" msgid="8421106606708891519">"Ovládací prvky zvětšovacího okna"</string>
+ <string name="quick_controls_title" msgid="525285759614231333">"Rychlé ovládací prvky"</string>
</resources>
diff --git a/packages/SystemUI/res/values-da/strings.xml b/packages/SystemUI/res/values-da/strings.xml
index f2ffdaac0e37..9fb778354bda 100644
--- a/packages/SystemUI/res/values-da/strings.xml
+++ b/packages/SystemUI/res/values-da/strings.xml
@@ -964,4 +964,5 @@
<string name="magnification_overlay_title" msgid="6584179429612427958">"Vindue med overlejret forstørrelse"</string>
<string name="magnification_window_title" msgid="4863914360847258333">"Vindue med forstørrelse"</string>
<string name="magnification_controls_title" msgid="8421106606708891519">"Vindue med forstørrelsesstyring"</string>
+ <string name="quick_controls_title" msgid="525285759614231333">"Hurtig betjening"</string>
</resources>
diff --git a/packages/SystemUI/res/values-de/strings.xml b/packages/SystemUI/res/values-de/strings.xml
index 58caef2b46bb..9d83e9cf55e7 100644
--- a/packages/SystemUI/res/values-de/strings.xml
+++ b/packages/SystemUI/res/values-de/strings.xml
@@ -968,4 +968,6 @@
<string name="magnification_overlay_title" msgid="6584179429612427958">"Overlay-Vergrößerungsfenster"</string>
<string name="magnification_window_title" msgid="4863914360847258333">"Vergrößerungsfenster"</string>
<string name="magnification_controls_title" msgid="8421106606708891519">"Einstellungen für Vergrößerungsfenster"</string>
+ <!-- no translation found for quick_controls_title (525285759614231333) -->
+ <skip />
</resources>
diff --git a/packages/SystemUI/res/values-el/strings.xml b/packages/SystemUI/res/values-el/strings.xml
index d3686c834882..b94d10c2a741 100644
--- a/packages/SystemUI/res/values-el/strings.xml
+++ b/packages/SystemUI/res/values-el/strings.xml
@@ -964,4 +964,5 @@
<string name="magnification_overlay_title" msgid="6584179429612427958">"Παράθυρο επικάλυψης μεγέθυνσης"</string>
<string name="magnification_window_title" msgid="4863914360847258333">"Παράθυρο μεγέθυνσης"</string>
<string name="magnification_controls_title" msgid="8421106606708891519">"Στοιχεία ελέγχου παραθύρου μεγέθυνσης"</string>
+ <string name="quick_controls_title" msgid="525285759614231333">"Στοιχεία γρήγορου ελέγχου"</string>
</resources>
diff --git a/packages/SystemUI/res/values-en-rAU/strings.xml b/packages/SystemUI/res/values-en-rAU/strings.xml
index 4f61daa2bf18..df2496951be7 100644
--- a/packages/SystemUI/res/values-en-rAU/strings.xml
+++ b/packages/SystemUI/res/values-en-rAU/strings.xml
@@ -964,4 +964,5 @@
<string name="magnification_overlay_title" msgid="6584179429612427958">"Magnification overlay window"</string>
<string name="magnification_window_title" msgid="4863914360847258333">"Magnification window"</string>
<string name="magnification_controls_title" msgid="8421106606708891519">"Magnification window controls"</string>
+ <string name="quick_controls_title" msgid="525285759614231333">"Quick controls"</string>
</resources>
diff --git a/packages/SystemUI/res/values-en-rCA/strings.xml b/packages/SystemUI/res/values-en-rCA/strings.xml
index 34a2f1975d75..2d087c92b2d6 100644
--- a/packages/SystemUI/res/values-en-rCA/strings.xml
+++ b/packages/SystemUI/res/values-en-rCA/strings.xml
@@ -964,4 +964,5 @@
<string name="magnification_overlay_title" msgid="6584179429612427958">"Magnification overlay window"</string>
<string name="magnification_window_title" msgid="4863914360847258333">"Magnification window"</string>
<string name="magnification_controls_title" msgid="8421106606708891519">"Magnification window controls"</string>
+ <string name="quick_controls_title" msgid="525285759614231333">"Quick controls"</string>
</resources>
diff --git a/packages/SystemUI/res/values-en-rGB/strings.xml b/packages/SystemUI/res/values-en-rGB/strings.xml
index 4f61daa2bf18..df2496951be7 100644
--- a/packages/SystemUI/res/values-en-rGB/strings.xml
+++ b/packages/SystemUI/res/values-en-rGB/strings.xml
@@ -964,4 +964,5 @@
<string name="magnification_overlay_title" msgid="6584179429612427958">"Magnification overlay window"</string>
<string name="magnification_window_title" msgid="4863914360847258333">"Magnification window"</string>
<string name="magnification_controls_title" msgid="8421106606708891519">"Magnification window controls"</string>
+ <string name="quick_controls_title" msgid="525285759614231333">"Quick controls"</string>
</resources>
diff --git a/packages/SystemUI/res/values-en-rIN/strings.xml b/packages/SystemUI/res/values-en-rIN/strings.xml
index 4f61daa2bf18..df2496951be7 100644
--- a/packages/SystemUI/res/values-en-rIN/strings.xml
+++ b/packages/SystemUI/res/values-en-rIN/strings.xml
@@ -964,4 +964,5 @@
<string name="magnification_overlay_title" msgid="6584179429612427958">"Magnification overlay window"</string>
<string name="magnification_window_title" msgid="4863914360847258333">"Magnification window"</string>
<string name="magnification_controls_title" msgid="8421106606708891519">"Magnification window controls"</string>
+ <string name="quick_controls_title" msgid="525285759614231333">"Quick controls"</string>
</resources>
diff --git a/packages/SystemUI/res/values-en-rXC/strings.xml b/packages/SystemUI/res/values-en-rXC/strings.xml
index 2bb51311aa0a..9d4e1622ede4 100644
--- a/packages/SystemUI/res/values-en-rXC/strings.xml
+++ b/packages/SystemUI/res/values-en-rXC/strings.xml
@@ -964,4 +964,5 @@
<string name="magnification_overlay_title" msgid="6584179429612427958">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‎‏‏‎‏‏‎‏‎‏‏‏‏‏‏‎‏‏‎‏‎‎‎‏‎‎‏‏‏‎‏‎‎‎‎‏‏‏‎‏‏‏‏‎‏‏‏‎‎‏‏‎‏‎‏‎‏‏‎‏‏‎‎Magnification Overlay Window‎‏‎‎‏‎"</string>
<string name="magnification_window_title" msgid="4863914360847258333">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‎‎‎‎‏‏‏‎‎‎‎‎‎‎‎‎‎‏‏‎‎‎‎‏‎‏‎‏‏‏‎‏‎‏‎‎‎‎‎‎‏‏‏‎‎‎‏‏‎‏‎‎‏‎‏‏‎‏‏‏‎‏‎Magnification Window‎‏‎‎‏‎"</string>
<string name="magnification_controls_title" msgid="8421106606708891519">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‏‏‎‏‎‎‏‏‎‏‏‏‎‏‏‏‎‎‎‏‏‏‏‏‎‏‎‏‏‏‏‎‎‎‎‏‎‎‏‎‎‏‎‏‎‎‎‎‎‏‏‏‏‏‎‏‏‏‏‏‏‏‎Magnification Window Controls‎‏‎‎‏‎"</string>
+ <string name="quick_controls_title" msgid="525285759614231333">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‎‏‏‏‏‏‎‏‎‎‏‎‏‎‎‎‏‏‎‎‎‎‏‎‎‏‏‏‎‏‏‎‎‎‎‎‏‏‎‏‏‎‎‎‎‎‏‎‏‎‎‎‏‏‎‎‏‎‎‏‎‏‎Quick Controls‎‏‎‎‏‎"</string>
</resources>
diff --git a/packages/SystemUI/res/values-es-rUS/strings.xml b/packages/SystemUI/res/values-es-rUS/strings.xml
index e2bf6efa538e..33689a1ac7d2 100644
--- a/packages/SystemUI/res/values-es-rUS/strings.xml
+++ b/packages/SystemUI/res/values-es-rUS/strings.xml
@@ -964,4 +964,5 @@
<string name="magnification_overlay_title" msgid="6584179429612427958">"Ventana superpuesta de ampliación"</string>
<string name="magnification_window_title" msgid="4863914360847258333">"Ventana de ampliación"</string>
<string name="magnification_controls_title" msgid="8421106606708891519">"Controles de ampliación de la ventana"</string>
+ <string name="quick_controls_title" msgid="525285759614231333">"Controles rápidos"</string>
</resources>
diff --git a/packages/SystemUI/res/values-es/strings.xml b/packages/SystemUI/res/values-es/strings.xml
index 40d7fe3bdf25..458df31e2231 100644
--- a/packages/SystemUI/res/values-es/strings.xml
+++ b/packages/SystemUI/res/values-es/strings.xml
@@ -964,4 +964,5 @@
<string name="magnification_overlay_title" msgid="6584179429612427958">"Ventana de superposición de ampliación"</string>
<string name="magnification_window_title" msgid="4863914360847258333">"Ventana de ampliación"</string>
<string name="magnification_controls_title" msgid="8421106606708891519">"Ventana de controles de ampliación"</string>
+ <string name="quick_controls_title" msgid="525285759614231333">"Controles rápidos"</string>
</resources>
diff --git a/packages/SystemUI/res/values-et/strings.xml b/packages/SystemUI/res/values-et/strings.xml
index d8f546ce48d4..4729a0d6787a 100644
--- a/packages/SystemUI/res/values-et/strings.xml
+++ b/packages/SystemUI/res/values-et/strings.xml
@@ -964,4 +964,6 @@
<string name="magnification_overlay_title" msgid="6584179429612427958">"Suurendamisakna ülekate"</string>
<string name="magnification_window_title" msgid="4863914360847258333">"Suurendamisaken"</string>
<string name="magnification_controls_title" msgid="8421106606708891519">"Suurendamisakna juhtelemendid"</string>
+ <!-- no translation found for quick_controls_title (525285759614231333) -->
+ <skip />
</resources>
diff --git a/packages/SystemUI/res/values-eu/strings.xml b/packages/SystemUI/res/values-eu/strings.xml
index 9dceb5fe3f05..470a410aa88d 100644
--- a/packages/SystemUI/res/values-eu/strings.xml
+++ b/packages/SystemUI/res/values-eu/strings.xml
@@ -964,4 +964,5 @@
<string name="magnification_overlay_title" msgid="6584179429612427958">"Lupa-leiho gainjarria"</string>
<string name="magnification_window_title" msgid="4863914360847258333">"Lupa-leihoa"</string>
<string name="magnification_controls_title" msgid="8421106606708891519">"Lupa-leihoaren aukerak"</string>
+ <string name="quick_controls_title" msgid="525285759614231333">"Kontrol bizkorrak"</string>
</resources>
diff --git a/packages/SystemUI/res/values-fa/strings.xml b/packages/SystemUI/res/values-fa/strings.xml
index d27b420c0e9c..ae85a260eaf3 100644
--- a/packages/SystemUI/res/values-fa/strings.xml
+++ b/packages/SystemUI/res/values-fa/strings.xml
@@ -964,4 +964,6 @@
<string name="magnification_overlay_title" msgid="6584179429612427958">"پنجره همپوشانی بزرگ‌نمایی"</string>
<string name="magnification_window_title" msgid="4863914360847258333">"پنجره بزرگ‌نمایی"</string>
<string name="magnification_controls_title" msgid="8421106606708891519">"کنترل‌های پنجره بزرگ‌نمایی"</string>
+ <!-- no translation found for quick_controls_title (525285759614231333) -->
+ <skip />
</resources>
diff --git a/packages/SystemUI/res/values-fi/strings.xml b/packages/SystemUI/res/values-fi/strings.xml
index cf01efd933d0..da22e1c502e5 100644
--- a/packages/SystemUI/res/values-fi/strings.xml
+++ b/packages/SystemUI/res/values-fi/strings.xml
@@ -964,4 +964,5 @@
<string name="magnification_overlay_title" msgid="6584179429612427958">"Suurennuksen peittoikkuna"</string>
<string name="magnification_window_title" msgid="4863914360847258333">"Suurennusikkuna"</string>
<string name="magnification_controls_title" msgid="8421106606708891519">"Suurennusikkunan ohjaimet"</string>
+ <string name="quick_controls_title" msgid="525285759614231333">"Pikasäätimet"</string>
</resources>
diff --git a/packages/SystemUI/res/values-fr-rCA/strings.xml b/packages/SystemUI/res/values-fr-rCA/strings.xml
index 5f7a358239c7..d5d3f56286cb 100644
--- a/packages/SystemUI/res/values-fr-rCA/strings.xml
+++ b/packages/SystemUI/res/values-fr-rCA/strings.xml
@@ -964,4 +964,6 @@
<string name="magnification_overlay_title" msgid="6584179429612427958">"Fenêtre d\'agrandissement superposée"</string>
<string name="magnification_window_title" msgid="4863914360847258333">"Fenêtre d\'agrandissement"</string>
<string name="magnification_controls_title" msgid="8421106606708891519">"Commandes pour la fenêtre d\'agrandissement"</string>
+ <!-- no translation found for quick_controls_title (525285759614231333) -->
+ <skip />
</resources>
diff --git a/packages/SystemUI/res/values-fr/strings.xml b/packages/SystemUI/res/values-fr/strings.xml
index 3c12f668d2b9..5c5d353e4103 100644
--- a/packages/SystemUI/res/values-fr/strings.xml
+++ b/packages/SystemUI/res/values-fr/strings.xml
@@ -964,4 +964,6 @@
<string name="magnification_overlay_title" msgid="6584179429612427958">"Fenêtre de superposition de l\'agrandissement"</string>
<string name="magnification_window_title" msgid="4863914360847258333">"Fenêtre d\'agrandissement"</string>
<string name="magnification_controls_title" msgid="8421106606708891519">"Fenêtre des commandes d\'agrandissement"</string>
+ <!-- no translation found for quick_controls_title (525285759614231333) -->
+ <skip />
</resources>
diff --git a/packages/SystemUI/res/values-gl/strings.xml b/packages/SystemUI/res/values-gl/strings.xml
index 0c444e48aee3..2c0eef659a60 100644
--- a/packages/SystemUI/res/values-gl/strings.xml
+++ b/packages/SystemUI/res/values-gl/strings.xml
@@ -964,4 +964,6 @@
<string name="magnification_overlay_title" msgid="6584179429612427958">"Ampliación da ventá de superposición"</string>
<string name="magnification_window_title" msgid="4863914360847258333">"Ventá de superposición"</string>
<string name="magnification_controls_title" msgid="8421106606708891519">"Controis de ampliación da ventá"</string>
+ <!-- no translation found for quick_controls_title (525285759614231333) -->
+ <skip />
</resources>
diff --git a/packages/SystemUI/res/values-gu/strings.xml b/packages/SystemUI/res/values-gu/strings.xml
index 9dde523d9a89..df4254ca4e0f 100644
--- a/packages/SystemUI/res/values-gu/strings.xml
+++ b/packages/SystemUI/res/values-gu/strings.xml
@@ -964,4 +964,6 @@
<string name="magnification_overlay_title" msgid="6584179429612427958">"વિસ્તૃતીકરણ ઓવરલે વિંડો"</string>
<string name="magnification_window_title" msgid="4863914360847258333">"વિસ્તૃતીકરણ વિંડો"</string>
<string name="magnification_controls_title" msgid="8421106606708891519">"વિસ્તૃતીકરણ વિંડોના નિયંત્રણો"</string>
+ <!-- no translation found for quick_controls_title (525285759614231333) -->
+ <skip />
</resources>
diff --git a/packages/SystemUI/res/values-hi/strings.xml b/packages/SystemUI/res/values-hi/strings.xml
index 5522605cd0f4..b62a45eb7747 100644
--- a/packages/SystemUI/res/values-hi/strings.xml
+++ b/packages/SystemUI/res/values-hi/strings.xml
@@ -390,8 +390,8 @@
<string name="quick_settings_dark_mode_secondary_label_battery_saver" msgid="4990712734503013251">"बैटरी सेवर"</string>
<string name="quick_settings_dark_mode_secondary_label_on_at_sunset" msgid="6017379738102015710">"शाम को चालू होगा"</string>
<string name="quick_settings_dark_mode_secondary_label_until_sunrise" msgid="4404885070316716472">"सुबह तक चालू रहेगी"</string>
- <string name="quick_settings_dark_mode_secondary_label_on_at" msgid="5128758823486361279">"<xliff:g id="TIME">%s</xliff:g> बजे चालू हाेगी"</string>
- <string name="quick_settings_dark_mode_secondary_label_until" msgid="2289774641256492437">"<xliff:g id="TIME">%s</xliff:g> बजे तक चालू रहेगी"</string>
+ <string name="quick_settings_dark_mode_secondary_label_on_at" msgid="5128758823486361279">"<xliff:g id="TIME">%s</xliff:g> पर चालू हाेगी"</string>
+ <string name="quick_settings_dark_mode_secondary_label_until" msgid="2289774641256492437">"<xliff:g id="TIME">%s</xliff:g> तक चालू रहेगी"</string>
<string name="quick_settings_nfc_label" msgid="1054317416221168085">"एनएफ़सी"</string>
<string name="quick_settings_nfc_off" msgid="3465000058515424663">"NFC बंद है"</string>
<string name="quick_settings_nfc_on" msgid="1004976611203202230">"NFC चालू है"</string>
@@ -964,4 +964,6 @@
<string name="magnification_overlay_title" msgid="6584179429612427958">"Magnification Overlay Window"</string>
<string name="magnification_window_title" msgid="4863914360847258333">"स्क्रीन को बड़ा करके दिखाने वाली विंडो"</string>
<string name="magnification_controls_title" msgid="8421106606708891519">"स्क्रीन को बड़ा करके दिखाने वाली विंडो के नियंत्रण"</string>
+ <!-- no translation found for quick_controls_title (525285759614231333) -->
+ <skip />
</resources>
diff --git a/packages/SystemUI/res/values-hr/strings.xml b/packages/SystemUI/res/values-hr/strings.xml
index 55a0e788581a..5eefc79a35f2 100644
--- a/packages/SystemUI/res/values-hr/strings.xml
+++ b/packages/SystemUI/res/values-hr/strings.xml
@@ -969,4 +969,5 @@
<string name="magnification_overlay_title" msgid="6584179429612427958">"Prozor preklapanja povećavanja"</string>
<string name="magnification_window_title" msgid="4863914360847258333">"Prozor za povećavanje"</string>
<string name="magnification_controls_title" msgid="8421106606708891519">"Kontrole prozora za povećavanje"</string>
+ <string name="quick_controls_title" msgid="525285759614231333">"Brze kontrole"</string>
</resources>
diff --git a/packages/SystemUI/res/values-hu/strings.xml b/packages/SystemUI/res/values-hu/strings.xml
index a9b2d036ebcc..5ce235dccce0 100644
--- a/packages/SystemUI/res/values-hu/strings.xml
+++ b/packages/SystemUI/res/values-hu/strings.xml
@@ -964,4 +964,5 @@
<string name="magnification_overlay_title" msgid="6584179429612427958">"Nagyítási fedvény ablaka"</string>
<string name="magnification_window_title" msgid="4863914360847258333">"Nagyítás ablaka"</string>
<string name="magnification_controls_title" msgid="8421106606708891519">"Nagyítási vezérlők ablaka"</string>
+ <string name="quick_controls_title" msgid="525285759614231333">"Gyorsvezérlők"</string>
</resources>
diff --git a/packages/SystemUI/res/values-hy/strings.xml b/packages/SystemUI/res/values-hy/strings.xml
index 78acb2ed164e..4fa7096217be 100644
--- a/packages/SystemUI/res/values-hy/strings.xml
+++ b/packages/SystemUI/res/values-hy/strings.xml
@@ -964,4 +964,5 @@
<string name="magnification_overlay_title" msgid="6584179429612427958">"Խոշորացման պատուհանի վրադրում"</string>
<string name="magnification_window_title" msgid="4863914360847258333">"Խոշորացման պատուհան"</string>
<string name="magnification_controls_title" msgid="8421106606708891519">"Խոշորացման պատուհանի կառավարման տարրեր"</string>
+ <string name="quick_controls_title" msgid="525285759614231333">"Արագ կառավարման տարրեր"</string>
</resources>
diff --git a/packages/SystemUI/res/values-in/strings.xml b/packages/SystemUI/res/values-in/strings.xml
index 95c16c87dc1a..b3db0f3fd30b 100644
--- a/packages/SystemUI/res/values-in/strings.xml
+++ b/packages/SystemUI/res/values-in/strings.xml
@@ -964,4 +964,5 @@
<string name="magnification_overlay_title" msgid="6584179429612427958">"Jendela Overlay Pembesaran"</string>
<string name="magnification_window_title" msgid="4863914360847258333">"Jendela Pembesaran"</string>
<string name="magnification_controls_title" msgid="8421106606708891519">"Kontrol Jendela Pembesaran"</string>
+ <string name="quick_controls_title" msgid="525285759614231333">"Kontrol Cepat"</string>
</resources>
diff --git a/packages/SystemUI/res/values-is/strings.xml b/packages/SystemUI/res/values-is/strings.xml
index 985f62d3e936..56e67ef3e0ff 100644
--- a/packages/SystemUI/res/values-is/strings.xml
+++ b/packages/SystemUI/res/values-is/strings.xml
@@ -964,4 +964,5 @@
<string name="magnification_overlay_title" msgid="6584179429612427958">"Stækkun yfirglugga"</string>
<string name="magnification_window_title" msgid="4863914360847258333">"Stækkunargluggi"</string>
<string name="magnification_controls_title" msgid="8421106606708891519">"Stækkunarstillingar glugga"</string>
+ <string name="quick_controls_title" msgid="525285759614231333">"Flýtistýringar"</string>
</resources>
diff --git a/packages/SystemUI/res/values-it/strings.xml b/packages/SystemUI/res/values-it/strings.xml
index 870e6b7a75e7..423067713aa8 100644
--- a/packages/SystemUI/res/values-it/strings.xml
+++ b/packages/SystemUI/res/values-it/strings.xml
@@ -964,4 +964,5 @@
<string name="magnification_overlay_title" msgid="6584179429612427958">"Finestra overlay ingrandimento"</string>
<string name="magnification_window_title" msgid="4863914360847258333">"Finestra ingrandimento"</string>
<string name="magnification_controls_title" msgid="8421106606708891519">"Finestra controlli di ingrandimento"</string>
+ <string name="quick_controls_title" msgid="525285759614231333">"Controlli rapidi"</string>
</resources>
diff --git a/packages/SystemUI/res/values-iw/strings.xml b/packages/SystemUI/res/values-iw/strings.xml
index 775be02e20e9..b20b10f227ba 100644
--- a/packages/SystemUI/res/values-iw/strings.xml
+++ b/packages/SystemUI/res/values-iw/strings.xml
@@ -974,4 +974,6 @@
<string name="magnification_overlay_title" msgid="6584179429612427958">"חלון ליצירת שכבת-על להגדלה"</string>
<string name="magnification_window_title" msgid="4863914360847258333">"חלון הגדלה"</string>
<string name="magnification_controls_title" msgid="8421106606708891519">"בקרות של חלון ההגדלה"</string>
+ <!-- no translation found for quick_controls_title (525285759614231333) -->
+ <skip />
</resources>
diff --git a/packages/SystemUI/res/values-ja/strings.xml b/packages/SystemUI/res/values-ja/strings.xml
index 7af3f8631f03..b5189732a11b 100644
--- a/packages/SystemUI/res/values-ja/strings.xml
+++ b/packages/SystemUI/res/values-ja/strings.xml
@@ -964,4 +964,5 @@
<string name="magnification_overlay_title" msgid="6584179429612427958">"拡大オーバーレイ ウィンドウ"</string>
<string name="magnification_window_title" msgid="4863914360847258333">"拡大ウィンドウ"</string>
<string name="magnification_controls_title" msgid="8421106606708891519">"拡大ウィンドウ コントロール"</string>
+ <string name="quick_controls_title" msgid="525285759614231333">"クイック コントロール"</string>
</resources>
diff --git a/packages/SystemUI/res/values-ka/strings.xml b/packages/SystemUI/res/values-ka/strings.xml
index 2433f9e32e8f..ab0dba829107 100644
--- a/packages/SystemUI/res/values-ka/strings.xml
+++ b/packages/SystemUI/res/values-ka/strings.xml
@@ -964,4 +964,6 @@
<string name="magnification_overlay_title" msgid="6584179429612427958">"გადიდების გადაფარვის ფანჯარა"</string>
<string name="magnification_window_title" msgid="4863914360847258333">"გადიდების ფანჯარა"</string>
<string name="magnification_controls_title" msgid="8421106606708891519">"გადიდების კონტროლის ფანჯარა"</string>
+ <!-- no translation found for quick_controls_title (525285759614231333) -->
+ <skip />
</resources>
diff --git a/packages/SystemUI/res/values-kk/strings.xml b/packages/SystemUI/res/values-kk/strings.xml
index be93c18006b9..3c44cabb2f2f 100644
--- a/packages/SystemUI/res/values-kk/strings.xml
+++ b/packages/SystemUI/res/values-kk/strings.xml
@@ -964,4 +964,6 @@
<string name="magnification_overlay_title" msgid="6584179429612427958">"Ұлғайту терезесін қабаттастыру"</string>
<string name="magnification_window_title" msgid="4863914360847258333">"Ұлғайту терезесі"</string>
<string name="magnification_controls_title" msgid="8421106606708891519">"Ұлғайту терезесінің басқару элементтері"</string>
+ <!-- no translation found for quick_controls_title (525285759614231333) -->
+ <skip />
</resources>
diff --git a/packages/SystemUI/res/values-km/strings.xml b/packages/SystemUI/res/values-km/strings.xml
index 413125cefb92..480b69490252 100644
--- a/packages/SystemUI/res/values-km/strings.xml
+++ b/packages/SystemUI/res/values-km/strings.xml
@@ -964,4 +964,5 @@
<string name="magnification_overlay_title" msgid="6584179429612427958">"វិនដូ​ត្រួតគ្នា​លើ​ការពង្រីក"</string>
<string name="magnification_window_title" msgid="4863914360847258333">"វិនដូ​ការពង្រីក"</string>
<string name="magnification_controls_title" msgid="8421106606708891519">"វិនដូគ្រប់គ្រង​​ការពង្រីក"</string>
+ <string name="quick_controls_title" msgid="525285759614231333">"ការគ្រប់គ្រង​រហ័ស"</string>
</resources>
diff --git a/packages/SystemUI/res/values-kn/strings.xml b/packages/SystemUI/res/values-kn/strings.xml
index 0b98e0655a7e..c65e653f0f51 100644
--- a/packages/SystemUI/res/values-kn/strings.xml
+++ b/packages/SystemUI/res/values-kn/strings.xml
@@ -964,4 +964,6 @@
<string name="magnification_overlay_title" msgid="6584179429612427958">"ವರ್ಧನೆಯ ಓವರ್‌ಲೇ ವಿಂಡೋ"</string>
<string name="magnification_window_title" msgid="4863914360847258333">"ವರ್ಧನೆಯ ವಿಂಡೋ"</string>
<string name="magnification_controls_title" msgid="8421106606708891519">"ವರ್ಧನೆಯ ವಿಂಡೋ ನಿಯಂತ್ರಣಗಳು"</string>
+ <!-- no translation found for quick_controls_title (525285759614231333) -->
+ <skip />
</resources>
diff --git a/packages/SystemUI/res/values-ko/strings.xml b/packages/SystemUI/res/values-ko/strings.xml
index 54b055dd674b..fb5818c710dc 100644
--- a/packages/SystemUI/res/values-ko/strings.xml
+++ b/packages/SystemUI/res/values-ko/strings.xml
@@ -964,4 +964,6 @@
<string name="magnification_overlay_title" msgid="6584179429612427958">"확대 오버레이 창"</string>
<string name="magnification_window_title" msgid="4863914360847258333">"확대 창"</string>
<string name="magnification_controls_title" msgid="8421106606708891519">"확대 창 컨트롤"</string>
+ <!-- no translation found for quick_controls_title (525285759614231333) -->
+ <skip />
</resources>
diff --git a/packages/SystemUI/res/values-ky/strings.xml b/packages/SystemUI/res/values-ky/strings.xml
index ed6b9186cdba..53d780ea488f 100644
--- a/packages/SystemUI/res/values-ky/strings.xml
+++ b/packages/SystemUI/res/values-ky/strings.xml
@@ -964,4 +964,6 @@
<string name="magnification_overlay_title" msgid="6584179429612427958">"Чоңойтуу терезесин үстүнө коюу"</string>
<string name="magnification_window_title" msgid="4863914360847258333">"Чоңойтуу терезеси"</string>
<string name="magnification_controls_title" msgid="8421106606708891519">"Чоңойтуу терезесин башкаруу каражаттары"</string>
+ <!-- no translation found for quick_controls_title (525285759614231333) -->
+ <skip />
</resources>
diff --git a/packages/SystemUI/res/values-lo/strings.xml b/packages/SystemUI/res/values-lo/strings.xml
index 11c4f7c45433..48166f394289 100644
--- a/packages/SystemUI/res/values-lo/strings.xml
+++ b/packages/SystemUI/res/values-lo/strings.xml
@@ -964,4 +964,5 @@
<string name="magnification_overlay_title" msgid="6584179429612427958">"ໜ້າຈໍວາງທັບການຂະຫຍາຍ"</string>
<string name="magnification_window_title" msgid="4863914360847258333">"ໜ້າຈໍການຂະຫຍາຍ"</string>
<string name="magnification_controls_title" msgid="8421106606708891519">"ການຄວບຄຸມໜ້າຈໍການຂະຫຍາຍ"</string>
+ <string name="quick_controls_title" msgid="525285759614231333">"ການຄວບຄຸມດ່ວນ"</string>
</resources>
diff --git a/packages/SystemUI/res/values-lt/strings.xml b/packages/SystemUI/res/values-lt/strings.xml
index 7da1174c0878..70bcf372d895 100644
--- a/packages/SystemUI/res/values-lt/strings.xml
+++ b/packages/SystemUI/res/values-lt/strings.xml
@@ -974,4 +974,5 @@
<string name="magnification_overlay_title" msgid="6584179429612427958">"Didinimo perdangos langas"</string>
<string name="magnification_window_title" msgid="4863914360847258333">"Didinimo langas"</string>
<string name="magnification_controls_title" msgid="8421106606708891519">"Didinimo lango valdikliai"</string>
+ <string name="quick_controls_title" msgid="525285759614231333">"Spartieji valdikliai"</string>
</resources>
diff --git a/packages/SystemUI/res/values-lv/strings.xml b/packages/SystemUI/res/values-lv/strings.xml
index fed86b324c78..6c28b103748f 100644
--- a/packages/SystemUI/res/values-lv/strings.xml
+++ b/packages/SystemUI/res/values-lv/strings.xml
@@ -969,4 +969,6 @@
<string name="magnification_overlay_title" msgid="6584179429612427958">"Palielināšanas pārklājuma logs"</string>
<string name="magnification_window_title" msgid="4863914360847258333">"Palielināšanas logs"</string>
<string name="magnification_controls_title" msgid="8421106606708891519">"Palielināšanas loga vadīklas"</string>
+ <!-- no translation found for quick_controls_title (525285759614231333) -->
+ <skip />
</resources>
diff --git a/packages/SystemUI/res/values-mk/strings.xml b/packages/SystemUI/res/values-mk/strings.xml
index a02f38ed267b..f81c5ff6fc64 100644
--- a/packages/SystemUI/res/values-mk/strings.xml
+++ b/packages/SystemUI/res/values-mk/strings.xml
@@ -964,4 +964,5 @@
<string name="magnification_overlay_title" msgid="6584179429612427958">"Прозорец за преклопување на зголемувањето"</string>
<string name="magnification_window_title" msgid="4863914360847258333">"Прозорец за зголемување"</string>
<string name="magnification_controls_title" msgid="8421106606708891519">"Контроли на прозорец за зголемување"</string>
+ <string name="quick_controls_title" msgid="525285759614231333">"Брзи контроли"</string>
</resources>
diff --git a/packages/SystemUI/res/values-ml/strings.xml b/packages/SystemUI/res/values-ml/strings.xml
index 2d3f73fc5978..3ffcbc278bac 100644
--- a/packages/SystemUI/res/values-ml/strings.xml
+++ b/packages/SystemUI/res/values-ml/strings.xml
@@ -964,4 +964,6 @@
<string name="magnification_overlay_title" msgid="6584179429612427958">"മാഗ്നിഫിക്കേഷൻ ഓവർലേ വിൻഡോ"</string>
<string name="magnification_window_title" msgid="4863914360847258333">"മാഗ്നിഫിക്കേഷൻ വിൻഡോ"</string>
<string name="magnification_controls_title" msgid="8421106606708891519">"മാഗ്നിഫിക്കേഷൻ വിൻഡോ നിയന്ത്രണങ്ങൾ"</string>
+ <!-- no translation found for quick_controls_title (525285759614231333) -->
+ <skip />
</resources>
diff --git a/packages/SystemUI/res/values-mn/strings.xml b/packages/SystemUI/res/values-mn/strings.xml
index 85d7eadce349..87d5fbffb1bc 100644
--- a/packages/SystemUI/res/values-mn/strings.xml
+++ b/packages/SystemUI/res/values-mn/strings.xml
@@ -964,4 +964,5 @@
<string name="magnification_overlay_title" msgid="6584179429612427958">"Томруулалтыг давхарласан цонх"</string>
<string name="magnification_window_title" msgid="4863914360847258333">"Томруулалтын цонх"</string>
<string name="magnification_controls_title" msgid="8421106606708891519">"Томруулалтын цонхны хяналт"</string>
+ <string name="quick_controls_title" msgid="525285759614231333">"Шуурхай хяналтууд"</string>
</resources>
diff --git a/packages/SystemUI/res/values-mr/strings.xml b/packages/SystemUI/res/values-mr/strings.xml
index 3406edf3f1bd..d67c0ef05c6d 100644
--- a/packages/SystemUI/res/values-mr/strings.xml
+++ b/packages/SystemUI/res/values-mr/strings.xml
@@ -964,4 +964,6 @@
<string name="magnification_overlay_title" msgid="6584179429612427958">"मॅग्निफिकेशन ओव्हरले विंडो"</string>
<string name="magnification_window_title" msgid="4863914360847258333">"मॅग्निफिकेशन विंडो"</string>
<string name="magnification_controls_title" msgid="8421106606708891519">"मॅग्निफिकेशन विंडो नियंत्रणे"</string>
+ <!-- no translation found for quick_controls_title (525285759614231333) -->
+ <skip />
</resources>
diff --git a/packages/SystemUI/res/values-ms/strings.xml b/packages/SystemUI/res/values-ms/strings.xml
index eeaaceae0669..e3e9746d728a 100644
--- a/packages/SystemUI/res/values-ms/strings.xml
+++ b/packages/SystemUI/res/values-ms/strings.xml
@@ -964,4 +964,6 @@
<string name="magnification_overlay_title" msgid="6584179429612427958">"Tetingkap Tindanan Pembesaran"</string>
<string name="magnification_window_title" msgid="4863914360847258333">"Tetingkap Pembesaran"</string>
<string name="magnification_controls_title" msgid="8421106606708891519">"Kawalan Tetingkap Pembesaran"</string>
+ <!-- no translation found for quick_controls_title (525285759614231333) -->
+ <skip />
</resources>
diff --git a/packages/SystemUI/res/values-my/strings.xml b/packages/SystemUI/res/values-my/strings.xml
index 5ae82eafbcf9..99075709a62a 100644
--- a/packages/SystemUI/res/values-my/strings.xml
+++ b/packages/SystemUI/res/values-my/strings.xml
@@ -964,4 +964,6 @@
<string name="magnification_overlay_title" msgid="6584179429612427958">"ဝင်းဒိုး ထပ်ပိုးလွှာ ချဲ့ခြင်း"</string>
<string name="magnification_window_title" msgid="4863914360847258333">"ဝင်းဒိုး ချဲ့ခြင်း"</string>
<string name="magnification_controls_title" msgid="8421106606708891519">"ဝင်းဒိုး ထိန်းချုပ်မှုများ ချဲ့ခြင်း"</string>
+ <!-- no translation found for quick_controls_title (525285759614231333) -->
+ <skip />
</resources>
diff --git a/packages/SystemUI/res/values-nb/strings.xml b/packages/SystemUI/res/values-nb/strings.xml
index 463b95c0107e..c273cc9c4bf2 100644
--- a/packages/SystemUI/res/values-nb/strings.xml
+++ b/packages/SystemUI/res/values-nb/strings.xml
@@ -964,4 +964,5 @@
<string name="magnification_overlay_title" msgid="6584179429612427958">"Overleggsvindu for forstørring"</string>
<string name="magnification_window_title" msgid="4863914360847258333">"Forstørringsvindu"</string>
<string name="magnification_controls_title" msgid="8421106606708891519">"Kontroller for forstørringsvindu"</string>
+ <string name="quick_controls_title" msgid="525285759614231333">"Hurtigkontroller"</string>
</resources>
diff --git a/packages/SystemUI/res/values-ne/strings.xml b/packages/SystemUI/res/values-ne/strings.xml
index 21a76a3a86d0..dea3311e558e 100644
--- a/packages/SystemUI/res/values-ne/strings.xml
+++ b/packages/SystemUI/res/values-ne/strings.xml
@@ -964,4 +964,6 @@
<string name="magnification_overlay_title" msgid="6584179429612427958">"म्याग्निफिकेसन ओभरले विन्डो"</string>
<string name="magnification_window_title" msgid="4863914360847258333">"म्याग्निफिकेसन विन्डो"</string>
<string name="magnification_controls_title" msgid="8421106606708891519">"म्याग्निफिकेसन विन्डोका नियन्त्रणहरू"</string>
+ <!-- no translation found for quick_controls_title (525285759614231333) -->
+ <skip />
</resources>
diff --git a/packages/SystemUI/res/values-nl/strings.xml b/packages/SystemUI/res/values-nl/strings.xml
index 3c040154437c..8b015ab3c048 100644
--- a/packages/SystemUI/res/values-nl/strings.xml
+++ b/packages/SystemUI/res/values-nl/strings.xml
@@ -964,4 +964,5 @@
<string name="magnification_overlay_title" msgid="6584179429612427958">"Overlay voor vergrotingsvenster"</string>
<string name="magnification_window_title" msgid="4863914360847258333">"Vergrotingsvenster"</string>
<string name="magnification_controls_title" msgid="8421106606708891519">"Bediening van vergrotingsvenster"</string>
+ <string name="quick_controls_title" msgid="525285759614231333">"Snelle bedieningselementen"</string>
</resources>
diff --git a/packages/SystemUI/res/values-or/strings.xml b/packages/SystemUI/res/values-or/strings.xml
index 42302756debd..e274f2720d58 100644
--- a/packages/SystemUI/res/values-or/strings.xml
+++ b/packages/SystemUI/res/values-or/strings.xml
@@ -964,4 +964,6 @@
<string name="magnification_overlay_title" msgid="6584179429612427958">"ମ୍ୟାଗ୍ନିଫିକେସନ୍ ଓଭର୍‌ଲେ ୱିଣ୍ଡୋ"</string>
<string name="magnification_window_title" msgid="4863914360847258333">"ମ୍ୟାଗ୍ନିଫିକେସନ୍ ୱିଣ୍ଡୋ"</string>
<string name="magnification_controls_title" msgid="8421106606708891519">"ମ୍ୟାଗ୍ନିଫିକେସନ୍ ୱିଣ୍ଡୋ ନିୟନ୍ତ୍ରଣଗୁଡ଼ିକ"</string>
+ <!-- no translation found for quick_controls_title (525285759614231333) -->
+ <skip />
</resources>
diff --git a/packages/SystemUI/res/values-pa/strings.xml b/packages/SystemUI/res/values-pa/strings.xml
index f345d6a8f7aa..28da4edbe50d 100644
--- a/packages/SystemUI/res/values-pa/strings.xml
+++ b/packages/SystemUI/res/values-pa/strings.xml
@@ -964,4 +964,6 @@
<string name="magnification_overlay_title" msgid="6584179429612427958">"ਵੱਡਦਰਸ਼ੀਕਰਨ ਓਵਰਲੇ Window"</string>
<string name="magnification_window_title" msgid="4863914360847258333">"ਵੱਡਦਰਸ਼ੀਕਰਨ Window"</string>
<string name="magnification_controls_title" msgid="8421106606708891519">"ਵੱਡਦਰਸ਼ੀਕਰਨ Window ਦੇ ਕੰਟਰੋਲ"</string>
+ <!-- no translation found for quick_controls_title (525285759614231333) -->
+ <skip />
</resources>
diff --git a/packages/SystemUI/res/values-pl/strings.xml b/packages/SystemUI/res/values-pl/strings.xml
index 68cb14eb32b3..fd48b9721e52 100644
--- a/packages/SystemUI/res/values-pl/strings.xml
+++ b/packages/SystemUI/res/values-pl/strings.xml
@@ -909,7 +909,7 @@
<string name="notification_channel_battery" msgid="9219995638046695106">"Bateria"</string>
<string name="notification_channel_screenshot" msgid="7665814998932211997">"Zrzuty ekranu"</string>
<string name="notification_channel_general" msgid="4384774889645929705">"Wiadomości"</string>
- <string name="notification_channel_storage" msgid="2720725707628094977">"Pamięć"</string>
+ <string name="notification_channel_storage" msgid="2720725707628094977">"Pamięć wewnętrzna"</string>
<string name="notification_channel_hints" msgid="7703783206000346876">"Wskazówki"</string>
<string name="instant_apps" msgid="8337185853050247304">"Aplikacje błyskawiczne"</string>
<string name="instant_apps_title" msgid="8942706782103036910">"Aplikacja <xliff:g id="APP">%1$s</xliff:g> działa"</string>
@@ -974,4 +974,5 @@
<string name="magnification_overlay_title" msgid="6584179429612427958">"Okno nakładki powiększenia"</string>
<string name="magnification_window_title" msgid="4863914360847258333">"Okno powiększenia"</string>
<string name="magnification_controls_title" msgid="8421106606708891519">"Elementy sterujące okna powiększenia"</string>
+ <string name="quick_controls_title" msgid="525285759614231333">"Szybkie sterowanie"</string>
</resources>
diff --git a/packages/SystemUI/res/values-pt-rBR/strings.xml b/packages/SystemUI/res/values-pt-rBR/strings.xml
index a9101d74706f..6f616626a633 100644
--- a/packages/SystemUI/res/values-pt-rBR/strings.xml
+++ b/packages/SystemUI/res/values-pt-rBR/strings.xml
@@ -964,4 +964,5 @@
<string name="magnification_overlay_title" msgid="6584179429612427958">"Janela de sobreposição de ampliação"</string>
<string name="magnification_window_title" msgid="4863914360847258333">"Janela de ampliação"</string>
<string name="magnification_controls_title" msgid="8421106606708891519">"Controles da janela de ampliação"</string>
+ <string name="quick_controls_title" msgid="525285759614231333">"Controles rápidos"</string>
</resources>
diff --git a/packages/SystemUI/res/values-pt-rPT/strings.xml b/packages/SystemUI/res/values-pt-rPT/strings.xml
index 1a6e6739e7ff..be534ae205c4 100644
--- a/packages/SystemUI/res/values-pt-rPT/strings.xml
+++ b/packages/SystemUI/res/values-pt-rPT/strings.xml
@@ -964,4 +964,5 @@
<string name="magnification_overlay_title" msgid="6584179429612427958">"Janela de sobreposição da ampliação"</string>
<string name="magnification_window_title" msgid="4863914360847258333">"Janela de ampliação"</string>
<string name="magnification_controls_title" msgid="8421106606708891519">"Controlos da janela de ampliação"</string>
+ <string name="quick_controls_title" msgid="525285759614231333">"Controlos rápidos"</string>
</resources>
diff --git a/packages/SystemUI/res/values-pt/strings.xml b/packages/SystemUI/res/values-pt/strings.xml
index a9101d74706f..6f616626a633 100644
--- a/packages/SystemUI/res/values-pt/strings.xml
+++ b/packages/SystemUI/res/values-pt/strings.xml
@@ -964,4 +964,5 @@
<string name="magnification_overlay_title" msgid="6584179429612427958">"Janela de sobreposição de ampliação"</string>
<string name="magnification_window_title" msgid="4863914360847258333">"Janela de ampliação"</string>
<string name="magnification_controls_title" msgid="8421106606708891519">"Controles da janela de ampliação"</string>
+ <string name="quick_controls_title" msgid="525285759614231333">"Controles rápidos"</string>
</resources>
diff --git a/packages/SystemUI/res/values-ro/strings.xml b/packages/SystemUI/res/values-ro/strings.xml
index e34fa2de54d2..44940bf84a77 100644
--- a/packages/SystemUI/res/values-ro/strings.xml
+++ b/packages/SystemUI/res/values-ro/strings.xml
@@ -969,4 +969,5 @@
<string name="magnification_overlay_title" msgid="6584179429612427958">"Fereastra de suprapunere pentru mărire"</string>
<string name="magnification_window_title" msgid="4863914360847258333">"Fereastra de mărire"</string>
<string name="magnification_controls_title" msgid="8421106606708891519">"Comenzi pentru fereastra de mărire"</string>
+ <string name="quick_controls_title" msgid="525285759614231333">"Comenzi rapide"</string>
</resources>
diff --git a/packages/SystemUI/res/values-ru/strings.xml b/packages/SystemUI/res/values-ru/strings.xml
index 0ee159d0c648..6fe20944448c 100644
--- a/packages/SystemUI/res/values-ru/strings.xml
+++ b/packages/SystemUI/res/values-ru/strings.xml
@@ -974,4 +974,6 @@
<string name="magnification_overlay_title" msgid="6584179429612427958">"Наложение окна увеличения"</string>
<string name="magnification_window_title" msgid="4863914360847258333">"Окно увеличения"</string>
<string name="magnification_controls_title" msgid="8421106606708891519">"Настройки окна увеличения"</string>
+ <!-- no translation found for quick_controls_title (525285759614231333) -->
+ <skip />
</resources>
diff --git a/packages/SystemUI/res/values-si/strings.xml b/packages/SystemUI/res/values-si/strings.xml
index 2c5e1d8ba117..38933ad6c46a 100644
--- a/packages/SystemUI/res/values-si/strings.xml
+++ b/packages/SystemUI/res/values-si/strings.xml
@@ -964,4 +964,5 @@
<string name="magnification_overlay_title" msgid="6584179429612427958">"විශාලන උඩැතිරි කවුළුව"</string>
<string name="magnification_window_title" msgid="4863914360847258333">"විශාලන කවුළුව"</string>
<string name="magnification_controls_title" msgid="8421106606708891519">"විශාලනය කිරීමේ කවුළු පාලන"</string>
+ <string name="quick_controls_title" msgid="525285759614231333">"ඉක්මන් පාලන"</string>
</resources>
diff --git a/packages/SystemUI/res/values-sk/strings.xml b/packages/SystemUI/res/values-sk/strings.xml
index 93f93a2860a9..5bd709c7142a 100644
--- a/packages/SystemUI/res/values-sk/strings.xml
+++ b/packages/SystemUI/res/values-sk/strings.xml
@@ -974,4 +974,5 @@
<string name="magnification_overlay_title" msgid="6584179429612427958">"Okno prekrytia priblíženia"</string>
<string name="magnification_window_title" msgid="4863914360847258333">"Okno priblíženia"</string>
<string name="magnification_controls_title" msgid="8421106606708891519">"Ovládacie prvky okna priblíženia"</string>
+ <string name="quick_controls_title" msgid="525285759614231333">"Rýchle ovládacie prvky"</string>
</resources>
diff --git a/packages/SystemUI/res/values-sl/strings.xml b/packages/SystemUI/res/values-sl/strings.xml
index 452827fa362f..7c06fa7da3cb 100644
--- a/packages/SystemUI/res/values-sl/strings.xml
+++ b/packages/SystemUI/res/values-sl/strings.xml
@@ -974,4 +974,5 @@
<string name="magnification_overlay_title" msgid="6584179429612427958">"Prekrivno povečevalno okno"</string>
<string name="magnification_window_title" msgid="4863914360847258333">"Povečevalno okno"</string>
<string name="magnification_controls_title" msgid="8421106606708891519">"Kontrolniki povečevalnega okna"</string>
+ <string name="quick_controls_title" msgid="525285759614231333">"Hitro upravljanje"</string>
</resources>
diff --git a/packages/SystemUI/res/values-sq/strings.xml b/packages/SystemUI/res/values-sq/strings.xml
index 2dc3cc8b1cd2..7549611ec071 100644
--- a/packages/SystemUI/res/values-sq/strings.xml
+++ b/packages/SystemUI/res/values-sq/strings.xml
@@ -964,4 +964,6 @@
<string name="magnification_overlay_title" msgid="6584179429612427958">"Dritarja e mbivendosjes së zmadhimit"</string>
<string name="magnification_window_title" msgid="4863914360847258333">"Dritarja e zmadhimit"</string>
<string name="magnification_controls_title" msgid="8421106606708891519">"Kontrollet e dritares së zmadhimit"</string>
+ <!-- no translation found for quick_controls_title (525285759614231333) -->
+ <skip />
</resources>
diff --git a/packages/SystemUI/res/values-sr/strings.xml b/packages/SystemUI/res/values-sr/strings.xml
index 4127806f9cd3..6b2d971df6ed 100644
--- a/packages/SystemUI/res/values-sr/strings.xml
+++ b/packages/SystemUI/res/values-sr/strings.xml
@@ -969,4 +969,5 @@
<string name="magnification_overlay_title" msgid="6584179429612427958">"Преклопни прозор за увећање"</string>
<string name="magnification_window_title" msgid="4863914360847258333">"Прозор за увећање"</string>
<string name="magnification_controls_title" msgid="8421106606708891519">"Контроле прозора за увећање"</string>
+ <string name="quick_controls_title" msgid="525285759614231333">"Брзе контроле"</string>
</resources>
diff --git a/packages/SystemUI/res/values-sv/strings.xml b/packages/SystemUI/res/values-sv/strings.xml
index 3e29fa6e6749..33c20789dc2b 100644
--- a/packages/SystemUI/res/values-sv/strings.xml
+++ b/packages/SystemUI/res/values-sv/strings.xml
@@ -964,4 +964,5 @@
<string name="magnification_overlay_title" msgid="6584179429612427958">"Överlagrat förstoringsfönster"</string>
<string name="magnification_window_title" msgid="4863914360847258333">"Förstoringsfönster"</string>
<string name="magnification_controls_title" msgid="8421106606708891519">"Inställningar för förstoringsfönster"</string>
+ <string name="quick_controls_title" msgid="525285759614231333">"Snabbinställningar"</string>
</resources>
diff --git a/packages/SystemUI/res/values-sw/strings.xml b/packages/SystemUI/res/values-sw/strings.xml
index 9fd4284710d1..bf2640f41042 100644
--- a/packages/SystemUI/res/values-sw/strings.xml
+++ b/packages/SystemUI/res/values-sw/strings.xml
@@ -958,10 +958,11 @@
<string name="bubble_accessibility_action_move_bottom_left" msgid="6339015902495504715">"Sogeza chini kushoto"</string>
<string name="bubble_accessibility_action_move_bottom_right" msgid="7471571700628346212">"Sogeza chini kulia"</string>
<string name="bubble_dismiss_text" msgid="7071770411580452911">"Ondoa"</string>
- <string name="notification_content_system_nav_changed" msgid="5077913144844684544">"Umesasisha usogezaji kwenye mfumo. Ili ufanye mabadiliko, nenda kwenye Mipangilio."</string>
+ <string name="notification_content_system_nav_changed" msgid="5077913144844684544">"Umesasisha usogezaji kwenye mfumo. Ili ubadilishe, nenda kwenye Mipangilio."</string>
<string name="notification_content_gesture_nav_available" msgid="4431460803004659888">"Nenda kwenye mipangilio ili usasishe usogezaji kwenye mfumo"</string>
<string name="inattentive_sleep_warning_title" msgid="3891371591713990373">"Hali tuli"</string>
<string name="magnification_overlay_title" msgid="6584179429612427958">"Dirisha la Kuwekelea Linalokuza"</string>
<string name="magnification_window_title" msgid="4863914360847258333">"Dirisha la Ukuzaji"</string>
<string name="magnification_controls_title" msgid="8421106606708891519">"Vidhibiti vya Dirisha la Ukuzaji"</string>
+ <string name="quick_controls_title" msgid="525285759614231333">"Vidhibiti vya Haraka"</string>
</resources>
diff --git a/packages/SystemUI/res/values-ta/strings.xml b/packages/SystemUI/res/values-ta/strings.xml
index 55bd472a7889..c5289ec55c50 100644
--- a/packages/SystemUI/res/values-ta/strings.xml
+++ b/packages/SystemUI/res/values-ta/strings.xml
@@ -964,4 +964,6 @@
<string name="magnification_overlay_title" msgid="6584179429612427958">"Magnification Overlay Window"</string>
<string name="magnification_window_title" msgid="4863914360847258333">"பெரிதாக்கல் சாளரம்"</string>
<string name="magnification_controls_title" msgid="8421106606708891519">"பெரிதாக்கல் சாளரக் கட்டுப்பாடுகள்"</string>
+ <!-- no translation found for quick_controls_title (525285759614231333) -->
+ <skip />
</resources>
diff --git a/packages/SystemUI/res/values-te/strings.xml b/packages/SystemUI/res/values-te/strings.xml
index 8b9c07ce0264..ed562f3d7377 100644
--- a/packages/SystemUI/res/values-te/strings.xml
+++ b/packages/SystemUI/res/values-te/strings.xml
@@ -71,7 +71,7 @@
<string name="compat_mode_on" msgid="4963711187149440884">"స్క్రీన్‌కు నింపేలా జూమ్ చేయండి"</string>
<string name="compat_mode_off" msgid="7682459748279487945">"స్క్రీన్‌కు నింపేలా విస్తరించండి"</string>
<string name="global_action_screenshot" msgid="2760267567509131654">"స్క్రీన్‌షాట్"</string>
- <string name="remote_input_image_insertion_text" msgid="4613177882724332877">"చిత్రం చొప్పించబడింది"</string>
+ <string name="remote_input_image_insertion_text" msgid="4613177882724332877">"ఇమేజ్ చొప్పించబడింది"</string>
<string name="screenshot_saving_ticker" msgid="6519186952674544916">"స్క్రీన్‌షాట్‌ను సేవ్ చేస్తోంది…"</string>
<string name="screenshot_saving_title" msgid="2298349784913287333">"స్క్రీన్‌షాట్‌ను సేవ్ చేస్తోంది…"</string>
<string name="screenshot_saved_title" msgid="8893267638659083153">"స్క్రీన్‌షాట్ సేవ్ చేయబడింది"</string>
@@ -390,7 +390,7 @@
<string name="quick_settings_dark_mode_secondary_label_battery_saver" msgid="4990712734503013251">"బ్యాటరీ సేవర్"</string>
<string name="quick_settings_dark_mode_secondary_label_on_at_sunset" msgid="6017379738102015710">"సూర్యాస్తమయానికి"</string>
<string name="quick_settings_dark_mode_secondary_label_until_sunrise" msgid="4404885070316716472">"సూర్యోదయం వరకు"</string>
- <string name="quick_settings_dark_mode_secondary_label_on_at" msgid="5128758823486361279">"<xliff:g id="TIME">%s</xliff:g> వద్ద"</string>
+ <string name="quick_settings_dark_mode_secondary_label_on_at" msgid="5128758823486361279">"<xliff:g id="TIME">%s</xliff:g> కు ఆన్ అవుతుంది"</string>
<string name="quick_settings_dark_mode_secondary_label_until" msgid="2289774641256492437">"<xliff:g id="TIME">%s</xliff:g> వరకు"</string>
<string name="quick_settings_nfc_label" msgid="1054317416221168085">"NFC"</string>
<string name="quick_settings_nfc_off" msgid="3465000058515424663">"NFC నిలిపివేయబడింది"</string>
@@ -964,4 +964,6 @@
<string name="magnification_overlay_title" msgid="6584179429612427958">"మాగ్నిఫికేషన్ ఓవర్‌లే విండో"</string>
<string name="magnification_window_title" msgid="4863914360847258333">"మాగ్నిఫికేషన్ విండో"</string>
<string name="magnification_controls_title" msgid="8421106606708891519">"మాగ్నిఫికేషన్ నియంత్రణల విండో"</string>
+ <!-- no translation found for quick_controls_title (525285759614231333) -->
+ <skip />
</resources>
diff --git a/packages/SystemUI/res/values-television/config.xml b/packages/SystemUI/res/values-television/config.xml
index dd4c3210922a..bb4999453d4f 100644
--- a/packages/SystemUI/res/values-television/config.xml
+++ b/packages/SystemUI/res/values-television/config.xml
@@ -20,6 +20,9 @@
<!-- These resources are around just to allow their values to be customized
for different hardware and product builds. -->
<resources>
+ <!-- SystemUIFactory component -->
+ <string name="config_systemUIFactoryComponent" translatable="false">com.android.systemui.tv.TvSystemUIFactory</string>
+
<!-- SystemUI Services: The classes of the stuff to start. -->
<string-array name="config_systemUIServiceComponents" translatable="false">
<item>com.android.systemui.util.NotificationChannels</item>
diff --git a/packages/SystemUI/res/values-th/strings.xml b/packages/SystemUI/res/values-th/strings.xml
index c5e01236912d..9e5b2dbaf18b 100644
--- a/packages/SystemUI/res/values-th/strings.xml
+++ b/packages/SystemUI/res/values-th/strings.xml
@@ -964,4 +964,5 @@
<string name="magnification_overlay_title" msgid="6584179429612427958">"หน้าต่างการขยายที่วางซ้อน"</string>
<string name="magnification_window_title" msgid="4863914360847258333">"หน้าต่างการขยาย"</string>
<string name="magnification_controls_title" msgid="8421106606708891519">"การควบคุมหน้าต่างการขยาย"</string>
+ <string name="quick_controls_title" msgid="525285759614231333">"การควบคุมอย่างรวดเร็ว"</string>
</resources>
diff --git a/packages/SystemUI/res/values-tl/strings.xml b/packages/SystemUI/res/values-tl/strings.xml
index 3d343c5b4c0b..9485a2bf3cc2 100644
--- a/packages/SystemUI/res/values-tl/strings.xml
+++ b/packages/SystemUI/res/values-tl/strings.xml
@@ -964,4 +964,5 @@
<string name="magnification_overlay_title" msgid="6584179429612427958">"Window ng Overlay sa Pag-magnify"</string>
<string name="magnification_window_title" msgid="4863914360847258333">"Window ng Pag-magnify"</string>
<string name="magnification_controls_title" msgid="8421106606708891519">"Mga Kontrol sa Pag-magnify ng Window"</string>
+ <string name="quick_controls_title" msgid="525285759614231333">"Mga Mabilisang Kontrol"</string>
</resources>
diff --git a/packages/SystemUI/res/values-tr/strings.xml b/packages/SystemUI/res/values-tr/strings.xml
index 46e583fb5124..950b66b11dbf 100644
--- a/packages/SystemUI/res/values-tr/strings.xml
+++ b/packages/SystemUI/res/values-tr/strings.xml
@@ -964,4 +964,5 @@
<string name="magnification_overlay_title" msgid="6584179429612427958">"Yer Paylaşımlı Büyütme Penceresi"</string>
<string name="magnification_window_title" msgid="4863914360847258333">"Büyütme Penceresi"</string>
<string name="magnification_controls_title" msgid="8421106606708891519">"Büyütme Penceresi Kontrolleri"</string>
+ <string name="quick_controls_title" msgid="525285759614231333">"Hızlı Kontroller"</string>
</resources>
diff --git a/packages/SystemUI/res/values-uk/strings.xml b/packages/SystemUI/res/values-uk/strings.xml
index 7baad0df2e55..3e1e4a96fb5c 100644
--- a/packages/SystemUI/res/values-uk/strings.xml
+++ b/packages/SystemUI/res/values-uk/strings.xml
@@ -974,4 +974,5 @@
<string name="magnification_overlay_title" msgid="6584179429612427958">"Вікно збільшення з накладанням"</string>
<string name="magnification_window_title" msgid="4863914360847258333">"Вікно збільшення"</string>
<string name="magnification_controls_title" msgid="8421106606708891519">"Елементи керування вікна збільшення"</string>
+ <string name="quick_controls_title" msgid="525285759614231333">"Елементи швидкого керування"</string>
</resources>
diff --git a/packages/SystemUI/res/values-ur/strings.xml b/packages/SystemUI/res/values-ur/strings.xml
index 97d0a5d1d2a3..6f3e7ab02ff3 100644
--- a/packages/SystemUI/res/values-ur/strings.xml
+++ b/packages/SystemUI/res/values-ur/strings.xml
@@ -964,4 +964,5 @@
<string name="magnification_overlay_title" msgid="6584179429612427958">"میگنیفیکیشن اوورلے ونڈو"</string>
<string name="magnification_window_title" msgid="4863914360847258333">"میگنیفکیشن ونڈو"</string>
<string name="magnification_controls_title" msgid="8421106606708891519">"میگنیفکیشن ونڈو کنٹرولز"</string>
+ <string name="quick_controls_title" msgid="525285759614231333">"فوری کنٹرولز"</string>
</resources>
diff --git a/packages/SystemUI/res/values-uz/strings.xml b/packages/SystemUI/res/values-uz/strings.xml
index b514f1e9754c..618463e91040 100644
--- a/packages/SystemUI/res/values-uz/strings.xml
+++ b/packages/SystemUI/res/values-uz/strings.xml
@@ -390,7 +390,7 @@
<string name="quick_settings_dark_mode_secondary_label_battery_saver" msgid="4990712734503013251">"Quvvat tejash"</string>
<string name="quick_settings_dark_mode_secondary_label_on_at_sunset" msgid="6017379738102015710">"Kunbotarda yoqish"</string>
<string name="quick_settings_dark_mode_secondary_label_until_sunrise" msgid="4404885070316716472">"Quyosh chiqqunicha"</string>
- <string name="quick_settings_dark_mode_secondary_label_on_at" msgid="5128758823486361279">"<xliff:g id="TIME">%s</xliff:g> da yoqish"</string>
+ <string name="quick_settings_dark_mode_secondary_label_on_at" msgid="5128758823486361279">"<xliff:g id="TIME">%s</xliff:g> da yoqiladi"</string>
<string name="quick_settings_dark_mode_secondary_label_until" msgid="2289774641256492437">"<xliff:g id="TIME">%s</xliff:g> gacha"</string>
<string name="quick_settings_nfc_label" msgid="1054317416221168085">"NFC"</string>
<string name="quick_settings_nfc_off" msgid="3465000058515424663">"NFC o‘chiq"</string>
@@ -477,9 +477,9 @@
<string name="media_projection_remember_text" msgid="6896767327140422951">"Boshqa ko‘rsatilmasin"</string>
<string name="clear_all_notifications_text" msgid="348312370303046130">"Hammasini tozalash"</string>
<string name="manage_notifications_text" msgid="6885645344647733116">"Boshqarish"</string>
- <string name="notification_section_header_gentle" msgid="3044910806569985386">"Tovushsiz bildirishnomalar"</string>
+ <string name="notification_section_header_gentle" msgid="3044910806569985386">"Sokin bildirishnomalar"</string>
<string name="notification_section_header_conversations" msgid="821834744538345661">"Suhbatlar"</string>
- <string name="accessibility_notification_section_header_gentle_clear_all" msgid="6490207897764933919">"Barcha tovushsiz bildirishnomalarni tozalash"</string>
+ <string name="accessibility_notification_section_header_gentle_clear_all" msgid="6490207897764933919">"Barcha sokin bildirishnomalarni tozalash"</string>
<string name="dnd_suppressing_shade_text" msgid="5588252250634464042">"Bezovta qilinmasin rejimida bildirishnomalar pauza qilingan"</string>
<string name="media_projection_action_text" msgid="3634906766918186440">"Boshlash"</string>
<string name="empty_shade_text" msgid="8935967157319717412">"Bildirishnomalar yo‘q"</string>
@@ -964,4 +964,5 @@
<string name="magnification_overlay_title" msgid="6584179429612427958">"Kattalashtirish oynasining ustidan ochilishi"</string>
<string name="magnification_window_title" msgid="4863914360847258333">"Kattalashtirish oynasi"</string>
<string name="magnification_controls_title" msgid="8421106606708891519">"Kattalashtirish oynasi sozlamalari"</string>
+ <string name="quick_controls_title" msgid="525285759614231333">"Tezkor tugmalar"</string>
</resources>
diff --git a/packages/SystemUI/res/values-vi/strings.xml b/packages/SystemUI/res/values-vi/strings.xml
index c4d76187c817..6be05aab1583 100644
--- a/packages/SystemUI/res/values-vi/strings.xml
+++ b/packages/SystemUI/res/values-vi/strings.xml
@@ -964,4 +964,6 @@
<string name="magnification_overlay_title" msgid="6584179429612427958">"Cửa sổ lớp phủ phóng to"</string>
<string name="magnification_window_title" msgid="4863914360847258333">"Cửa sổ phóng to"</string>
<string name="magnification_controls_title" msgid="8421106606708891519">"Các tùy chọn điều khiển cửa sổ phóng to"</string>
+ <!-- no translation found for quick_controls_title (525285759614231333) -->
+ <skip />
</resources>
diff --git a/packages/SystemUI/res/values-zh-rCN/strings.xml b/packages/SystemUI/res/values-zh-rCN/strings.xml
index 56edb64fd798..e10abbb342a0 100644
--- a/packages/SystemUI/res/values-zh-rCN/strings.xml
+++ b/packages/SystemUI/res/values-zh-rCN/strings.xml
@@ -964,4 +964,6 @@
<string name="magnification_overlay_title" msgid="6584179429612427958">"放大叠加窗口"</string>
<string name="magnification_window_title" msgid="4863914360847258333">"放大窗口"</string>
<string name="magnification_controls_title" msgid="8421106606708891519">"放大窗口控件"</string>
+ <!-- no translation found for quick_controls_title (525285759614231333) -->
+ <skip />
</resources>
diff --git a/packages/SystemUI/res/values-zh-rHK/strings.xml b/packages/SystemUI/res/values-zh-rHK/strings.xml
index 251e23d1526b..a83ce53ca1e4 100644
--- a/packages/SystemUI/res/values-zh-rHK/strings.xml
+++ b/packages/SystemUI/res/values-zh-rHK/strings.xml
@@ -964,4 +964,5 @@
<string name="magnification_overlay_title" msgid="6584179429612427958">"放大重疊視窗"</string>
<string name="magnification_window_title" msgid="4863914360847258333">"放大視窗"</string>
<string name="magnification_controls_title" msgid="8421106606708891519">"放大視窗控制項"</string>
+ <string name="quick_controls_title" msgid="525285759614231333">"快速控制介面"</string>
</resources>
diff --git a/packages/SystemUI/res/values-zh-rTW/strings.xml b/packages/SystemUI/res/values-zh-rTW/strings.xml
index a7ec5985a745..903259593765 100644
--- a/packages/SystemUI/res/values-zh-rTW/strings.xml
+++ b/packages/SystemUI/res/values-zh-rTW/strings.xml
@@ -964,4 +964,5 @@
<string name="magnification_overlay_title" msgid="6584179429612427958">"放大重疊視窗"</string>
<string name="magnification_window_title" msgid="4863914360847258333">"放大視窗"</string>
<string name="magnification_controls_title" msgid="8421106606708891519">"放大視窗控制項"</string>
+ <string name="quick_controls_title" msgid="525285759614231333">"快速控制項"</string>
</resources>
diff --git a/packages/SystemUI/res/values-zu/strings.xml b/packages/SystemUI/res/values-zu/strings.xml
index 1203b579c52f..019128b0ab5b 100644
--- a/packages/SystemUI/res/values-zu/strings.xml
+++ b/packages/SystemUI/res/values-zu/strings.xml
@@ -964,4 +964,5 @@
<string name="magnification_overlay_title" msgid="6584179429612427958">"Iwindi Lembondela Lesikhulisi"</string>
<string name="magnification_window_title" msgid="4863914360847258333">"Iwindi Lesikhulisi"</string>
<string name="magnification_controls_title" msgid="8421106606708891519">"Izilawuli Zewindi Lesikhulisi"</string>
+ <string name="quick_controls_title" msgid="525285759614231333">"Izilawuli Ezisheshayo"</string>
</resources>
diff --git a/packages/SystemUI/res/values/dimens.xml b/packages/SystemUI/res/values/dimens.xml
index 1f9fb0511d26..9c997e88c0c2 100644
--- a/packages/SystemUI/res/values/dimens.xml
+++ b/packages/SystemUI/res/values/dimens.xml
@@ -1164,10 +1164,12 @@
<dimen name="new_qs_vertical_margin">8dp</dimen>
<!-- Size of media cards in the QSPanel carousel -->
- <dimen name="qs_media_height">150dp</dimen>
<dimen name="qs_media_width">350dp</dimen>
<dimen name="qs_media_padding">8dp</dimen>
<dimen name="qs_media_corner_radius">10dp</dimen>
+ <dimen name="qs_media_album_size">72dp</dimen>
+ <dimen name="qs_seamless_icon_size">20dp</dimen>
+ <dimen name="qqs_media_spacing">8dp</dimen>
<dimen name="magnification_border_size">5dp</dimen>
<dimen name="magnification_frame_move_short">5dp</dimen>
@@ -1185,7 +1187,27 @@
<dimen name="control_corner_radius">15dp</dimen>
<dimen name="control_height">100dp</dimen>
<dimen name="control_padding">15dp</dimen>
- <dimen name="control_status_normal">12dp</dimen>
- <dimen name="control_status_expanded">18dp</dimen>
- <dimen name="app_icon_size">32dp</dimen>
+ <dimen name="control_status_normal">12sp</dimen>
+ <dimen name="control_status_expanded">18sp</dimen>
+ <dimen name="control_base_item_margin">2dp</dimen>
+ <dimen name="control_status_padding">3dp</dimen>
+
+ <!-- Home Controls management screens -->
+ <dimen name="controls_management_top_padding">48dp</dimen>
+ <dimen name="controls_management_side_padding">8dp</dimen>
+ <dimen name="controls_management_titles_margin">8dp</dimen>
+ <dimen name="controls_management_list_margin">16dp</dimen>
+ <dimen name="controls_title_size">26sp</dimen>
+
+ <dimen name="controls_app_icon_size">32dp</dimen>
+ <dimen name="controls_app_icon_frame_side_padding">8dp</dimen>
+ <dimen name="controls_app_icon_frame_top_padding">4dp</dimen>
+ <dimen name="controls_app_bottom_margin">8dp</dimen>
+ <dimen name="controls_app_text_padding">8dp</dimen>
+ <dimen name="controls_app_divider_height">2dp</dimen>
+ <dimen name="controls_app_divider_side_margin">32dp</dimen>
+
+ <dimen name="controls_card_margin">2dp</dimen>
+
+
</resources>
diff --git a/packages/SystemUI/res/values/strings.xml b/packages/SystemUI/res/values/strings.xml
index 1f13f8dc02fe..82cd5f71e8a0 100644
--- a/packages/SystemUI/res/values/strings.xml
+++ b/packages/SystemUI/res/values/strings.xml
@@ -196,7 +196,7 @@
<!-- text to show in place of RemoteInput images when they cannot be shown.
[CHAR LIMIT=50] -->
- <string name="remote_input_image_insertion_text">Image inserted</string>
+ <string name="remote_input_image_insertion_text">sent an image</string>
<!-- Notification ticker displayed when a screenshot is being saved to the Gallery. [CHAR LIMIT=30] -->
<string name="screenshot_saving_ticker">Saving screenshot\u2026</string>
@@ -2547,6 +2547,24 @@
<!-- Title for Magnification Controls Window [CHAR LIMIT=NONE] -->
<string name="magnification_controls_title">Magnification Window Controls</string>
- <!-- Quick Controls strings [CHAR LIMIT=30] -->
+ <!-- Quick Controls strings -->
+ <!-- Quick Controls view header [CHAR LIMIT=30] -->
<string name="quick_controls_title">Quick Controls</string>
+
+ <!-- Controls management providers screen title [CHAR LIMIT=30]-->
+ <string name="controls_providers_title">Add Controls</string>
+ <!-- Controls management providers screen subtitle [CHAR LIMIT=NONE] -->
+ <string name="controls_providers_subtitle">Choose an app from which to add controls</string>
+ <!-- Number of favorites for controls management screen [CHAR LIMIT=NONE]-->
+ <plurals name="controls_number_of_favorites">
+ <item quantity="one"><xliff:g id="number" example="1">%s</xliff:g> current favorite.</item>
+ <item quantity="other"><xliff:g id="number" example="3">%s</xliff:g> current favorites.</item>
+ </plurals>
+
+ <!-- Controls management controls screen default title [CHAR LIMIT=30] -->
+ <string name="controls_favorite_default_title">Controls</string>
+ <!-- Controls management controls screen subtitle [CHAR LIMIT=NONE] -->
+ <string name="controls_favorite_subtitle">Choose controls for quick access</string>
+
+
</resources>
diff --git a/packages/SystemUI/res/values/styles.xml b/packages/SystemUI/res/values/styles.xml
index 10f59a423805..bcffa8d0feea 100644
--- a/packages/SystemUI/res/values/styles.xml
+++ b/packages/SystemUI/res/values/styles.xml
@@ -234,6 +234,37 @@
<style name="TextAppearance.DeviceManagementDialog.Title" parent="@android:style/TextAppearance.DeviceDefault.DialogWindowTitle"/>
+ <style name="TextAppearance.AuthCredential">
+ <item name="android:gravity">center_horizontal</item>
+ <item name="android:fontFamily">google-sans</item>
+ <item name="android:textAlignment">gravity</item>
+ <item name="android:layout_gravity">top</item>
+ <item name="android:textColor">?android:attr/textColorPrimary</item>
+ </style>
+
+ <style name="TextAppearance.AuthCredential.Title">
+ <item name="android:layout_marginBottom">2dp</item>
+ <item name="android:layout_marginLeft">24dp</item>
+ <item name="android:layout_marginRight">24dp</item>
+ <item name="android:layout_marginTop">16dp</item>
+ <item name="android:textSize">24sp</item>
+ </style>
+
+ <style name="TextAppearance.AuthCredential.Description">
+ <item name="android:layout_marginBottom">12dp</item>
+ <item name="android:layout_marginStart">40dp</item>
+ <item name="android:layout_marginEnd">40dp</item>
+ <item name="android:layout_marginTop">3dp</item>
+ <item name="android:textSize">16sp</item>
+ </style>
+
+ <style name="TextAppearance.AuthCredential.PasswordEntry" parent="@android:style/TextAppearance.DeviceDefault">
+ <item name="android:gravity">center</item>
+ <item name="android:singleLine">true</item>
+ <item name="android:textColor">?android:attr/colorForeground</item>
+ <item name="android:textSize">24sp</item>
+ </style>
+
<style name="DeviceManagementDialogTitle">
<item name="android:gravity">center</item>
<item name="android:textAppearance">@style/TextAppearance.DeviceManagementDialog.Title</item>
@@ -297,6 +328,16 @@
<item name="android:textColor">?attr/wallpaperTextColor</item>
</style>
+ <style name="LockPatternContainerStyle">
+ <item name="android:maxHeight">400dp</item>
+ <item name="android:maxWidth">420dp</item>
+ <item name="android:minHeight">0dp</item>
+ <item name="android:minWidth">0dp</item>
+ <item name="android:paddingBottom">0dp</item>
+ <item name="android:paddingHorizontal">44dp</item>
+ <item name="android:paddingTop">0dp</item>
+ </style>
+
<style name="LockPatternStyle">
<item name="*android:regularColor">?attr/wallpaperTextColor</item>
<item name="*android:successColor">?attr/wallpaperTextColor</item>
@@ -583,4 +624,28 @@
<item name="android:background">?android:attr/selectableItemBackground</item>
<item name="android:textColor">?android:attr/textColorPrimary</item>
</style>
+
+ <!-- Controls styles -->
+ <style name="Theme.ControlsManagement" parent="@android:style/Theme.DeviceDefault.NoActionBar">
+ <item name="android:windowIsTranslucent">false</item>
+ </style>
+
+ <style name="TextAppearance.Control">
+ <item name="android:fontFamily">@*android:string/config_bodyFontFamily</item>
+ </style>
+
+ <style name="TextAppearance.Control.Status">
+ <item name="android:textSize">12sp</item>
+ <item name="android:textColor">?android:attr/textColorPrimary</item>
+ </style>
+
+ <style name="TextAppearance.Control.Title">
+ <item name="android:textSize">14sp</item>
+ <item name="android:textColor">?android:attr/textColorPrimary</item>
+ </style>
+ <style name="TextAppearance.Control.Subtitle">
+ <item name="android:textSize">12sp</item>
+ <item name="android:textColor">?android:attr/textColorSecondary</item>
+ </style>
+
</resources>
diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/tracing/FrameProtoTracer.java b/packages/SystemUI/shared/src/com/android/systemui/shared/tracing/FrameProtoTracer.java
index 557845c34bf2..356e0ca7193c 100644
--- a/packages/SystemUI/shared/src/com/android/systemui/shared/tracing/FrameProtoTracer.java
+++ b/packages/SystemUI/shared/src/com/android/systemui/shared/tracing/FrameProtoTracer.java
@@ -40,7 +40,7 @@ import java.util.function.Consumer;
* @param <R> The proto class type of the entry root proto in the buffer
*/
public class FrameProtoTracer<P, S extends P, T extends P, R>
- implements TraceBuffer.ProtoProvider<P, S, T>, Choreographer.FrameCallback {
+ implements Choreographer.FrameCallback {
private static final String TAG = "FrameProtoTracer";
private static final int BUFFER_CAPACITY = 1024 * 1024;
@@ -57,6 +57,25 @@ public class FrameProtoTracer<P, S extends P, T extends P, R>
private volatile boolean mEnabled;
private boolean mFrameScheduled;
+ private final TraceBuffer.ProtoProvider<P, S, T> mProvider =
+ new TraceBuffer.ProtoProvider<P, S, T>() {
+ @Override
+ public int getItemSize(P proto) {
+ return mParams.getProtoSize(proto);
+ }
+
+ @Override
+ public byte[] getBytes(P proto) {
+ return mParams.getProtoBytes(proto);
+ }
+
+ @Override
+ public void write(S encapsulatingProto, Queue<T> buffer, OutputStream os)
+ throws IOException {
+ os.write(mParams.serializeEncapsulatingProto(encapsulatingProto, buffer));
+ }
+ };
+
public interface ProtoTraceParams<P, S, T, R> {
File getTraceFile();
S getEncapsulatingTraceProto();
@@ -68,7 +87,7 @@ public class FrameProtoTracer<P, S extends P, T extends P, R>
public FrameProtoTracer(ProtoTraceParams<P, S, T, R> params) {
mParams = params;
- mBuffer = new TraceBuffer<>(BUFFER_CAPACITY, this, new Consumer<T>() {
+ mBuffer = new TraceBuffer<>(BUFFER_CAPACITY, mProvider, new Consumer<T>() {
@Override
public void accept(T t) {
onProtoDequeued(t);
@@ -78,21 +97,6 @@ public class FrameProtoTracer<P, S extends P, T extends P, R>
mChoreographer = Choreographer.getMainThreadInstance();
}
- @Override
- public int getItemSize(P proto) {
- return mParams.getProtoSize(proto);
- }
-
- @Override
- public byte[] getBytes(P proto) {
- return mParams.getProtoBytes(proto);
- }
-
- @Override
- public void write(S encapsulatingProto, Queue<T> buffer, OutputStream os) throws IOException {
- os.write(mParams.serializeEncapsulatingProto(encapsulatingProto, buffer));
- }
-
public void start() {
synchronized (mLock) {
if (mEnabled) {
diff --git a/packages/SystemUI/src/com/android/systemui/ForegroundServiceNotificationListener.java b/packages/SystemUI/src/com/android/systemui/ForegroundServiceNotificationListener.java
index eab970626bf1..924d16dd27d7 100644
--- a/packages/SystemUI/src/com/android/systemui/ForegroundServiceNotificationListener.java
+++ b/packages/SystemUI/src/com/android/systemui/ForegroundServiceNotificationListener.java
@@ -89,7 +89,7 @@ public class ForegroundServiceNotificationListener {
}
@Override
- public void onEntryRemoved(NotificationEntry entry, int reason, boolean removedByUser) {
+ public void onEntryRemoved(NotificationEntry entry, int reason) {
removeNotification(entry.getSbn());
}
});
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/AuthBiometricView.java b/packages/SystemUI/src/com/android/systemui/biometrics/AuthBiometricView.java
index 044c5a027dac..7dea7f83f0c6 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/AuthBiometricView.java
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/AuthBiometricView.java
@@ -664,6 +664,7 @@ public abstract class AuthBiometricView extends LinearLayout {
setTextOrHide(mSubtitleView,
mBiometricPromptBundle.getString(BiometricPrompt.KEY_SUBTITLE));
+
setTextOrHide(mDescriptionView,
mBiometricPromptBundle.getString(BiometricPrompt.KEY_DESCRIPTION));
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/AuthCredentialView.java b/packages/SystemUI/src/com/android/systemui/biometrics/AuthCredentialView.java
index 8f2cf70a8184..68b05e358786 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/AuthCredentialView.java
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/AuthCredentialView.java
@@ -17,6 +17,7 @@
package com.android.systemui.biometrics;
import android.content.Context;
+import android.graphics.drawable.Drawable;
import android.hardware.biometrics.BiometricPrompt;
import android.os.AsyncTask;
import android.os.Bundle;
@@ -28,6 +29,7 @@ import android.text.TextUtils;
import android.util.AttributeSet;
import android.view.View;
import android.view.accessibility.AccessibilityManager;
+import android.widget.ImageView;
import android.widget.LinearLayout;
import android.widget.TextView;
@@ -56,6 +58,7 @@ public abstract class AuthCredentialView extends LinearLayout {
private TextView mTitleView;
private TextView mSubtitleView;
private TextView mDescriptionView;
+ private ImageView mIconView;
protected TextView mErrorView;
protected @Utils.CredentialType int mCredentialType;
@@ -176,6 +179,16 @@ public abstract class AuthCredentialView extends LinearLayout {
setTextOrHide(mDescriptionView,
mBiometricPromptBundle.getString(BiometricPrompt.KEY_DESCRIPTION));
+ final boolean isManagedProfile = Utils.isManagedProfile(mContext, mEffectiveUserId);
+ final Drawable image;
+ if (isManagedProfile) {
+ image = getResources().getDrawable(R.drawable.auth_dialog_enterprise,
+ mContext.getTheme());
+ } else {
+ image = getResources().getDrawable(R.drawable.auth_dialog_lock, mContext.getTheme());
+ }
+ mIconView.setImageDrawable(image);
+
// Only animate this if we're transitioning from a biometric view.
if (mShouldAnimateContents) {
setTranslationY(getResources()
@@ -207,6 +220,7 @@ public abstract class AuthCredentialView extends LinearLayout {
mTitleView = findViewById(R.id.title);
mSubtitleView = findViewById(R.id.subtitle);
mDescriptionView = findViewById(R.id.description);
+ mIconView = findViewById(R.id.icon);
mErrorView = findViewById(R.id.error);
}
diff --git a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleController.java b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleController.java
index a26cce0bfc0c..7c07c9d81000 100644
--- a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleController.java
+++ b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleController.java
@@ -429,12 +429,11 @@ public class BubbleController implements ConfigurationController.ConfigurationLi
}
});
- mNotificationEntryManager.setNotificationRemoveInterceptor(
+ mNotificationEntryManager.addNotificationRemoveInterceptor(
new NotificationRemoveInterceptor() {
@Override
- public boolean onNotificationRemoveRequested(String key, int reason) {
- NotificationEntry entry =
- mNotificationEntryManager.getActiveNotificationUnfiltered(key);
+ public boolean onNotificationRemoveRequested(
+ String key, NotificationEntry entry, int reason) {
return shouldInterceptDismissal(entry, reason);
}
});
diff --git a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleExpandedView.java b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleExpandedView.java
index fa1392644735..50a50633f43c 100644
--- a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleExpandedView.java
+++ b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleExpandedView.java
@@ -448,12 +448,8 @@ public class BubbleExpandedView extends LinearLayout implements View.OnClickList
int bottomInset = getRootWindowInsets() != null
? getRootWindowInsets().getStableInsetBottom()
: 0;
- int mh = mDisplaySize.y - windowLocation[1] - mSettingsIconHeight - mPointerHeight
+ return mDisplaySize.y - windowLocation[1] - mSettingsIconHeight - mPointerHeight
- mPointerMargin - bottomInset;
- Log.i(TAG, "max exp height: " + mh);
-// return mDisplaySize.y - windowLocation[1] - mSettingsIconHeight - mPointerHeight
-// - mPointerMargin - bottomInset;
- return mh;
}
/**
diff --git a/packages/SystemUI/src/com/android/systemui/controls/controller/ControlsController.kt b/packages/SystemUI/src/com/android/systemui/controls/controller/ControlsController.kt
index b02de4500043..b3ba2b22f6df 100644
--- a/packages/SystemUI/src/com/android/systemui/controls/controller/ControlsController.kt
+++ b/packages/SystemUI/src/com/android/systemui/controls/controller/ControlsController.kt
@@ -29,6 +29,7 @@ interface ControlsController : UserAwareController {
fun loadForComponent(componentName: ComponentName, callback: (List<ControlStatus>) -> Unit)
fun subscribeToFavorites()
fun changeFavoriteStatus(controlInfo: ControlInfo, state: Boolean)
+ fun countFavoritesForComponent(componentName: ComponentName): Int = 0
fun unsubscribe()
fun action(controlInfo: ControlInfo, action: ControlAction)
fun refreshStatus(componentName: ComponentName, control: Control)
diff --git a/packages/SystemUI/src/com/android/systemui/controls/controller/ControlsControllerImpl.kt b/packages/SystemUI/src/com/android/systemui/controls/controller/ControlsControllerImpl.kt
index 6ff1cf8474d3..a6f1d84877c5 100644
--- a/packages/SystemUI/src/com/android/systemui/controls/controller/ControlsControllerImpl.kt
+++ b/packages/SystemUI/src/com/android/systemui/controls/controller/ControlsControllerImpl.kt
@@ -328,6 +328,12 @@ class ControlsControllerImpl @Inject constructor (
}
}
+ override fun countFavoritesForComponent(componentName: ComponentName): Int {
+ return synchronized(currentFavorites) {
+ currentFavorites.get(componentName)?.size ?: 0
+ }
+ }
+
override fun dump(fd: FileDescriptor, pw: PrintWriter, args: Array<out String>) {
pw.println("ControlsController state:")
pw.println(" Available: $available")
diff --git a/packages/SystemUI/src/com/android/systemui/controls/management/AppAdapter.kt b/packages/SystemUI/src/com/android/systemui/controls/management/AppAdapter.kt
index 22c69086cf8c..b12243964fc1 100644
--- a/packages/SystemUI/src/com/android/systemui/controls/management/AppAdapter.kt
+++ b/packages/SystemUI/src/com/android/systemui/controls/management/AppAdapter.kt
@@ -17,6 +17,7 @@
package com.android.systemui.controls.management
import android.content.ComponentName
+import android.content.res.Resources
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
@@ -25,6 +26,7 @@ import android.widget.TextView
import androidx.lifecycle.Lifecycle
import androidx.lifecycle.LifecycleOwner
import androidx.recyclerview.widget.RecyclerView
+import com.android.settingslib.applications.DefaultAppInfo
import com.android.settingslib.widget.CandidateInfo
import com.android.systemui.R
import java.util.concurrent.Executor
@@ -46,7 +48,8 @@ class AppAdapter(
lifecycle: Lifecycle,
controlsListingController: ControlsListingController,
private val layoutInflater: LayoutInflater,
- private val onAppSelected: (ComponentName?) -> Unit = {}
+ private val onAppSelected: (ComponentName?) -> Unit = {},
+ private val favoritesRenderer: FavoritesRenderer
) : RecyclerView.Adapter<AppAdapter.Holder>() {
private var listOfServices = emptyList<CandidateInfo>()
@@ -54,7 +57,9 @@ class AppAdapter(
private val callback = object : ControlsListingController.ControlsListingCallback {
override fun onServicesUpdated(list: List<CandidateInfo>) {
uiExecutor.execute {
- listOfServices = list
+ listOfServices = list.sortedBy {
+ it.loadLabel().toString()
+ }
notifyDataSetChanged()
}
}
@@ -65,7 +70,8 @@ class AppAdapter(
}
override fun onCreateViewHolder(parent: ViewGroup, i: Int): Holder {
- return Holder(layoutInflater.inflate(R.layout.app_item, parent, false))
+ return Holder(layoutInflater.inflate(R.layout.controls_app_item, parent, false),
+ favoritesRenderer)
}
override fun getItemCount() = listOfServices.size
@@ -80,9 +86,10 @@ class AppAdapter(
/**
* Holder for binding views in the [RecyclerView]-
*/
- class Holder(view: View) : RecyclerView.ViewHolder(view) {
+ class Holder(view: View, val favRenderer: FavoritesRenderer) : RecyclerView.ViewHolder(view) {
private val icon: ImageView = itemView.requireViewById(com.android.internal.R.id.icon)
private val title: TextView = itemView.requireViewById(com.android.internal.R.id.title)
+ private val favorites: TextView = itemView.requireViewById(R.id.favorites)
/**
* Bind data to the view
@@ -91,6 +98,19 @@ class AppAdapter(
fun bindData(data: CandidateInfo) {
icon.setImageDrawable(data.loadIcon())
title.text = data.loadLabel()
+ favorites.text = favRenderer.renderFavoritesForComponent(
+ (data as DefaultAppInfo).componentName)
}
}
+}
+
+class FavoritesRenderer(
+ private val resources: Resources,
+ private val favoriteFunction: (ComponentName) -> Int
+) {
+
+ fun renderFavoritesForComponent(component: ComponentName): String {
+ val qty = favoriteFunction(component)
+ return resources.getQuantityString(R.plurals.controls_number_of_favorites, qty, qty)
+ }
} \ No newline at end of file
diff --git a/packages/SystemUI/src/com/android/systemui/controls/management/ControlAdapter.kt b/packages/SystemUI/src/com/android/systemui/controls/management/ControlAdapter.kt
index e6d3c26ea7b8..65dcc2b193d5 100644
--- a/packages/SystemUI/src/com/android/systemui/controls/management/ControlAdapter.kt
+++ b/packages/SystemUI/src/com/android/systemui/controls/management/ControlAdapter.kt
@@ -16,15 +16,20 @@
package com.android.systemui.controls.management
+import android.graphics.Rect
+import android.graphics.drawable.Icon
+import android.service.controls.DeviceTypes
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import android.widget.CheckBox
+import android.widget.ImageView
import android.widget.TextView
import androidx.recyclerview.widget.RecyclerView
import com.android.systemui.R
import com.android.systemui.controls.ControlStatus
import com.android.systemui.controls.controller.ControlInfo
+import com.android.systemui.controls.ui.RenderInfo
/**
* Adapter for binding [Control] information to views.
@@ -43,7 +48,12 @@ class ControlAdapter(
var listOfControls = emptyList<ControlStatus>()
override fun onCreateViewHolder(parent: ViewGroup, i: Int): Holder {
- return Holder(layoutInflater.inflate(R.layout.control_item, parent, false))
+ return Holder(layoutInflater.inflate(R.layout.controls_base_item, parent, false).apply {
+ layoutParams.apply {
+ width = ViewGroup.LayoutParams.MATCH_PARENT
+ }
+ elevation = 15f
+ })
}
override fun getItemCount() = listOfControls.size
@@ -56,9 +66,13 @@ class ControlAdapter(
* Holder for binding views in the [RecyclerView]-
*/
class Holder(view: View) : RecyclerView.ViewHolder(view) {
+ private val icon: ImageView = itemView.requireViewById(R.id.icon)
private val title: TextView = itemView.requireViewById(R.id.title)
private val subtitle: TextView = itemView.requireViewById(R.id.subtitle)
- private val favorite: CheckBox = itemView.requireViewById(R.id.favorite)
+ private val removed: TextView = itemView.requireViewById(R.id.status)
+ private val favorite: CheckBox = itemView.requireViewById<CheckBox>(R.id.favorite).apply {
+ visibility = View.VISIBLE
+ }
/**
* Bind data to the view
@@ -68,9 +82,11 @@ class ControlAdapter(
* pre-populated with the [Control] information and the new favorite status.
*/
fun bindData(data: ControlStatus, callback: (ControlInfo.Builder, Boolean) -> Unit) {
+ val renderInfo = getRenderInfo(data.control.deviceType, data.favorite)
title.text = data.control.title
subtitle.text = data.control.subtitle
favorite.isChecked = data.favorite
+ removed.text = if (data.removed) "Removed" else ""
favorite.setOnClickListener {
val infoBuilder = ControlInfo.Builder().apply {
controlId = data.control.controlId
@@ -79,6 +95,25 @@ class ControlAdapter(
}
callback(infoBuilder, favorite.isChecked)
}
+ itemView.setOnClickListener {
+ favorite.performClick()
+ }
+ applyRenderInfo(renderInfo)
+ }
+
+ private fun getRenderInfo(
+ @DeviceTypes.DeviceType deviceType: Int,
+ favorite: Boolean
+ ): RenderInfo {
+ return RenderInfo.lookup(deviceType, favorite)
+ }
+
+ private fun applyRenderInfo(ri: RenderInfo) {
+ val context = itemView.context
+ val fg = context.getResources().getColorStateList(ri.foreground, context.getTheme())
+
+ icon.setImageIcon(Icon.createWithResource(context, ri.iconResourceId))
+ icon.setImageTintList(fg)
}
}
@@ -86,4 +121,23 @@ class ControlAdapter(
listOfControls = list
notifyDataSetChanged()
}
+}
+
+class MarginItemDecorator(
+ private val topMargin: Int,
+ private val sideMargins: Int
+) : RecyclerView.ItemDecoration() {
+
+ override fun getItemOffsets(
+ outRect: Rect,
+ view: View,
+ parent: RecyclerView,
+ state: RecyclerView.State
+ ) {
+ outRect.apply {
+ top = topMargin
+ left = sideMargins
+ right = sideMargins
+ }
+ }
} \ No newline at end of file
diff --git a/packages/SystemUI/src/com/android/systemui/controls/management/ControlsFavoritingActivity.kt b/packages/SystemUI/src/com/android/systemui/controls/management/ControlsFavoritingActivity.kt
index 7ee4fd5b059e..be5258344492 100644
--- a/packages/SystemUI/src/com/android/systemui/controls/management/ControlsFavoritingActivity.kt
+++ b/packages/SystemUI/src/com/android/systemui/controls/management/ControlsFavoritingActivity.kt
@@ -20,8 +20,10 @@ import android.app.Activity
import android.content.ComponentName
import android.os.Bundle
import android.view.LayoutInflater
-import androidx.recyclerview.widget.LinearLayoutManager
+import android.widget.TextView
+import androidx.recyclerview.widget.GridLayoutManager
import androidx.recyclerview.widget.RecyclerView
+import com.android.systemui.R
import com.android.systemui.broadcast.BroadcastDispatcher
import com.android.systemui.controls.controller.ControlInfo
import com.android.systemui.controls.controller.ControlsControllerImpl
@@ -44,6 +46,7 @@ class ControlsFavoritingActivity @Inject constructor(
private lateinit var recyclerView: RecyclerView
private lateinit var adapter: ControlAdapter
+ private var component: ComponentName? = null
private val currentUserTracker = object : CurrentUserTracker(broadcastDispatcher) {
private val startingUser = controller.currentUserId
@@ -56,10 +59,9 @@ class ControlsFavoritingActivity @Inject constructor(
}
}
- private var component: ComponentName? = null
-
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
+ setContentView(R.layout.controls_management)
val app = intent.getCharSequenceExtra(EXTRA_APP)
component = intent.getParcelableExtra<ComponentName>(EXTRA_COMPONENT)
@@ -72,17 +74,17 @@ class ControlsFavoritingActivity @Inject constructor(
}
} ?: { _, _ -> Unit }
- recyclerView = RecyclerView(applicationContext)
+ recyclerView = requireViewById(R.id.list)
adapter = ControlAdapter(LayoutInflater.from(applicationContext), callback)
recyclerView.adapter = adapter
- recyclerView.layoutManager = LinearLayoutManager(applicationContext)
+ recyclerView.layoutManager = GridLayoutManager(applicationContext, 2)
+ val margin = resources.getDimensionPixelSize(R.dimen.controls_card_margin)
+ recyclerView.addItemDecoration(MarginItemDecorator(margin, margin))
- if (app != null) {
- setTitle("Controls for $app")
- } else {
- setTitle("Controls")
- }
- setContentView(recyclerView)
+ requireViewById<TextView>(R.id.title).text = app?.let { it }
+ ?: resources.getText(R.string.controls_favorite_default_title)
+ requireViewById<TextView>(R.id.subtitle).text =
+ resources.getText(R.string.controls_favorite_subtitle)
currentUserTracker.startTracking()
}
diff --git a/packages/SystemUI/src/com/android/systemui/controls/management/ControlsProviderSelectorActivity.kt b/packages/SystemUI/src/com/android/systemui/controls/management/ControlsProviderSelectorActivity.kt
index 5ff949c98806..645e929d6a10 100644
--- a/packages/SystemUI/src/com/android/systemui/controls/management/ControlsProviderSelectorActivity.kt
+++ b/packages/SystemUI/src/com/android/systemui/controls/management/ControlsProviderSelectorActivity.kt
@@ -20,9 +20,12 @@ import android.content.ComponentName
import android.content.Intent
import android.os.Bundle
import android.view.LayoutInflater
+import android.widget.TextView
import androidx.recyclerview.widget.LinearLayoutManager
import androidx.recyclerview.widget.RecyclerView
+import com.android.systemui.R
import com.android.systemui.broadcast.BroadcastDispatcher
+import com.android.systemui.controls.controller.ControlsController
import com.android.systemui.dagger.qualifiers.Background
import com.android.systemui.dagger.qualifiers.Main
import com.android.systemui.settings.CurrentUserTracker
@@ -37,6 +40,7 @@ class ControlsProviderSelectorActivity @Inject constructor(
@Main private val executor: Executor,
@Background private val backExecutor: Executor,
private val listingController: ControlsListingController,
+ private val controlsController: ControlsController,
broadcastDispatcher: BroadcastDispatcher
) : LifecycleActivity() {
@@ -58,13 +62,19 @@ class ControlsProviderSelectorActivity @Inject constructor(
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
+ setContentView(R.layout.controls_management)
- recyclerView = RecyclerView(applicationContext)
+ recyclerView = requireViewById(R.id.list)
recyclerView.adapter = AppAdapter(executor, lifecycle, listingController,
- LayoutInflater.from(this), ::launchFavoritingActivity)
+ LayoutInflater.from(this), ::launchFavoritingActivity,
+ FavoritesRenderer(resources, controlsController::countFavoritesForComponent))
recyclerView.layoutManager = LinearLayoutManager(applicationContext)
- setContentView(recyclerView)
+ requireViewById<TextView>(R.id.title).text =
+ resources.getText(R.string.controls_providers_title)
+ requireViewById<TextView>(R.id.subtitle).text =
+ resources.getText(R.string.controls_providers_subtitle)
+
currentUserTracker.startTracking()
}
diff --git a/packages/SystemUI/src/com/android/systemui/controls/ui/Behavior.kt b/packages/SystemUI/src/com/android/systemui/controls/ui/Behavior.kt
new file mode 100644
index 000000000000..44f3fbc58eaf
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/controls/ui/Behavior.kt
@@ -0,0 +1,21 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.controls.ui
+
+interface Behavior {
+ fun apply(cvh: ControlViewHolder, cws: ControlWithState)
+}
diff --git a/packages/SystemUI/src/com/android/systemui/controls/ui/ControlViewHolder.kt b/packages/SystemUI/src/com/android/systemui/controls/ui/ControlViewHolder.kt
index 81b5f3698567..fad2d94d6cf3 100644
--- a/packages/SystemUI/src/com/android/systemui/controls/ui/ControlViewHolder.kt
+++ b/packages/SystemUI/src/com/android/systemui/controls/ui/ControlViewHolder.kt
@@ -17,22 +17,17 @@
package com.android.systemui.controls.ui
import android.content.Context
+import android.content.Intent
import android.graphics.drawable.ClipDrawable
-import android.graphics.drawable.Drawable
import android.graphics.drawable.GradientDrawable
import android.graphics.drawable.Icon
import android.graphics.drawable.LayerDrawable
import android.service.controls.Control
-import android.service.controls.DeviceTypes
-import android.service.controls.actions.BooleanAction
import android.service.controls.actions.ControlAction
-import android.service.controls.actions.FloatAction
import android.service.controls.templates.ControlTemplate
-import android.service.controls.templates.RangeTemplate
+import android.service.controls.templates.TemperatureControlTemplate
import android.service.controls.templates.ToggleRangeTemplate
import android.service.controls.templates.ToggleTemplate
-import android.util.TypedValue
-import android.view.MotionEvent
import android.view.View
import android.view.ViewGroup
import android.widget.ImageView
@@ -41,8 +36,8 @@ import android.widget.TextView
import com.android.systemui.controls.controller.ControlsController
import com.android.systemui.R
-private const val MIN_LEVEL = 0
-private const val MAX_LEVEL = 10000
+const val MIN_LEVEL = 0
+const val MAX_LEVEL = 10000
class ControlViewHolder(
val layout: ViewGroup,
@@ -78,6 +73,16 @@ class ControlViewHolder(
Pair(Control.STATUS_UNKNOWN, ControlTemplate.NO_TEMPLATE)
}
+ cws.control?.let { c ->
+ layout.setOnLongClickListener(View.OnLongClickListener() {
+ val closeDialog = Intent(Intent.ACTION_CLOSE_SYSTEM_DIALOGS)
+ context.sendBroadcast(closeDialog)
+
+ c.getAppIntent().send()
+ true
+ })
+ }
+
findBehavior(status, template).apply(this, cws)
}
@@ -88,13 +93,14 @@ class ControlViewHolder(
private fun findBehavior(status: Int, template: ControlTemplate): Behavior {
return when {
status == Control.STATUS_UNKNOWN -> UnknownBehavior()
- template is ToggleTemplate -> ToggleTemplateBehavior()
- template is ToggleRangeTemplate -> ToggleRangeTemplateBehavior()
+ template is ToggleTemplate -> ToggleBehavior()
+ template is ToggleRangeTemplate -> ToggleRangeBehavior()
+ template is TemperatureControlTemplate -> TemperatureControlBehavior()
else -> {
object : Behavior {
override fun apply(cvh: ControlViewHolder, cws: ControlWithState) {
cvh.status.setText(cws.control?.getStatusText())
- cvh.applyRenderInfo(findRenderInfo(cws.ci.deviceType, false))
+ cvh.applyRenderInfo(RenderInfo.lookup(cws.ci.deviceType, false))
}
}
}
@@ -118,283 +124,3 @@ class ControlViewHolder(
icon.setEnabled(enabled)
}
}
-
-private interface Behavior {
- fun apply(cvh: ControlViewHolder, cws: ControlWithState)
-
- fun findRenderInfo(deviceType: Int, isActive: Boolean): RenderInfo =
- deviceRenderMap.getOrDefault(deviceType, unknownDeviceMap).getValue(isActive)
-}
-
-private class UnknownBehavior : Behavior {
- override fun apply(cvh: ControlViewHolder, cws: ControlWithState) {
- cvh.status.setText("Loading...")
- cvh.applyRenderInfo(findRenderInfo(cws.ci.deviceType, false))
- }
-}
-
-private class ToggleRangeTemplateBehavior : Behavior {
- lateinit var clipLayer: Drawable
- lateinit var template: ToggleRangeTemplate
- lateinit var control: Control
- lateinit var cvh: ControlViewHolder
- lateinit var rangeTemplate: RangeTemplate
- lateinit var statusExtra: TextView
- lateinit var status: TextView
- lateinit var context: Context
-
- override fun apply(cvh: ControlViewHolder, cws: ControlWithState) {
- this.control = cws.control!!
- this.cvh = cvh
-
- statusExtra = cvh.statusExtra
- status = cvh.status
-
- status.setText(control.getStatusText())
-
- context = status.getContext()
-
- cvh.layout.setOnTouchListener(ToggleRangeTouchListener())
-
- val ld = cvh.layout.getBackground() as LayerDrawable
- clipLayer = ld.findDrawableByLayerId(R.id.clip_layer)
-
- template = control.getControlTemplate() as ToggleRangeTemplate
- rangeTemplate = template.getRange()
-
- val checked = template.isChecked()
- val deviceType = control.getDeviceType()
-
- updateRange((rangeTemplate.getCurrentValue() / 100.0f), checked)
-
- cvh.setEnabled(checked)
- cvh.applyRenderInfo(findRenderInfo(deviceType, checked))
- }
-
- fun toggle() {
- cvh.action(BooleanAction(template.getTemplateId(), !template.isChecked()))
-
- val nextLevel = if (template.isChecked()) MIN_LEVEL else MAX_LEVEL
- clipLayer.setLevel(nextLevel)
- }
-
- fun beginUpdateRange() {
- status.setVisibility(View.GONE)
- statusExtra.setTextSize(TypedValue.COMPLEX_UNIT_PX, context.getResources()
- .getDimensionPixelSize(R.dimen.control_status_expanded).toFloat())
- }
-
- fun updateRange(f: Float, checked: Boolean) {
- clipLayer.setLevel(if (checked) (MAX_LEVEL * f).toInt() else MIN_LEVEL)
-
- if (checked && f < 100.0f && f > 0.0f) {
- statusExtra.setText("" + (f * 100.0).toInt() + "%")
- statusExtra.setVisibility(View.VISIBLE)
- } else {
- statusExtra.setText("")
- statusExtra.setVisibility(View.GONE)
- }
- }
-
- fun endUpdateRange(f: Float) {
- statusExtra.setText(" - " + (f * 100.0).toInt() + "%")
-
- val newValue = rangeTemplate.getMinValue() +
- (f * (rangeTemplate.getMaxValue() - rangeTemplate.getMinValue()))
-
- statusExtra.setTextSize(TypedValue.COMPLEX_UNIT_PX, context.getResources()
- .getDimensionPixelSize(R.dimen.control_status_normal).toFloat())
- status.setVisibility(View.VISIBLE)
-
- cvh.action(FloatAction(rangeTemplate.getTemplateId(), findNearestStep(newValue)))
- }
-
- fun findNearestStep(value: Float): Float {
- var minDiff = 1000f
-
- var f = rangeTemplate.getMinValue()
- while (f <= rangeTemplate.getMaxValue()) {
- val currentDiff = Math.abs(value - f)
- if (currentDiff < minDiff) {
- minDiff = currentDiff
- } else {
- return f - rangeTemplate.getStepValue()
- }
-
- f += rangeTemplate.getStepValue()
- }
-
- return rangeTemplate.getMaxValue()
- }
-
- inner class ToggleRangeTouchListener() : View.OnTouchListener {
- private var initialTouchX: Float = 0.0f
- private var initialTouchY: Float = 0.0f
- private var isDragging: Boolean = false
- private val minDragDiff = 20
-
- override fun onTouch(v: View, e: MotionEvent): Boolean {
- when (e.getActionMasked()) {
- MotionEvent.ACTION_DOWN -> setupTouch(e)
- MotionEvent.ACTION_MOVE -> detectDrag(v, e)
- MotionEvent.ACTION_UP -> endTouch(v, e)
- }
-
- return true
- }
-
- private fun setupTouch(e: MotionEvent) {
- initialTouchX = e.getX()
- initialTouchY = e.getY()
- }
-
- private fun detectDrag(v: View, e: MotionEvent) {
- val xDiff = Math.abs(e.getX() - initialTouchX)
- val yDiff = Math.abs(e.getY() - initialTouchY)
-
- if (xDiff < minDragDiff) {
- isDragging = false
- } else {
- if (!isDragging) {
- this@ToggleRangeTemplateBehavior.beginUpdateRange()
- }
- v.getParent().requestDisallowInterceptTouchEvent(true)
- isDragging = true
- if (yDiff > xDiff) {
- endTouch(v, e)
- } else {
- val percent = Math.max(0.0f, Math.min(1.0f, e.getX() / v.getWidth()))
- this@ToggleRangeTemplateBehavior.updateRange(percent, true)
- }
- }
- }
-
- private fun endTouch(v: View, e: MotionEvent) {
- if (!isDragging) {
- this@ToggleRangeTemplateBehavior.toggle()
- } else {
- val percent = Math.max(0.0f, Math.min(1.0f, e.getX() / v.getWidth()))
- this@ToggleRangeTemplateBehavior.endUpdateRange(percent)
- }
-
- initialTouchX = 0.0f
- initialTouchY = 0.0f
- isDragging = false
- }
- }
-}
-
-private class ToggleTemplateBehavior : Behavior {
- lateinit var clipLayer: Drawable
- lateinit var template: ToggleTemplate
- lateinit var control: Control
- lateinit var cvh: ControlViewHolder
- lateinit var context: Context
- lateinit var status: TextView
-
- override fun apply(cvh: ControlViewHolder, cws: ControlWithState) {
- this.control = cws.control!!
- this.cvh = cvh
- status = cvh.status
-
- status.setText(control.getStatusText())
-
- cvh.layout.setOnClickListener(View.OnClickListener() { toggle() })
-
- val ld = cvh.layout.getBackground() as LayerDrawable
- clipLayer = ld.findDrawableByLayerId(R.id.clip_layer)
-
- template = control.getControlTemplate() as ToggleTemplate
-
- val checked = template.isChecked()
- val deviceType = control.getDeviceType()
-
- clipLayer.setLevel(if (checked) MAX_LEVEL else MIN_LEVEL)
- cvh.setEnabled(checked)
- cvh.applyRenderInfo(findRenderInfo(deviceType, checked))
- }
-
- fun toggle() {
- cvh.action(BooleanAction(template.getTemplateId(), !template.isChecked()))
-
- val nextLevel = if (template.isChecked()) MIN_LEVEL else MAX_LEVEL
- clipLayer.setLevel(nextLevel)
- }
-}
-
-internal data class RenderInfo(val iconResourceId: Int, val foreground: Int, val background: Int)
-
-private val unknownDeviceMap = mapOf(
- false to RenderInfo(
- R.drawable.ic_light_off_gm2_24px,
- R.color.unknown_foreground,
- R.color.unknown_foreground),
- true to RenderInfo(
- R.drawable.ic_lightbulb_outline_gm2_24px,
- R.color.unknown_foreground,
- R.color.unknown_foreground)
-)
-
-private val deviceRenderMap = mapOf<Int, Map<Boolean, RenderInfo>>(
- DeviceTypes.TYPE_UNKNOWN to unknownDeviceMap,
- DeviceTypes.TYPE_LIGHT to mapOf(
- false to RenderInfo(
- R.drawable.ic_light_off_gm2_24px,
- R.color.light_foreground,
- R.color.light_background),
- true to RenderInfo(
- R.drawable.ic_lightbulb_outline_gm2_24px,
- R.color.light_foreground,
- R.color.light_background)
- ),
- DeviceTypes.TYPE_THERMOSTAT to mapOf(
- false to RenderInfo(
- R.drawable.ic_device_thermostat_gm2_24px,
- R.color.light_foreground,
- R.color.light_background),
- true to RenderInfo(
- R.drawable.ic_device_thermostat_gm2_24px,
- R.color.light_foreground,
- R.color.light_background)
- ),
- DeviceTypes.TYPE_CAMERA to mapOf(
- false to RenderInfo(
- R.drawable.ic_videocam_gm2_24px,
- R.color.light_foreground,
- R.color.light_background),
- true to RenderInfo(
- R.drawable.ic_videocam_gm2_24px,
- R.color.light_foreground,
- R.color.light_background)
- ),
- DeviceTypes.TYPE_LOCK to mapOf(
- false to RenderInfo(
- R.drawable.ic_lock_open_gm2_24px,
- R.color.lock_foreground,
- R.color.lock_background),
- true to RenderInfo(
- R.drawable.ic_lock_gm2_24px,
- R.color.lock_foreground,
- R.color.lock_background)
- ),
- DeviceTypes.TYPE_SWITCH to mapOf(
- false to RenderInfo(
- R.drawable.ic_switches_gm2_24px,
- R.color.lock_foreground,
- R.color.lock_background),
- true to RenderInfo(
- R.drawable.ic_switches_gm2_24px,
- R.color.lock_foreground,
- R.color.lock_background)
- ),
- DeviceTypes.TYPE_OUTLET to mapOf(
- false to RenderInfo(
- R.drawable.ic_power_off_gm2_24px,
- R.color.lock_foreground,
- R.color.lock_background),
- true to RenderInfo(
- R.drawable.ic_power_gm2_24px,
- R.color.lock_foreground,
- R.color.lock_background)
- )
-)
diff --git a/packages/SystemUI/src/com/android/systemui/controls/ui/ControlsUiControllerImpl.kt b/packages/SystemUI/src/com/android/systemui/controls/ui/ControlsUiControllerImpl.kt
index 40a93d69fef4..a777faf57fce 100644
--- a/packages/SystemUI/src/com/android/systemui/controls/ui/ControlsUiControllerImpl.kt
+++ b/packages/SystemUI/src/com/android/systemui/controls/ui/ControlsUiControllerImpl.kt
@@ -138,7 +138,10 @@ class ControlsUiControllerImpl @Inject constructor (
val serviceIntent = Intent()
serviceIntent.setComponent(ComponentName("com.android.systemui.home.mock",
"com.android.systemui.home.mock.AuthService"))
- context.bindService(serviceIntent, tokenProviderConnection!!, Context.BIND_AUTO_CREATE)
+ if (!context.bindService(serviceIntent, tokenProviderConnection!!,
+ Context.BIND_AUTO_CREATE)) {
+ controlsController.get().subscribeToFavorites()
+ }
}
private fun showInitialSetupView() {
diff --git a/packages/SystemUI/src/com/android/systemui/controls/ui/RenderInfo.kt b/packages/SystemUI/src/com/android/systemui/controls/ui/RenderInfo.kt
new file mode 100644
index 000000000000..093c99f57a9a
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/controls/ui/RenderInfo.kt
@@ -0,0 +1,117 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.controls.ui
+
+import android.service.controls.DeviceTypes
+import android.service.controls.templates.TemperatureControlTemplate
+
+import com.android.systemui.R
+
+data class IconState(val disabledResourceId: Int, val enabledResourceId: Int) {
+ operator fun get(state: Boolean): Int {
+ return if (state) {
+ enabledResourceId
+ } else {
+ disabledResourceId
+ }
+ }
+}
+
+data class RenderInfo(val iconResourceId: Int, val foreground: Int, val background: Int) {
+
+ companion object {
+ fun lookup(deviceType: Int, enabled: Boolean): RenderInfo {
+ val iconState = deviceIconMap.getValue(deviceType)
+ val (fg, bg) = deviceColorMap.getValue(deviceType)
+ return RenderInfo(iconState[enabled], fg, bg)
+ }
+
+ fun lookup(deviceType: Int, offset: Int, enabled: Boolean): RenderInfo {
+ val key = deviceType * BUCKET_SIZE + offset
+ return lookup(key, enabled)
+ }
+ }
+}
+
+private const val BUCKET_SIZE = 1000
+private const val THERMOSTAT_RANGE = DeviceTypes.TYPE_THERMOSTAT * BUCKET_SIZE
+
+private val deviceColorMap = mapOf<Int, Pair<Int, Int>>(
+ (THERMOSTAT_RANGE + TemperatureControlTemplate.MODE_HEAT) to
+ Pair(R.color.thermo_heat_foreground, R.color.thermo_heat_background),
+ (THERMOSTAT_RANGE + TemperatureControlTemplate.MODE_COOL) to
+ Pair(R.color.thermo_cool_foreground, R.color.thermo_cool_background),
+ DeviceTypes.TYPE_LIGHT to Pair(R.color.light_foreground, R.color.light_background)
+).withDefault {
+ Pair(R.color.control_foreground, R.color.control_background)
+}
+
+private val deviceIconMap = mapOf<Int, IconState>(
+ THERMOSTAT_RANGE to IconState(
+ R.drawable.ic_device_thermostat_gm2_24px,
+ R.drawable.ic_device_thermostat_gm2_24px
+ ),
+ (THERMOSTAT_RANGE + TemperatureControlTemplate.MODE_OFF) to IconState(
+ R.drawable.ic_device_thermostat_gm2_24px,
+ R.drawable.ic_device_thermostat_gm2_24px
+ ),
+ (THERMOSTAT_RANGE + TemperatureControlTemplate.MODE_HEAT) to IconState(
+ R.drawable.ic_device_thermostat_gm2_24px,
+ R.drawable.ic_device_thermostat_gm2_24px
+ ),
+ (THERMOSTAT_RANGE + TemperatureControlTemplate.MODE_COOL) to IconState(
+ R.drawable.ic_device_thermostat_gm2_24px,
+ R.drawable.ic_device_thermostat_gm2_24px
+ ),
+ (THERMOSTAT_RANGE + TemperatureControlTemplate.MODE_HEAT_COOL) to IconState(
+ R.drawable.ic_device_thermostat_gm2_24px,
+ R.drawable.ic_device_thermostat_gm2_24px
+ ),
+ (THERMOSTAT_RANGE + TemperatureControlTemplate.MODE_ECO) to IconState(
+ R.drawable.ic_device_thermostat_gm2_24px,
+ R.drawable.ic_device_thermostat_gm2_24px
+ ),
+ DeviceTypes.TYPE_THERMOSTAT to IconState(
+ R.drawable.ic_device_thermostat_gm2_24px,
+ R.drawable.ic_device_thermostat_gm2_24px
+ ),
+ DeviceTypes.TYPE_LIGHT to IconState(
+ R.drawable.ic_light_off_gm2_24px,
+ R.drawable.ic_lightbulb_outline_gm2_24px
+ ),
+ DeviceTypes.TYPE_CAMERA to IconState(
+ R.drawable.ic_videocam_gm2_24px,
+ R.drawable.ic_videocam_gm2_24px
+ ),
+ DeviceTypes.TYPE_LOCK to IconState(
+ R.drawable.ic_lock_open_gm2_24px,
+ R.drawable.ic_lock_gm2_24px
+ ),
+ DeviceTypes.TYPE_SWITCH to IconState(
+ R.drawable.ic_switches_gm2_24px,
+ R.drawable.ic_switches_gm2_24px
+ ),
+ DeviceTypes.TYPE_OUTLET to IconState(
+ R.drawable.ic_power_off_gm2_24px,
+ R.drawable.ic_power_gm2_24px
+ )
+).withDefault {
+ IconState(
+ R.drawable.ic_light_off_gm2_24px,
+ R.drawable.ic_lightbulb_outline_gm2_24px
+ )
+}
diff --git a/packages/SystemUI/src/com/android/systemui/controls/ui/TemperatureControlBehavior.kt b/packages/SystemUI/src/com/android/systemui/controls/ui/TemperatureControlBehavior.kt
new file mode 100644
index 000000000000..ae0ebbb9e1bb
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/controls/ui/TemperatureControlBehavior.kt
@@ -0,0 +1,56 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.controls.ui
+
+import android.content.Context
+import android.graphics.drawable.Drawable
+import android.graphics.drawable.LayerDrawable
+import android.service.controls.Control
+import android.service.controls.templates.TemperatureControlTemplate
+import android.widget.TextView
+
+import com.android.systemui.R
+
+class TemperatureControlBehavior : Behavior {
+ lateinit var clipLayer: Drawable
+ lateinit var control: Control
+ lateinit var cvh: ControlViewHolder
+ lateinit var template: TemperatureControlTemplate
+ lateinit var status: TextView
+ lateinit var context: Context
+
+ override fun apply(cvh: ControlViewHolder, cws: ControlWithState) {
+ this.control = cws.control!!
+ this.cvh = cvh
+ status = cvh.status
+
+ status.setText(control.getStatusText())
+
+ val ld = cvh.layout.getBackground() as LayerDrawable
+ clipLayer = ld.findDrawableByLayerId(R.id.clip_layer)
+
+ template = control.getControlTemplate() as TemperatureControlTemplate
+
+ val activeMode = template.getCurrentActiveMode()
+ val enabled = activeMode != 0 && activeMode != TemperatureControlTemplate.MODE_OFF
+ val deviceType = control.getDeviceType()
+
+ clipLayer.setLevel(if (enabled) MAX_LEVEL else MIN_LEVEL)
+ cvh.setEnabled(enabled)
+ cvh.applyRenderInfo(RenderInfo.lookup(deviceType, activeMode, enabled))
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/controls/ui/ToggleBehavior.kt b/packages/SystemUI/src/com/android/systemui/controls/ui/ToggleBehavior.kt
new file mode 100644
index 000000000000..7cd3ab795678
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/controls/ui/ToggleBehavior.kt
@@ -0,0 +1,66 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.controls.ui
+
+import android.content.Context
+import android.graphics.drawable.Drawable
+import android.graphics.drawable.LayerDrawable
+import android.view.View
+import android.widget.TextView
+import android.service.controls.Control
+import android.service.controls.actions.BooleanAction
+import android.service.controls.templates.ToggleTemplate
+
+import com.android.systemui.R
+
+class ToggleBehavior : Behavior {
+ lateinit var clipLayer: Drawable
+ lateinit var template: ToggleTemplate
+ lateinit var control: Control
+ lateinit var cvh: ControlViewHolder
+ lateinit var context: Context
+ lateinit var status: TextView
+
+ override fun apply(cvh: ControlViewHolder, cws: ControlWithState) {
+ this.control = cws.control!!
+ this.cvh = cvh
+ status = cvh.status
+
+ status.setText(control.getStatusText())
+
+ cvh.layout.setOnClickListener(View.OnClickListener() { toggle() })
+
+ val ld = cvh.layout.getBackground() as LayerDrawable
+ clipLayer = ld.findDrawableByLayerId(R.id.clip_layer)
+
+ template = control.getControlTemplate() as ToggleTemplate
+
+ val checked = template.isChecked()
+ val deviceType = control.getDeviceType()
+
+ clipLayer.setLevel(if (checked) MAX_LEVEL else MIN_LEVEL)
+ cvh.setEnabled(checked)
+ cvh.applyRenderInfo(RenderInfo.lookup(deviceType, checked))
+ }
+
+ fun toggle() {
+ cvh.action(BooleanAction(template.getTemplateId(), !template.isChecked()))
+
+ val nextLevel = if (template.isChecked()) MIN_LEVEL else MAX_LEVEL
+ clipLayer.setLevel(nextLevel)
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/controls/ui/ToggleRangeBehavior.kt b/packages/SystemUI/src/com/android/systemui/controls/ui/ToggleRangeBehavior.kt
new file mode 100644
index 000000000000..a6918f50a977
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/controls/ui/ToggleRangeBehavior.kt
@@ -0,0 +1,183 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.controls.ui
+
+import android.content.Context
+import android.graphics.drawable.Drawable
+import android.graphics.drawable.LayerDrawable
+import android.view.MotionEvent
+import android.view.View
+import android.widget.TextView
+import android.service.controls.Control
+import android.service.controls.actions.BooleanAction
+import android.service.controls.actions.FloatAction
+import android.service.controls.templates.RangeTemplate
+import android.service.controls.templates.ToggleRangeTemplate
+import android.util.TypedValue
+
+import com.android.systemui.R
+
+class ToggleRangeBehavior : Behavior {
+ lateinit var clipLayer: Drawable
+ lateinit var template: ToggleRangeTemplate
+ lateinit var control: Control
+ lateinit var cvh: ControlViewHolder
+ lateinit var rangeTemplate: RangeTemplate
+ lateinit var statusExtra: TextView
+ lateinit var status: TextView
+ lateinit var context: Context
+
+ override fun apply(cvh: ControlViewHolder, cws: ControlWithState) {
+ this.control = cws.control!!
+ this.cvh = cvh
+
+ statusExtra = cvh.statusExtra
+ status = cvh.status
+
+ status.setText(control.getStatusText())
+
+ context = status.getContext()
+
+ cvh.layout.setOnTouchListener(ToggleRangeTouchListener())
+
+ val ld = cvh.layout.getBackground() as LayerDrawable
+ clipLayer = ld.findDrawableByLayerId(R.id.clip_layer)
+
+ template = control.getControlTemplate() as ToggleRangeTemplate
+ rangeTemplate = template.getRange()
+
+ val checked = template.isChecked()
+ val deviceType = control.getDeviceType()
+
+ updateRange((rangeTemplate.getCurrentValue() / 100.0f), checked)
+
+ cvh.setEnabled(checked)
+ cvh.applyRenderInfo(RenderInfo.lookup(deviceType, checked))
+ }
+
+ fun toggle() {
+ cvh.action(BooleanAction(template.getTemplateId(), !template.isChecked()))
+
+ val nextLevel = if (template.isChecked()) MIN_LEVEL else MAX_LEVEL
+ clipLayer.setLevel(nextLevel)
+ }
+
+ fun beginUpdateRange() {
+ status.setVisibility(View.GONE)
+ statusExtra.setTextSize(TypedValue.COMPLEX_UNIT_PX, context.getResources()
+ .getDimensionPixelSize(R.dimen.control_status_expanded).toFloat())
+ }
+
+ fun updateRange(f: Float, checked: Boolean) {
+ clipLayer.setLevel(if (checked) (MAX_LEVEL * f).toInt() else MIN_LEVEL)
+
+ if (checked && f < 100.0f && f > 0.0f) {
+ statusExtra.setText("" + (f * 100.0).toInt() + "%")
+ statusExtra.setVisibility(View.VISIBLE)
+ } else {
+ statusExtra.setText("")
+ statusExtra.setVisibility(View.GONE)
+ }
+ }
+
+ fun endUpdateRange(f: Float) {
+ statusExtra.setText(" - " + (f * 100.0).toInt() + "%")
+
+ val newValue = rangeTemplate.getMinValue() +
+ (f * (rangeTemplate.getMaxValue() - rangeTemplate.getMinValue()))
+
+ statusExtra.setTextSize(TypedValue.COMPLEX_UNIT_PX, context.getResources()
+ .getDimensionPixelSize(R.dimen.control_status_normal).toFloat())
+ status.setVisibility(View.VISIBLE)
+
+ cvh.action(FloatAction(rangeTemplate.getTemplateId(), findNearestStep(newValue)))
+ }
+
+ fun findNearestStep(value: Float): Float {
+ var minDiff = 1000f
+
+ var f = rangeTemplate.getMinValue()
+ while (f <= rangeTemplate.getMaxValue()) {
+ val currentDiff = Math.abs(value - f)
+ if (currentDiff < minDiff) {
+ minDiff = currentDiff
+ } else {
+ return f - rangeTemplate.getStepValue()
+ }
+
+ f += rangeTemplate.getStepValue()
+ }
+
+ return rangeTemplate.getMaxValue()
+ }
+
+ inner class ToggleRangeTouchListener() : View.OnTouchListener {
+ private var initialTouchX: Float = 0.0f
+ private var initialTouchY: Float = 0.0f
+ private var isDragging: Boolean = false
+ private val minDragDiff = 20
+
+ override fun onTouch(v: View, e: MotionEvent): Boolean {
+ when (e.getActionMasked()) {
+ MotionEvent.ACTION_DOWN -> setupTouch(e)
+ MotionEvent.ACTION_MOVE -> detectDrag(v, e)
+ MotionEvent.ACTION_UP -> endTouch(v, e)
+ }
+
+ return true
+ }
+
+ private fun setupTouch(e: MotionEvent) {
+ initialTouchX = e.getX()
+ initialTouchY = e.getY()
+ }
+
+ private fun detectDrag(v: View, e: MotionEvent) {
+ val xDiff = Math.abs(e.getX() - initialTouchX)
+ val yDiff = Math.abs(e.getY() - initialTouchY)
+
+ if (xDiff < minDragDiff) {
+ isDragging = false
+ } else {
+ if (!isDragging) {
+ this@ToggleRangeBehavior.beginUpdateRange()
+ }
+ v.getParent().requestDisallowInterceptTouchEvent(true)
+ isDragging = true
+ if (yDiff > xDiff) {
+ endTouch(v, e)
+ } else {
+ val percent = Math.max(0.0f, Math.min(1.0f, e.getX() / v.getWidth()))
+ this@ToggleRangeBehavior.updateRange(percent, true)
+ }
+ }
+ }
+
+ private fun endTouch(v: View, e: MotionEvent) {
+ if (!isDragging) {
+ this@ToggleRangeBehavior.toggle()
+ } else {
+ val percent = Math.max(0.0f, Math.min(1.0f, e.getX() / v.getWidth()))
+ this@ToggleRangeBehavior.endUpdateRange(percent)
+ }
+
+ initialTouchX = 0.0f
+ initialTouchY = 0.0f
+ isDragging = false
+ }
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/controls/ui/UnknownBehavior.kt b/packages/SystemUI/src/com/android/systemui/controls/ui/UnknownBehavior.kt
new file mode 100644
index 000000000000..5a6e5b481544
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/controls/ui/UnknownBehavior.kt
@@ -0,0 +1,25 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.controls.ui
+
+class UnknownBehavior : Behavior {
+ override fun apply(cvh: ControlViewHolder, cws: ControlWithState) {
+ cvh.status.setText("Loading...")
+ cvh.setEnabled(false)
+ cvh.applyRenderInfo(RenderInfo.lookup(cws.ci.deviceType, false))
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/dagger/DefaultComponentBinder.java b/packages/SystemUI/src/com/android/systemui/dagger/DefaultComponentBinder.java
index 18fe3ec92827..50252460d5c1 100644
--- a/packages/SystemUI/src/com/android/systemui/dagger/DefaultComponentBinder.java
+++ b/packages/SystemUI/src/com/android/systemui/dagger/DefaultComponentBinder.java
@@ -25,7 +25,6 @@ import dagger.Module;
*/
@Module(includes = {DefaultActivityBinder.class,
DefaultBroadcastReceiverBinder.class,
- DefaultServiceBinder.class,
- SystemUIBinder.class})
+ DefaultServiceBinder.class})
public abstract class DefaultComponentBinder {
}
diff --git a/packages/SystemUI/src/com/android/systemui/dagger/SystemUIDefaultModule.java b/packages/SystemUI/src/com/android/systemui/dagger/SystemUIDefaultModule.java
index 5c171e41db41..a57ec5b483dd 100644
--- a/packages/SystemUI/src/com/android/systemui/dagger/SystemUIDefaultModule.java
+++ b/packages/SystemUI/src/com/android/systemui/dagger/SystemUIDefaultModule.java
@@ -56,7 +56,7 @@ import dagger.Provides;
* overridden by the System UI implementation.
*/
@Module(includes = {DividerModule.class})
-abstract class SystemUIDefaultModule {
+public abstract class SystemUIDefaultModule {
@Singleton
@Provides
diff --git a/packages/SystemUI/src/com/android/systemui/dagger/SystemUIRootComponent.java b/packages/SystemUI/src/com/android/systemui/dagger/SystemUIRootComponent.java
index e8509b366c5b..3bf5ad759267 100644
--- a/packages/SystemUI/src/com/android/systemui/dagger/SystemUIRootComponent.java
+++ b/packages/SystemUI/src/com/android/systemui/dagger/SystemUIRootComponent.java
@@ -45,6 +45,7 @@ import dagger.Component;
DependencyBinder.class,
SystemServicesModule.class,
SystemUIFactory.ContextHolder.class,
+ SystemUIBinder.class,
SystemUIModule.class,
SystemUIDefaultModule.class})
public interface SystemUIRootComponent {
diff --git a/packages/SystemUI/src/com/android/systemui/qs/DoubleLineTileLayout.kt b/packages/SystemUI/src/com/android/systemui/qs/DoubleLineTileLayout.kt
index f66a1ece1868..f710f7fc47e2 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/DoubleLineTileLayout.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/DoubleLineTileLayout.kt
@@ -68,7 +68,7 @@ class DoubleLineTileLayout(context: Context) : ViewGroup(context), QSPanel.QSTil
override fun updateResources(): Boolean {
with(mContext.resources) {
smallTileSize = getDimensionPixelSize(R.dimen.qs_quick_tile_size)
- cellMarginHorizontal = getDimensionPixelSize(R.dimen.qs_tile_margin_horizontal) / 2
+ cellMarginHorizontal = getDimensionPixelSize(R.dimen.qs_tile_margin_horizontal)
cellMarginVertical = getDimensionPixelSize(R.dimen.new_qs_vertical_margin)
}
requestLayout()
diff --git a/packages/SystemUI/src/com/android/systemui/qs/QSCarrier.java b/packages/SystemUI/src/com/android/systemui/qs/QSCarrier.java
index 4c7d82e99d06..5a9c360e3104 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/QSCarrier.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/QSCarrier.java
@@ -30,6 +30,8 @@ import com.android.settingslib.graph.SignalDrawable;
import com.android.systemui.DualToneHandler;
import com.android.systemui.R;
+import java.util.Objects;
+
public class QSCarrier extends LinearLayout {
private View mMobileGroup;
@@ -39,6 +41,7 @@ public class QSCarrier extends LinearLayout {
private DualToneHandler mDualToneHandler;
private ColorStateList mColorForegroundStateList;
private float mColorForegroundIntensity;
+ private QSCarrierGroupController.CellSignalState mLastSignalState;
public QSCarrier(Context context) {
super(context);
@@ -74,6 +77,8 @@ public class QSCarrier extends LinearLayout {
}
public void updateState(QSCarrierGroupController.CellSignalState state) {
+ if (Objects.equals(state, mLastSignalState)) return;
+ mLastSignalState = state;
mMobileGroup.setVisibility(state.visible ? View.VISIBLE : View.GONE);
if (state.visible) {
mMobileRoaming.setVisibility(state.roaming ? View.VISIBLE : View.GONE);
diff --git a/packages/SystemUI/src/com/android/systemui/qs/QSCarrierGroupController.java b/packages/SystemUI/src/com/android/systemui/qs/QSCarrierGroupController.java
index fb106425ec63..86fccd7f9d52 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/QSCarrierGroupController.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/QSCarrierGroupController.java
@@ -29,6 +29,7 @@ import android.util.Log;
import android.view.View;
import android.widget.TextView;
+import androidx.annotation.Nullable;
import androidx.annotation.VisibleForTesting;
import com.android.keyguard.CarrierTextController;
@@ -37,6 +38,7 @@ import com.android.systemui.dagger.qualifiers.Main;
import com.android.systemui.plugins.ActivityStarter;
import com.android.systemui.statusbar.policy.NetworkController;
+import java.util.Objects;
import java.util.function.Consumer;
import javax.inject.Inject;
@@ -297,6 +299,27 @@ public class QSCarrierGroupController {
String contentDescription;
String typeContentDescription;
boolean roaming;
+
+ @Override
+ public boolean equals(@Nullable Object obj) {
+ if (this == obj) return true;
+ if (!(obj instanceof CellSignalState)) return false;
+ CellSignalState other = (CellSignalState) obj;
+ return this.visible == other.visible
+ && this.mobileSignalIconId == other.mobileSignalIconId
+ && Objects.equals(this.contentDescription, other.contentDescription)
+ && Objects.equals(this.typeContentDescription, other.typeContentDescription)
+ && this.roaming == other.roaming;
+ }
+
+ @Override
+ public int hashCode() {
+ return Objects.hash(visible,
+ mobileSignalIconId,
+ contentDescription,
+ typeContentDescription,
+ roaming);
+ }
}
public static class Builder {
diff --git a/packages/SystemUI/src/com/android/systemui/qs/QSColorController.kt b/packages/SystemUI/src/com/android/systemui/qs/QSColorController.kt
deleted file mode 100644
index 3f0c5bb7f620..000000000000
--- a/packages/SystemUI/src/com/android/systemui/qs/QSColorController.kt
+++ /dev/null
@@ -1,140 +0,0 @@
-/*
- * Copyright (C) 2019 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.systemui.qs
-
-import android.content.ContentResolver
-import android.database.ContentObserver
-import android.net.Uri
-import android.os.Handler
-import android.os.Looper
-import android.provider.Settings
-import android.util.Log
-import com.android.systemui.plugins.qs.QSTile
-
-private const val TAG = "QSColorController"
-private const val QS_COLOR_ICON = "qs_color_icon"
-private const val QS_COLOR_ENABLED = "qs_color_enabled"
-private const val QS_COLOR_OVERRIDDEN_TILES = "qs_color_overridden_tiles"
-private val qsColorIconUri = Settings.System.getUriFor(QS_COLOR_ICON)
-private val qsColorEnabledUri = Settings.System.getUriFor(QS_COLOR_ENABLED)
-class QSColorController private constructor() {
-
- private var overrideColor = false
- private var colorIcon = false
- private lateinit var colorCache: SettingBackedMap
-
- companion object {
- val instance = QSColorController()
- internal fun overrideColor() = instance.overrideColor
- internal fun colorIcon() = instance.colorIcon
-
- @QSTile.ColorTile
- private fun getColorFromSetting(setting: String): Int {
- return when (setting.toLowerCase()) {
- "red" -> QSTile.COLOR_TILE_RED
- "blue" -> QSTile.COLOR_TILE_BLUE
- "green" -> QSTile.COLOR_TILE_GREEN
- "yellow" -> QSTile.COLOR_TILE_YELLOW
- else -> QSTile.COLOR_TILE_ACCENT
- }
- }
- }
-
- private fun getBooleanSetting(key: String, default: Boolean = false): Boolean =
- try {
- Settings.System.getInt(contentResolver, key) != 0
- } catch (_: Settings.SettingNotFoundException) {
- default
- }
-
- private lateinit var tileHost: QSHost
- private lateinit var contentResolver: ContentResolver
-
- fun initQSTileHost(host: QSHost) {
- tileHost = host
- contentResolver = tileHost.context.contentResolver
- colorCache = SettingBackedMap(contentResolver, mutableMapOf())
- colorIcon = getBooleanSetting(QS_COLOR_ICON)
- overrideColor = getBooleanSetting(QS_COLOR_ENABLED)
- readExistingSettings()
- contentResolver.registerContentObserver(qsColorEnabledUri, true, settingsListener)
- contentResolver.registerContentObserver(qsColorIconUri, false, settingsListener)
- }
-
- private fun readExistingSettings() {
- Settings.System.getString(contentResolver, QS_COLOR_OVERRIDDEN_TILES)?.split(",")
- ?.mapNotNull { spec ->
- Settings.System.getString(contentResolver, "$QS_COLOR_ENABLED/$spec")?.let {
- spec to it
- }
- }?.forEach {
- modifyTileColor(it.first, getColorFromSetting(it.second))
- }
- }
-
- private val settingsListener = object : ContentObserver(Handler(Looper.getMainLooper())) {
- override fun onChange(selfChange: Boolean, uri: Uri) {
- super.onChange(selfChange, uri)
- when (uri) {
- qsColorIconUri -> colorIcon = getBooleanSetting(QS_COLOR_ICON)
- qsColorEnabledUri -> overrideColor = getBooleanSetting(QS_COLOR_ENABLED)
- else -> {
- uri.path?.drop("/system/".length)?.let {
- val color = getColorFromSetting(
- Settings.System.getString(contentResolver, it) ?: "accent")
- val tileSpec = uri.lastPathSegment ?: ""
- modifyTileColor(tileSpec, color)
- }
- }
- }
- }
- }
-
- private fun modifyTileColor(spec: String, @QSTile.ColorTile color: Int) {
- Log.w(TAG, "Setting color of tile $spec to $color")
- colorCache.put(spec, color)
- tileHost.tiles.firstOrNull { it.tileSpec == spec }?.setColor(color)
- }
-
- fun applyColorToTile(tile: QSTile) {
- colorCache.get(tile.tileSpec)?.let {
- modifyTileColor(tile.tileSpec, it)
- }
- }
-
- fun applyColorToAllTiles() = tileHost.tiles.forEach(::applyColorToTile)
-
- fun destroy() {
- contentResolver.unregisterContentObserver(settingsListener)
- }
-
- class SettingBackedMap(
- private val contentResolver: ContentResolver,
- private val map: MutableMap<String, Int>
- ) : MutableMap<String, @QSTile.ColorTile Int> by map {
- override fun put(key: String, @QSTile.ColorTile value: Int): Int? {
- return map.put(key, value).also {
- Settings.System.putString(contentResolver, QS_COLOR_OVERRIDDEN_TILES,
- map.filterValues { it != QSTile.COLOR_TILE_ACCENT }
- .keys
- .joinToString(","))
- }
- }
- }
-}
-fun overrideColor() = QSColorController.overrideColor()
-fun colorIcon() = QSColorController.colorIcon() \ No newline at end of file
diff --git a/packages/SystemUI/src/com/android/systemui/qs/QSMediaPlayer.java b/packages/SystemUI/src/com/android/systemui/qs/QSMediaPlayer.java
index 1077834e7146..9e3e94ce4186 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/QSMediaPlayer.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/QSMediaPlayer.java
@@ -36,7 +36,6 @@ import android.media.MediaMetadata;
import android.media.session.MediaController;
import android.media.session.MediaSession;
import android.media.session.PlaybackState;
-import android.text.TextUtils;
import android.util.Log;
import android.view.KeyEvent;
import android.view.LayoutInflater;
@@ -45,7 +44,6 @@ import android.view.ViewGroup;
import android.widget.ImageButton;
import android.widget.ImageView;
import android.widget.LinearLayout;
-import android.widget.RemoteViews;
import android.widget.TextView;
import androidx.core.graphics.drawable.RoundedBitmapDrawable;
@@ -72,8 +70,6 @@ public class QSMediaPlayer {
private View mSeamless;
private MediaSession.Token mToken;
private MediaController mController;
- private int mWidth;
- private int mHeight;
private int mForegroundColor;
private int mBackgroundColor;
private ComponentName mRecvComponent;
@@ -158,16 +154,11 @@ public class QSMediaPlayer {
*
* @param context
* @param parent
- * @param width
- * @param height
*/
- public QSMediaPlayer(Context context, ViewGroup parent, int width, int height) {
+ public QSMediaPlayer(Context context, ViewGroup parent) {
mContext = context;
LayoutInflater inflater = LayoutInflater.from(mContext);
mMediaNotifView = (LinearLayout) inflater.inflate(R.layout.qs_media_panel, parent, false);
-
- mWidth = width;
- mHeight = height;
}
public View getView() {
@@ -217,27 +208,18 @@ public class QSMediaPlayer {
Notification.Builder builder = Notification.Builder.recoverBuilder(mContext, notif);
// Album art
- addAlbumArtBackground(mMediaMetadata, bgColor, mWidth, mHeight);
+ addAlbumArt(mMediaMetadata, bgColor);
- // Reuse notification header instead of reimplementing everything
- RemoteViews headerRemoteView = builder.makeNotificationHeader();
LinearLayout headerView = mMediaNotifView.findViewById(R.id.header);
- View result = headerRemoteView.apply(mContext, headerView);
- result.setPadding(0, 0, 0, 0);
- LinearLayout.LayoutParams lp = new LinearLayout.LayoutParams(
- ViewGroup.LayoutParams.WRAP_CONTENT, 75);
- result.setLayoutParams(lp);
- headerView.removeAllViews();
- headerView.addView(result);
// App icon
- ImageView appIcon = headerView.findViewById(com.android.internal.R.id.icon);
+ ImageView appIcon = headerView.findViewById(R.id.icon);
Drawable iconDrawable = icon.loadDrawable(mContext);
iconDrawable.setTint(iconColor);
appIcon.setImageDrawable(iconDrawable);
// App title
- TextView appName = headerView.findViewById(com.android.internal.R.id.app_name_text);
+ TextView appName = headerView.findViewById(R.id.app_name);
String appNameString = builder.loadHeaderAppName();
appName.setText(appNameString);
appName.setTextColor(iconColor);
@@ -254,25 +236,8 @@ public class QSMediaPlayer {
}
});
- // Separator
- TextView separator = headerView.findViewById(com.android.internal.R.id.header_text_divider);
- separator.setTextColor(iconColor);
-
- // Album name
- TextView albumName = headerView.findViewById(com.android.internal.R.id.header_text);
- String albumString = mMediaMetadata.getString(MediaMetadata.METADATA_KEY_ALBUM);
- if (TextUtils.isEmpty(albumString)) {
- albumName.setVisibility(View.GONE);
- separator.setVisibility(View.GONE);
- } else {
- albumName.setText(albumString);
- albumName.setTextColor(iconColor);
- albumName.setVisibility(View.VISIBLE);
- separator.setVisibility(View.VISIBLE);
- }
-
// Transfer chip
- mSeamless = headerView.findViewById(com.android.internal.R.id.media_seamless);
+ mSeamless = headerView.findViewById(R.id.media_seamless);
mSeamless.setVisibility(View.VISIBLE);
updateChip(device);
ActivityStarter mActivityStarter = Dependency.get(ActivityStarter.class);
@@ -284,13 +249,13 @@ public class QSMediaPlayer {
});
// Artist name
- TextView artistText = mMediaNotifView.findViewById(R.id.header_title);
+ TextView artistText = headerView.findViewById(R.id.header_artist);
String artistName = mMediaMetadata.getString(MediaMetadata.METADATA_KEY_ARTIST);
artistText.setText(artistName);
artistText.setTextColor(iconColor);
// Song name
- TextView titleText = mMediaNotifView.findViewById(R.id.header_text);
+ TextView titleText = headerView.findViewById(R.id.header_text);
String songName = mMediaMetadata.getString(MediaMetadata.METADATA_KEY_TITLE);
titleText.setText(songName);
titleText.setTextColor(iconColor);
@@ -363,34 +328,25 @@ public class QSMediaPlayer {
return (state.getState() == PlaybackState.STATE_PLAYING);
}
- private void addAlbumArtBackground(MediaMetadata metadata, int bgColor, int width, int height) {
+ private void addAlbumArt(MediaMetadata metadata, int bgColor) {
Bitmap albumArt = metadata.getBitmap(MediaMetadata.METADATA_KEY_ALBUM_ART);
float radius = mContext.getResources().getDimension(R.dimen.qs_media_corner_radius);
+ ImageView albumView = mMediaNotifView.findViewById(R.id.album_art);
if (albumArt != null) {
Log.d(TAG, "updating album art");
Bitmap original = albumArt.copy(Bitmap.Config.ARGB_8888, true);
- Bitmap scaled = scaleBitmap(original, width, height);
- Canvas canvas = new Canvas(scaled);
-
- // Add translucent layer over album art to improve contrast
- Paint p = new Paint();
- p.setStyle(Paint.Style.FILL);
- p.setColor(bgColor);
- p.setAlpha(200);
- canvas.drawRect(0, 0, canvas.getWidth(), canvas.getHeight(), p);
-
+ int albumSize = (int) mContext.getResources().getDimension(R.dimen.qs_media_album_size);
+ Bitmap scaled = scaleBitmap(original, albumSize, albumSize);
RoundedBitmapDrawable roundedDrawable = RoundedBitmapDrawableFactory.create(
mContext.getResources(), scaled);
roundedDrawable.setCornerRadius(radius);
-
- mMediaNotifView.setBackground(roundedDrawable);
+ albumView.setImageDrawable(roundedDrawable);
} else {
Log.e(TAG, "No album art available");
- GradientDrawable rect = new GradientDrawable();
- rect.setCornerRadius(radius);
- rect.setColor(bgColor);
- mMediaNotifView.setBackground(rect);
+ albumView.setImageDrawable(null);
}
+
+ mMediaNotifView.setBackgroundTintList(ColorStateList.valueOf(bgColor));
}
private Bitmap scaleBitmap(Bitmap original, int width, int height) {
@@ -423,8 +379,8 @@ public class QSMediaPlayer {
rect.setStroke(2, mForegroundColor);
rect.setColor(mBackgroundColor);
- ImageView iconView = mSeamless.findViewById(com.android.internal.R.id.media_seamless_image);
- TextView deviceName = mSeamless.findViewById(com.android.internal.R.id.media_seamless_text);
+ ImageView iconView = mSeamless.findViewById(R.id.media_seamless_image);
+ TextView deviceName = mSeamless.findViewById(R.id.media_seamless_text);
deviceName.setTextColor(fgTintList);
if (device != null) {
diff --git a/packages/SystemUI/src/com/android/systemui/qs/QSPanel.java b/packages/SystemUI/src/com/android/systemui/qs/QSPanel.java
index 0fd74547334b..14a117ca40a1 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/QSPanel.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/QSPanel.java
@@ -95,6 +95,7 @@ public class QSPanel extends LinearLayout implements Tunable, Callback, Brightne
private final ArrayList<QSMediaPlayer> mMediaPlayers = new ArrayList<>();
private LocalMediaManager mLocalMediaManager;
private MediaDevice mDevice;
+ private boolean mUpdateCarousel = false;
protected boolean mExpanded;
protected boolean mListening;
@@ -184,7 +185,7 @@ public class QSPanel extends LinearLayout implements Tunable, Callback, Brightne
HorizontalScrollView mediaScrollView = (HorizontalScrollView) LayoutInflater.from(
mContext).inflate(R.layout.media_carousel, this, false);
mMediaCarousel = mediaScrollView.findViewById(R.id.media_carousel);
- addView(mediaScrollView);
+ addView(mediaScrollView, 0);
} else {
mMediaCarousel = null;
}
@@ -200,6 +201,23 @@ public class QSPanel extends LinearLayout implements Tunable, Callback, Brightne
mPluginManager = pluginManager;
}
+ @Override
+ public void onVisibilityAggregated(boolean isVisible) {
+ super.onVisibilityAggregated(isVisible);
+ if (!isVisible && mUpdateCarousel) {
+ for (QSMediaPlayer player : mMediaPlayers) {
+ if (player.isPlaying()) {
+ LayoutParams lp = (LayoutParams) player.getView().getLayoutParams();
+ mMediaCarousel.removeView(player.getView());
+ mMediaCarousel.addView(player.getView(), 0, lp);
+ ((HorizontalScrollView) mMediaCarousel.getParent()).fullScroll(View.FOCUS_LEFT);
+ mUpdateCarousel = false;
+ break;
+ }
+ }
+ }
+ }
+
/**
* Add or update a player for the associated media session
* @param token
@@ -237,7 +255,6 @@ public class QSPanel extends LinearLayout implements Tunable, Callback, Brightne
}
}
- int playerHeight = (int) getResources().getDimension(R.dimen.qs_media_height);
int playerWidth = (int) getResources().getDimension(R.dimen.qs_media_width);
int padding = (int) getResources().getDimension(R.dimen.qs_media_padding);
LayoutParams lp = new LayoutParams(playerWidth, ViewGroup.LayoutParams.MATCH_PARENT);
@@ -246,8 +263,7 @@ public class QSPanel extends LinearLayout implements Tunable, Callback, Brightne
if (player == null) {
Log.d(TAG, "creating new player");
-
- player = new QSMediaPlayer(mContext, this, playerWidth, playerHeight);
+ player = new QSMediaPlayer(mContext, this);
if (player.isPlaying()) {
mMediaCarousel.addView(player.getView(), 0, lp); // add in front
@@ -256,9 +272,7 @@ public class QSPanel extends LinearLayout implements Tunable, Callback, Brightne
}
mMediaPlayers.add(player);
} else if (player.isPlaying()) {
- // move it to the front
- mMediaCarousel.removeView(player.getView());
- mMediaCarousel.addView(player.getView(), 0, lp);
+ mUpdateCarousel = true;
}
Log.d(TAG, "setting player session");
diff --git a/packages/SystemUI/src/com/android/systemui/qs/QSTileHost.java b/packages/SystemUI/src/com/android/systemui/qs/QSTileHost.java
index f71150fc348c..00e09f8d4725 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/QSTileHost.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/QSTileHost.java
@@ -90,8 +90,6 @@ public class QSTileHost implements QSHost, Tunable, PluginListener<QSFactory>, D
private int mCurrentUser;
private final Optional<StatusBar> mStatusBarOptional;
- private QSColorController mQSColorController = QSColorController.Companion.getInstance();
-
@Inject
public QSTileHost(Context context,
StatusBarIconController iconController,
@@ -127,8 +125,6 @@ public class QSTileHost implements QSHost, Tunable, PluginListener<QSFactory>, D
// AutoTileManager can modify mTiles so make sure mTiles has already been initialized.
mAutoTiles = autoTiles.get();
});
-
- mQSColorController.initQSTileHost(this);
}
public StatusBarIconController getIconController() {
@@ -142,8 +138,6 @@ public class QSTileHost implements QSHost, Tunable, PluginListener<QSFactory>, D
mServices.destroy();
mPluginManager.removePluginListener(this);
mDumpController.unregisterDumpable(this);
-
- mQSColorController.destroy();
}
@Override
@@ -280,8 +274,6 @@ public class QSTileHost implements QSHost, Tunable, PluginListener<QSFactory>, D
mCallbacks.get(i).onTilesChanged();
}
}
-
- mQSColorController.applyColorToAllTiles();
}
@Override
diff --git a/packages/SystemUI/src/com/android/systemui/qs/QuickQSMediaPlayer.java b/packages/SystemUI/src/com/android/systemui/qs/QuickQSMediaPlayer.java
index cec1cb2fb53b..9018a375c365 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/QuickQSMediaPlayer.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/QuickQSMediaPlayer.java
@@ -23,13 +23,7 @@ import android.content.Intent;
import android.content.pm.PackageManager;
import android.content.pm.ResolveInfo;
import android.content.res.ColorStateList;
-import android.graphics.Bitmap;
-import android.graphics.Canvas;
-import android.graphics.Matrix;
-import android.graphics.Paint;
-import android.graphics.Rect;
import android.graphics.drawable.Drawable;
-import android.graphics.drawable.GradientDrawable;
import android.graphics.drawable.Icon;
import android.media.MediaMetadata;
import android.media.session.MediaController;
@@ -45,9 +39,6 @@ import android.widget.ImageView;
import android.widget.LinearLayout;
import android.widget.TextView;
-import androidx.core.graphics.drawable.RoundedBitmapDrawable;
-import androidx.core.graphics.drawable.RoundedBitmapDrawableFactory;
-
import com.android.systemui.R;
import java.util.List;
@@ -63,7 +54,6 @@ public class QuickQSMediaPlayer {
private LinearLayout mMediaNotifView;
private MediaSession.Token mToken;
private MediaController mController;
- private int mBackgroundColor;
private int mForegroundColor;
private ComponentName mRecvComponent;
@@ -142,7 +132,6 @@ public class QuickQSMediaPlayer {
View actionsContainer, int[] actionsToShow, PendingIntent contentIntent) {
mToken = token;
mForegroundColor = iconColor;
- mBackgroundColor = bgColor;
String oldPackage = "";
if (mController != null) {
@@ -185,8 +174,7 @@ public class QuickQSMediaPlayer {
}
});
- // Album art
- addAlbumArtBackground(mMediaMetadata, mBackgroundColor);
+ mMediaNotifView.setBackgroundTintList(ColorStateList.valueOf(bgColor));
// App icon
ImageView appIcon = mMediaNotifView.findViewById(R.id.icon);
@@ -194,14 +182,8 @@ public class QuickQSMediaPlayer {
iconDrawable.setTint(mForegroundColor);
appIcon.setImageDrawable(iconDrawable);
- // Artist name
- TextView appText = mMediaNotifView.findViewById(R.id.header_title);
- String artistName = mMediaMetadata.getString(MediaMetadata.METADATA_KEY_ARTIST);
- appText.setText(artistName);
- appText.setTextColor(mForegroundColor);
-
// Song name
- TextView titleText = mMediaNotifView.findViewById(R.id.header_text);
+ TextView titleText = mMediaNotifView.findViewById(R.id.header_title);
String songName = mMediaMetadata.getString(MediaMetadata.METADATA_KEY_TITLE);
titleText.setText(songName);
titleText.setTextColor(mForegroundColor);
@@ -277,54 +259,4 @@ public class QuickQSMediaPlayer {
public boolean hasMediaSession() {
return mController != null && mController.getPlaybackState() != null;
}
-
- private void addAlbumArtBackground(MediaMetadata metadata, int bgColor) {
- Bitmap albumArt = metadata.getBitmap(MediaMetadata.METADATA_KEY_ALBUM_ART);
- float radius = mContext.getResources().getDimension(R.dimen.qs_media_corner_radius);
- Rect bounds = new Rect();
- mMediaNotifView.getBoundsOnScreen(bounds);
- int width = bounds.width();
- int height = bounds.height();
- if (albumArt != null && width > 0 && height > 0) {
- Bitmap original = albumArt.copy(Bitmap.Config.ARGB_8888, true);
- Bitmap scaled = scaleBitmap(original, width, height);
- Canvas canvas = new Canvas(scaled);
-
- // Add translucent layer over album art to improve contrast
- Paint p = new Paint();
- p.setStyle(Paint.Style.FILL);
- p.setColor(bgColor);
- p.setAlpha(200);
- canvas.drawRect(0, 0, canvas.getWidth(), canvas.getHeight(), p);
-
- RoundedBitmapDrawable roundedDrawable = RoundedBitmapDrawableFactory.create(
- mContext.getResources(), scaled);
- roundedDrawable.setCornerRadius(radius);
-
- mMediaNotifView.setBackground(roundedDrawable);
- } else {
- Log.e(TAG, "No album art available");
- GradientDrawable rect = new GradientDrawable();
- rect.setCornerRadius(radius);
- rect.setColor(bgColor);
- mMediaNotifView.setBackground(rect);
- }
- }
-
- private Bitmap scaleBitmap(Bitmap original, int width, int height) {
- Bitmap cropped = Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888);
- Canvas canvas = new Canvas(cropped);
-
- float scale = (float) cropped.getWidth() / (float) original.getWidth();
- float dy = (cropped.getHeight() - original.getHeight() * scale) / 2.0f;
- Matrix transformation = new Matrix();
- transformation.postTranslate(0, dy);
- transformation.preScale(scale, scale);
-
- Paint paint = new Paint();
- paint.setFilterBitmap(true);
- canvas.drawBitmap(original, transformation, paint);
-
- return cropped;
- }
}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/QuickQSPanel.java b/packages/SystemUI/src/com/android/systemui/qs/QuickQSPanel.java
index b05d4fdf7db7..e9207016eb68 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/QuickQSPanel.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/QuickQSPanel.java
@@ -85,20 +85,21 @@ public class QuickQSPanel extends QSPanel {
mHorizontalLinearLayout.setClipChildren(false);
mHorizontalLinearLayout.setClipToPadding(false);
+ int marginSize = (int) mContext.getResources().getDimension(R.dimen.qqs_media_spacing);
+ mMediaPlayer = new QuickQSMediaPlayer(mContext, mHorizontalLinearLayout);
+ LayoutParams lp2 = new LayoutParams(0, LayoutParams.MATCH_PARENT, 1);
+ lp2.setMarginEnd(marginSize);
+ lp2.setMarginStart(0);
+ mHorizontalLinearLayout.addView(mMediaPlayer.getView(), lp2);
+
mTileLayout = new DoubleLineTileLayout(context);
mMediaTileLayout = mTileLayout;
mRegularTileLayout = new HeaderTileLayout(context);
LayoutParams lp = new LayoutParams(0, LayoutParams.MATCH_PARENT, 1);
- lp.setMarginEnd(10);
- lp.setMarginStart(0);
+ lp.setMarginEnd(0);
+ lp.setMarginStart(marginSize);
mHorizontalLinearLayout.addView((View) mTileLayout, lp);
- mMediaPlayer = new QuickQSMediaPlayer(mContext, mHorizontalLinearLayout);
- LayoutParams lp2 = new LayoutParams(0, LayoutParams.MATCH_PARENT, 1);
- lp2.setMarginEnd(0);
- lp2.setMarginStart(25);
- mHorizontalLinearLayout.addView(mMediaPlayer.getView(), lp2);
-
sDefaultMaxTiles = getResources().getInteger(R.integer.quick_qs_panel_max_columns);
mTileLayout.setListening(mListening);
diff --git a/packages/SystemUI/src/com/android/systemui/qs/TileLayout.java b/packages/SystemUI/src/com/android/systemui/qs/TileLayout.java
index 37743ec55517..9f59277c918a 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/TileLayout.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/TileLayout.java
@@ -1,5 +1,7 @@
package com.android.systemui.qs;
+import static com.android.systemui.util.Utils.useQsMediaPlayer;
+
import android.content.Context;
import android.content.res.Resources;
import android.provider.Settings;
@@ -42,7 +44,8 @@ public class TileLayout extends ViewGroup implements QSTileLayout {
public TileLayout(Context context, AttributeSet attrs) {
super(context, attrs);
setFocusableInTouchMode(true);
- mLessRows = Settings.System.getInt(context.getContentResolver(), "qs_less_rows", 0) != 0;
+ mLessRows = (Settings.System.getInt(context.getContentResolver(), "qs_less_rows", 0) != 0)
+ || useQsMediaPlayer(context);
updateResources();
}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSIconViewImpl.java b/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSIconViewImpl.java
index 88c7964144c1..31526bf8f5e1 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSIconViewImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSIconViewImpl.java
@@ -14,12 +14,10 @@
package com.android.systemui.qs.tileimpl;
-import static com.android.systemui.qs.QSColorControllerKt.colorIcon;
import static com.android.systemui.qs.tileimpl.QSTileImpl.getColorForState;
import android.animation.Animator;
import android.animation.AnimatorListenerAdapter;
-import android.animation.ArgbEvaluator;
import android.animation.ValueAnimator;
import android.content.Context;
import android.content.res.ColorStateList;
@@ -150,15 +148,10 @@ public class QSIconViewImpl extends QSIconView {
iv.clearColorFilter();
}
if (state.state != mState) {
- int color = getColor(state.state, state.colorActive);
+ int color = getColor(state.state);
mState = state.state;
if (mTint != 0 && allowAnimations && shouldAnimate(iv)) {
- if (colorIcon()) {
- animateColor(mTint, color, iv, () -> updateIcon(iv, state, allowAnimations));
- } else {
- animateGrayScale(mTint, color, iv,
- () -> updateIcon(iv, state, allowAnimations));
- }
+ animateGrayScale(mTint, color, iv, () -> updateIcon(iv, state, allowAnimations));
mTint = color;
} else {
if (iv instanceof AlphaControlledSlashImageView) {
@@ -175,12 +168,8 @@ public class QSIconViewImpl extends QSIconView {
}
}
- protected int getColor(int state, int colorActive) {
- return getColorForState(getContext(), state, colorActive);
- }
-
protected int getColor(int state) {
- return getColor(state, -1);
+ return getColorForState(getContext(), state);
}
private void animateGrayScale(int fromColor, int toColor, ImageView iv,
@@ -217,37 +206,6 @@ public class QSIconViewImpl extends QSIconView {
}
}
- private void animateColor(int fromColor, int toColor, ImageView iv,
- final Runnable endRunnable) {
- if (iv instanceof AlphaControlledSlashImageView) {
- ((AlphaControlledSlashImageView) iv)
- .setFinalImageTintList(ColorStateList.valueOf(toColor));
- }
- if (mAnimationEnabled && ValueAnimator.areAnimatorsEnabled()) {
-
-
- ValueAnimator anim = ValueAnimator.ofFloat(0, 1);
- anim.setDuration(QS_ANIM_LENGTH);
- anim.addUpdateListener(animation -> {
- float fraction = animation.getAnimatedFraction();
- int color = (int) ArgbEvaluator.getInstance().evaluate(fraction, fromColor,
- toColor);
-
- setTint(iv, color);
- });
- anim.addListener(new AnimatorListenerAdapter() {
- @Override
- public void onAnimationEnd(Animator animation) {
- endRunnable.run();
- }
- });
- anim.start();
- } else {
- setTint(iv, toColor);
- endRunnable.run();
- }
- }
-
public static void setTint(ImageView iv, int color) {
iv.setImageTintList(ColorStateList.valueOf(color));
}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSTileBaseView.java b/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSTileBaseView.java
index 8b7f280608a5..2fe64d26f3ac 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSTileBaseView.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSTileBaseView.java
@@ -13,8 +13,6 @@
*/
package com.android.systemui.qs.tileimpl;
-import static com.android.systemui.qs.QSColorControllerKt.colorIcon;
-import static com.android.systemui.qs.QSColorControllerKt.overrideColor;
import static com.android.systemui.qs.tileimpl.QSIconViewImpl.QS_ANIM_LENGTH;
import android.animation.ValueAnimator;
@@ -76,8 +74,6 @@ public class QSTileBaseView extends com.android.systemui.plugins.qs.QSTileView {
private int mCircleColor;
private int mBgSize;
- private final boolean mQsColors = overrideColor();
- private final boolean mQSIcons = colorIcon();
public QSTileBaseView(Context context, QSIconView icon) {
this(context, icon, false);
@@ -212,7 +208,7 @@ public class QSTileBaseView extends com.android.systemui.plugins.qs.QSTileView {
}
protected void handleStateChanged(QSTile.State state) {
- int circleColor = getCircleColor(state.state, mQsColors ? state.colorActive : -1);
+ int circleColor = getCircleColor(state.state);
boolean allowAnimations = animationsEnabled();
if (circleColor != mCircleColor) {
if (allowAnimations) {
@@ -259,11 +255,10 @@ public class QSTileBaseView extends com.android.systemui.plugins.qs.QSTileView {
return mLocInScreen[1] >= -getHeight();
}
- private int getCircleColor(int state, int colorActive) {
+ private int getCircleColor(int state) {
switch (state) {
case Tile.STATE_ACTIVE:
- int color = (colorActive == -1) ? mColorActive : colorActive;
- return mQsColors && mQSIcons ? Utils.applyAlpha(0.5f, color) : color;
+ return mColorActive;
case Tile.STATE_INACTIVE:
case Tile.STATE_UNAVAILABLE:
return mColorDisabled;
@@ -273,10 +268,6 @@ public class QSTileBaseView extends com.android.systemui.plugins.qs.QSTileView {
}
}
- private int getCircleColor(int state) {
- return getCircleColor(state, -1);
- }
-
@Override
public void setClickable(boolean clickable) {
super.setClickable(clickable);
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSTileImpl.java b/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSTileImpl.java
index 1d379110325a..e1b61c670a12 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSTileImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSTileImpl.java
@@ -26,7 +26,6 @@ import static com.android.internal.logging.nano.MetricsProto.MetricsEvent.FIELD_
import static com.android.internal.logging.nano.MetricsProto.MetricsEvent.FIELD_STATUS_BAR_STATE;
import static com.android.internal.logging.nano.MetricsProto.MetricsEvent.TYPE_ACTION;
import static com.android.settingslib.RestrictedLockUtils.EnforcedAdmin;
-import static com.android.systemui.qs.QSColorControllerKt.colorIcon;
import android.app.ActivityManager;
import android.content.Context;
@@ -55,7 +54,6 @@ import com.android.settingslib.Utils;
import com.android.systemui.Dependency;
import com.android.systemui.Dumpable;
import com.android.systemui.Prefs;
-import com.android.systemui.R;
import com.android.systemui.plugins.ActivityStarter;
import com.android.systemui.plugins.qs.DetailAdapter;
import com.android.systemui.plugins.qs.QSIconView;
@@ -432,10 +430,6 @@ public abstract class QSTileImpl<TState extends State> implements QSTile, Lifecy
public abstract CharSequence getTileLabel();
public static int getColorForState(Context context, int state) {
- return getColorForState(context, state, -1);
- }
-
- public static int getColorForState(Context context, int state, int colorActive) {
switch (state) {
case Tile.STATE_UNAVAILABLE:
return Utils.getDisabled(context,
@@ -443,25 +437,13 @@ public abstract class QSTileImpl<TState extends State> implements QSTile, Lifecy
case Tile.STATE_INACTIVE:
return Utils.getColorAttrDefaultColor(context, android.R.attr.textColorSecondary);
case Tile.STATE_ACTIVE:
- return getActiveColor(context, colorActive);
+ return Utils.getColorAttrDefaultColor(context, android.R.attr.colorPrimary);
default:
Log.e("QSTile", "Invalid state " + state);
return 0;
}
}
- private static int getActiveColor(Context context, int colorActive) {
- if (colorIcon()) {
- if (colorActive == -1) {
- return Utils.getColorAccentDefaultColor(context);
- } else {
- return colorActive;
- }
- } else {
- return Utils.getColorAttrDefaultColor(context, android.R.attr.colorPrimary);
- }
- }
-
protected final class H extends Handler {
private static final int ADD_CALLBACK = 1;
private static final int CLICK = 2;
@@ -645,27 +627,4 @@ public abstract class QSTileImpl<TState extends State> implements QSTile, Lifecy
pw.println(this.getClass().getSimpleName() + ":");
pw.print(" "); pw.println(getState().toString());
}
-
- @Override
- public void setColor(@ColorTile int color) {
- int resId;
- switch(color) {
- case COLOR_TILE_RED:
- resId = R.color.GM2_red_500;
- break;
- case COLOR_TILE_BLUE:
- resId = R.color.GM2_blue_500;
- break;
- case COLOR_TILE_GREEN:
- resId = R.color.GM2_green_500;
- break;
- case COLOR_TILE_YELLOW:
- resId = R.color.GM2_yellow_500;
- break;
- default:
- resId = -1;
- }
- mTmpState.colorActive = resId == -1 ? -1 : mContext.getColor(resId);
- refreshState();
- }
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationRemoveInterceptor.java b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationRemoveInterceptor.java
index 930116ee4ae7..caa1e2db6ac0 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationRemoveInterceptor.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationRemoveInterceptor.java
@@ -16,8 +16,12 @@
package com.android.systemui.statusbar;
+import android.annotation.NonNull;
+import android.annotation.Nullable;
import android.service.notification.NotificationListenerService;
+import com.android.systemui.statusbar.notification.collection.NotificationEntry;
+
/**
* Interface for anything that may need to prevent notifications from being removed. This is
* similar to a {@link NotificationLifetimeExtender} in the sense that it extends the life of
@@ -30,11 +34,15 @@ public interface NotificationRemoveInterceptor {
/**
* Called when a notification has been removed.
*
- * @param key the entry key of the notification being removed.
+ * @param key the key of the notification being removed. Never null
+ * @param entry the entry of the notification being removed.
* @param removeReason why the notification is being removed, e.g.
* {@link NotificationListenerService#REASON_CANCEL} or 0 if unknown.
*
* @return true if the removal should be ignored, false otherwise.
*/
- boolean onNotificationRemoveRequested(String key, int removeReason);
+ boolean onNotificationRemoveRequested(
+ @NonNull String key,
+ @Nullable NotificationEntry entry,
+ int removeReason);
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationViewHierarchyManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationViewHierarchyManager.java
index 8d4a9efbcd7a..37f9f88f6328 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationViewHierarchyManager.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationViewHierarchyManager.java
@@ -34,6 +34,7 @@ import com.android.systemui.statusbar.notification.NotificationEntryManager;
import com.android.systemui.statusbar.notification.VisualStabilityManager;
import com.android.systemui.statusbar.notification.collection.NotificationEntry;
import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow;
+import com.android.systemui.statusbar.notification.stack.ForegroundServiceSectionController;
import com.android.systemui.statusbar.notification.stack.NotificationListContainer;
import com.android.systemui.statusbar.phone.KeyguardBypassController;
import com.android.systemui.statusbar.phone.NotificationGroupManager;
@@ -81,6 +82,7 @@ public class NotificationViewHierarchyManager implements DynamicPrivacyControlle
private final BubbleController mBubbleController;
private final DynamicPrivacyController mDynamicPrivacyController;
private final KeyguardBypassController mBypassController;
+ private final ForegroundServiceSectionController mFgsSectionController;
private final Context mContext;
private NotificationPresenter mPresenter;
@@ -101,7 +103,9 @@ public class NotificationViewHierarchyManager implements DynamicPrivacyControlle
NotificationEntryManager notificationEntryManager,
KeyguardBypassController bypassController,
BubbleController bubbleController,
- DynamicPrivacyController privacyController) {
+ DynamicPrivacyController privacyController,
+ ForegroundServiceSectionController fgsSectionController
+ ) {
mContext = context;
mHandler = mainHandler;
mLockscreenUserManager = notificationLockscreenUserManager;
@@ -110,6 +114,7 @@ public class NotificationViewHierarchyManager implements DynamicPrivacyControlle
mVisualStabilityManager = visualStabilityManager;
mStatusBarStateController = (SysuiStatusBarStateController) statusBarStateController;
mEntryManager = notificationEntryManager;
+ mFgsSectionController = fgsSectionController;
Resources res = context.getResources();
mAlwaysExpandNonGroupedNotification =
res.getBoolean(R.bool.config_alwaysExpandNonGroupedNotifications);
@@ -140,7 +145,8 @@ public class NotificationViewHierarchyManager implements DynamicPrivacyControlle
boolean hideMedia = Utils.useQsMediaPlayer(mContext);
if (ent.isRowDismissed() || ent.isRowRemoved()
|| (ent.isMediaNotification() && hideMedia)
- || mBubbleController.isBubbleNotificationSuppressedFromShade(ent)) {
+ || mBubbleController.isBubbleNotificationSuppressedFromShade(ent)
+ || mFgsSectionController.hasEntry(ent)) {
// we don't want to update removed notifications because they could
// temporarily become children if they were isolated before.
continue;
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/ForegroundServiceDismissalFeatureController.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/ForegroundServiceDismissalFeatureController.kt
new file mode 100644
index 000000000000..b1d6b40fcc1e
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/ForegroundServiceDismissalFeatureController.kt
@@ -0,0 +1,49 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.statusbar.notification
+
+import android.content.Context
+import android.provider.DeviceConfig
+import com.android.internal.config.sysui.SystemUiDeviceConfigFlags.NOTIFICATIONS_ALLOW_FGS_DISMISSAL
+import com.android.systemui.util.DeviceConfigProxy
+import javax.inject.Inject
+import javax.inject.Singleton
+
+private var sIsEnabled: Boolean? = null
+
+/**
+ * Feature controller for NOTIFICATIONS_ALLOW_FGS_DISMISSAL config.
+ */
+// TODO: this is really boilerplatey, make a base class that just wraps the device config
+@Singleton
+class ForegroundServiceDismissalFeatureController @Inject constructor(
+ val proxy: DeviceConfigProxy,
+ val context: Context
+) {
+ fun isForegroundServiceDismissalEnabled(): Boolean {
+ return isEnabled(proxy)
+ }
+}
+
+private fun isEnabled(proxy: DeviceConfigProxy): Boolean {
+ if (sIsEnabled == null) {
+ sIsEnabled = proxy.getBoolean(
+ DeviceConfig.NAMESPACE_SYSTEMUI, NOTIFICATIONS_ALLOW_FGS_DISMISSAL, false)
+ }
+
+ return sIsEnabled!!
+} \ No newline at end of file
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationEntryManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationEntryManager.java
index 4a2283171694..6bb377e89278 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationEntryManager.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationEntryManager.java
@@ -135,6 +135,7 @@ public class NotificationEntryManager implements
private final NotificationGroupManager mGroupManager;
private final NotificationRankingManager mRankingManager;
private final FeatureFlags mFeatureFlags;
+ private final ForegroundServiceDismissalFeatureController mFgsFeatureController;
private NotificationPresenter mPresenter;
private RankingMap mLatestRankingMap;
@@ -144,7 +145,7 @@ public class NotificationEntryManager implements
final ArrayList<NotificationLifetimeExtender> mNotificationLifetimeExtenders
= new ArrayList<>();
private final List<NotificationEntryListener> mNotificationEntryListeners = new ArrayList<>();
- private NotificationRemoveInterceptor mRemoveInterceptor;
+ private final List<NotificationRemoveInterceptor> mRemoveInterceptors = new ArrayList<>();
@Override
public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
@@ -157,6 +158,14 @@ public class NotificationEntryManager implements
pw.println(entry.getSbn());
}
}
+ pw.println(" Remove interceptors registered:");
+ for (NotificationRemoveInterceptor interceptor : mRemoveInterceptors) {
+ pw.println(" " + interceptor.getClass().getSimpleName());
+ }
+ pw.println(" Lifetime extenders registered:");
+ for (NotificationLifetimeExtender extender : mNotificationLifetimeExtenders) {
+ pw.println(" " + extender.getClass().getSimpleName());
+ }
pw.println(" Lifetime-extended notifications:");
if (mRetainedNotifications.isEmpty()) {
pw.println(" None");
@@ -178,7 +187,8 @@ public class NotificationEntryManager implements
FeatureFlags featureFlags,
Lazy<NotificationRowBinder> notificationRowBinderLazy,
Lazy<NotificationRemoteInputManager> notificationRemoteInputManagerLazy,
- LeakDetector leakDetector) {
+ LeakDetector leakDetector,
+ ForegroundServiceDismissalFeatureController fgsFeatureController) {
mNotifLog = notifLog;
mGroupManager = groupManager;
mRankingManager = rankingManager;
@@ -187,6 +197,7 @@ public class NotificationEntryManager implements
mNotificationRowBinderLazy = notificationRowBinderLazy;
mRemoteInputManagerLazy = notificationRemoteInputManagerLazy;
mLeakDetector = leakDetector;
+ mFgsFeatureController = fgsFeatureController;
}
/** Once called, the NEM will start processing notification events from system server. */
@@ -207,9 +218,14 @@ public class NotificationEntryManager implements
mNotificationEntryListeners.remove(listener);
}
- /** Sets the {@link NotificationRemoveInterceptor}. */
- public void setNotificationRemoveInterceptor(NotificationRemoveInterceptor interceptor) {
- mRemoveInterceptor = interceptor;
+ /** Add a {@link NotificationRemoveInterceptor}. */
+ public void addNotificationRemoveInterceptor(NotificationRemoveInterceptor interceptor) {
+ mRemoveInterceptors.add(interceptor);
+ }
+
+ /** Remove a {@link NotificationRemoveInterceptor} */
+ public void removeNotificationRemoveInterceptor(NotificationRemoveInterceptor interceptor) {
+ mRemoveInterceptors.remove(interceptor);
}
public void setUpWithPresenter(NotificationPresenter presenter,
@@ -398,14 +414,16 @@ public class NotificationEntryManager implements
boolean removedByUser,
int reason) {
- if (mRemoveInterceptor != null
- && mRemoveInterceptor.onNotificationRemoveRequested(key, reason)) {
- // Remove intercepted; log and skip
- mNotifLog.log(NotifEvent.REMOVE_INTERCEPTED);
- return;
+ final NotificationEntry entry = getActiveNotificationUnfiltered(key);
+
+ for (NotificationRemoveInterceptor interceptor : mRemoveInterceptors) {
+ if (interceptor.onNotificationRemoveRequested(key, entry, reason)) {
+ // Remove intercepted; log and skip
+ mNotifLog.log(NotifEvent.REMOVE_INTERCEPTED);
+ return;
+ }
}
- final NotificationEntry entry = getActiveNotificationUnfiltered(key);
boolean lifetimeExtended = false;
// Notification was canceled before it got inflated
@@ -528,7 +546,10 @@ public class NotificationEntryManager implements
Ranking ranking = new Ranking();
rankingMap.getRanking(key, ranking);
- NotificationEntry entry = new NotificationEntry(notification, ranking);
+ NotificationEntry entry = new NotificationEntry(
+ notification,
+ ranking,
+ mFgsFeatureController.isForegroundServiceDismissalEnabled());
mLeakDetector.trackInstance(entry);
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/ListDumper.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/ListDumper.java
index eaa9d78c08f4..7fe229c26f3a 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/ListDumper.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/ListDumper.java
@@ -16,6 +16,9 @@
package com.android.systemui.statusbar.notification.collection;
+import static com.android.systemui.statusbar.notification.collection.NotifCollection.REASON_NOT_CANCELED;
+import static com.android.systemui.statusbar.notification.collection.NotificationEntry.DismissState.NOT_DISMISSED;
+
import java.util.Arrays;
import java.util.List;
@@ -132,8 +135,20 @@ public class ListDumper {
.append(" ");
}
+ if (notifEntry.mCancellationReason != REASON_NOT_CANCELED) {
+ rksb.append("cancellationReason=")
+ .append(notifEntry.mCancellationReason)
+ .append(" ");
+ }
+
if (notifEntry.hasInflationError()) {
- rksb.append("hasInflationError ");
+ rksb.append("(!)hasInflationError ");
+ }
+
+ if (notifEntry.getDismissState() != NOT_DISMISSED) {
+ rksb.append("dismissState=")
+ .append(notifEntry.getDismissState())
+ .append(" ");
}
String rkString = rksb.toString();
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotifCollection.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotifCollection.java
index cb8cef8fdd72..3b2fe9441c32 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotifCollection.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotifCollection.java
@@ -35,9 +35,14 @@ import static android.service.notification.NotificationListenerService.REASON_TI
import static android.service.notification.NotificationListenerService.REASON_UNAUTOBUNDLED;
import static android.service.notification.NotificationListenerService.REASON_USER_STOPPED;
+import static com.android.systemui.statusbar.notification.collection.NotificationEntry.DismissState.DISMISSED;
+import static com.android.systemui.statusbar.notification.collection.NotificationEntry.DismissState.NOT_DISMISSED;
+import static com.android.systemui.statusbar.notification.collection.NotificationEntry.DismissState.PARENT_DISMISSED;
+
+import static java.util.Objects.requireNonNull;
+
import android.annotation.IntDef;
import android.annotation.MainThread;
-import android.annotation.NonNull;
import android.annotation.Nullable;
import android.os.RemoteException;
import android.service.notification.NotificationListenerService.Ranking;
@@ -45,6 +50,8 @@ import android.service.notification.NotificationListenerService.RankingMap;
import android.service.notification.StatusBarNotification;
import android.util.ArrayMap;
+import androidx.annotation.NonNull;
+
import com.android.internal.statusbar.IStatusBarService;
import com.android.systemui.DumpController;
import com.android.systemui.Dumpable;
@@ -172,15 +179,65 @@ public class NotifCollection implements Dumpable {
/**
* Dismiss a notification on behalf of the user.
*/
- void dismissNotification(
- NotificationEntry entry,
- @CancellationReason int reason,
- @NonNull DismissedByUserStats stats) {
+ void dismissNotification(NotificationEntry entry, @NonNull DismissedByUserStats stats) {
Assert.isMainThread();
- Objects.requireNonNull(stats);
+ requireNonNull(stats);
checkForReentrantCall();
- removeNotification(entry.getKey(), null, reason, stats);
+ if (entry != mNotificationSet.get(entry.getKey())) {
+ throw new IllegalStateException("Invalid entry: " + entry.getKey());
+ }
+
+ if (entry.getDismissState() == DISMISSED) {
+ return;
+ }
+
+ // Optimistically mark the notification as dismissed -- we'll wait for the signal from
+ // system server before removing it from our notification set.
+ entry.setDismissState(DISMISSED);
+ mLogger.logNotifDismissed(entry.getKey());
+
+ List<NotificationEntry> canceledEntries = new ArrayList<>();
+
+ if (isCanceled(entry)) {
+ canceledEntries.add(entry);
+ } else {
+ // Ask system server to remove it for us
+ try {
+ mStatusBarService.onNotificationClear(
+ entry.getSbn().getPackageName(),
+ entry.getSbn().getTag(),
+ entry.getSbn().getId(),
+ entry.getSbn().getUser().getIdentifier(),
+ entry.getSbn().getKey(),
+ stats.dismissalSurface,
+ stats.dismissalSentiment,
+ stats.notificationVisibility);
+ } catch (RemoteException e) {
+ // system process is dead if we're here.
+ }
+
+ // Also mark any children as dismissed as system server will auto-dismiss them as well
+ if (entry.getSbn().getNotification().isGroupSummary()) {
+ for (NotificationEntry otherEntry : mNotificationSet.values()) {
+ if (otherEntry.getSbn().getGroupKey().equals(entry.getSbn().getGroupKey())
+ && otherEntry.getDismissState() != DISMISSED) {
+ otherEntry.setDismissState(PARENT_DISMISSED);
+ if (isCanceled(otherEntry)) {
+ canceledEntries.add(otherEntry);
+ }
+ }
+ }
+ }
+ }
+
+ // Immediately remove any dismissed notifs that have already been canceled by system server
+ // (probably due to being lifetime-extended up until this point).
+ for (NotificationEntry canceledEntry : canceledEntries) {
+ tryRemoveNotification(canceledEntry);
+ }
+
+ rebuildList();
}
private void onNotificationPosted(StatusBarNotification sbn, RankingMap rankingMap) {
@@ -208,7 +265,15 @@ public class NotifCollection implements Dumpable {
Assert.isMainThread();
mLogger.logNotifRemoved(sbn.getKey(), reason);
- removeNotification(sbn.getKey(), rankingMap, reason, null);
+
+ final NotificationEntry entry = mNotificationSet.get(sbn.getKey());
+ if (entry == null) {
+ throw new IllegalStateException("No notification to remove with key " + sbn.getKey());
+ }
+ entry.mCancellationReason = reason;
+ applyRanking(rankingMap);
+ tryRemoveNotification(entry);
+ rebuildList();
}
private void onNotificationRankingUpdate(RankingMap rankingMap) {
@@ -242,9 +307,12 @@ public class NotifCollection implements Dumpable {
// Update to an existing entry
mLogger.logNotifUpdated(sbn.getKey());
- // Notification is updated so it is essentially re-added and thus alive again. Don't
+ cancelLocalDismissal(entry);
+
+ // Notification is updated so it is essentially re-added and thus alive again. Don't
// need to keep its lifetime extended.
cancelLifetimeExtension(entry);
+ entry.mCancellationReason = REASON_NOT_CANCELED;
entry.setSbn(sbn);
if (rankingMap != null) {
@@ -255,71 +323,62 @@ public class NotifCollection implements Dumpable {
}
}
- private void removeNotification(
- String key,
- @Nullable RankingMap rankingMap,
- @CancellationReason int reason,
- @Nullable DismissedByUserStats dismissedByUserStats) {
+ /**
+ * Tries to remove a notification from the notification set. This removal may be blocked by
+ * lifetime extenders. Does not trigger a rebuild of the list; caller must do that manually.
+ *
+ * @return True if the notification was removed, false otherwise.
+ */
+ private boolean tryRemoveNotification(NotificationEntry entry) {
+ if (mNotificationSet.get(entry.getKey()) != entry) {
+ throw new IllegalStateException("No notification to remove with key " + entry.getKey());
+ }
- NotificationEntry entry = mNotificationSet.get(key);
- if (entry == null) {
- throw new IllegalStateException("No notification to remove with key " + key);
+ if (!isCanceled(entry)) {
+ throw new IllegalStateException("Cannot remove notification " + entry.getKey()
+ + ": has not been marked for removal");
}
- entry.mLifetimeExtenders.clear();
- mAmDispatchingToOtherCode = true;
- for (NotifLifetimeExtender extender : mLifetimeExtenders) {
- if (extender.shouldExtendLifetime(entry, reason)) {
- entry.mLifetimeExtenders.add(extender);
- }
+ if (isDismissedByUser(entry)) {
+ // User-dismissed notifications cannot be lifetime-extended
+ cancelLifetimeExtension(entry);
+ } else {
+ updateLifetimeExtension(entry);
}
- mAmDispatchingToOtherCode = false;
if (!isLifetimeExtended(entry)) {
mNotificationSet.remove(entry.getKey());
-
- if (dismissedByUserStats != null) {
- try {
- mStatusBarService.onNotificationClear(
- entry.getSbn().getPackageName(),
- entry.getSbn().getTag(),
- entry.getSbn().getId(),
- entry.getSbn().getUser().getIdentifier(),
- entry.getSbn().getKey(),
- dismissedByUserStats.dismissalSurface,
- dismissedByUserStats.dismissalSentiment,
- dismissedByUserStats.notificationVisibility);
- } catch (RemoteException e) {
- // system process is dead if we're here.
- }
- }
-
- if (rankingMap != null) {
- applyRanking(rankingMap);
- }
-
- dispatchOnEntryRemoved(entry, reason, dismissedByUserStats != null /* removedByUser */);
+ dispatchOnEntryRemoved(entry, entry.mCancellationReason);
dispatchOnEntryCleanUp(entry);
+ return true;
+ } else {
+ return false;
}
-
- rebuildList();
}
private void applyRanking(@NonNull RankingMap rankingMap) {
for (NotificationEntry entry : mNotificationSet.values()) {
- if (!isLifetimeExtended(entry)) {
- Ranking ranking = requireRanking(rankingMap, entry.getKey());
- entry.setRanking(ranking);
-
- // TODO: (b/145659174) update the sbn's overrideGroupKey in
- // NotificationEntry.setRanking instead of here once we fully migrate to the
- // NewNotifPipeline
- if (mFeatureFlags.isNewNotifPipelineRenderingEnabled()) {
- final String newOverrideGroupKey = ranking.getOverrideGroupKey();
- if (!Objects.equals(entry.getSbn().getOverrideGroupKey(),
- newOverrideGroupKey)) {
- entry.getSbn().setOverrideGroupKey(newOverrideGroupKey);
+ if (!isCanceled(entry)) {
+
+ // TODO: (b/148791039) We should crash if we are ever handed a ranking with
+ // incomplete entries. Right now, there's a race condition in NotificationListener
+ // that means this might occur when SystemUI is starting up.
+ Ranking ranking = new Ranking();
+ if (rankingMap.getRanking(entry.getKey(), ranking)) {
+ entry.setRanking(ranking);
+
+ // TODO: (b/145659174) update the sbn's overrideGroupKey in
+ // NotificationEntry.setRanking instead of here once we fully migrate to the
+ // NewNotifPipeline
+ if (mFeatureFlags.isNewNotifPipelineRenderingEnabled()) {
+ final String newOverrideGroupKey = ranking.getOverrideGroupKey();
+ if (!Objects.equals(entry.getSbn().getOverrideGroupKey(),
+ newOverrideGroupKey)) {
+ entry.getSbn().setOverrideGroupKey(newOverrideGroupKey);
+ }
}
+ } else {
+ mLogger.logRankingMissing(entry.getKey(), rankingMap);
}
}
}
@@ -347,9 +406,9 @@ public class NotifCollection implements Dumpable {
}
if (!isLifetimeExtended(entry)) {
- // TODO: This doesn't need to be undefined -- we can set either EXTENDER_EXPIRED or
- // save the original reason
- removeNotification(entry.getKey(), null, REASON_UNKNOWN, null);
+ if (tryRemoveNotification(entry)) {
+ rebuildList();
+ }
}
}
@@ -366,6 +425,31 @@ public class NotifCollection implements Dumpable {
return entry.mLifetimeExtenders.size() > 0;
}
+ private void updateLifetimeExtension(NotificationEntry entry) {
+ entry.mLifetimeExtenders.clear();
+ mAmDispatchingToOtherCode = true;
+ for (NotifLifetimeExtender extender : mLifetimeExtenders) {
+ if (extender.shouldExtendLifetime(entry, entry.mCancellationReason)) {
+ entry.mLifetimeExtenders.add(extender);
+ }
+ }
+ mAmDispatchingToOtherCode = false;
+ }
+
+ private void cancelLocalDismissal(NotificationEntry entry) {
+ if (isDismissedByUser(entry)) {
+ entry.setDismissState(NOT_DISMISSED);
+ if (entry.getSbn().getNotification().isGroupSummary()) {
+ for (NotificationEntry otherEntry : mNotificationSet.values()) {
+ if (otherEntry.getSbn().getGroupKey().equals(entry.getSbn().getGroupKey())
+ && otherEntry.getDismissState() == PARENT_DISMISSED) {
+ otherEntry.setDismissState(NOT_DISMISSED);
+ }
+ }
+ }
+ }
+ }
+
private void checkForReentrantCall() {
if (mAmDispatchingToOtherCode) {
throw new IllegalStateException("Reentrant call detected");
@@ -381,6 +465,19 @@ public class NotifCollection implements Dumpable {
return ranking;
}
+ /**
+ * True if the notification has been canceled by system server. Usually, such notifications are
+ * immediately removed from the collection, but can sometimes stick around due to lifetime
+ * extenders.
+ */
+ private static boolean isCanceled(NotificationEntry entry) {
+ return entry.mCancellationReason != REASON_NOT_CANCELED;
+ }
+
+ private static boolean isDismissedByUser(NotificationEntry entry) {
+ return entry.getDismissState() != NOT_DISMISSED;
+ }
+
private void dispatchOnEntryInit(NotificationEntry entry) {
mAmDispatchingToOtherCode = true;
for (NotifCollectionListener listener : mNotifCollectionListeners) {
@@ -413,13 +510,10 @@ public class NotifCollection implements Dumpable {
mAmDispatchingToOtherCode = false;
}
- private void dispatchOnEntryRemoved(
- NotificationEntry entry,
- @CancellationReason int reason,
- boolean removedByUser) {
+ private void dispatchOnEntryRemoved(NotificationEntry entry, @CancellationReason int reason) {
mAmDispatchingToOtherCode = true;
for (NotifCollectionListener listener : mNotifCollectionListeners) {
- listener.onEntryRemoved(entry, reason, removedByUser);
+ listener.onEntryRemoved(entry, reason);
}
mAmDispatchingToOtherCode = false;
}
@@ -431,6 +525,20 @@ public class NotifCollection implements Dumpable {
}
mAmDispatchingToOtherCode = false;
}
+ @Override
+ public void dump(@NonNull FileDescriptor fd, PrintWriter pw, @NonNull String[] args) {
+ final List<NotificationEntry> entries = new ArrayList<>(getActiveNotifs());
+
+ pw.println("\t" + TAG + " unsorted/unfiltered notifications:");
+ if (entries.size() == 0) {
+ pw.println("\t\t None");
+ }
+ pw.println(
+ ListDumper.dumpList(
+ entries,
+ true,
+ "\t\t"));
+ }
private final BatchableNotificationHandler mNotifHandler = new BatchableNotificationHandler() {
@Override
@@ -486,20 +594,6 @@ public class NotifCollection implements Dumpable {
@Retention(RetentionPolicy.SOURCE)
public @interface CancellationReason {}
+ public static final int REASON_NOT_CANCELED = -1;
public static final int REASON_UNKNOWN = 0;
-
- @Override
- public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
- final List<NotificationEntry> entries = new ArrayList<>(getActiveNotifs());
-
- pw.println("\t" + TAG + " unsorted/unfiltered notifications:");
- if (entries.size() == 0) {
- pw.println("\t\t None");
- }
- pw.println(
- ListDumper.dumpList(
- entries,
- true,
- "\t\t"));
- }
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotifInflaterImpl.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotifInflaterImpl.java
index e7b772f1c7b2..7a6d4f1c7d7b 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotifInflaterImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotifInflaterImpl.java
@@ -98,13 +98,12 @@ public class NotifInflaterImpl implements NotifInflater {
@Override
public void run() {
int dismissalSurface = NotificationStats.DISMISSAL_SHADE;
- /**
+ /*
* TODO: determine dismissal surface (ie: shade / headsup / aod)
* see {@link NotificationLogger#logNotificationClear}
*/
mNotifCollection.dismissNotification(
entry,
- 0,
new DismissedByUserStats(
dismissalSurface,
DISMISS_SENTIMENT_NEUTRAL,
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotificationEntry.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotificationEntry.java
index 1f77ec229041..df65dacf12dd 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotificationEntry.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotificationEntry.java
@@ -22,6 +22,7 @@ import static android.app.Notification.CATEGORY_EVENT;
import static android.app.Notification.CATEGORY_MESSAGE;
import static android.app.Notification.CATEGORY_REMINDER;
import static android.app.Notification.FLAG_BUBBLE;
+import static android.app.Notification.FLAG_FOREGROUND_SERVICE;
import static android.app.NotificationManager.Policy.SUPPRESSED_EFFECT_AMBIENT;
import static android.app.NotificationManager.Policy.SUPPRESSED_EFFECT_BADGE;
import static android.app.NotificationManager.Policy.SUPPRESSED_EFFECT_FULL_SCREEN_INTENT;
@@ -29,9 +30,11 @@ import static android.app.NotificationManager.Policy.SUPPRESSED_EFFECT_NOTIFICAT
import static android.app.NotificationManager.Policy.SUPPRESSED_EFFECT_PEEK;
import static android.app.NotificationManager.Policy.SUPPRESSED_EFFECT_STATUS_BAR;
+import static com.android.systemui.statusbar.notification.collection.NotifCollection.REASON_NOT_CANCELED;
import static com.android.systemui.statusbar.notification.stack.NotificationSectionsManager.BUCKET_ALERTING;
-import android.annotation.NonNull;
+import static java.util.Objects.requireNonNull;
+
import android.app.Notification;
import android.app.Notification.MessagingStyle.Message;
import android.app.NotificationChannel;
@@ -50,6 +53,7 @@ import android.util.ArraySet;
import android.view.View;
import android.widget.ImageView;
+import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import com.android.internal.annotations.VisibleForTesting;
@@ -59,6 +63,7 @@ import com.android.internal.util.ContrastColorUtil;
import com.android.systemui.statusbar.InflationTask;
import com.android.systemui.statusbar.StatusBarIconView;
import com.android.systemui.statusbar.notification.InflationException;
+import com.android.systemui.statusbar.notification.collection.NotifCollection.CancellationReason;
import com.android.systemui.statusbar.notification.collection.listbuilder.pluggable.NotifFilter;
import com.android.systemui.statusbar.notification.collection.listbuilder.pluggable.NotifPromoter;
import com.android.systemui.statusbar.notification.collection.notifcollection.NotifLifetimeExtender;
@@ -105,9 +110,18 @@ public final class NotificationEntry extends ListEntry {
/** If this was a group child that was promoted to the top level, then who did the promoting. */
@Nullable NotifPromoter mNotifPromoter;
- /** If this notification had an issue with inflating. Only used with the NewNotifPipeline **/
+ /**
+ * If this notification was cancelled by system server, then the reason that was supplied.
+ * Uncancelled notifications always have REASON_NOT_CANCELED. Note that lifetime-extended
+ * notifications will have this set even though they are still in the active notification set.
+ */
+ @CancellationReason int mCancellationReason = REASON_NOT_CANCELED;
+
+ /** @see #hasInflationError() */
private boolean mHasInflationError;
+ /** @see #getDismissState() */
+ @NonNull private DismissState mDismissState = DismissState.NOT_DISMISSED;
/*
* Old members
@@ -164,18 +178,29 @@ public final class NotificationEntry extends ListEntry {
private Runnable mOnSensitiveChangedListener;
private boolean mAutoHeadsUp;
private boolean mPulseSupressed;
+ private boolean mAllowFgsDismissal;
private int mBucket = BUCKET_ALERTING;
public NotificationEntry(
@NonNull StatusBarNotification sbn,
@NonNull Ranking ranking) {
- super(Objects.requireNonNull(Objects.requireNonNull(sbn).getKey()));
+ this(sbn, ranking, false);
+ }
+
+ public NotificationEntry(
+ @NonNull StatusBarNotification sbn,
+ @NonNull Ranking ranking,
+ boolean allowFgsDismissal
+ ) {
+ super(requireNonNull(Objects.requireNonNull(sbn).getKey()));
- Objects.requireNonNull(ranking);
+ requireNonNull(ranking);
mKey = sbn.getKey();
setSbn(sbn);
setRanking(ranking);
+
+ mAllowFgsDismissal = allowFgsDismissal;
}
@Override
@@ -201,8 +226,8 @@ public final class NotificationEntry extends ListEntry {
* TODO: Make this package-private
*/
public void setSbn(@NonNull StatusBarNotification sbn) {
- Objects.requireNonNull(sbn);
- Objects.requireNonNull(sbn.getKey());
+ requireNonNull(sbn);
+ requireNonNull(sbn.getKey());
if (!sbn.getKey().equals(mKey)) {
throw new IllegalArgumentException("New key " + sbn.getKey()
@@ -227,8 +252,8 @@ public final class NotificationEntry extends ListEntry {
* TODO: Make this package-private
*/
public void setRanking(@NonNull Ranking ranking) {
- Objects.requireNonNull(ranking);
- Objects.requireNonNull(ranking.getKey());
+ requireNonNull(ranking);
+ requireNonNull(ranking.getKey());
if (!ranking.getKey().equals(mKey)) {
throw new IllegalArgumentException("New key " + ranking.getKey()
@@ -239,6 +264,34 @@ public final class NotificationEntry extends ListEntry {
}
/*
+ * Bookkeeping getters and setters
+ */
+
+ /**
+ * Whether this notification had an error when attempting to inflate. This is only used in
+ * the NewNotifPipeline
+ */
+ public boolean hasInflationError() {
+ return mHasInflationError;
+ }
+
+ void setHasInflationError(boolean hasError) {
+ mHasInflationError = hasError;
+ }
+
+ /**
+ * Set if the user has dismissed this notif but we haven't yet heard back from system server to
+ * confirm the dismissal.
+ */
+ @NonNull public DismissState getDismissState() {
+ return mDismissState;
+ }
+
+ void setDismissState(@NonNull DismissState dismissState) {
+ mDismissState = requireNonNull(dismissState);
+ }
+
+ /*
* Convenience getters for SBN and Ranking members
*/
@@ -275,7 +328,6 @@ public final class NotificationEntry extends ListEntry {
return mRanking.canBubble();
}
-
public @NonNull List<Notification.Action> getSmartActions() {
return mRanking.getSmartActions();
}
@@ -578,18 +630,6 @@ public final class NotificationEntry extends ListEntry {
remoteInputTextWhenReset = null;
}
- void setHasInflationError(boolean hasError) {
- mHasInflationError = hasError;
- }
-
- /**
- * Whether this notification had an error when attempting to inflate. This is only used in
- * the NewNotifPipeline
- */
- public boolean hasInflationError() {
- return mHasInflationError;
- }
-
public void setHasSentReply() {
hasSentReply = true;
}
@@ -799,8 +839,11 @@ public final class NotificationEntry extends ListEntry {
* notification can be dismissed in case notifications are sensitive on the lockscreen.
* @see #canViewBeDismissed()
*/
+ // TOOD: This logic doesn't belong on NotificationEntry. It should be moved to the
+ // ForegroundsServiceDismissalFeatureController or some other controller that can be added
+ // as a dependency to any class that needs to answer this question.
public boolean isClearable() {
- if (!mSbn.isClearable()) {
+ if (!isDismissable()) {
return false;
}
@@ -808,7 +851,7 @@ public final class NotificationEntry extends ListEntry {
if (children != null && children.size() > 0) {
for (int i = 0; i < children.size(); i++) {
NotificationEntry child = children.get(i);
- if (!child.isClearable()) {
+ if (!child.isDismissable()) {
return false;
}
}
@@ -816,6 +859,31 @@ public final class NotificationEntry extends ListEntry {
return true;
}
+ /**
+ * Notifications might have any combination of flags:
+ * - FLAG_ONGOING_EVENT
+ * - FLAG_NO_CLEAR
+ * - FLAG_FOREGROUND_SERVICE
+ *
+ * We want to allow dismissal of notifications that represent foreground services, which may
+ * have all 3 flags set. If we only find NO_CLEAR though, we don't want to allow dismissal
+ */
+ private boolean isDismissable() {
+ boolean ongoing = ((mSbn.getNotification().flags & Notification.FLAG_ONGOING_EVENT) != 0);
+ boolean noclear = ((mSbn.getNotification().flags & Notification.FLAG_NO_CLEAR) != 0);
+ boolean fgs = ((mSbn.getNotification().flags & FLAG_FOREGROUND_SERVICE) != 0);
+
+ if (mAllowFgsDismissal) {
+ if (noclear && !ongoing && !fgs) {
+ return false;
+ }
+ return true;
+ } else {
+ return mSbn.isClearable();
+ }
+
+ }
+
public boolean canViewBeDismissed() {
if (row == null) return true;
return row.canViewBeDismissed();
@@ -974,6 +1042,16 @@ public final class NotificationEntry extends ListEntry {
}
}
+ /** @see #getDismissState() */
+ public enum DismissState {
+ /** User has not dismissed this notif or its parent */
+ NOT_DISMISSED,
+ /** User has dismissed this notif specifically */
+ DISMISSED,
+ /** User has dismissed this notif's parent (which implicitly dismisses this one as well) */
+ PARENT_DISMISSED,
+ }
+
private static final long LAUNCH_COOLDOWN = 2000;
private static final long REMOTE_INPUT_COOLDOWN = 500;
private static final long INITIALIZATION_DELAY = 400;
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/HideLocallyDismissedNotifsCoordinator.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/HideLocallyDismissedNotifsCoordinator.java
new file mode 100644
index 000000000000..0059e7baa3c2
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/HideLocallyDismissedNotifsCoordinator.java
@@ -0,0 +1,41 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.statusbar.notification.collection.coordinator;
+
+import static com.android.systemui.statusbar.notification.collection.NotificationEntry.DismissState.NOT_DISMISSED;
+
+import com.android.systemui.statusbar.notification.collection.NotifPipeline;
+import com.android.systemui.statusbar.notification.collection.NotificationEntry;
+import com.android.systemui.statusbar.notification.collection.listbuilder.pluggable.NotifFilter;
+
+/**
+ * Filters out notifications that have been dismissed locally (by the user) but that system server
+ * hasn't yet confirmed the removal of.
+ */
+public class HideLocallyDismissedNotifsCoordinator implements Coordinator {
+ @Override
+ public void attach(NotifPipeline pipeline) {
+ pipeline.addPreGroupFilter(mFilter);
+ }
+
+ private final NotifFilter mFilter = new NotifFilter("HideLocallyDismissedNotifsFilter") {
+ @Override
+ public boolean shouldFilterOut(NotificationEntry entry, long now) {
+ return entry.getDismissState() != NOT_DISMISSED;
+ }
+ };
+}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/NotifCoordinators.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/NotifCoordinators.java
index 8d0dd5b111ba..0a1e09f4c99d 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/NotifCoordinators.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/NotifCoordinators.java
@@ -56,6 +56,7 @@ public class NotifCoordinators implements Dumpable {
PreparationCoordinator preparationCoordinator) {
dumpController.registerDumpable(TAG, this);
+ mCoordinators.add(new HideLocallyDismissedNotifsCoordinator());
mCoordinators.add(keyguardCoordinator);
mCoordinators.add(rankingCoordinator);
mCoordinators.add(foregroundCoordinator);
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/PreparationCoordinator.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/PreparationCoordinator.java
index 20c9cbc8790d..41314b86695a 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/PreparationCoordinator.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/PreparationCoordinator.java
@@ -72,7 +72,7 @@ public class PreparationCoordinator implements Coordinator {
}
@Override
- public void onEntryRemoved(NotificationEntry entry, int reason, boolean removedByUser) {
+ public void onEntryRemoved(NotificationEntry entry, int reason) {
abortInflation(entry, "entryRemoved reason=" + reason);
}
};
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/notifcollection/NotifCollectionListener.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/notifcollection/NotifCollectionListener.java
index ff6da12bd0bc..b2c53dae16cd 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/notifcollection/NotifCollectionListener.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/notifcollection/NotifCollectionListener.java
@@ -54,10 +54,7 @@ public interface NotifCollectionListener {
* immediately after a user dismisses a notification: we wait until we receive confirmation from
* system server before considering the notification removed.
*/
- default void onEntryRemoved(
- NotificationEntry entry,
- @CancellationReason int reason,
- boolean removedByUser) {
+ default void onEntryRemoved(NotificationEntry entry, @CancellationReason int reason) {
}
/**
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/notifcollection/NotifCollectionLogger.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/notifcollection/NotifCollectionLogger.kt
index bd1bd860f80c..14e15031056f 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/notifcollection/NotifCollectionLogger.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/notifcollection/NotifCollectionLogger.kt
@@ -16,8 +16,11 @@
package com.android.systemui.statusbar.notification.collection.notifcollection
+import android.service.notification.NotificationListenerService.RankingMap
import com.android.systemui.log.LogBuffer
-import com.android.systemui.log.LogLevel
+import com.android.systemui.log.LogLevel.DEBUG
+import com.android.systemui.log.LogLevel.INFO
+import com.android.systemui.log.LogLevel.WARNING
import com.android.systemui.log.dagger.NotificationLog
import javax.inject.Inject
@@ -25,7 +28,7 @@ class NotifCollectionLogger @Inject constructor(
@NotificationLog private val buffer: LogBuffer
) {
fun logNotifPosted(key: String) {
- buffer.log(TAG, LogLevel.INFO, {
+ buffer.log(TAG, INFO, {
str1 = key
}, {
"POSTED $str1"
@@ -33,7 +36,7 @@ class NotifCollectionLogger @Inject constructor(
}
fun logNotifGroupPosted(groupKey: String, batchSize: Int) {
- buffer.log(TAG, LogLevel.INFO, {
+ buffer.log(TAG, INFO, {
str1 = groupKey
int1 = batchSize
}, {
@@ -42,7 +45,7 @@ class NotifCollectionLogger @Inject constructor(
}
fun logNotifUpdated(key: String) {
- buffer.log(TAG, LogLevel.INFO, {
+ buffer.log(TAG, INFO, {
str1 = key
}, {
"UPDATED $str1"
@@ -50,13 +53,29 @@ class NotifCollectionLogger @Inject constructor(
}
fun logNotifRemoved(key: String, reason: Int) {
- buffer.log(TAG, LogLevel.INFO, {
+ buffer.log(TAG, INFO, {
str1 = key
int1 = reason
}, {
"REMOVED $str1 reason=$int1"
})
}
+
+ fun logNotifDismissed(key: String) {
+ buffer.log(TAG, INFO, {
+ str1 = key
+ }, {
+ "DISMISSED $str1"
+ })
+ }
+
+ fun logRankingMissing(key: String, rankingMap: RankingMap) {
+ buffer.log(TAG, WARNING, { str1 = key }, { "Ranking update is missing ranking for $str1" })
+ buffer.log(TAG, DEBUG, {}, { "Ranking map contents:" })
+ for (entry in rankingMap.orderedKeys) {
+ buffer.log(TAG, DEBUG, { str1 = entry }, { " $str1" })
+ }
+ }
}
private const val TAG = "NotifCollection" \ No newline at end of file
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/DungeonRow.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/DungeonRow.kt
new file mode 100644
index 000000000000..373457d4e336
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/DungeonRow.kt
@@ -0,0 +1,43 @@
+/*
+* Copyright (C) 2020 The Android Open Source Project
+*
+* Licensed under the Apache License, Version 2.0 (the "License");
+* you may not use this file except in compliance with the License.
+* You may obtain a copy of the License at
+*
+* http://www.apache.org/licenses/LICENSE-2.0
+*
+* Unless required by applicable law or agreed to in writing, software
+* distributed under the License is distributed on an "AS IS" BASIS,
+* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+* See the License for the specific language governing permissions and
+* limitations under the License.
+*/
+
+package com.android.systemui.statusbar.notification.row
+
+import android.content.Context
+import android.util.AttributeSet
+import android.widget.LinearLayout
+import android.widget.TextView
+import com.android.systemui.R
+import com.android.systemui.statusbar.StatusBarIconView
+import com.android.systemui.statusbar.notification.collection.NotificationEntry
+
+class DungeonRow(context: Context, attrs: AttributeSet) : LinearLayout(context, attrs) {
+ var entry: NotificationEntry? = null
+ set(value) {
+ field = value
+ update()
+ }
+
+ private fun update() {
+ (findViewById(R.id.app_name) as TextView).apply {
+ text = entry?.row?.appName
+ }
+
+ (findViewById(R.id.icon) as StatusBarIconView).apply {
+ set(entry?.icon?.statusBarIcon)
+ }
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRow.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRow.java
index b71bedaca707..253be2fcb5ca 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRow.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRow.java
@@ -745,6 +745,11 @@ public class ExpandableNotificationRow extends ActivatableNotificationView
mPrivateLayout.setRemoteInputController(r);
}
+
+ String getAppName() {
+ return mAppName;
+ }
+
public void addChildNotification(ExpandableNotificationRow row) {
addChildNotification(row, -1);
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ForegroundServiceDungeonView.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ForegroundServiceDungeonView.kt
new file mode 100644
index 000000000000..17396ad31ba2
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ForegroundServiceDungeonView.kt
@@ -0,0 +1,38 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.statusbar.notification.row
+
+import android.content.Context
+import android.util.AttributeSet
+import android.view.View
+
+import com.android.systemui.R
+
+class ForegroundServiceDungeonView(context: Context, attrs: AttributeSet)
+ : StackScrollerDecorView(context, attrs) {
+ override fun findContentView(): View? {
+ return findViewById(R.id.foreground_service_dungeon)
+ }
+
+ override fun findSecondaryView(): View? {
+ return null
+ }
+
+ override fun setVisible(visible: Boolean, animate: Boolean) {
+ // Visibility is controlled by the ForegroundServiceSectionController
+ }
+} \ No newline at end of file
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/ForegroundServiceSectionController.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/ForegroundServiceSectionController.kt
new file mode 100644
index 000000000000..5757fe8040f0
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/ForegroundServiceSectionController.kt
@@ -0,0 +1,171 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.statusbar.notification.stack
+
+import android.content.Context
+import android.service.notification.NotificationListenerService.REASON_APP_CANCEL
+import android.service.notification.NotificationListenerService.REASON_APP_CANCEL_ALL
+import android.service.notification.NotificationListenerService.REASON_CANCEL
+import android.service.notification.NotificationListenerService.REASON_CANCEL_ALL
+import android.service.notification.NotificationListenerService.REASON_CLICK
+import android.service.notification.NotificationListenerService.REASON_GROUP_SUMMARY_CANCELED
+import android.view.LayoutInflater
+import android.view.View
+import android.widget.LinearLayout
+
+import com.android.systemui.statusbar.notification.collection.NotificationEntry
+import com.android.systemui.R
+import com.android.systemui.statusbar.notification.ForegroundServiceDismissalFeatureController
+import com.android.systemui.statusbar.notification.NotificationEntryListener
+import com.android.systemui.statusbar.notification.NotificationEntryManager
+import com.android.systemui.statusbar.notification.row.DungeonRow
+import com.android.systemui.util.Assert
+
+import javax.inject.Inject
+import javax.inject.Singleton
+
+/**
+ * Controller for the bottom area of NotificationStackScrollLayout. It owns swiped-away foreground
+ * service notifications and can reinstantiate them when requested.
+ */
+@Singleton
+class ForegroundServiceSectionController @Inject constructor(
+ val entryManager: NotificationEntryManager,
+ val featureController: ForegroundServiceDismissalFeatureController
+) {
+ private val TAG = "FgsSectionController"
+ private var context: Context? = null
+
+ private val entries = mutableSetOf<NotificationEntry>()
+
+ private var entriesView: View? = null
+
+ init {
+ if (featureController.isForegroundServiceDismissalEnabled()) {
+ entryManager.addNotificationRemoveInterceptor(this::shouldInterceptRemoval)
+
+ entryManager.addNotificationEntryListener(object : NotificationEntryListener {
+ override fun onPostEntryUpdated(entry: NotificationEntry) {
+ if (entries.contains(entry)) {
+ removeEntry(entry)
+ addEntry(entry)
+ update()
+ }
+ }
+ })
+ }
+ }
+
+ private fun shouldInterceptRemoval(
+ key: String,
+ entry: NotificationEntry?,
+ reason: Int
+ ): Boolean {
+ Assert.isMainThread()
+ val isClearAll = reason == REASON_CANCEL_ALL
+ val isUserDismiss = reason == REASON_CANCEL || reason == REASON_CLICK
+ val isAppCancel = reason == REASON_APP_CANCEL || reason == REASON_APP_CANCEL_ALL
+ val isSummaryCancel = reason == REASON_GROUP_SUMMARY_CANCELED
+
+ if (entry == null) return false
+
+ // We only want to retain notifications that the user dismissed
+ // TODO: centralize the entry.isClearable logic and this so that it's clear when a notif is
+ // clearable
+ if (isUserDismiss && !entry.sbn.isClearable) {
+ if (!hasEntry(entry)) {
+ addEntry(entry)
+ update()
+ }
+ // TODO: This isn't ideal. Slightly better would at least be to have NEM update the
+ // notif list when an entry gets intercepted
+ entryManager.updateNotifications(
+ "FgsSectionController.onNotificationRemoveRequested")
+ return true
+ } else if ((isClearAll || isSummaryCancel) && !entry.sbn.isClearable) {
+ // In the case where a FGS notification is part of a group that is cleared or a clear
+ // all, we actually want to stop its removal but also not put it into the dungeon
+ return true
+ } else if (hasEntry(entry)) {
+ removeEntry(entry)
+ update()
+ return false
+ }
+
+ return false
+ }
+
+ private fun removeEntry(entry: NotificationEntry) {
+ Assert.isMainThread()
+ entries.remove(entry)
+ }
+
+ private fun addEntry(entry: NotificationEntry) {
+ Assert.isMainThread()
+ entries.add(entry)
+ }
+
+ fun hasEntry(entry: NotificationEntry): Boolean {
+ Assert.isMainThread()
+ return entries.contains(entry)
+ }
+
+ fun initialize(context: Context) {
+ this.context = context
+ }
+
+ fun createView(li: LayoutInflater): View {
+ entriesView = li.inflate(R.layout.foreground_service_dungeon, null)
+ // Start out gone
+ entriesView!!.visibility = View.GONE
+ return entriesView!!
+ }
+
+ private fun update() {
+ Assert.isMainThread()
+ if (entriesView == null) {
+ throw IllegalStateException("ForegroundServiceSectionController is trying to show " +
+ "dismissed fgs notifications without having been initialized!")
+ }
+
+ // TODO: these views should be recycled and not inflating on the main thread
+ (entriesView!!.findViewById(R.id.entry_list) as LinearLayout).apply {
+ removeAllViews()
+ entries.sortedBy { it.ranking.rank }.forEach { entry ->
+ val child = LayoutInflater.from(context)
+ .inflate(R.layout.foreground_service_dungeon_row, null) as DungeonRow
+
+ child.entry = entry
+ child.setOnClickListener {
+ removeEntry(child.entry!!)
+ update()
+ entry.row.unDismiss()
+ entry.row.resetTranslation()
+ entryManager.updateNotifications("ForegroundServiceSectionController.onClick")
+ }
+
+ addView(child)
+ }
+ }
+
+ if (entries.isEmpty()) {
+ entriesView?.visibility = View.GONE
+ } else {
+ entriesView?.visibility = View.VISIBLE
+ }
+ }
+}
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 84a293eebc95..4b9976cc2097 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
@@ -35,7 +35,6 @@ import android.annotation.NonNull;
import android.annotation.Nullable;
import android.content.Context;
import android.content.Intent;
-import android.content.pm.UserInfo;
import android.content.res.Configuration;
import android.content.res.Resources;
import android.graphics.Canvas;
@@ -57,7 +56,6 @@ import android.util.DisplayMetrics;
import android.util.Log;
import android.util.MathUtils;
import android.util.Pair;
-import android.util.SparseArray;
import android.view.ContextThemeWrapper;
import android.view.InputDevice;
import android.view.LayoutInflater;
@@ -109,6 +107,7 @@ import com.android.systemui.statusbar.StatusBarState;
import com.android.systemui.statusbar.SysuiStatusBarStateController;
import com.android.systemui.statusbar.notification.DynamicPrivacyController;
import com.android.systemui.statusbar.notification.FakeShadowView;
+import com.android.systemui.statusbar.notification.ForegroundServiceDismissalFeatureController;
import com.android.systemui.statusbar.notification.NotificationEntryListener;
import com.android.systemui.statusbar.notification.NotificationEntryManager;
import com.android.systemui.statusbar.notification.NotificationUtils;
@@ -121,6 +120,7 @@ import com.android.systemui.statusbar.notification.row.ActivatableNotificationVi
import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow;
import com.android.systemui.statusbar.notification.row.ExpandableView;
import com.android.systemui.statusbar.notification.row.FooterView;
+import com.android.systemui.statusbar.notification.row.ForegroundServiceDungeonView;
import com.android.systemui.statusbar.notification.row.NotificationBlockingHelperManager;
import com.android.systemui.statusbar.notification.row.NotificationGuts;
import com.android.systemui.statusbar.notification.row.NotificationGutsManager;
@@ -506,6 +506,8 @@ public class NotificationStackScrollLayout extends ViewGroup implements ScrollAd
private final NotificationGutsManager mNotificationGutsManager;
private final NotificationSectionsManager mSectionsManager;
+ private final ForegroundServiceSectionController mFgsSectionController;
+ private ForegroundServiceDungeonView mFgsSectionView;
private boolean mAnimateBottomOnLayout;
private float mLastSentAppear;
private float mLastSentExpandedHeight;
@@ -525,7 +527,10 @@ public class NotificationStackScrollLayout extends ViewGroup implements ScrollAd
NotificationLockscreenUserManager notificationLockscreenUserManager,
NotificationGutsManager notificationGutsManager,
ZenModeController zenController,
- NotificationSectionsManager notificationSectionsManager) {
+ NotificationSectionsManager notificationSectionsManager,
+ ForegroundServiceSectionController fgsSectionController,
+ ForegroundServiceDismissalFeatureController fgsFeatureController
+ ) {
super(context, attrs, 0, 0);
Resources res = getResources();
@@ -541,6 +546,7 @@ public class NotificationStackScrollLayout extends ViewGroup implements ScrollAd
mKeyguardBypassController = keyguardBypassController;
mFalsingManager = falsingManager;
mZenController = zenController;
+ mFgsSectionController = fgsSectionController;
mSectionsManager = notificationSectionsManager;
mSectionsManager.initialize(this, LayoutInflater.from(context));
@@ -614,6 +620,17 @@ public class NotificationStackScrollLayout extends ViewGroup implements ScrollAd
dynamicPrivacyController.addListener(this);
mDynamicPrivacyController = dynamicPrivacyController;
mStatusbarStateController = statusBarStateController;
+ initializeForegroundServiceSection(fgsFeatureController);
+ }
+
+ private void initializeForegroundServiceSection(
+ ForegroundServiceDismissalFeatureController featureController) {
+ if (featureController.isForegroundServiceDismissalEnabled()) {
+ LayoutInflater li = LayoutInflater.from(mContext);
+ mFgsSectionView =
+ (ForegroundServiceDungeonView) mFgsSectionController.createView(li);
+ addView(mFgsSectionView, -1);
+ }
}
private void updateDismissRtlSetting(boolean dismissRtl) {
@@ -3374,7 +3391,7 @@ public class NotificationStackScrollLayout extends ViewGroup implements ScrollAd
if (currentIndex == -1) {
boolean isTransient = false;
if (child instanceof ExpandableNotificationRow
- && ((ExpandableNotificationRow) child).getTransientContainer() != null) {
+ && child.getTransientContainer() != null) {
isTransient = true;
}
Log.e(TAG, "Attempting to re-position "
@@ -3387,10 +3404,10 @@ public class NotificationStackScrollLayout extends ViewGroup implements ScrollAd
if (child != null && child.getParent() == this && currentIndex != newIndex) {
mChangePositionInProgress = true;
- ((ExpandableView) child).setChangingPosition(true);
+ child.setChangingPosition(true);
removeView(child);
addView(child, newIndex);
- ((ExpandableView) child).setChangingPosition(false);
+ child.setChangingPosition(false);
mChangePositionInProgress = false;
if (mIsExpanded && mAnimationsEnabled && child.getVisibility() != View.GONE) {
mChildrenChangingPositions.add(child);
@@ -5637,15 +5654,17 @@ public class NotificationStackScrollLayout extends ViewGroup implements ScrollAd
*/
@ShadeViewRefactor(RefactorComponent.STATE_RESOLVER)
public void onUpdateRowStates() {
- changeViewPosition(mFooterView, -1);
// The following views will be moved to the end of mStackScroller. This counter represents
// the offset from the last child. Initialized to 1 for the very last position. It is post-
// incremented in the following "changeViewPosition" calls so that its value is correct for
// subsequent calls.
int offsetFromEnd = 1;
- changeViewPosition(mEmptyShadeView,
- getChildCount() - offsetFromEnd++);
+ if (mFgsSectionView != null) {
+ changeViewPosition(mFgsSectionView, getChildCount() - offsetFromEnd++);
+ }
+ changeViewPosition(mFooterView, getChildCount() - offsetFromEnd++);
+ changeViewPosition(mEmptyShadeView, getChildCount() - offsetFromEnd++);
// No post-increment for this call because it is the last one. Make sure to add one if
// another "changeViewPosition" call is ever added.
diff --git a/packages/SystemUI/src/com/android/systemui/tracing/sysui_trace.proto b/packages/SystemUI/src/com/android/systemui/tracing/sysui_trace.proto
index 08ae99ceb7a1..d940a6b5c460 100644
--- a/packages/SystemUI/src/com/android/systemui/tracing/sysui_trace.proto
+++ b/packages/SystemUI/src/com/android/systemui/tracing/sysui_trace.proto
@@ -29,3 +29,29 @@ message EdgeBackGestureHandlerProto {
optional bool allow_gesture = 1;
}
+
+/* represents a file full of system ui trace entries.
+ Encoded, it should start with 0x9 0x53 0x59 0x53 0x55 0x49 0x54 0x52 0x43 (.SYSUITRC), such
+ that they can be easily identified. */
+message SystemUiTraceFileProto {
+
+ /* constant; MAGIC_NUMBER = (long) MAGIC_NUMBER_H << 32 | MagicNumber.MAGIC_NUMBER_L
+ (this is needed because enums have to be 32 bits and there's no nice way to put 64bit
+ constants into .proto files. */
+ enum MagicNumber {
+ INVALID = 0;
+ MAGIC_NUMBER_L = 0x55535953; /* SYSU (little-endian ASCII) */
+ MAGIC_NUMBER_H = 0x43525449; /* ITRC (little-endian ASCII) */
+ }
+
+ optional fixed64 magic_number = 1; /* Must be the first field, set to value in MagicNumber */
+ repeated SystemUiTraceEntryProto entry = 2;
+}
+
+/* one system ui trace entry. */
+message SystemUiTraceEntryProto {
+ /* required: elapsed realtime in nanos since boot of when this entry was logged */
+ optional fixed64 elapsed_realtime_nanos = 1;
+
+ optional SystemUiTraceProto system_ui = 3;
+}
diff --git a/packages/SystemUI/src/com/android/systemui/tracing/sysui_trace_file.proto b/packages/SystemUI/src/com/android/systemui/tracing/sysui_trace_file.proto
deleted file mode 100644
index d1523ef13501..000000000000
--- a/packages/SystemUI/src/com/android/systemui/tracing/sysui_trace_file.proto
+++ /dev/null
@@ -1,49 +0,0 @@
-/*
- * Copyright (C) 2019 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-syntax = "proto2";
-
-import "frameworks/base/packages/SystemUI/src/com/android/systemui/tracing/sysui_trace.proto";
-
-package com.android.systemui.tracing;
-
-option java_multiple_files = true;
-
-/* represents a file full of system ui trace entries.
- Encoded, it should start with 0x9 0x53 0x59 0x53 0x55 0x49 0x54 0x52 0x43 (.SYSUITRC), such
- that they can be easily identified. */
-message SystemUiTraceFileProto {
-
- /* constant; MAGIC_NUMBER = (long) MAGIC_NUMBER_H << 32 | MagicNumber.MAGIC_NUMBER_L
- (this is needed because enums have to be 32 bits and there's no nice way to put 64bit
- constants into .proto files. */
- enum MagicNumber {
- INVALID = 0;
- MAGIC_NUMBER_L = 0x55535953; /* SYSU (little-endian ASCII) */
- MAGIC_NUMBER_H = 0x43525449; /* ITRC (little-endian ASCII) */
- }
-
- optional fixed64 magic_number = 1; /* Must be the first field, set to value in MagicNumber */
- repeated SystemUiTraceEntryProto entry = 2;
-}
-
-/* one system ui trace entry. */
-message SystemUiTraceEntryProto {
- /* required: elapsed realtime in nanos since boot of when this entry was logged */
- optional fixed64 elapsed_realtime_nanos = 1;
-
- optional SystemUiTraceProto system_ui = 3;
-}
diff --git a/packages/SystemUI/src/com/android/systemui/tv/TvSystemUIBinder.java b/packages/SystemUI/src/com/android/systemui/tv/TvSystemUIBinder.java
new file mode 100644
index 000000000000..264ddc026781
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/tv/TvSystemUIBinder.java
@@ -0,0 +1,28 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.tv;
+
+import com.android.systemui.dagger.SystemUIRootComponent;
+
+import dagger.Binds;
+import dagger.Module;
+
+@Module
+interface TvSystemUIBinder {
+ @Binds
+ SystemUIRootComponent bindSystemUIRootComponent(TvSystemUIRootComponent systemUIRootComponent);
+}
diff --git a/packages/SystemUI/src/com/android/systemui/tv/TvSystemUIFactory.java b/packages/SystemUI/src/com/android/systemui/tv/TvSystemUIFactory.java
new file mode 100644
index 000000000000..7d3ec678fd5f
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/tv/TvSystemUIFactory.java
@@ -0,0 +1,35 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.tv;
+
+import android.content.Context;
+
+import com.android.systemui.SystemUIFactory;
+import com.android.systemui.dagger.SystemUIRootComponent;
+
+/**
+ * TV variant {@link SystemUIFactory}, that substitutes default {@link SystemUIRootComponent} for
+ * {@link TvSystemUIRootComponent}
+ */
+public class TvSystemUIFactory extends SystemUIFactory {
+ @Override
+ protected SystemUIRootComponent buildSystemUIRootComponent(Context context) {
+ return DaggerTvSystemUIRootComponent.builder()
+ .context(context)
+ .build();
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/tv/TvSystemUIRootComponent.java b/packages/SystemUI/src/com/android/systemui/tv/TvSystemUIRootComponent.java
new file mode 100644
index 000000000000..fcf27009883a
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/tv/TvSystemUIRootComponent.java
@@ -0,0 +1,58 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.tv;
+
+import android.content.Context;
+
+import com.android.systemui.dagger.DefaultComponentBinder;
+import com.android.systemui.dagger.DependencyBinder;
+import com.android.systemui.dagger.DependencyProvider;
+import com.android.systemui.dagger.SystemServicesModule;
+import com.android.systemui.dagger.SystemUIBinder;
+import com.android.systemui.dagger.SystemUIDefaultModule;
+import com.android.systemui.dagger.SystemUIModule;
+import com.android.systemui.dagger.SystemUIRootComponent;
+
+import javax.inject.Singleton;
+
+import dagger.BindsInstance;
+import dagger.Component;
+
+/**
+ * Root component for Dagger injection.
+ */
+@Singleton
+@Component(modules = {
+ DefaultComponentBinder.class,
+ DependencyProvider.class,
+ DependencyBinder.class,
+ SystemServicesModule.class,
+ SystemUIBinder.class,
+ SystemUIModule.class,
+ SystemUIDefaultModule.class,
+ TvSystemUIBinder.class})
+public interface TvSystemUIRootComponent extends SystemUIRootComponent {
+ /**
+ * Component Builder interface. This allows to bind Context instance in the component
+ */
+ @Component.Builder
+ interface Builder {
+ @BindsInstance Builder context(Context context);
+
+ TvSystemUIRootComponent build();
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/wm/DisplayLayout.java b/packages/SystemUI/src/com/android/systemui/wm/DisplayLayout.java
index 264a683f6a3d..64b0b66a47a9 100644
--- a/packages/SystemUI/src/com/android/systemui/wm/DisplayLayout.java
+++ b/packages/SystemUI/src/com/android/systemui/wm/DisplayLayout.java
@@ -31,9 +31,11 @@ import android.annotation.NonNull;
import android.content.ContentResolver;
import android.content.Context;
import android.content.res.Resources;
+import android.graphics.Insets;
import android.graphics.Rect;
import android.os.SystemProperties;
import android.provider.Settings;
+import android.util.RotationUtils;
import android.util.Size;
import android.view.Display;
import android.view.DisplayCutout;
@@ -43,8 +45,6 @@ import android.view.Surface;
import com.android.internal.R;
-import java.util.List;
-
/**
* Contains information about the layout-properties of a display. This refers to internal layout
* like insets/cutout/rotation. In general, this can be thought of as the System-UI analog to
@@ -323,28 +323,38 @@ public class DisplayLayout {
if (cutout == null || cutout == DisplayCutout.NO_CUTOUT) {
return null;
}
+ final Insets waterfallInsets =
+ RotationUtils.rotateInsets(cutout.getWaterfallInsets(), rotation);
if (rotation == ROTATION_0) {
- return computeSafeInsets(
- cutout, displayWidth, displayHeight);
+ return computeSafeInsets(cutout, displayWidth, displayHeight);
}
final boolean rotated = (rotation == ROTATION_90 || rotation == ROTATION_270);
- Rect[] cutoutRects = computeSafeInsets(cutout, displayWidth, displayHeight)
- .getBoundingRectsAll();
+ Rect[] cutoutRects = cutout.getBoundingRectsAll();
final Rect[] newBounds = new Rect[cutoutRects.length];
final Rect displayBounds = new Rect(0, 0, displayWidth, displayHeight);
for (int i = 0; i < cutoutRects.length; ++i) {
- newBounds[i] = new Rect(cutoutRects[i]);
- rotateBounds(newBounds[i], displayBounds, rotation);
+ final Rect rect = new Rect(cutoutRects[i]);
+ if (!rect.isEmpty()) {
+ rotateBounds(rect, displayBounds, rotation);
+ }
+ newBounds[getBoundIndexFromRotation(i, rotation)] = rect;
}
- return computeSafeInsets(DisplayCutout.fromBounds(newBounds),
+ return computeSafeInsets(
+ DisplayCutout.fromBoundsAndWaterfall(newBounds, waterfallInsets),
rotated ? displayHeight : displayWidth,
rotated ? displayWidth : displayHeight);
}
+ private static int getBoundIndexFromRotation(int index, int rotation) {
+ return (index - rotation) < 0
+ ? index - rotation + DisplayCutout.BOUNDS_POSITION_LENGTH
+ : index - rotation;
+ }
+
/** Calculate safe insets. */
public static DisplayCutout computeSafeInsets(DisplayCutout inner,
int displayWidth, int displayHeight) {
- if (inner == DisplayCutout.NO_CUTOUT || inner.isBoundsEmpty()) {
+ if (inner == DisplayCutout.NO_CUTOUT) {
return null;
}
@@ -353,58 +363,44 @@ public class DisplayLayout {
return inner.replaceSafeInsets(safeInsets);
}
- private static Rect computeSafeInsets(Size displaySize, DisplayCutout cutout) {
- if (displaySize.getWidth() < displaySize.getHeight()) {
- final List<Rect> boundingRects = cutout.replaceSafeInsets(
- new Rect(0, displaySize.getHeight() / 2, 0, displaySize.getHeight() / 2))
- .getBoundingRects();
- int topInset = findInsetForSide(displaySize, boundingRects, Gravity.TOP);
- int bottomInset = findInsetForSide(displaySize, boundingRects, Gravity.BOTTOM);
- return new Rect(0, topInset, 0, bottomInset);
- } else if (displaySize.getWidth() > displaySize.getHeight()) {
- final List<Rect> boundingRects = cutout.replaceSafeInsets(
- new Rect(displaySize.getWidth() / 2, 0, displaySize.getWidth() / 2, 0))
- .getBoundingRects();
- int leftInset = findInsetForSide(displaySize, boundingRects, Gravity.LEFT);
- int right = findInsetForSide(displaySize, boundingRects, Gravity.RIGHT);
- return new Rect(leftInset, 0, right, 0);
- } else {
+ private static Rect computeSafeInsets(
+ Size displaySize, DisplayCutout cutout) {
+ if (displaySize.getWidth() == displaySize.getHeight()) {
throw new UnsupportedOperationException("not implemented: display=" + displaySize
+ " cutout=" + cutout);
}
+
+ int leftInset = Math.max(cutout.getWaterfallInsets().left,
+ findCutoutInsetForSide(displaySize, cutout.getBoundingRectLeft(), Gravity.LEFT));
+ int topInset = Math.max(cutout.getWaterfallInsets().top,
+ findCutoutInsetForSide(displaySize, cutout.getBoundingRectTop(), Gravity.TOP));
+ int rightInset = Math.max(cutout.getWaterfallInsets().right,
+ findCutoutInsetForSide(displaySize, cutout.getBoundingRectRight(), Gravity.RIGHT));
+ int bottomInset = Math.max(cutout.getWaterfallInsets().bottom,
+ findCutoutInsetForSide(displaySize, cutout.getBoundingRectBottom(),
+ Gravity.BOTTOM));
+
+ return new Rect(leftInset, topInset, rightInset, bottomInset);
}
- private static int findInsetForSide(Size display, List<Rect> boundingRects, int gravity) {
+ private static int findCutoutInsetForSide(Size display, Rect boundingRect, int gravity) {
+ if (boundingRect.isEmpty()) {
+ return 0;
+ }
+
int inset = 0;
- final int size = boundingRects.size();
- for (int i = 0; i < size; i++) {
- Rect boundingRect = boundingRects.get(i);
- switch (gravity) {
- case Gravity.TOP:
- if (boundingRect.top == 0) {
- inset = Math.max(inset, boundingRect.bottom);
- }
- break;
- case Gravity.BOTTOM:
- if (boundingRect.bottom == display.getHeight()) {
- inset = Math.max(inset, display.getHeight() - boundingRect.top);
- }
- break;
- case Gravity.LEFT:
- if (boundingRect.left == 0) {
- inset = Math.max(inset, boundingRect.right);
- }
- break;
- case Gravity.RIGHT:
- if (boundingRect.right == display.getWidth()) {
- inset = Math.max(inset, display.getWidth() - boundingRect.left);
- }
- break;
- default:
- throw new IllegalArgumentException("unknown gravity: " + gravity);
- }
+ switch (gravity) {
+ case Gravity.TOP:
+ return Math.max(inset, boundingRect.bottom);
+ case Gravity.BOTTOM:
+ return Math.max(inset, display.getHeight() - boundingRect.top);
+ case Gravity.LEFT:
+ return Math.max(inset, boundingRect.right);
+ case Gravity.RIGHT:
+ return Math.max(inset, display.getWidth() - boundingRect.left);
+ default:
+ throw new IllegalArgumentException("unknown gravity: " + gravity);
}
- return inset;
}
static boolean hasNavigationBar(DisplayInfo info, Context context, int displayId) {
diff --git a/packages/SystemUI/tests/src/com/android/systemui/bubbles/BubbleControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/bubbles/BubbleControllerTest.java
index 333b4a778621..dcaf4ec6f8ae 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/bubbles/BubbleControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/bubbles/BubbleControllerTest.java
@@ -237,7 +237,7 @@ public class BubbleControllerTest extends SysuiTestCase {
mEntryListener = mEntryListenerCaptor.getValue();
// And the remove interceptor
verify(mNotificationEntryManager, atLeastOnce())
- .setNotificationRemoveInterceptor(mRemoveInterceptorCaptor.capture());
+ .addNotificationRemoveInterceptor(mRemoveInterceptorCaptor.capture());
mRemoveInterceptor = mRemoveInterceptorCaptor.getValue();
}
@@ -581,7 +581,7 @@ public class BubbleControllerTest extends SysuiTestCase {
// Simulate notification cancellation.
mRemoveInterceptor.onNotificationRemoveRequested(
- mRow.getEntry().getKey(), REASON_APP_CANCEL);
+ mRow.getEntry().getKey(), mRow.getEntry(), REASON_APP_CANCEL);
mBubbleController.expandStackAndSelectBubble(key);
}
@@ -649,7 +649,7 @@ public class BubbleControllerTest extends SysuiTestCase {
assertTrue(mBubbleController.hasBubbles());
boolean intercepted = mRemoveInterceptor.onNotificationRemoveRequested(
- mRow.getEntry().getKey(), REASON_APP_CANCEL);
+ mRow.getEntry().getKey(), mRow.getEntry(), REASON_APP_CANCEL);
// Cancels always remove so no need to intercept
assertFalse(intercepted);
@@ -666,7 +666,7 @@ public class BubbleControllerTest extends SysuiTestCase {
mRow.getEntry()));
boolean intercepted = mRemoveInterceptor.onNotificationRemoveRequested(
- mRow.getEntry().getKey(), REASON_CANCEL_ALL);
+ mRow.getEntry().getKey(), mRow.getEntry(), REASON_CANCEL_ALL);
// Intercept!
assertTrue(intercepted);
@@ -689,7 +689,7 @@ public class BubbleControllerTest extends SysuiTestCase {
mRow.getEntry()));
boolean intercepted = mRemoveInterceptor.onNotificationRemoveRequested(
- mRow.getEntry().getKey(), REASON_CANCEL);
+ mRow.getEntry().getKey(), mRow.getEntry(), REASON_CANCEL);
// Intercept!
assertTrue(intercepted);
@@ -718,7 +718,7 @@ public class BubbleControllerTest extends SysuiTestCase {
// Dismiss the notification
boolean intercepted = mRemoveInterceptor.onNotificationRemoveRequested(
- mRow.getEntry().getKey(), REASON_CANCEL);
+ mRow.getEntry().getKey(), mRow.getEntry(), REASON_CANCEL);
// It's no longer a bubble so we shouldn't intercept
assertFalse(intercepted);
@@ -736,7 +736,8 @@ public class BubbleControllerTest extends SysuiTestCase {
assertFalse(mBubbleController.isBubbleNotificationSuppressedFromShade(
mRow.getEntry()));
- mRemoveInterceptor.onNotificationRemoveRequested(mRow.getEntry().getKey(), REASON_CANCEL);
+ mRemoveInterceptor.onNotificationRemoveRequested(
+ mRow.getEntry().getKey(), mRow.getEntry(), REASON_CANCEL);
// Should update show in shade state
assertTrue(mBubbleController.isBubbleNotificationSuppressedFromShade(
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationViewHierarchyManagerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationViewHierarchyManagerTest.java
index c97813de8c0c..63c911b53db9 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationViewHierarchyManagerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationViewHierarchyManagerTest.java
@@ -49,6 +49,7 @@ import com.android.systemui.statusbar.notification.collection.NotificationEntryB
import com.android.systemui.statusbar.notification.logging.NotificationLogger;
import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow;
import com.android.systemui.statusbar.notification.row.ExpandableView;
+import com.android.systemui.statusbar.notification.stack.ForegroundServiceSectionController;
import com.android.systemui.statusbar.notification.stack.NotificationListContainer;
import com.android.systemui.statusbar.phone.KeyguardBypassController;
import com.android.systemui.statusbar.phone.NotificationGroupManager;
@@ -104,7 +105,8 @@ public class NotificationViewHierarchyManagerTest extends SysuiTestCase {
mock(StatusBarStateControllerImpl.class), mEntryManager,
mock(KeyguardBypassController.class),
mock(BubbleController.class),
- mock(DynamicPrivacyController.class));
+ mock(DynamicPrivacyController.class),
+ mock(ForegroundServiceSectionController.class));
mViewHierarchyManager.setUpWithPresenter(mPresenter, mListContainer);
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/NotificationEntryManagerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/NotificationEntryManagerTest.java
index 296d0cef715c..20c67fae6c9d 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/NotificationEntryManagerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/NotificationEntryManagerTest.java
@@ -247,11 +247,12 @@ public class NotificationEntryManagerTest extends SysuiTestCase {
mFeatureFlags,
() -> notificationRowBinder,
() -> mRemoteInputManager,
- mLeakDetector
+ mLeakDetector,
+ mock(ForegroundServiceDismissalFeatureController.class)
);
mEntryManager.setUpWithPresenter(mPresenter, mListContainer, mHeadsUpManager);
mEntryManager.addNotificationEntryListener(mEntryListener);
- mEntryManager.setNotificationRemoveInterceptor(mRemoveInterceptor);
+ mEntryManager.addNotificationRemoveInterceptor(mRemoveInterceptor);
notificationRowBinder.setUpWithPresenter(
mPresenter, mListContainer, mHeadsUpManager, mBindCallback);
@@ -546,7 +547,8 @@ public class NotificationEntryManagerTest extends SysuiTestCase {
mEntryManager.addActiveNotificationForTest(mEntry);
// GIVEN interceptor that intercepts that entry
- when(mRemoveInterceptor.onNotificationRemoveRequested(eq(mEntry.getKey()), anyInt()))
+ when(mRemoveInterceptor.onNotificationRemoveRequested(
+ eq(mEntry.getKey()), eq(mEntry), anyInt()))
.thenReturn(true);
// WHEN the notification is removed
@@ -564,7 +566,8 @@ public class NotificationEntryManagerTest extends SysuiTestCase {
mEntryManager.addActiveNotificationForTest(mEntry);
// GIVEN interceptor that doesn't intercept
- when(mRemoveInterceptor.onNotificationRemoveRequested(eq(mEntry.getKey()), anyInt()))
+ when(mRemoveInterceptor.onNotificationRemoveRequested(
+ eq(mEntry.getKey()), eq(mEntry), anyInt()))
.thenReturn(false);
// WHEN the notification is removed
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/TestableNotificationEntryManager.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/TestableNotificationEntryManager.kt
index 29ce92074027..7431459aac8f 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/TestableNotificationEntryManager.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/TestableNotificationEntryManager.kt
@@ -41,9 +41,10 @@ class TestableNotificationEntryManager(
ff: FeatureFlags,
rb: dagger.Lazy<NotificationRowBinder>,
notificationRemoteInputManagerLazy: dagger.Lazy<NotificationRemoteInputManager>,
- leakDetector: LeakDetector
+ leakDetector: LeakDetector,
+ fgsFeatureController: ForegroundServiceDismissalFeatureController
) : NotificationEntryManager(log, gm, rm, ke, ff, rb,
- notificationRemoteInputManagerLazy, leakDetector) {
+ notificationRemoteInputManagerLazy, leakDetector, fgsFeatureController) {
public var countDownLatch: CountDownLatch = CountDownLatch(1)
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/NotifCollectionTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/NotifCollectionTest.java
index b65298097ff1..7c94ed20e95a 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/NotifCollectionTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/NotifCollectionTest.java
@@ -17,9 +17,16 @@
package com.android.systemui.statusbar.notification.collection;
import static android.service.notification.NotificationListenerService.REASON_APP_CANCEL;
+import static android.service.notification.NotificationListenerService.REASON_CANCEL;
import static android.service.notification.NotificationListenerService.REASON_CLICK;
+import static android.service.notification.NotificationStats.DISMISSAL_SHADE;
+import static android.service.notification.NotificationStats.DISMISS_SENTIMENT_NEUTRAL;
+import static com.android.systemui.statusbar.notification.collection.NotifCollection.REASON_NOT_CANCELED;
import static com.android.systemui.statusbar.notification.collection.NotifCollection.REASON_UNKNOWN;
+import static com.android.systemui.statusbar.notification.collection.NotificationEntry.DismissState.DISMISSED;
+import static com.android.systemui.statusbar.notification.collection.NotificationEntry.DismissState.NOT_DISMISSED;
+import static com.android.systemui.statusbar.notification.collection.NotificationEntry.DismissState.PARENT_DISMISSED;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
@@ -40,7 +47,6 @@ import static java.util.Objects.requireNonNull;
import android.annotation.Nullable;
import android.os.RemoteException;
import android.service.notification.NotificationListenerService.Ranking;
-import android.service.notification.NotificationStats;
import android.testing.AndroidTestingRunner;
import android.testing.TestableLooper;
import android.util.ArrayMap;
@@ -77,6 +83,7 @@ import org.mockito.Spy;
import java.util.Arrays;
import java.util.Collection;
+import java.util.Collections;
import java.util.List;
import java.util.Map;
@@ -236,7 +243,7 @@ public class NotifCollectionTest extends SysuiTestCase {
mNoMan.retractNotif(notif.sbn, REASON_APP_CANCEL);
// THEN the listener is notified
- verify(mCollectionListener).onEntryRemoved(entry, REASON_APP_CANCEL, false);
+ verify(mCollectionListener).onEntryRemoved(entry, REASON_APP_CANCEL);
verify(mCollectionListener).onEntryCleanUp(entry);
assertEquals(notif.sbn, entry.getSbn());
assertEquals(notif.ranking, entry.getRanking());
@@ -322,24 +329,15 @@ public class NotifCollectionTest extends SysuiTestCase {
}
@Test
- public void testDismissNotification() throws RemoteException {
- // GIVEN a collection with a couple notifications and a lifetime extender
- mCollection.addNotificationLifetimeExtender(mExtender1);
-
+ public void testDismissNotificationSentToSystemServer() throws RemoteException {
+ // GIVEN a collection with a couple notifications
NotifEvent notif1 = mNoMan.postNotif(buildNotif(TEST_PACKAGE, 47, "myTag"));
NotifEvent notif2 = mNoMan.postNotif(buildNotif(TEST_PACKAGE2, 88, "barTag"));
NotificationEntry entry2 = mCollectionListener.getEntry(notif2.key);
// WHEN a notification is manually dismissed
- DismissedByUserStats stats = new DismissedByUserStats(
- NotificationStats.DISMISSAL_SHADE,
- NotificationStats.DISMISS_SENTIMENT_NEUTRAL,
- NotificationVisibility.obtain(entry2.getKey(), 7, 2, true));
-
- mCollection.dismissNotification(entry2, REASON_CLICK, stats);
-
- // THEN we check for lifetime extension
- verify(mExtender1).shouldExtendLifetime(entry2, REASON_CLICK);
+ DismissedByUserStats stats = defaultStats(entry2);
+ mCollection.dismissNotification(entry2, defaultStats(entry2));
// THEN we send the dismissal to system server
verify(mStatusBarService).onNotificationClear(
@@ -351,9 +349,96 @@ public class NotifCollectionTest extends SysuiTestCase {
stats.dismissalSurface,
stats.dismissalSentiment,
stats.notificationVisibility);
+ }
+
+ @Test
+ public void testDismissedNotificationsAreMarkedAsDismissedLocally() {
+ // GIVEN a collection with a notification
+ NotifEvent notif1 = mNoMan.postNotif(buildNotif(TEST_PACKAGE, 47, "myTag"));
+ NotificationEntry entry1 = mCollectionListener.getEntry(notif1.key);
+
+ // WHEN a notification is manually dismissed
+ mCollection.dismissNotification(entry1, defaultStats(entry1));
- // THEN we fire a remove event
- verify(mCollectionListener).onEntryRemoved(entry2, REASON_CLICK, true);
+ // THEN the entry is marked as dismissed locally
+ assertEquals(DISMISSED, entry1.getDismissState());
+ }
+
+ @Test
+ public void testDismissedNotificationsCannotBeLifetimeExtended() {
+ // GIVEN a collection with a notification and a lifetime extender
+ mCollection.addNotificationLifetimeExtender(mExtender1);
+ NotifEvent notif1 = mNoMan.postNotif(buildNotif(TEST_PACKAGE, 47, "myTag"));
+ NotificationEntry entry1 = mCollectionListener.getEntry(notif1.key);
+
+ // WHEN a notification is manually dismissed
+ mCollection.dismissNotification(entry1, defaultStats(entry1));
+
+ // THEN lifetime extenders are never queried
+ verify(mExtender1, never()).shouldExtendLifetime(eq(entry1), anyInt());
+ }
+
+ @Test
+ public void testDismissedNotificationsDoNotTriggerRemovalEvents() {
+ // GIVEN a collection with a notification
+ NotifEvent notif1 = mNoMan.postNotif(buildNotif(TEST_PACKAGE, 47, "myTag"));
+ NotificationEntry entry1 = mCollectionListener.getEntry(notif1.key);
+
+ // WHEN a notification is manually dismissed
+ mCollection.dismissNotification(entry1, defaultStats(entry1));
+
+ // THEN onEntryRemoved is not called
+ verify(mCollectionListener, never()).onEntryRemoved(eq(entry1), anyInt());
+ }
+
+ @Test
+ public void testDismissedNotificationsStillAppearInNotificationSet() {
+ // GIVEN a collection with a notification
+ NotifEvent notif1 = mNoMan.postNotif(buildNotif(TEST_PACKAGE, 47, "myTag"));
+ NotificationEntry entry1 = mCollectionListener.getEntry(notif1.key);
+
+ // WHEN a notification is manually dismissed
+ mCollection.dismissNotification(entry1, defaultStats(entry1));
+
+ // THEN the dismissed entry still appears in the notification set
+ assertEquals(
+ new ArraySet<>(Collections.singletonList(entry1)),
+ new ArraySet<>(mCollection.getActiveNotifs()));
+ }
+
+ @Test
+ public void testDismissingLifetimeExtendedSummaryDoesNotDismissChildren() {
+ // GIVEN A notif group with one summary and two children
+ mCollection.addNotificationLifetimeExtender(mExtender1);
+ NotifEvent notif1 = mNoMan.postNotif(buildNotif(TEST_PACKAGE, 1, "myTag")
+ .setGroup(mContext, GROUP_1)
+ .setGroupSummary(mContext, true));
+ NotifEvent notif2 = mNoMan.postNotif(buildNotif(TEST_PACKAGE, 2, "myTag")
+ .setGroup(mContext, GROUP_1));
+ NotifEvent notif3 = mNoMan.postNotif(buildNotif(TEST_PACKAGE, 3, "myTag")
+ .setGroup(mContext, GROUP_1));
+
+ NotificationEntry entry1 = mCollectionListener.getEntry(notif1.key);
+ NotificationEntry entry2 = mCollectionListener.getEntry(notif2.key);
+ NotificationEntry entry3 = mCollectionListener.getEntry(notif3.key);
+
+ // GIVEN that the summary and one child are retracted, but both are lifetime-extended
+ mExtender1.shouldExtendLifetime = true;
+ mNoMan.retractNotif(notif1.sbn, REASON_CANCEL);
+ mNoMan.retractNotif(notif2.sbn, REASON_CANCEL);
+ assertEquals(
+ new ArraySet<>(List.of(entry1, entry2, entry3)),
+ new ArraySet<>(mCollection.getActiveNotifs()));
+
+ // WHEN the summary is dismissed by the user
+ mCollection.dismissNotification(entry1, defaultStats(entry1));
+
+ // THEN the summary is removed, but both children stick around
+ assertEquals(
+ new ArraySet<>(List.of(entry2, entry3)),
+ new ArraySet<>(mCollection.getActiveNotifs()));
+ assertEquals(NOT_DISMISSED, entry2.getDismissState());
+ assertEquals(NOT_DISMISSED, entry3.getDismissState());
}
@Test(expected = IllegalStateException.class)
@@ -366,15 +451,115 @@ public class NotifCollectionTest extends SysuiTestCase {
mNoMan.retractNotif(notif2.sbn, REASON_UNKNOWN);
// WHEN we try to dismiss a notification that isn't present
- mCollection.dismissNotification(
- entry2,
- REASON_CLICK,
- new DismissedByUserStats(0, 0, NotificationVisibility.obtain("foo", 47, 3, true)));
+ mCollection.dismissNotification(entry2, defaultStats(entry2));
// THEN an exception is thrown
}
@Test
+ public void testGroupChildrenAreDismissedLocallyWhenSummaryIsDismissed() {
+ // GIVEN a collection with two grouped notifs in it
+ NotifEvent notif0 = mNoMan.postNotif(
+ buildNotif(TEST_PACKAGE, 0)
+ .setGroup(mContext, GROUP_1)
+ .setGroupSummary(mContext, true));
+ NotifEvent notif1 = mNoMan.postNotif(
+ buildNotif(TEST_PACKAGE, 1)
+ .setGroup(mContext, GROUP_1));
+ NotificationEntry entry0 = mCollectionListener.getEntry(notif0.key);
+ NotificationEntry entry1 = mCollectionListener.getEntry(notif1.key);
+
+ // WHEN the summary is dismissed
+ mCollection.dismissNotification(entry0, defaultStats(entry0));
+
+ // THEN all members of the group are marked as dismissed locally
+ assertEquals(DISMISSED, entry0.getDismissState());
+ assertEquals(PARENT_DISMISSED, entry1.getDismissState());
+ }
+
+ @Test
+ public void testUpdatingDismissedSummaryBringsChildrenBack() {
+ // GIVEN a collection with two grouped notifs in it
+ NotifEvent notif0 = mNoMan.postNotif(
+ buildNotif(TEST_PACKAGE, 0)
+ .setGroup(mContext, GROUP_1)
+ .setGroupSummary(mContext, true));
+ NotifEvent notif1 = mNoMan.postNotif(
+ buildNotif(TEST_PACKAGE, 1)
+ .setGroup(mContext, GROUP_1));
+ NotificationEntry entry0 = mCollectionListener.getEntry(notif0.key);
+ NotificationEntry entry1 = mCollectionListener.getEntry(notif1.key);
+
+ // WHEN the summary is dismissed but then reposted without a group
+ mCollection.dismissNotification(entry0, defaultStats(entry0));
+ NotifEvent notif0a = mNoMan.postNotif(
+ buildNotif(TEST_PACKAGE, 0));
+
+ // THEN it and all of its previous children are no longer dismissed locally
+ assertEquals(NOT_DISMISSED, entry0.getDismissState());
+ assertEquals(NOT_DISMISSED, entry1.getDismissState());
+ }
+
+ @Test
+ public void testDismissedChildrenAreNotResetByParentUpdate() {
+ // GIVEN a collection with three grouped notifs in it
+ NotifEvent notif0 = mNoMan.postNotif(
+ buildNotif(TEST_PACKAGE, 0)
+ .setGroup(mContext, GROUP_1)
+ .setGroupSummary(mContext, true));
+ NotifEvent notif1 = mNoMan.postNotif(
+ buildNotif(TEST_PACKAGE, 1)
+ .setGroup(mContext, GROUP_1));
+ NotifEvent notif2 = mNoMan.postNotif(
+ buildNotif(TEST_PACKAGE, 2)
+ .setGroup(mContext, GROUP_1));
+ NotificationEntry entry0 = mCollectionListener.getEntry(notif0.key);
+ NotificationEntry entry1 = mCollectionListener.getEntry(notif1.key);
+ NotificationEntry entry2 = mCollectionListener.getEntry(notif2.key);
+
+ // WHEN a child is dismissed, then the parent is dismissed, then the parent is updated
+ mCollection.dismissNotification(entry1, defaultStats(entry1));
+ mCollection.dismissNotification(entry0, defaultStats(entry0));
+ NotifEvent notif0a = mNoMan.postNotif(
+ buildNotif(TEST_PACKAGE, 0));
+
+ // THEN the manually-dismissed child is still marked as dismissed
+ assertEquals(NOT_DISMISSED, entry0.getDismissState());
+ assertEquals(DISMISSED, entry1.getDismissState());
+ assertEquals(NOT_DISMISSED, entry2.getDismissState());
+ }
+
+ @Test
+ public void testUpdatingGroupKeyOfDismissedSummaryBringsChildrenBack() {
+ // GIVEN a collection with two grouped notifs in it
+ NotifEvent notif0 = mNoMan.postNotif(
+ buildNotif(TEST_PACKAGE, 0)
+ .setOverrideGroupKey(GROUP_1)
+ .setGroupSummary(mContext, true));
+ NotifEvent notif1 = mNoMan.postNotif(
+ buildNotif(TEST_PACKAGE, 1)
+ .setOverrideGroupKey(GROUP_1));
+ NotificationEntry entry0 = mCollectionListener.getEntry(notif0.key);
+ NotificationEntry entry1 = mCollectionListener.getEntry(notif1.key);
+
+ // WHEN the summary is dismissed but then reposted AND in the same update one of the
+ // children's ranking loses its override group
+ mCollection.dismissNotification(entry0, defaultStats(entry0));
+ mNoMan.setRanking(entry1.getKey(), new RankingBuilder()
+ .setKey(entry1.getKey())
+ .build());
+ mNoMan.postNotif(
+ buildNotif(TEST_PACKAGE, 0)
+ .setOverrideGroupKey(GROUP_1)
+ .setGroupSummary(mContext, true));
+
+ // THEN it and all of its previous children are no longer dismissed locally, including the
+ // child that is no longer part of the group
+ assertEquals(NOT_DISMISSED, entry0.getDismissState());
+ assertEquals(NOT_DISMISSED, entry1.getDismissState());
+ }
+
+ @Test
public void testLifetimeExtendersAreQueriedWhenNotifRemoved() {
// GIVEN a couple notifications and a few lifetime extenders
mExtender1.shouldExtendLifetime = true;
@@ -389,12 +574,12 @@ public class NotifCollectionTest extends SysuiTestCase {
NotificationEntry entry2 = mCollectionListener.getEntry(notif2.key);
// WHEN a notification is removed
- mNoMan.retractNotif(notif2.sbn, REASON_UNKNOWN);
+ mNoMan.retractNotif(notif2.sbn, REASON_CLICK);
// THEN each extender is asked whether to extend, even if earlier ones return true
- verify(mExtender1).shouldExtendLifetime(entry2, REASON_UNKNOWN);
- verify(mExtender2).shouldExtendLifetime(entry2, REASON_UNKNOWN);
- verify(mExtender3).shouldExtendLifetime(entry2, REASON_UNKNOWN);
+ verify(mExtender1).shouldExtendLifetime(entry2, REASON_CLICK);
+ verify(mExtender2).shouldExtendLifetime(entry2, REASON_CLICK);
+ verify(mExtender3).shouldExtendLifetime(entry2, REASON_CLICK);
// THEN the entry is not removed
assertTrue(mCollection.getActiveNotifs().contains(entry2));
@@ -428,9 +613,9 @@ public class NotifCollectionTest extends SysuiTestCase {
mExtender2.callback.onEndLifetimeExtension(mExtender2, entry2);
// THEN each extender is re-queried
- verify(mExtender1).shouldExtendLifetime(entry2, REASON_UNKNOWN);
- verify(mExtender2).shouldExtendLifetime(entry2, REASON_UNKNOWN);
- verify(mExtender3).shouldExtendLifetime(entry2, REASON_UNKNOWN);
+ verify(mExtender1).shouldExtendLifetime(entry2, REASON_APP_CANCEL);
+ verify(mExtender2).shouldExtendLifetime(entry2, REASON_APP_CANCEL);
+ verify(mExtender3).shouldExtendLifetime(entry2, REASON_APP_CANCEL);
// THEN the entry is not removed
assertTrue(mCollection.getActiveNotifs().contains(entry2));
@@ -466,9 +651,9 @@ public class NotifCollectionTest extends SysuiTestCase {
assertTrue(mCollection.getActiveNotifs().contains(entry2));
// THEN we don't re-query the extenders
- verify(mExtender1, never()).shouldExtendLifetime(eq(entry2), anyInt());
- verify(mExtender2, never()).shouldExtendLifetime(eq(entry2), anyInt());
- verify(mExtender3, never()).shouldExtendLifetime(eq(entry2), anyInt());
+ verify(mExtender1, never()).shouldExtendLifetime(entry2, REASON_APP_CANCEL);
+ verify(mExtender2, never()).shouldExtendLifetime(entry2, REASON_APP_CANCEL);
+ verify(mExtender3, never()).shouldExtendLifetime(entry2, REASON_APP_CANCEL);
// THEN the entry properly records all extenders that returned true
assertEquals(Arrays.asList(mExtender1), entry2.mLifetimeExtenders);
@@ -501,7 +686,7 @@ public class NotifCollectionTest extends SysuiTestCase {
// THEN the entry removed
assertFalse(mCollection.getActiveNotifs().contains(entry2));
- verify(mCollectionListener).onEntryRemoved(entry2, REASON_UNKNOWN, false);
+ verify(mCollectionListener).onEntryRemoved(entry2, REASON_UNKNOWN);
}
@Test
@@ -591,6 +776,36 @@ public class NotifCollectionTest extends SysuiTestCase {
assertEquals(notif2a.ranking, entry2.getRanking());
}
+ @Test
+ public void testCancellationReasonIsSetWhenNotifIsCancelled() {
+ // GIVEN a notification
+ NotifEvent notif0 = mNoMan.postNotif(buildNotif(TEST_PACKAGE, 3));
+ NotificationEntry entry0 = mCollectionListener.getEntry(notif0.key);
+
+ // WHEN the notification is retracted
+ mNoMan.retractNotif(notif0.sbn, REASON_APP_CANCEL);
+
+ // THEN the retraction reason is stored on the notif
+ assertEquals(REASON_APP_CANCEL, entry0.mCancellationReason);
+ }
+
+ @Test
+ public void testCancellationReasonIsClearedWhenNotifIsUpdated() {
+ // GIVEN a notification and a lifetime extender that will preserve it
+ NotifEvent notif0 = mNoMan.postNotif(buildNotif(TEST_PACKAGE, 3));
+ NotificationEntry entry0 = mCollectionListener.getEntry(notif0.key);
+ mCollection.addNotificationLifetimeExtender(mExtender1);
+ mExtender1.shouldExtendLifetime = true;
+
+ // WHEN the notification is retracted and subsequently reposted
+ mNoMan.retractNotif(notif0.sbn, REASON_APP_CANCEL);
+ assertEquals(REASON_APP_CANCEL, entry0.mCancellationReason);
+ mNoMan.postNotif(buildNotif(TEST_PACKAGE, 3));
+
+ // THEN the notification has its cancellation reason cleared
+ assertEquals(REASON_NOT_CANCELED, entry0.mCancellationReason);
+ }
+
private static NotificationEntryBuilder buildNotif(String pkg, int id, String tag) {
return new NotificationEntryBuilder()
.setPkg(pkg)
@@ -604,6 +819,13 @@ public class NotifCollectionTest extends SysuiTestCase {
.setId(id);
}
+ private static DismissedByUserStats defaultStats(NotificationEntry entry) {
+ return new DismissedByUserStats(
+ DISMISSAL_SHADE,
+ DISMISS_SENTIMENT_NEUTRAL,
+ NotificationVisibility.obtain(entry.getKey(), 7, 2, true));
+ }
+
private static class RecordingCollectionListener implements NotifCollectionListener {
private final Map<String, NotificationEntry> mLastSeenEntries = new ArrayMap<>();
@@ -621,7 +843,7 @@ public class NotifCollectionTest extends SysuiTestCase {
}
@Override
- public void onEntryRemoved(NotificationEntry entry, int reason, boolean removedByUser) {
+ public void onEntryRemoved(NotificationEntry entry, int reason) {
}
@Override
@@ -674,4 +896,6 @@ public class NotifCollectionTest extends SysuiTestCase {
private static final String TEST_PACKAGE = "com.android.test.collection";
private static final String TEST_PACKAGE2 = "com.android.test.collection2";
+
+ private static final String GROUP_1 = "group_1";
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutTest.java
index 5bd5638b3d5a..70d76f0c3a52 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutTest.java
@@ -63,6 +63,7 @@ import com.android.systemui.statusbar.RemoteInputController;
import com.android.systemui.statusbar.StatusBarState;
import com.android.systemui.statusbar.SysuiStatusBarStateController;
import com.android.systemui.statusbar.notification.DynamicPrivacyController;
+import com.android.systemui.statusbar.notification.ForegroundServiceDismissalFeatureController;
import com.android.systemui.statusbar.notification.NotificationEntryManager;
import com.android.systemui.statusbar.notification.NotificationFilter;
import com.android.systemui.statusbar.notification.NotificationSectionsFeatureManager;
@@ -178,7 +179,9 @@ public class NotificationStackScrollLayoutTest extends SysuiTestCase {
mock(FeatureFlags.class),
() -> mock(NotificationRowBinder.class),
() -> mRemoteInputManager,
- mock(LeakDetector.class));
+ mock(LeakDetector.class),
+ mock(ForegroundServiceDismissalFeatureController.class)
+ );
mDependency.injectTestDependency(NotificationEntryManager.class, mEntryManager);
mEntryManager.setUpForTest(mock(NotificationPresenter.class), null, mHeadsUpManager);
@@ -203,7 +206,10 @@ public class NotificationStackScrollLayoutTest extends SysuiTestCase {
mLockscreenUserManager,
mock(NotificationGutsManager.class),
mZenModeController,
- mNotificationSectionsManager);
+ mNotificationSectionsManager,
+ mock(ForegroundServiceSectionController.class),
+ mock(ForegroundServiceDismissalFeatureController.class)
+ );
verify(mLockscreenUserManager).addUserChangedListener(userChangedCaptor.capture());
mUserChangedListener = userChangedCaptor.getValue();
mStackScroller = spy(mStackScrollerInternal);
@@ -394,8 +400,11 @@ public class NotificationStackScrollLayoutTest extends SysuiTestCase {
mStackScroller.onUpdateRowStates();
+ // Expecting the footer to be the last child
+ int expected = mStackScroller.getChildCount() - 1;
+
// move footer to end
- verify(mStackScroller).changeViewPosition(any(FooterView.class), eq(-1 /* end */));
+ verify(mStackScroller).changeViewPosition(any(FooterView.class), eq(expected));
}
@Test
diff --git a/packages/Tethering/res/values/config.xml b/packages/Tethering/res/values/config.xml
index c489cbcd5a1c..4af5c5371952 100644
--- a/packages/Tethering/res/values/config.xml
+++ b/packages/Tethering/res/values/config.xml
@@ -47,6 +47,7 @@
should be empty. An example would be "p2p-p2p\\d-.*" -->
<string-array translatable="false" name="config_tether_wifi_p2p_regexs">
<item>"p2p-p2p\\d-.*"</item>
+ <item>"p2p\\d"</item>
</string-array>
<!-- List of regexpressions describing the interface (if any) that represent tetherable
diff --git a/packages/VpnDialogs/AndroidManifest.xml b/packages/VpnDialogs/AndroidManifest.xml
index e4de6259e07d..693ca52b4ed1 100644
--- a/packages/VpnDialogs/AndroidManifest.xml
+++ b/packages/VpnDialogs/AndroidManifest.xml
@@ -34,6 +34,13 @@
</intent-filter>
</activity>
+ <activity android:name=".PlatformVpnConfirmDialog"
+ android:theme="@*android:style/Theme.DeviceDefault.Dialog.Alert.DayNight"
+ android:noHistory="true"
+ android:excludeFromRecents="true"
+ android:exported="true">
+ </activity>
+
<activity android:name=".ManageDialog"
android:theme="@*android:style/Theme.DeviceDefault.Dialog.Alert.DayNight"
android:noHistory="true"
diff --git a/packages/VpnDialogs/src/com/android/vpndialogs/ConfirmDialog.java b/packages/VpnDialogs/src/com/android/vpndialogs/ConfirmDialog.java
index 48adb9ba3f63..e66f2cc17a7f 100644
--- a/packages/VpnDialogs/src/com/android/vpndialogs/ConfirmDialog.java
+++ b/packages/VpnDialogs/src/com/android/vpndialogs/ConfirmDialog.java
@@ -23,6 +23,7 @@ import android.content.DialogInterface;
import android.content.pm.PackageManager;
import android.graphics.drawable.Drawable;
import android.net.IConnectivityManager;
+import android.net.VpnManager;
import android.os.Bundle;
import android.os.RemoteException;
import android.os.ServiceManager;
@@ -43,10 +44,20 @@ public class ConfirmDialog extends AlertActivity
implements DialogInterface.OnClickListener, ImageGetter {
private static final String TAG = "VpnConfirm";
+ @VpnManager.VpnType private final int mVpnType;
+
private String mPackage;
private IConnectivityManager mService;
+ public ConfirmDialog() {
+ this(VpnManager.TYPE_VPN_SERVICE);
+ }
+
+ public ConfirmDialog(@VpnManager.VpnType int vpnType) {
+ mVpnType = vpnType;
+ }
+
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
@@ -138,7 +149,7 @@ public class ConfirmDialog extends AlertActivity
if (mService.prepareVpn(null, mPackage, UserHandle.myUserId())) {
// Authorize this app to initiate VPN connections in the future without user
// intervention.
- mService.setVpnPackageAuthorization(mPackage, UserHandle.myUserId(), true);
+ mService.setVpnPackageAuthorization(mPackage, UserHandle.myUserId(), mVpnType);
setResult(RESULT_OK);
}
} catch (Exception e) {
diff --git a/packages/VpnDialogs/src/com/android/vpndialogs/PlatformVpnConfirmDialog.java b/packages/VpnDialogs/src/com/android/vpndialogs/PlatformVpnConfirmDialog.java
new file mode 100644
index 000000000000..e2e297caad14
--- /dev/null
+++ b/packages/VpnDialogs/src/com/android/vpndialogs/PlatformVpnConfirmDialog.java
@@ -0,0 +1,29 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.vpndialogs;
+
+import android.net.VpnManager;
+
+/**
+ * PlatformVpnConfirmDialog is a minimal subclass for requesting user consent for platform VPN
+ * profiles.
+ */
+public class PlatformVpnConfirmDialog extends ConfirmDialog {
+ public PlatformVpnConfirmDialog() {
+ super(VpnManager.TYPE_VPN_PLATFORM);
+ }
+}
diff --git a/packages/overlays/Android.mk b/packages/overlays/Android.mk
index 8a3ac9404408..2bb673eba758 100644
--- a/packages/overlays/Android.mk
+++ b/packages/overlays/Android.mk
@@ -26,6 +26,7 @@ LOCAL_REQUIRED_MODULES := \
AccentColorPurpleOverlay \
DisplayCutoutEmulationCornerOverlay \
DisplayCutoutEmulationDoubleOverlay \
+ DisplayCutoutEmulationHoleOverlay \
DisplayCutoutEmulationTallOverlay \
FontNotoSerifSourceOverlay \
IconPackCircularAndroidOverlay \
diff --git a/packages/overlays/DisplayCutoutEmulationHoleOverlay/Android.mk b/packages/overlays/DisplayCutoutEmulationHoleOverlay/Android.mk
new file mode 100644
index 000000000000..6d8fc2493506
--- /dev/null
+++ b/packages/overlays/DisplayCutoutEmulationHoleOverlay/Android.mk
@@ -0,0 +1,14 @@
+LOCAL_PATH:= $(call my-dir)
+include $(CLEAR_VARS)
+
+LOCAL_RRO_THEME := DisplayCutoutEmulationHole
+
+
+LOCAL_PRODUCT_MODULE := true
+
+LOCAL_RESOURCE_DIR := $(LOCAL_PATH)/res
+
+LOCAL_PACKAGE_NAME := DisplayCutoutEmulationHoleOverlay
+LOCAL_SDK_VERSION := current
+
+include $(BUILD_RRO_PACKAGE)
diff --git a/packages/overlays/DisplayCutoutEmulationHoleOverlay/AndroidManifest.xml b/packages/overlays/DisplayCutoutEmulationHoleOverlay/AndroidManifest.xml
new file mode 100644
index 000000000000..9fd82abd6c8c
--- /dev/null
+++ b/packages/overlays/DisplayCutoutEmulationHoleOverlay/AndroidManifest.xml
@@ -0,0 +1,26 @@
+<!--
+ ~ Copyright (C) 2020 The Android Open Source Project
+ ~
+ ~ Licensed under the Apache License, Version 2.0 (the "License");
+ ~ you may not use this file except in compliance with the License.
+ ~ You may obtain a copy of the License at
+ ~
+ ~ http://www.apache.org/licenses/LICENSE-2.0
+ ~
+ ~ Unless required by applicable law or agreed to in writing, software
+ ~ distributed under the License is distributed on an "AS IS" BASIS,
+ ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ ~ See the License for the specific language governing permissions and
+ ~ limitations under the License.
+ -->
+
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+ package="com.android.internal.display.cutout.emulation.hole"
+ android:versionCode="1"
+ android:versionName="1.0">
+ <overlay android:targetPackage="android"
+ android:category="com.android.internal.display_cutout_emulation"
+ android:priority="1"/>
+
+ <application android:label="@string/display_cutout_emulation_overlay" android:hasCode="false"/>
+</manifest>
diff --git a/packages/overlays/DisplayCutoutEmulationHoleOverlay/res/values-land/config.xml b/packages/overlays/DisplayCutoutEmulationHoleOverlay/res/values-land/config.xml
new file mode 100644
index 000000000000..2e971ded2256
--- /dev/null
+++ b/packages/overlays/DisplayCutoutEmulationHoleOverlay/res/values-land/config.xml
@@ -0,0 +1,22 @@
+<!--
+ ~ Copyright (C) 2020 The Android Open Source Project
+ ~
+ ~ Licensed under the Apache License, Version 2.0 (the "License");
+ ~ you may not use this file except in compliance with the License.
+ ~ You may obtain a copy of the License at
+ ~
+ ~ http://www.apache.org/licenses/LICENSE-2.0
+ ~
+ ~ Unless required by applicable law or agreed to in writing, software
+ ~ distributed under the License is distributed on an "AS IS" BASIS,
+ ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ ~ See the License for the specific language governing permissions and
+ ~ limitations under the License
+ -->
+
+<resources>
+ <!-- Can't link to other dimensions here, but this should be status_bar_height_landscape -->
+ <dimen name="quick_qs_offset_height">28dp</dimen>
+ <!-- Total height of QQS in landscape; quick_qs_offset_height + 128 -->
+ <dimen name="quick_qs_total_height">156dp</dimen>
+</resources>
diff --git a/packages/overlays/DisplayCutoutEmulationHoleOverlay/res/values/config.xml b/packages/overlays/DisplayCutoutEmulationHoleOverlay/res/values/config.xml
new file mode 100644
index 000000000000..9f558d0e2bd5
--- /dev/null
+++ b/packages/overlays/DisplayCutoutEmulationHoleOverlay/res/values/config.xml
@@ -0,0 +1,61 @@
+<!--
+ ~ Copyright (C) 2020 The Android Open Source Project
+ ~
+ ~ Licensed under the Apache License, Version 2.0 (the "License");
+ ~ you may not use this file except in compliance with the License.
+ ~ You may obtain a copy of the License at
+ ~
+ ~ http://www.apache.org/licenses/LICENSE-2.0
+ ~
+ ~ Unless required by applicable law or agreed to in writing, software
+ ~ distributed under the License is distributed on an "AS IS" BASIS,
+ ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ ~ See the License for the specific language governing permissions and
+ ~ limitations under the License.
+ -->
+
+<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+
+ <!-- The bounding path of the cutout region of the main built-in display.
+ Must either be empty if there is no cutout region, or a string that is parsable by
+ {@link android.util.PathParser}.
+
+ The path is assumed to be specified in display coordinates with pixel units and in
+ the display's native orientation, with the origin of the coordinate system at the
+ center top of the display.
+
+ To facilitate writing device-independent emulation overlays, the marker `@dp` can be
+ appended after the path string to interpret coordinates in dp instead of px units.
+ Note that a physical cutout should be configured in pixels for the best results.
+ -->
+ <!-- Display cutout configuration -->
+ <string translatable="false" name="config_mainBuiltInDisplayCutout">
+ M 128,83 A 44,44 0 0 1 84,127 44,44 0 0 1 40,83 44,44 0 0 1 84,39 44,44 0 0 1 128,83 Z
+ @left
+ </string>
+
+ <string translatable="false" name="config_mainBuiltInDisplayCutoutRectApproximation">
+ M 0.0,0.0
+ h 136
+ v 136
+ h -136
+ Z
+ @left
+ </string>
+
+ <!-- Whether the display cutout region of the main built-in display should be forced to
+ black in software (to avoid aliasing or emulate a cutout that is not physically existent).
+ -->
+ <bool name="config_fillMainBuiltInDisplayCutout">true</bool>
+
+ <!-- Height of the status bar -->
+ <dimen name="status_bar_height_portrait">136px</dimen>
+ <dimen name="status_bar_height_landscape">28dp</dimen>
+ <!-- Height of area above QQS where battery/time go (equal to status bar) -->
+ <dimen name="quick_qs_offset_height">136px</dimen>
+ <!-- Total height of QQS (quick_qs_offset_height + 128) -->
+ <dimen name="quick_qs_total_height">488px</dimen>
+
+</resources>
+
+
diff --git a/packages/overlays/DisplayCutoutEmulationHoleOverlay/res/values/strings.xml b/packages/overlays/DisplayCutoutEmulationHoleOverlay/res/values/strings.xml
new file mode 100644
index 000000000000..ab25695e7289
--- /dev/null
+++ b/packages/overlays/DisplayCutoutEmulationHoleOverlay/res/values/strings.xml
@@ -0,0 +1,22 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ ~ Copyright (C) 2020 The Android Open Source Project
+ ~
+ ~ Licensed under the Apache License, Version 2.0 (the "License");
+ ~ you may not use this file except in compliance with the License.
+ ~ You may obtain a copy of the License at
+ ~
+ ~ http://www.apache.org/licenses/LICENSE-2.0
+ ~
+ ~ Unless required by applicable law or agreed to in writing, software
+ ~ distributed under the License is distributed on an "AS IS" BASIS,
+ ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ ~ See the License for the specific language governing permissions and
+ ~ limitations under the License.
+ -->
+<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+
+ <string name="display_cutout_emulation_overlay">Punch Hole cutout</string>
+
+</resources>
+
diff --git a/packages/overlays/IconShapePebbleOverlay/Android.mk b/packages/overlays/IconShapePebbleOverlay/Android.mk
new file mode 100644
index 000000000000..c163bb91f76a
--- /dev/null
+++ b/packages/overlays/IconShapePebbleOverlay/Android.mk
@@ -0,0 +1,29 @@
+#
+# Copyright 2020, The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+
+LOCAL_PATH:= $(call my-dir)
+include $(CLEAR_VARS)
+
+LOCAL_RRO_THEME := IconShapePebble
+
+LOCAL_PRODUCT_MODULE := true
+
+LOCAL_RESOURCE_DIR := $(LOCAL_PATH)/res
+
+LOCAL_PACKAGE_NAME := IconShapePebbleOverlay
+LOCAL_SDK_VERSION := current
+
+include $(BUILD_RRO_PACKAGE)
diff --git a/packages/overlays/IconShapePebbleOverlay/AndroidManifest.xml b/packages/overlays/IconShapePebbleOverlay/AndroidManifest.xml
new file mode 100644
index 000000000000..6842dde36264
--- /dev/null
+++ b/packages/overlays/IconShapePebbleOverlay/AndroidManifest.xml
@@ -0,0 +1,29 @@
+<!--
+/**
+ * Copyright (c) 2020, The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+-->
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+ package="com.android.theme.icon.pebble"
+ android:versionCode="1"
+ android:versionName="1.0">
+<overlay
+ android:targetPackage="android"
+ android:targetName="IconShapeCustomization"
+ android:category="android.theme.customization.adaptive_icon_shape"
+ android:priority="1"/>
+
+ <application android:label="@string/icon_shape_pebble_overlay" android:hasCode="false"/>
+</manifest>
diff --git a/packages/overlays/IconShapePebbleOverlay/res/values/config.xml b/packages/overlays/IconShapePebbleOverlay/res/values/config.xml
new file mode 100644
index 000000000000..2465fe015538
--- /dev/null
+++ b/packages/overlays/IconShapePebbleOverlay/res/values/config.xml
@@ -0,0 +1,30 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+/*
+** Copyright 2019, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License");
+** you may not use this file except in compliance with the License.
+** You may obtain a copy of the License at
+**
+** http://www.apache.org/licenses/LICENSE-2.0
+**
+** Unless required by applicable law or agreed to in writing, software
+** distributed under the License is distributed on an "AS IS" BASIS,
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+** See the License for the specific language governing permissions and
+** limitations under the License.
+*/
+-->
+<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <!-- Specifies the path that is used by AdaptiveIconDrawable class to crop launcher icons. -->
+ <string name="config_icon_mask" translatable="false">"MM55,0 C25,0 0,25 0,50 0,78 28,100 55,100 85,100 100,85 100,58 100,30 86,0 55,0 Z"</string>
+ <!-- Flag indicating whether round icons should be parsed from the application manifest. -->
+ <bool name="config_useRoundIcon">false</bool>
+ <!-- Corner radius of system dialogs -->
+ <dimen name="config_dialogCornerRadius">8dp</dimen>
+ <!-- Corner radius for bottom sheet system dialogs -->
+ <dimen name="config_bottomDialogCornerRadius">16dp</dimen>
+
+</resources>
+
diff --git a/packages/overlays/IconShapePebbleOverlay/res/values/strings.xml b/packages/overlays/IconShapePebbleOverlay/res/values/strings.xml
new file mode 100644
index 000000000000..aec4a82a6ba4
--- /dev/null
+++ b/packages/overlays/IconShapePebbleOverlay/res/values/strings.xml
@@ -0,0 +1,23 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+/**
+ * Copyright (c) 2019, The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+-->
+<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <!-- Heart icon overlay -->
+ <string name="icon_shape_pebble_overlay" translatable="false">Pebble</string>
+
+</resources>
diff --git a/services/Android.bp b/services/Android.bp
index a58245361939..28c8aeea4e53 100644
--- a/services/Android.bp
+++ b/services/Android.bp
@@ -114,7 +114,7 @@ droidstubs {
srcs: [":services-all-sources"],
installable: false,
// TODO: remove the --hide options below
- args: " --show-annotation android.annotation.SystemApi\\(client=android.annotation.SystemApi.Client.MODULE_LIBRARIES,process=android.annotation.SystemApi.Process.SYSTEM_SERVER\\)" +
+ args: " --show-annotation android.annotation.SystemApi\\(client=android.annotation.SystemApi.Client.SYSTEM_SERVER\\)" +
" --hide-annotation android.annotation.Hide" +
" --hide-package com.google.android.startop.iorap" +
" --hide ReferencesHidden" +
diff --git a/services/accessibility/java/com/android/server/accessibility/gestures/EventDispatcher.java b/services/accessibility/java/com/android/server/accessibility/gestures/EventDispatcher.java
index 5ac3b69549c1..667364c9c901 100644
--- a/services/accessibility/java/com/android/server/accessibility/gestures/EventDispatcher.java
+++ b/services/accessibility/java/com/android/server/accessibility/gestures/EventDispatcher.java
@@ -301,7 +301,7 @@ class EventDispatcher {
* @param policyFlags The policy flags associated with the event.
*/
void sendUpForInjectedDownPointers(MotionEvent prototype, int policyFlags) {
- int pointerIdBits = 0;
+ int pointerIdBits = prototype.getPointerIdBits();
final int pointerCount = prototype.getPointerCount();
for (int i = 0; i < pointerCount; i++) {
final int pointerId = prototype.getPointerId(i);
@@ -309,10 +309,10 @@ class EventDispatcher {
if (!isInjectedPointerDown(pointerId)) {
continue;
}
- pointerIdBits |= (1 << pointerId);
- final int action = computeInjectionAction(MotionEvent.ACTION_UP, i);
+ final int action = computeInjectionAction(MotionEvent.ACTION_POINTER_UP, i);
sendMotionEvent(
prototype, action, mState.getLastReceivedEvent(), pointerIdBits, policyFlags);
+ pointerIdBits &= ~(1 << pointerId);
}
}
}
diff --git a/services/appprediction/java/com/android/server/appprediction/AppPredictionPerUserService.java b/services/appprediction/java/com/android/server/appprediction/AppPredictionPerUserService.java
index 4f49fb7578a1..48d976b1504d 100644
--- a/services/appprediction/java/com/android/server/appprediction/AppPredictionPerUserService.java
+++ b/services/appprediction/java/com/android/server/appprediction/AppPredictionPerUserService.java
@@ -32,11 +32,14 @@ import android.os.RemoteCallbackList;
import android.os.RemoteException;
import android.service.appprediction.AppPredictionService;
import android.util.ArrayMap;
+import android.util.ArraySet;
import android.util.Slog;
import com.android.internal.annotations.GuardedBy;
import com.android.server.infra.AbstractPerUserSystemService;
+import java.util.Map;
+import java.util.Set;
import java.util.function.Consumer;
/**
@@ -48,9 +51,13 @@ public class AppPredictionPerUserService extends
private static final String TAG = AppPredictionPerUserService.class.getSimpleName();
- @Nullable
+ /**
+ * A lookup of remote services in respect to their {@link ComponentName}.
+ */
+ @NonNull
@GuardedBy("mLock")
- private RemoteAppPredictionService mRemoteService;
+ private final ArrayMap<ComponentName, RemoteAppPredictionService> mRemoteServices =
+ new ArrayMap<>();
/**
* When {@code true}, remote service died but service state is kept so it's restored after
@@ -92,7 +99,7 @@ public class AppPredictionPerUserService extends
if (enabledChanged) {
if (!isEnabledLocked()) {
// Clear the remote service for the next call
- mRemoteService = null;
+ mRemoteServices.clear();
}
}
return enabledChanged;
@@ -104,14 +111,14 @@ public class AppPredictionPerUserService extends
@GuardedBy("mLock")
public void onCreatePredictionSessionLocked(@NonNull AppPredictionContext context,
@NonNull AppPredictionSessionId sessionId) {
- final RemoteAppPredictionService service = getRemoteServiceLocked();
+ if (!mSessionInfos.containsKey(sessionId)) {
+ mSessionInfos.put(sessionId, new AppPredictionSessionInfo(sessionId, context,
+ resolveComponentName(context), this::removeAppPredictionSessionInfo));
+ }
+ final RemoteAppPredictionService service = getRemoteServiceLocked(
+ getComponentName(sessionId));
if (service != null) {
service.onCreatePredictionSession(context, sessionId);
-
- if (!mSessionInfos.containsKey(sessionId)) {
- mSessionInfos.put(sessionId, new AppPredictionSessionInfo(sessionId, context,
- this::removeAppPredictionSessionInfo));
- }
}
}
@@ -121,7 +128,8 @@ public class AppPredictionPerUserService extends
@GuardedBy("mLock")
public void notifyAppTargetEventLocked(@NonNull AppPredictionSessionId sessionId,
@NonNull AppTargetEvent event) {
- final RemoteAppPredictionService service = getRemoteServiceLocked();
+ final RemoteAppPredictionService service = getRemoteServiceLocked(
+ getComponentName(sessionId));
if (service != null) {
service.notifyAppTargetEvent(sessionId, event);
}
@@ -133,7 +141,8 @@ public class AppPredictionPerUserService extends
@GuardedBy("mLock")
public void notifyLaunchLocationShownLocked(@NonNull AppPredictionSessionId sessionId,
@NonNull String launchLocation, @NonNull ParceledListSlice targetIds) {
- final RemoteAppPredictionService service = getRemoteServiceLocked();
+ final RemoteAppPredictionService service = getRemoteServiceLocked(
+ getComponentName(sessionId));
if (service != null) {
service.notifyLaunchLocationShown(sessionId, launchLocation, targetIds);
}
@@ -145,7 +154,8 @@ public class AppPredictionPerUserService extends
@GuardedBy("mLock")
public void sortAppTargetsLocked(@NonNull AppPredictionSessionId sessionId,
@NonNull ParceledListSlice targets, @NonNull IPredictionCallback callback) {
- final RemoteAppPredictionService service = getRemoteServiceLocked();
+ final RemoteAppPredictionService service = getRemoteServiceLocked(
+ getComponentName(sessionId));
if (service != null) {
service.sortAppTargets(sessionId, targets, callback);
}
@@ -157,7 +167,8 @@ public class AppPredictionPerUserService extends
@GuardedBy("mLock")
public void registerPredictionUpdatesLocked(@NonNull AppPredictionSessionId sessionId,
@NonNull IPredictionCallback callback) {
- final RemoteAppPredictionService service = getRemoteServiceLocked();
+ final RemoteAppPredictionService service = getRemoteServiceLocked(
+ getComponentName(sessionId));
if (service != null) {
service.registerPredictionUpdates(sessionId, callback);
@@ -174,7 +185,8 @@ public class AppPredictionPerUserService extends
@GuardedBy("mLock")
public void unregisterPredictionUpdatesLocked(@NonNull AppPredictionSessionId sessionId,
@NonNull IPredictionCallback callback) {
- final RemoteAppPredictionService service = getRemoteServiceLocked();
+ final RemoteAppPredictionService service = getRemoteServiceLocked(
+ getComponentName(sessionId));
if (service != null) {
service.unregisterPredictionUpdates(sessionId, callback);
@@ -190,7 +202,8 @@ public class AppPredictionPerUserService extends
*/
@GuardedBy("mLock")
public void requestPredictionUpdateLocked(@NonNull AppPredictionSessionId sessionId) {
- final RemoteAppPredictionService service = getRemoteServiceLocked();
+ final RemoteAppPredictionService service = getRemoteServiceLocked(
+ getComponentName(sessionId));
if (service != null) {
service.requestPredictionUpdate(sessionId);
}
@@ -201,7 +214,8 @@ public class AppPredictionPerUserService extends
*/
@GuardedBy("mLock")
public void onDestroyPredictionSessionLocked(@NonNull AppPredictionSessionId sessionId) {
- final RemoteAppPredictionService service = getRemoteServiceLocked();
+ final RemoteAppPredictionService service = getRemoteServiceLocked(
+ getComponentName(sessionId));
if (service != null) {
service.onDestroyPredictionSession(sessionId);
@@ -229,7 +243,7 @@ public class AppPredictionPerUserService extends
synchronized (mLock) {
if (mZombie) {
// Sanity check - shouldn't happen
- if (mRemoteService == null) {
+ if (mRemoteServices.isEmpty()) {
Slog.w(TAG, "Cannot resurrect sessions because remote service is null");
return;
}
@@ -266,22 +280,30 @@ public class AppPredictionPerUserService extends
}
private void destroyAndRebindRemoteService() {
- if (mRemoteService == null) {
+ if (mRemoteServices.isEmpty()) {
return;
}
if (isDebug()) {
Slog.d(TAG, "Destroying the old remote service.");
}
- mRemoteService.destroy();
- mRemoteService = null;
+ final Set<Map.Entry<ComponentName, RemoteAppPredictionService>> services =
+ new ArraySet<>(mRemoteServices.entrySet());
+ mRemoteServices.clear();
+ services.stream().forEach(entry -> destroyAndRebindRemoteService(
+ entry.getKey(), entry.getValue()));
+ }
- mRemoteService = getRemoteServiceLocked();
- if (mRemoteService != null) {
+ private void destroyAndRebindRemoteService(
+ @NonNull final ComponentName component,
+ @NonNull final RemoteAppPredictionService service) {
+ service.destroy();
+ final RemoteAppPredictionService newService = getRemoteServiceLocked(component);
+ if (newService != null) {
if (isDebug()) {
Slog.d(TAG, "Rebinding to the new remote service.");
}
- mRemoteService.reconnect();
+ newService.reconnect();
}
}
@@ -292,7 +314,7 @@ public class AppPredictionPerUserService extends
private void resurrectSessionsLocked() {
final int numSessions = mSessionInfos.size();
if (isDebug()) {
- Slog.d(TAG, "Resurrecting remote service (" + mRemoteService + ") on "
+ Slog.d(TAG, "Resurrecting remote service (" + mRemoteServices + ") on "
+ numSessions + " sessions.");
}
@@ -310,32 +332,49 @@ public class AppPredictionPerUserService extends
}
}
- @GuardedBy("mLock")
@Nullable
- private RemoteAppPredictionService getRemoteServiceLocked() {
- if (mRemoteService == null) {
- final String serviceName = getComponentNameLocked();
- if (serviceName == null) {
- if (mMaster.verbose) {
- Slog.v(TAG, "getRemoteServiceLocked(): not set");
- }
- return null;
+ private ComponentName resolveComponentName(@NonNull final AppPredictionContext context) {
+ // TODO: add logic to determine serviceName based on context
+ final String serviceName = getComponentNameLocked();
+ if (serviceName == null) {
+ if (mMaster.verbose) {
+ Slog.v(TAG, "getRemoteServiceLocked(): not set, context = " + context);
}
- ComponentName serviceComponent = ComponentName.unflattenFromString(serviceName);
+ return null;
+ }
+ return ComponentName.unflattenFromString(serviceName);
+ }
+
+ @Nullable
+ private ComponentName getComponentName(@NonNull final AppPredictionSessionId sessionId) {
+ AppPredictionSessionInfo sessionInfo = mSessionInfos.get(sessionId);
+ return sessionInfo == null ? null : sessionInfo.mComponentName;
+ }
- mRemoteService = new RemoteAppPredictionService(getContext(),
+ @GuardedBy("mLock")
+ @Nullable
+ private RemoteAppPredictionService getRemoteServiceLocked(
+ @Nullable final ComponentName serviceComponent) {
+ if (serviceComponent == null) return null;
+ if (!mRemoteServices.containsKey(serviceComponent)) {
+ mRemoteServices.put(serviceComponent, new RemoteAppPredictionService(getContext(),
AppPredictionService.SERVICE_INTERFACE, serviceComponent, mUserId, this,
- mMaster.isBindInstantServiceAllowed(), mMaster.verbose);
+ mMaster.isBindInstantServiceAllowed(), mMaster.verbose));
}
- return mRemoteService;
+ return mRemoteServices.get(serviceComponent);
}
private static final class AppPredictionSessionInfo {
private static final boolean DEBUG = false; // Do not submit with true
+ @NonNull
private final AppPredictionSessionId mSessionId;
+ @NonNull
private final AppPredictionContext mPredictionContext;
+ @Nullable
+ private final ComponentName mComponentName;
+ @NonNull
private final Consumer<AppPredictionSessionId> mRemoveSessionInfoAction;
private final RemoteCallbackList<IPredictionCallback> mCallbacks =
@@ -352,13 +391,17 @@ public class AppPredictionPerUserService extends
}
};
- AppPredictionSessionInfo(AppPredictionSessionId id, AppPredictionContext predictionContext,
- Consumer<AppPredictionSessionId> removeSessionInfoAction) {
+ AppPredictionSessionInfo(
+ @NonNull final AppPredictionSessionId id,
+ @NonNull final AppPredictionContext predictionContext,
+ @Nullable final ComponentName componentName,
+ @NonNull final Consumer<AppPredictionSessionId> removeSessionInfoAction) {
if (DEBUG) {
Slog.d(TAG, "Creating AppPredictionSessionInfo for session Id=" + id);
}
mSessionId = id;
mPredictionContext = predictionContext;
+ mComponentName = componentName;
mRemoveSessionInfoAction = removeSessionInfoAction;
}
@@ -390,8 +433,8 @@ public class AppPredictionPerUserService extends
void resurrectSessionLocked(AppPredictionPerUserService service) {
int callbackCount = mCallbacks.getRegisteredCallbackCount();
if (DEBUG) {
- Slog.d(TAG, "Resurrecting remote service (" + service.getRemoteServiceLocked()
- + ") for session Id=" + mSessionId + " and "
+ Slog.d(TAG, "Resurrecting remote service (" + service.getRemoteServiceLocked(
+ mComponentName) + ") for session Id=" + mSessionId + " and "
+ callbackCount + " callbacks.");
}
service.onCreatePredictionSessionLocked(mPredictionContext, mSessionId);
diff --git a/services/autofill/java/com/android/server/autofill/ui/SaveUi.java b/services/autofill/java/com/android/server/autofill/ui/SaveUi.java
index 8eea04759cdb..507e98369855 100644
--- a/services/autofill/java/com/android/server/autofill/ui/SaveUi.java
+++ b/services/autofill/java/com/android/server/autofill/ui/SaveUi.java
@@ -27,6 +27,7 @@ import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
import android.content.IntentSender;
+import android.content.pm.PackageManager;
import android.content.res.Resources;
import android.graphics.drawable.Drawable;
import android.metrics.LogMaker;
@@ -187,7 +188,10 @@ final class SaveUi {
context = new ContextThemeWrapper(context, mThemeId) {
@Override
public void startActivity(Intent intent) {
- if (intent.resolveActivity(getPackageManager()) == null) {
+ if (resolveActivity(intent) == null) {
+ if (sDebug) {
+ Slog.d(TAG, "Can not startActivity for save UI with intent=" + intent);
+ }
return;
}
intent.putExtra(AutofillManager.EXTRA_RESTORE_CROSS_ACTIVITY, true);
@@ -199,6 +203,16 @@ final class SaveUi {
// Apply restore mechanism
startIntentSenderWithRestore(p, intent);
}
+
+ private ComponentName resolveActivity(Intent intent) {
+ final PackageManager packageManager = getPackageManager();
+ final ComponentName componentName = intent.resolveActivity(packageManager);
+ if (componentName != null) {
+ return componentName;
+ }
+ intent.addFlags(Intent.FLAG_ACTIVITY_MATCH_EXTERNAL);
+ return intent.resolveActivity(packageManager);
+ }
};
final LayoutInflater inflater = LayoutInflater.from(context);
final View view = inflater.inflate(R.layout.autofill_save, null);
diff --git a/services/backup/backuplib/java/com/android/server/backup/transport/TransportClient.java b/services/backup/backuplib/java/com/android/server/backup/transport/TransportClient.java
index 7c5a57c004e4..ca89f7f69fbc 100644
--- a/services/backup/backuplib/java/com/android/server/backup/transport/TransportClient.java
+++ b/services/backup/backuplib/java/com/android/server/backup/transport/TransportClient.java
@@ -26,6 +26,7 @@ import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
import android.content.ServiceConnection;
+import android.os.Binder;
import android.os.DeadObjectException;
import android.os.Handler;
import android.os.IBinder;
@@ -662,6 +663,10 @@ public class TransportClient {
referenceLost("TransportConnection.onServiceConnected()");
return;
}
+ // TODO (b/147705255): Remove when binder calls to IBackupTransport are not blocking
+ // In short-term, blocking calls are OK as the transports come from the whitelist at
+ // {@link SystemConfig#getBackupTransportWhitelist()}
+ Binder.allowBlocking(binder);
transportClient.onServiceConnected(binder);
}
diff --git a/services/contentcapture/java/com/android/server/contentcapture/ContentCaptureMetricsLogger.java b/services/contentcapture/java/com/android/server/contentcapture/ContentCaptureMetricsLogger.java
index dd1b84b16f68..7ea4eff3381c 100644
--- a/services/contentcapture/java/com/android/server/contentcapture/ContentCaptureMetricsLogger.java
+++ b/services/contentcapture/java/com/android/server/contentcapture/ContentCaptureMetricsLogger.java
@@ -20,7 +20,8 @@ import android.annotation.Nullable;
import android.content.ComponentName;
import android.content.ContentCaptureOptions;
import android.service.contentcapture.FlushMetrics;
-import android.util.StatsLog;
+
+import com.android.internal.util.FrameworkStatsLog;
import java.util.List;
@@ -35,8 +36,8 @@ public final class ContentCaptureMetricsLogger {
/** @hide */
public static void writeServiceEvent(int eventType, @NonNull String serviceName,
@Nullable String targetPackage) {
- StatsLog.write(StatsLog.CONTENT_CAPTURE_SERVICE_EVENTS, eventType, serviceName,
- targetPackage);
+ FrameworkStatsLog.write(FrameworkStatsLog.CONTENT_CAPTURE_SERVICE_EVENTS, eventType,
+ serviceName, targetPackage);
}
/** @hide */
@@ -79,16 +80,16 @@ public final class ContentCaptureMetricsLogger {
stringBuilder.append(activities.get(i).flattenToShortString());
}
}
- StatsLog.write(StatsLog.CONTENT_CAPTURE_SERVICE_EVENTS,
- StatsLog.CONTENT_CAPTURE_SERVICE_EVENTS__EVENT__SET_WHITELIST,
+ FrameworkStatsLog.write(FrameworkStatsLog.CONTENT_CAPTURE_SERVICE_EVENTS,
+ FrameworkStatsLog.CONTENT_CAPTURE_SERVICE_EVENTS__EVENT__SET_WHITELIST,
serviceName, stringBuilder.toString());
}
/** @hide */
public static void writeSessionEvent(int sessionId, int event, int flags,
@NonNull ComponentName service, @Nullable ComponentName app, boolean isChildSession) {
- StatsLog.write(StatsLog.CONTENT_CAPTURE_SESSION_EVENTS, sessionId, event, flags,
- ComponentName.flattenToShortString(service),
+ FrameworkStatsLog.write(FrameworkStatsLog.CONTENT_CAPTURE_SESSION_EVENTS, sessionId, event,
+ flags, ComponentName.flattenToShortString(service),
ComponentName.flattenToShortString(app), isChildSession);
}
@@ -96,7 +97,7 @@ public final class ContentCaptureMetricsLogger {
public static void writeSessionFlush(int sessionId, @NonNull ComponentName service,
@Nullable ComponentName app, @NonNull FlushMetrics fm,
@NonNull ContentCaptureOptions options, int flushReason) {
- StatsLog.write(StatsLog.CONTENT_CAPTURE_FLUSHED, sessionId,
+ FrameworkStatsLog.write(FrameworkStatsLog.CONTENT_CAPTURE_FLUSHED, sessionId,
ComponentName.flattenToShortString(service),
ComponentName.flattenToShortString(app), fm.sessionStarted, fm.sessionFinished,
fm.viewAppearedCount, fm.viewDisappearedCount, fm.viewTextChangedCount,
diff --git a/services/contentcapture/java/com/android/server/contentcapture/ContentCapturePerUserService.java b/services/contentcapture/java/com/android/server/contentcapture/ContentCapturePerUserService.java
index 0f1122e3886a..583c5b593b88 100644
--- a/services/contentcapture/java/com/android/server/contentcapture/ContentCapturePerUserService.java
+++ b/services/contentcapture/java/com/android/server/contentcapture/ContentCapturePerUserService.java
@@ -61,13 +61,13 @@ import android.util.ArraySet;
import android.util.Slog;
import android.util.SparseArray;
import android.util.SparseBooleanArray;
-import android.util.StatsLog;
import android.view.contentcapture.ContentCaptureCondition;
import android.view.contentcapture.DataRemovalRequest;
import android.view.contentcapture.DataShareRequest;
import com.android.internal.annotations.GuardedBy;
import com.android.internal.os.IResultReceiver;
+import com.android.internal.util.FrameworkStatsLog;
import com.android.server.LocalServices;
import com.android.server.contentcapture.RemoteContentCaptureService.ContentCaptureServiceCallbacks;
import com.android.server.infra.AbstractPerUserSystemService;
@@ -275,7 +275,7 @@ final class ContentCapturePerUserService
/* binder= */ null);
// Log metrics.
writeSessionEvent(sessionId,
- StatsLog.CONTENT_CAPTURE_SESSION_EVENTS__EVENT__SESSION_NOT_CREATED,
+ FrameworkStatsLog.CONTENT_CAPTURE_SESSION_EVENTS__EVENT__SESSION_NOT_CREATED,
STATE_DISABLED | STATE_NO_SERVICE, serviceComponentName,
componentName, /* isChildSession= */ false);
return;
@@ -299,7 +299,7 @@ final class ContentCapturePerUserService
/* binder= */ null);
// Log metrics.
writeSessionEvent(sessionId,
- StatsLog.CONTENT_CAPTURE_SESSION_EVENTS__EVENT__SESSION_NOT_CREATED,
+ FrameworkStatsLog.CONTENT_CAPTURE_SESSION_EVENTS__EVENT__SESSION_NOT_CREATED,
STATE_DISABLED | STATE_NOT_WHITELISTED, serviceComponentName,
componentName, /* isChildSession= */ false);
return;
@@ -313,7 +313,7 @@ final class ContentCapturePerUserService
/* binder=*/ null);
// Log metrics.
writeSessionEvent(sessionId,
- StatsLog.CONTENT_CAPTURE_SESSION_EVENTS__EVENT__SESSION_NOT_CREATED,
+ FrameworkStatsLog.CONTENT_CAPTURE_SESSION_EVENTS__EVENT__SESSION_NOT_CREATED,
STATE_DISABLED | STATE_DUPLICATED_ID,
serviceComponentName, componentName, /* isChildSession= */ false);
return;
@@ -330,7 +330,7 @@ final class ContentCapturePerUserService
/* binder= */ null);
// Log metrics.
writeSessionEvent(sessionId,
- StatsLog.CONTENT_CAPTURE_SESSION_EVENTS__EVENT__SESSION_NOT_CREATED,
+ FrameworkStatsLog.CONTENT_CAPTURE_SESSION_EVENTS__EVENT__SESSION_NOT_CREATED,
STATE_DISABLED | STATE_NO_SERVICE, serviceComponentName,
componentName, /* isChildSession= */ false);
return;
@@ -651,7 +651,7 @@ final class ContentCapturePerUserService
} finally {
Binder.restoreCallingIdentity(token);
}
- writeServiceEvent(StatsLog.CONTENT_CAPTURE_SERVICE_EVENTS__EVENT__SET_DISABLED,
+ writeServiceEvent(FrameworkStatsLog.CONTENT_CAPTURE_SERVICE_EVENTS__EVENT__SET_DISABLED,
getServiceComponentName());
}
diff --git a/services/contentcapture/java/com/android/server/contentcapture/RemoteContentCaptureService.java b/services/contentcapture/java/com/android/server/contentcapture/RemoteContentCaptureService.java
index c16df0f19943..9a170ac6da10 100644
--- a/services/contentcapture/java/com/android/server/contentcapture/RemoteContentCaptureService.java
+++ b/services/contentcapture/java/com/android/server/contentcapture/RemoteContentCaptureService.java
@@ -32,13 +32,13 @@ import android.service.contentcapture.IContentCaptureServiceCallback;
import android.service.contentcapture.IDataShareCallback;
import android.service.contentcapture.SnapshotData;
import android.util.Slog;
-import android.util.StatsLog;
import android.view.contentcapture.ContentCaptureContext;
import android.view.contentcapture.DataRemovalRequest;
import android.view.contentcapture.DataShareRequest;
import com.android.internal.infra.AbstractMultiplePendingRequestsRemoteService;
import com.android.internal.os.IResultReceiver;
+import com.android.internal.util.FrameworkStatsLog;
final class RemoteContentCaptureService
extends AbstractMultiplePendingRequestsRemoteService<RemoteContentCaptureService,
@@ -83,7 +83,8 @@ final class RemoteContentCaptureService
if (connected) {
try {
mService.onConnected(mServerCallback, sVerbose, sDebug);
- writeServiceEvent(StatsLog.CONTENT_CAPTURE_SERVICE_EVENTS__EVENT__ON_CONNECTED,
+ writeServiceEvent(
+ FrameworkStatsLog.CONTENT_CAPTURE_SERVICE_EVENTS__EVENT__ON_CONNECTED,
mComponentName);
} finally {
// Update the system-service state, in case the service reconnected after
@@ -92,7 +93,8 @@ final class RemoteContentCaptureService
}
} else {
mService.onDisconnected();
- writeServiceEvent(StatsLog.CONTENT_CAPTURE_SERVICE_EVENTS__EVENT__ON_DISCONNECTED,
+ writeServiceEvent(
+ FrameworkStatsLog.CONTENT_CAPTURE_SERVICE_EVENTS__EVENT__ON_DISCONNECTED,
mComponentName);
}
} catch (Exception e) {
@@ -114,8 +116,9 @@ final class RemoteContentCaptureService
(s) -> s.onSessionStarted(context, sessionId, uid, clientReceiver, initialState));
// Metrics logging.
writeSessionEvent(sessionId,
- StatsLog.CONTENT_CAPTURE_SESSION_EVENTS__EVENT__ON_SESSION_STARTED, initialState,
- getComponentName(), context.getActivityComponent(), /* is_child_session= */ false);
+ FrameworkStatsLog.CONTENT_CAPTURE_SESSION_EVENTS__EVENT__ON_SESSION_STARTED,
+ initialState, getComponentName(), context.getActivityComponent(),
+ /* is_child_session= */ false);
}
/**
@@ -126,7 +129,7 @@ final class RemoteContentCaptureService
scheduleAsyncRequest((s) -> s.onSessionFinished(sessionId));
// Metrics logging.
writeSessionEvent(sessionId,
- StatsLog.CONTENT_CAPTURE_SESSION_EVENTS__EVENT__ON_SESSION_FINISHED,
+ FrameworkStatsLog.CONTENT_CAPTURE_SESSION_EVENTS__EVENT__ON_SESSION_FINISHED,
/* flags= */ 0, getComponentName(), /* app= */ null,
/* is_child_session= */ false);
}
@@ -143,7 +146,8 @@ final class RemoteContentCaptureService
*/
public void onDataRemovalRequest(@NonNull DataRemovalRequest request) {
scheduleAsyncRequest((s) -> s.onDataRemovalRequest(request));
- writeServiceEvent(StatsLog.CONTENT_CAPTURE_SERVICE_EVENTS__EVENT__ON_USER_DATA_REMOVED,
+ writeServiceEvent(
+ FrameworkStatsLog.CONTENT_CAPTURE_SERVICE_EVENTS__EVENT__ON_USER_DATA_REMOVED,
mComponentName);
}
diff --git a/services/core/java/android/content/pm/PackageManagerInternal.java b/services/core/java/android/content/pm/PackageManagerInternal.java
index 27b6bfb8f5fd..bdcd832e4f4a 100644
--- a/services/core/java/android/content/pm/PackageManagerInternal.java
+++ b/services/core/java/android/content/pm/PackageManagerInternal.java
@@ -67,6 +67,7 @@ public abstract class PackageManagerInternal {
public static final int PACKAGE_TELEPHONY = 12;
public static final int PACKAGE_WIFI = 13;
public static final int PACKAGE_COMPANION = 14;
+ public static final int PACKAGE_RETAIL_DEMO = 15;
@IntDef(value = {
INTEGRITY_VERIFICATION_ALLOW,
@@ -105,6 +106,7 @@ public abstract class PackageManagerInternal {
PACKAGE_TELEPHONY,
PACKAGE_WIFI,
PACKAGE_COMPANION,
+ PACKAGE_RETAIL_DEMO,
})
@Retention(RetentionPolicy.SOURCE)
public @interface KnownPackage {}
diff --git a/services/core/java/com/android/server/AlarmManagerService.java b/services/core/java/com/android/server/AlarmManagerService.java
index 0f2fb9252c29..3441a5f19508 100644
--- a/services/core/java/com/android/server/AlarmManagerService.java
+++ b/services/core/java/com/android/server/AlarmManagerService.java
@@ -86,7 +86,6 @@ import android.util.SparseArray;
import android.util.SparseBooleanArray;
import android.util.SparseIntArray;
import android.util.SparseLongArray;
-import android.util.StatsLog;
import android.util.TimeUtils;
import android.util.proto.ProtoOutputStream;
@@ -94,6 +93,7 @@ import com.android.internal.annotations.GuardedBy;
import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.util.ArrayUtils;
import com.android.internal.util.DumpUtils;
+import com.android.internal.util.FrameworkStatsLog;
import com.android.internal.util.IndentingPrintWriter;
import com.android.internal.util.LocalLog;
import com.android.internal.util.StatLogger;
@@ -331,12 +331,16 @@ class AlarmManagerService extends SystemService {
return (history == null) ? 0 : history.size();
}
- long getLastWakeupForPackage(String packageName, int userId, int positionFromEnd) {
+ /**
+ * @param n The desired nth-last wakeup
+ * (1=1st-last=the ultimate wakeup and 2=2nd-last=the penultimate wakeup)
+ */
+ long getNthLastWakeupForPackage(String packageName, int userId, int n) {
final LongArrayQueue history = mPackageHistory.get(Pair.create(packageName, userId));
if (history == null) {
return 0;
}
- final int i = history.size() - positionFromEnd;
+ final int i = history.size() - n;
return (i < 0) ? 0 : history.get(i);
}
@@ -400,6 +404,11 @@ class AlarmManagerService extends SystemService {
"standby_rare_quota",
"standby_never_quota",
};
+ // Not putting this in the KEYS_APP_STANDBY_QUOTAS array because this uses a different
+ // window size.
+ private static final String KEY_APP_STANDBY_RESTRICTED_QUOTA = "standby_restricted_quota";
+ private static final String KEY_APP_STANDBY_RESTRICTED_WINDOW =
+ "app_standby_restricted_window";
private static final long DEFAULT_MIN_FUTURITY = 5 * 1000;
private static final long DEFAULT_MIN_INTERVAL = 60 * 1000;
@@ -420,6 +429,8 @@ class AlarmManagerService extends SystemService {
1, // Rare
0 // Never
};
+ private static final int DEFAULT_APP_STANDBY_RESTRICTED_QUOTA = 1;
+ private static final long DEFAULT_APP_STANDBY_RESTRICTED_WINDOW = MILLIS_IN_DAY;
// Minimum futurity of a new alarm
public long MIN_FUTURITY = DEFAULT_MIN_FUTURITY;
@@ -446,6 +457,8 @@ class AlarmManagerService extends SystemService {
public long APP_STANDBY_WINDOW = DEFAULT_APP_STANDBY_WINDOW;
public int[] APP_STANDBY_QUOTAS = new int[DEFAULT_APP_STANDBY_QUOTAS.length];
+ public int APP_STANDBY_RESTRICTED_QUOTA = DEFAULT_APP_STANDBY_RESTRICTED_QUOTA;
+ public long APP_STANDBY_RESTRICTED_WINDOW = DEFAULT_APP_STANDBY_RESTRICTED_WINDOW;
private ContentResolver mResolver;
private final KeyValueListParser mParser = new KeyValueListParser(',');
@@ -520,6 +533,14 @@ class AlarmManagerService extends SystemService {
Math.min(APP_STANDBY_QUOTAS[i - 1], DEFAULT_APP_STANDBY_QUOTAS[i]));
}
+ APP_STANDBY_RESTRICTED_QUOTA = Math.max(1,
+ mParser.getInt(KEY_APP_STANDBY_RESTRICTED_QUOTA,
+ DEFAULT_APP_STANDBY_RESTRICTED_QUOTA));
+
+ APP_STANDBY_RESTRICTED_WINDOW = Math.max(APP_STANDBY_WINDOW,
+ mParser.getLong(KEY_APP_STANDBY_RESTRICTED_WINDOW,
+ DEFAULT_APP_STANDBY_RESTRICTED_WINDOW));
+
MAX_ALARMS_PER_UID = mParser.getInt(KEY_MAX_ALARMS_PER_UID,
DEFAULT_MAX_ALARMS_PER_UID);
if (MAX_ALARMS_PER_UID < DEFAULT_MAX_ALARMS_PER_UID) {
@@ -581,6 +602,14 @@ class AlarmManagerService extends SystemService {
pw.println(APP_STANDBY_QUOTAS[i]);
}
+ pw.print(KEY_APP_STANDBY_RESTRICTED_QUOTA); pw.print("=");
+ TimeUtils.formatDuration(APP_STANDBY_RESTRICTED_QUOTA, pw);
+ pw.println();
+
+ pw.print(KEY_APP_STANDBY_RESTRICTED_WINDOW); pw.print("=");
+ TimeUtils.formatDuration(APP_STANDBY_RESTRICTED_WINDOW, pw);
+ pw.println();
+
pw.decreaseIndent();
}
@@ -1814,25 +1843,44 @@ class AlarmManagerService extends SystemService {
sourcePackage, sourceUserId, mInjector.getElapsedRealtime());
// Quota deferring implementation:
+ boolean deferred = false;
final int wakeupsInWindow = mAppWakeupHistory.getTotalWakeupsInWindow(sourcePackage,
sourceUserId);
- final int quotaForBucket = getQuotaForBucketLocked(standbyBucket);
- boolean deferred = false;
- if (wakeupsInWindow >= quotaForBucket) {
- final long minElapsed;
- if (quotaForBucket <= 0) {
- // Just keep deferring for a day till the quota changes
- minElapsed = mInjector.getElapsedRealtime() + MILLIS_IN_DAY;
- } else {
- // Suppose the quota for window was q, and the qth last delivery time for this
- // package was t(q) then the next delivery must be after t(q) + <window_size>
- final long t = mAppWakeupHistory.getLastWakeupForPackage(sourcePackage,
- sourceUserId, quotaForBucket);
- minElapsed = t + 1 + mConstants.APP_STANDBY_WINDOW;
+ if (standbyBucket == UsageStatsManager.STANDBY_BUCKET_RESTRICTED) {
+ // Special case because it's 1/day instead of 1/hour.
+ // AppWakeupHistory doesn't delete old wakeup times until a new one is logged, so we
+ // should always have the last wakeup available.
+ if (wakeupsInWindow > 0) {
+ final long lastWakeupTime = mAppWakeupHistory.getNthLastWakeupForPackage(
+ sourcePackage, sourceUserId, mConstants.APP_STANDBY_RESTRICTED_QUOTA);
+ if (mInjector.getElapsedRealtime() - lastWakeupTime
+ < mConstants.APP_STANDBY_RESTRICTED_WINDOW) {
+ final long minElapsed =
+ lastWakeupTime + mConstants.APP_STANDBY_RESTRICTED_WINDOW;
+ if (alarm.expectedWhenElapsed < minElapsed) {
+ alarm.whenElapsed = alarm.maxWhenElapsed = minElapsed;
+ deferred = true;
+ }
+ }
}
- if (alarm.expectedWhenElapsed < minElapsed) {
- alarm.whenElapsed = alarm.maxWhenElapsed = minElapsed;
- deferred = true;
+ } else {
+ final int quotaForBucket = getQuotaForBucketLocked(standbyBucket);
+ if (wakeupsInWindow >= quotaForBucket) {
+ final long minElapsed;
+ if (quotaForBucket <= 0) {
+ // Just keep deferring for a day till the quota changes
+ minElapsed = mInjector.getElapsedRealtime() + MILLIS_IN_DAY;
+ } else {
+ // Suppose the quota for window was q, and the qth last delivery time for this
+ // package was t(q) then the next delivery must be after t(q) + <window_size>
+ final long t = mAppWakeupHistory.getNthLastWakeupForPackage(
+ sourcePackage, sourceUserId, quotaForBucket);
+ minElapsed = t + 1 + mConstants.APP_STANDBY_WINDOW;
+ }
+ if (alarm.expectedWhenElapsed < minElapsed) {
+ alarm.whenElapsed = alarm.maxWhenElapsed = minElapsed;
+ deferred = true;
+ }
}
}
if (!deferred) {
@@ -3915,7 +3963,7 @@ class AlarmManagerService extends SystemService {
Slog.v(TAG, "Time changed notification from kernel; rebatching");
}
// StatsLog requires currentTimeMillis(), which == nowRTC to within usecs.
- StatsLog.write(StatsLog.WALL_CLOCK_TIME_SHIFTED, nowRTC);
+ FrameworkStatsLog.write(FrameworkStatsLog.WALL_CLOCK_TIME_SHIFTED, nowRTC);
removeImpl(null, mTimeTickTrigger);
removeImpl(mDateChangeSender, null);
rebatchAllAlarms();
diff --git a/services/core/java/com/android/server/BluetoothManagerService.java b/services/core/java/com/android/server/BluetoothManagerService.java
index 3774b645339d..311a494ee570 100644
--- a/services/core/java/com/android/server/BluetoothManagerService.java
+++ b/services/core/java/com/android/server/BluetoothManagerService.java
@@ -69,17 +69,17 @@ import android.text.TextUtils;
import android.util.FeatureFlagUtils;
import android.util.Log;
import android.util.Slog;
-import android.util.StatsLog;
import com.android.internal.R;
import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.util.DumpUtils;
+import com.android.internal.util.FrameworkStatsLog;
import com.android.server.pm.UserRestrictionsUtils;
import java.io.FileDescriptor;
import java.io.PrintWriter;
-import java.util.HashMap;
import java.util.ArrayList;
+import java.util.HashMap;
import java.util.LinkedList;
import java.util.Locale;
import java.util.Map;
@@ -87,7 +87,6 @@ import java.util.NoSuchElementException;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.locks.ReentrantReadWriteLock;
-
class BluetoothManagerService extends IBluetoothManager.Stub {
private static final String TAG = "BluetoothManagerService";
private static final boolean DBG = true;
@@ -2300,9 +2299,9 @@ class BluetoothManagerService extends IBluetoothManager.Stub {
new ActiveLog(reason, packageName, enable, System.currentTimeMillis()));
}
- int state = enable ? StatsLog.BLUETOOTH_ENABLED_STATE_CHANGED__STATE__ENABLED :
- StatsLog.BLUETOOTH_ENABLED_STATE_CHANGED__STATE__DISABLED;
- StatsLog.write_non_chained(StatsLog.BLUETOOTH_ENABLED_STATE_CHANGED,
+ int state = enable ? FrameworkStatsLog.BLUETOOTH_ENABLED_STATE_CHANGED__STATE__ENABLED :
+ FrameworkStatsLog.BLUETOOTH_ENABLED_STATE_CHANGED__STATE__DISABLED;
+ FrameworkStatsLog.write_non_chained(FrameworkStatsLog.BLUETOOTH_ENABLED_STATE_CHANGED,
Binder.getCallingUid(), null, state, reason, packageName);
}
diff --git a/services/core/java/com/android/server/ConnectivityService.java b/services/core/java/com/android/server/ConnectivityService.java
index 0194a213f185..2968a816835d 100644
--- a/services/core/java/com/android/server/ConnectivityService.java
+++ b/services/core/java/com/android/server/ConnectivityService.java
@@ -109,6 +109,7 @@ import android.net.SocketKeepalive;
import android.net.TetheringManager;
import android.net.UidRange;
import android.net.Uri;
+import android.net.VpnManager;
import android.net.VpnService;
import android.net.metrics.IpConnectivityLog;
import android.net.metrics.NetworkEvent;
@@ -4317,7 +4318,7 @@ public class ConnectivityService extends IConnectivityManager.Stub
throwIfLockdownEnabled();
Vpn vpn = mVpns.get(userId);
if (vpn != null) {
- return vpn.prepare(oldPackage, newPackage);
+ return vpn.prepare(oldPackage, newPackage, VpnManager.TYPE_VPN_SERVICE);
} else {
return false;
}
@@ -4325,26 +4326,29 @@ public class ConnectivityService extends IConnectivityManager.Stub
}
/**
- * Set whether the VPN package has the ability to launch VPNs without user intervention.
- * This method is used by system-privileged apps.
- * VPN permissions are checked in the {@link Vpn} class. If the caller is not {@code userId},
- * {@link android.Manifest.permission.INTERACT_ACROSS_USERS_FULL} permission is required.
+ * Set whether the VPN package has the ability to launch VPNs without user intervention. This
+ * method is used by system-privileged apps. VPN permissions are checked in the {@link Vpn}
+ * class. If the caller is not {@code userId}, {@link
+ * android.Manifest.permission.INTERACT_ACROSS_USERS_FULL} permission is required.
*
* @param packageName The package for which authorization state should change.
* @param userId User for whom {@code packageName} is installed.
* @param authorized {@code true} if this app should be able to start a VPN connection without
- * explicit user approval, {@code false} if not.
- *
+ * explicit user approval, {@code false} if not.
+ * @param vpnType The {@link VpnManager.VpnType} constant representing what class of VPN
+ * permissions should be granted. When unauthorizing an app, {@link
+ * VpnManager.TYPE_VPN_NONE} should be used.
* @hide
*/
@Override
- public void setVpnPackageAuthorization(String packageName, int userId, boolean authorized) {
+ public void setVpnPackageAuthorization(
+ String packageName, int userId, @VpnManager.VpnType int vpnType) {
enforceCrossUserPermission(userId);
synchronized (mVpns) {
Vpn vpn = mVpns.get(userId);
if (vpn != null) {
- vpn.setPackageAuthorization(packageName, authorized);
+ vpn.setPackageAuthorization(packageName, vpnType);
}
}
}
@@ -4366,6 +4370,78 @@ public class ConnectivityService extends IConnectivityManager.Stub
}
/**
+ * Stores the given VPN profile based on the provisioning package name.
+ *
+ * <p>If there is already a VPN profile stored for the provisioning package, this call will
+ * overwrite the profile.
+ *
+ * <p>This is designed to serve the VpnManager only; settings-based VPN profiles are managed
+ * exclusively by the Settings app, and passed into the platform at startup time.
+ *
+ * @return {@code true} if user consent has already been granted, {@code false} otherwise.
+ * @hide
+ */
+ @Override
+ public boolean provisionVpnProfile(@NonNull VpnProfile profile, @NonNull String packageName) {
+ final int user = UserHandle.getUserId(Binder.getCallingUid());
+ synchronized (mVpns) {
+ return mVpns.get(user).provisionVpnProfile(packageName, profile, mKeyStore);
+ }
+ }
+
+ /**
+ * Deletes the stored VPN profile for the provisioning package
+ *
+ * <p>If there are no profiles for the given package, this method will silently succeed.
+ *
+ * <p>This is designed to serve the VpnManager only; settings-based VPN profiles are managed
+ * exclusively by the Settings app, and passed into the platform at startup time.
+ *
+ * @hide
+ */
+ @Override
+ public void deleteVpnProfile(@NonNull String packageName) {
+ final int user = UserHandle.getUserId(Binder.getCallingUid());
+ synchronized (mVpns) {
+ mVpns.get(user).deleteVpnProfile(packageName, mKeyStore);
+ }
+ }
+
+ /**
+ * Starts the VPN based on the stored profile for the given package
+ *
+ * <p>This is designed to serve the VpnManager only; settings-based VPN profiles are managed
+ * exclusively by the Settings app, and passed into the platform at startup time.
+ *
+ * @throws IllegalArgumentException if no profile was found for the given package name.
+ * @hide
+ */
+ @Override
+ public void startVpnProfile(@NonNull String packageName) {
+ final int user = UserHandle.getUserId(Binder.getCallingUid());
+ synchronized (mVpns) {
+ throwIfLockdownEnabled();
+ mVpns.get(user).startVpnProfile(packageName, mKeyStore);
+ }
+ }
+
+ /**
+ * Stops the Platform VPN if the provided package is running one.
+ *
+ * <p>This is designed to serve the VpnManager only; settings-based VPN profiles are managed
+ * exclusively by the Settings app, and passed into the platform at startup time.
+ *
+ * @hide
+ */
+ @Override
+ public void stopVpnProfile(@NonNull String packageName) {
+ final int user = UserHandle.getUserId(Binder.getCallingUid());
+ synchronized (mVpns) {
+ mVpns.get(user).stopVpnProfile(packageName);
+ }
+ }
+
+ /**
* Start legacy VPN, controlling native daemons as needed. Creates a
* secondary thread to perform connection work, returning quickly.
*/
@@ -4568,6 +4644,13 @@ public class ConnectivityService extends IConnectivityManager.Stub
}
}
+ /**
+ * Throws if there is any currently running, always-on Legacy VPN.
+ *
+ * <p>The LockdownVpnTracker and mLockdownEnabled both track whether an always-on Legacy VPN is
+ * running across the entire system. Tracking for app-based VPNs is done on a per-user,
+ * per-package basis in Vpn.java
+ */
@GuardedBy("mVpns")
private void throwIfLockdownEnabled() {
if (mLockdownEnabled) {
@@ -6109,12 +6192,16 @@ public class ConnectivityService extends IConnectivityManager.Stub
}
}
- private void sendUpdatedScoreToFactories(NetworkRequest networkRequest, NetworkAgentInfo nai) {
- int score = 0;
- int serial = 0;
+ private void sendUpdatedScoreToFactories(@NonNull NetworkRequest networkRequest,
+ @Nullable NetworkAgentInfo nai) {
+ final int score;
+ final int serial;
if (nai != null) {
score = nai.getCurrentScore();
serial = nai.factorySerialNumber;
+ } else {
+ score = 0;
+ serial = 0;
}
if (VDBG || DDBG){
log("sending new Min Network Score(" + score + "): " + networkRequest.toString());
@@ -6277,20 +6364,28 @@ public class ConnectivityService extends IConnectivityManager.Stub
}
}
- private void makeDefault(@NonNull final NetworkAgentInfo newNetwork) {
+ private void makeDefault(@Nullable final NetworkAgentInfo newNetwork) {
if (DBG) log("Switching to new default network: " + newNetwork);
+ mDefaultNetworkNai = newNetwork;
+
try {
- mNMS.setDefaultNetId(newNetwork.network.netId);
+ if (null != newNetwork) {
+ mNMS.setDefaultNetId(newNetwork.network.netId);
+ } else {
+ mNMS.clearDefaultNetId();
+ }
} catch (Exception e) {
loge("Exception setting default network :" + e);
}
- mDefaultNetworkNai = newNetwork;
notifyLockdownVpn(newNetwork);
- handleApplyDefaultProxy(newNetwork.linkProperties.getHttpProxy());
- updateTcpBufferSizes(newNetwork.linkProperties.getTcpBufferSizes());
- mDnsManager.setDefaultDnsSystemProperties(newNetwork.linkProperties.getDnsServers());
+ handleApplyDefaultProxy(null != newNetwork
+ ? newNetwork.linkProperties.getHttpProxy() : null);
+ updateTcpBufferSizes(null != newNetwork
+ ? newNetwork.linkProperties.getTcpBufferSizes() : null);
+ mDnsManager.setDefaultDnsSystemProperties(null != newNetwork
+ ? newNetwork.linkProperties.getDnsServers() : Collections.EMPTY_LIST);
notifyIfacesChangedForNetworkStats();
// Fix up the NetworkCapabilities of any VPNs that don't specify underlying networks.
updateAllVpnsCapabilities();
@@ -6369,6 +6464,16 @@ public class ConnectivityService extends IConnectivityManager.Stub
void addRematchedNetwork(@NonNull final NetworkBgStatePair network) {
mRematchedNetworks.add(network);
}
+
+ // Will return null if this reassignment does not change the network assigned to
+ // the passed request.
+ @Nullable
+ private RequestReassignment getReassignment(@NonNull final NetworkRequestInfo nri) {
+ for (final RequestReassignment event : getRequestReassignments()) {
+ if (nri == event.mRequest) return event;
+ }
+ return null;
+ }
}
private ArrayMap<NetworkRequestInfo, NetworkAgentInfo> computeRequestReassignmentForNetwork(
@@ -6435,8 +6540,6 @@ public class ConnectivityService extends IConnectivityManager.Stub
@NonNull final NetworkAgentInfo newNetwork, final long now) {
ensureRunningOnConnectivityServiceThread();
if (!newNetwork.everConnected) return;
- boolean isNewDefault = false;
- NetworkAgentInfo oldDefaultNetwork = null;
changes.addRematchedNetwork(new NetworkReassignment.NetworkBgStatePair(newNetwork,
newNetwork.isBackgroundNetwork()));
@@ -6453,6 +6556,8 @@ public class ConnectivityService extends IConnectivityManager.Stub
final NetworkRequestInfo nri = entry.getKey();
final NetworkAgentInfo previousSatisfier = nri.mSatisfier;
final NetworkAgentInfo newSatisfier = entry.getValue();
+ changes.addRequestReassignment(new NetworkReassignment.RequestReassignment(
+ nri, previousSatisfier, newSatisfier));
if (newSatisfier != null) {
if (VDBG) log("rematch for " + newSatisfier.name());
if (previousSatisfier != null) {
@@ -6465,25 +6570,9 @@ public class ConnectivityService extends IConnectivityManager.Stub
if (VDBG || DDBG) log(" accepting network in place of null");
}
newSatisfier.unlingerRequest(nri.request);
- nri.mSatisfier = newSatisfier;
if (!newSatisfier.addRequest(nri.request)) {
Slog.wtf(TAG, "BUG: " + newSatisfier.name() + " already has " + nri.request);
}
- changes.addRequestReassignment(new NetworkReassignment.RequestReassignment(
- nri, previousSatisfier, newSatisfier));
- // Tell NetworkProviders about the new score, so they can stop
- // trying to connect if they know they cannot match it.
- // TODO - this could get expensive if we have a lot of requests for this
- // network. Think about if there is a way to reduce this. Push
- // netid->request mapping to each provider?
- sendUpdatedScoreToFactories(nri.request, newSatisfier);
- if (isDefaultRequest(nri)) {
- isNewDefault = true;
- oldDefaultNetwork = previousSatisfier;
- if (previousSatisfier != null) {
- mLingerMonitor.noteLingerDefaultNetwork(previousSatisfier, newSatisfier);
- }
- }
} else {
// If "newNetwork" is listed as satisfying "nri" but no longer satisfies "nri",
// mark it as no longer satisfying "nri". Because networks are processed by
@@ -6497,35 +6586,8 @@ public class ConnectivityService extends IConnectivityManager.Stub
" request " + nri.request.requestId);
}
newNetwork.removeRequest(nri.request.requestId);
- if (previousSatisfier == newNetwork) {
- nri.mSatisfier = null;
- if (isDefaultRequest(nri)) mDefaultNetworkNai = null;
- sendUpdatedScoreToFactories(nri.request, null);
- } else {
- Slog.wtf(TAG, "BUG: Removing request " + nri.request.requestId + " from " +
- newNetwork.name() +
- " without updating mSatisfier or providers!");
- }
- // TODO: Technically, sending CALLBACK_LOST here is
- // incorrect if there is a replacement network currently
- // connected that can satisfy nri, which is a request
- // (not a listen). However, the only capability that can both
- // a) be requested and b) change is NET_CAPABILITY_TRUSTED,
- // so this code is only incorrect for a network that loses
- // the TRUSTED capability, which is a rare case.
- callCallbackForRequest(nri, newNetwork, ConnectivityManager.CALLBACK_LOST, 0);
}
- }
-
- if (isNewDefault) {
- updateDataActivityTracking(newNetwork, oldDefaultNetwork);
- // Notify system services that this network is up.
- makeDefault(newNetwork);
- // Log 0 -> X and Y -> X default network transitions, where X is the new default.
- mDeps.getMetricsLogger().defaultNetworkMetrics().logDefaultNetworkEvent(
- now, newNetwork, oldDefaultNetwork);
- // Have a new default network, release the transition wakelock in
- scheduleReleaseNetworkTransitionWakelock();
+ nri.mSatisfier = newSatisfier;
}
}
@@ -6553,14 +6615,48 @@ public class ConnectivityService extends IConnectivityManager.Stub
rematchNetworkAndRequests(changes, nai, now);
}
- final NetworkAgentInfo newDefaultNetwork = getDefaultNetwork();
+ final NetworkRequestInfo defaultRequestInfo = mNetworkRequests.get(mDefaultRequest);
+ final NetworkReassignment.RequestReassignment reassignment =
+ changes.getReassignment(defaultRequestInfo);
+ final NetworkAgentInfo newDefaultNetwork =
+ null != reassignment ? reassignment.mNewNetwork : oldDefaultNetwork;
+
+ if (oldDefaultNetwork != newDefaultNetwork) {
+ if (oldDefaultNetwork != null) {
+ mLingerMonitor.noteLingerDefaultNetwork(oldDefaultNetwork, newDefaultNetwork);
+ }
+ updateDataActivityTracking(newDefaultNetwork, oldDefaultNetwork);
+ // Notify system services of the new default.
+ makeDefault(newDefaultNetwork);
+ // Log 0 -> X and Y -> X default network transitions, where X is the new default.
+ mDeps.getMetricsLogger().defaultNetworkMetrics().logDefaultNetworkEvent(
+ now, newDefaultNetwork, oldDefaultNetwork);
+ // Have a new default network, release the transition wakelock in
+ scheduleReleaseNetworkTransitionWakelock();
+ }
// Notify requested networks are available after the default net is switched, but
// before LegacyTypeTracker sends legacy broadcasts
for (final NetworkReassignment.RequestReassignment event :
changes.getRequestReassignments()) {
+ // Tell NetworkProviders about the new score, so they can stop
+ // trying to connect if they know they cannot match it.
+ // TODO - this could get expensive if there are a lot of outstanding requests for this
+ // network. Think of a way to reduce this. Push netid->request mapping to each factory?
+ sendUpdatedScoreToFactories(event.mRequest.request, event.mNewNetwork);
+
if (null != event.mNewNetwork) {
notifyNetworkAvailable(event.mNewNetwork, event.mRequest);
+ } else {
+ // TODO: Technically, sending CALLBACK_LOST here is
+ // incorrect if there is a replacement network currently
+ // connected that can satisfy nri, which is a request
+ // (not a listen). However, the only capability that can both
+ // a) be requested and b) change is NET_CAPABILITY_TRUSTED,
+ // so this code is only incorrect for a network that loses
+ // the TRUSTED capability, which is a rare case.
+ callCallbackForRequest(event.mRequest, event.mOldNetwork,
+ ConnectivityManager.CALLBACK_LOST, 0);
}
}
@@ -7138,7 +7234,7 @@ public class ConnectivityService extends IConnectivityManager.Stub
final String alwaysOnPackage = getAlwaysOnVpnPackage(userId);
if (alwaysOnPackage != null) {
setAlwaysOnVpnPackage(userId, null, false, null);
- setVpnPackageAuthorization(alwaysOnPackage, userId, false);
+ setVpnPackageAuthorization(alwaysOnPackage, userId, VpnManager.TYPE_VPN_NONE);
}
// Turn Always-on VPN off
@@ -7161,7 +7257,8 @@ public class ConnectivityService extends IConnectivityManager.Stub
} else {
// Prevent this app (packagename = vpnConfig.user) from initiating
// VPN connections in the future without user intervention.
- setVpnPackageAuthorization(vpnConfig.user, userId, false);
+ setVpnPackageAuthorization(
+ vpnConfig.user, userId, VpnManager.TYPE_VPN_NONE);
prepareVpn(null, VpnConfig.LEGACY_VPN, userId);
}
diff --git a/services/core/java/com/android/server/RescueParty.java b/services/core/java/com/android/server/RescueParty.java
index 3e5c278a7fd4..80036bb4b4fa 100644
--- a/services/core/java/com/android/server/RescueParty.java
+++ b/services/core/java/com/android/server/RescueParty.java
@@ -42,11 +42,11 @@ import android.util.ExceptionUtils;
import android.util.Log;
import android.util.MathUtils;
import android.util.Slog;
-import android.util.StatsLog;
import com.android.internal.annotations.GuardedBy;
import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.util.ArrayUtils;
+import com.android.internal.util.FrameworkStatsLog;
import com.android.server.PackageWatchdog.FailureReasons;
import com.android.server.PackageWatchdog.PackageHealthObserver;
import com.android.server.PackageWatchdog.PackageHealthObserverImpact;
@@ -253,7 +253,7 @@ public class RescueParty {
private static void executeRescueLevelInternal(Context context, int level, @Nullable
String failedPackage) throws Exception {
- StatsLog.write(StatsLog.RESCUE_PARTY_RESET_REPORTED, level);
+ FrameworkStatsLog.write(FrameworkStatsLog.RESCUE_PARTY_RESET_REPORTED, level);
switch (level) {
case LEVEL_RESET_SETTINGS_UNTRUSTED_DEFAULTS:
resetAllSettings(context, Settings.RESET_MODE_UNTRUSTED_DEFAULTS, failedPackage);
diff --git a/services/core/java/com/android/server/StorageManagerService.java b/services/core/java/com/android/server/StorageManagerService.java
index e17dde9a766e..a08bdb23dd22 100644
--- a/services/core/java/com/android/server/StorageManagerService.java
+++ b/services/core/java/com/android/server/StorageManagerService.java
@@ -3942,8 +3942,12 @@ class StorageManagerService extends IStorageManager.Stub
final boolean hasMtp = mIPackageManager.checkUidPermission(ACCESS_MTP, uid) ==
PERMISSION_GRANTED;
if (mIsFuseEnabled && hasMtp) {
- // The process hosting the MTP server should be able to write in Android/
- return Zygote.MOUNT_EXTERNAL_ANDROID_WRITABLE;
+ ApplicationInfo ai = mIPackageManager.getApplicationInfo(packageName,
+ 0, UserHandle.getUserId(uid));
+ if (ai.isSignedWithPlatformKey()) {
+ // Platform processes hosting the MTP server should be able to write in Android/
+ return Zygote.MOUNT_EXTERNAL_ANDROID_WRITABLE;
+ }
}
// Determine if caller is holding runtime permission
diff --git a/services/core/java/com/android/server/SystemService.java b/services/core/java/com/android/server/SystemService.java
index b1584fea90c1..e3c732523264 100644
--- a/services/core/java/com/android/server/SystemService.java
+++ b/services/core/java/com/android/server/SystemService.java
@@ -23,7 +23,6 @@ import android.annotation.NonNull;
import android.annotation.Nullable;
import android.annotation.SystemApi;
import android.annotation.SystemApi.Client;
-import android.annotation.SystemApi.Process;
import android.annotation.UserIdInt;
import android.app.ActivityThread;
import android.content.Context;
@@ -65,7 +64,7 @@ import java.util.List;
*
* {@hide}
*/
-@SystemApi(client = Client.MODULE_LIBRARIES, process = Process.SYSTEM_SERVER)
+@SystemApi(client = Client.SYSTEM_SERVER)
public abstract class SystemService {
/** @hide */
@@ -132,7 +131,7 @@ public abstract class SystemService {
* Class representing user in question in the lifecycle callbacks.
* @hide
*/
- @SystemApi(client = Client.MODULE_LIBRARIES, process = Process.SYSTEM_SERVER)
+ @SystemApi(client = Client.SYSTEM_SERVER)
public static final class TargetUser {
@NonNull
private final UserInfo mUserInfo;
diff --git a/services/core/java/com/android/server/TelephonyRegistry.java b/services/core/java/com/android/server/TelephonyRegistry.java
index 139a87154223..cbf6c274e865 100644
--- a/services/core/java/com/android/server/TelephonyRegistry.java
+++ b/services/core/java/com/android/server/TelephonyRegistry.java
@@ -77,7 +77,6 @@ import android.telephony.data.ApnSetting;
import android.telephony.emergency.EmergencyNumber;
import android.telephony.ims.ImsReasonInfo;
import android.util.LocalLog;
-import android.util.StatsLog;
import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.app.IBatteryStats;
@@ -87,6 +86,7 @@ import com.android.internal.telephony.ITelephonyRegistry;
import com.android.internal.telephony.TelephonyPermissions;
import com.android.internal.util.ArrayUtils;
import com.android.internal.util.DumpUtils;
+import com.android.internal.util.FrameworkStatsLog;
import com.android.internal.util.IndentingPrintWriter;
import com.android.server.am.BatteryStatsService;
@@ -285,7 +285,8 @@ public class TelephonyRegistry extends ITelephonyRegistry.Stub {
PhoneStateListener.LISTEN_CALL_FORWARDING_INDICATOR
| PhoneStateListener.LISTEN_MESSAGE_WAITING_INDICATOR
| PhoneStateListener.LISTEN_EMERGENCY_NUMBER_LIST
- | PhoneStateListener.LISTEN_REGISTRATION_FAILURE;
+ | PhoneStateListener.LISTEN_REGISTRATION_FAILURE
+ | PhoneStateListener.LISTEN_BARRING_INFO;
static final int PRECISE_PHONE_STATE_PERMISSION_MASK =
PhoneStateListener.LISTEN_PRECISE_CALL_STATE
@@ -2395,12 +2396,12 @@ public class TelephonyRegistry extends ITelephonyRegistry.Stub {
try {
if (state == TelephonyManager.CALL_STATE_IDLE) {
mBatteryStats.notePhoneOff();
- StatsLog.write(StatsLog.PHONE_STATE_CHANGED,
- StatsLog.PHONE_STATE_CHANGED__STATE__OFF);
+ FrameworkStatsLog.write(FrameworkStatsLog.PHONE_STATE_CHANGED,
+ FrameworkStatsLog.PHONE_STATE_CHANGED__STATE__OFF);
} else {
mBatteryStats.notePhoneOn();
- StatsLog.write(StatsLog.PHONE_STATE_CHANGED,
- StatsLog.PHONE_STATE_CHANGED__STATE__ON);
+ FrameworkStatsLog.write(FrameworkStatsLog.PHONE_STATE_CHANGED,
+ FrameworkStatsLog.PHONE_STATE_CHANGED__STATE__ON);
}
} catch (RemoteException e) {
/* The remote entity disappeared, we can safely ignore the exception. */
diff --git a/services/core/java/com/android/server/VibratorService.java b/services/core/java/com/android/server/VibratorService.java
index 6bc090a4f7ba..f7d7d6c5d15f 100644
--- a/services/core/java/com/android/server/VibratorService.java
+++ b/services/core/java/com/android/server/VibratorService.java
@@ -65,12 +65,13 @@ import android.provider.Settings.SettingNotFoundException;
import android.util.DebugUtils;
import android.util.Slog;
import android.util.SparseArray;
-import android.util.StatsLog;
+import android.util.proto.ProtoOutputStream;
import android.view.InputDevice;
import com.android.internal.annotations.GuardedBy;
import com.android.internal.app.IBatteryStats;
import com.android.internal.util.DumpUtils;
+import com.android.internal.util.FrameworkStatsLog;
import java.io.FileDescriptor;
import java.io.PrintWriter;
@@ -165,6 +166,7 @@ public class VibratorService extends IVibratorService.Stub
private ExternalVibration mCurrentExternalVibration;
private boolean mVibratorUnderExternalControl;
private boolean mLowPowerMode;
+ private boolean mIsVibrating;
private int mHapticFeedbackIntensity;
private int mNotificationIntensity;
private int mRingIntensity;
@@ -327,6 +329,14 @@ public class VibratorService extends IVibratorService.Stub
.append(mReason)
.toString();
}
+
+ void dumpProto(ProtoOutputStream proto, long fieldId) {
+ synchronized (this) {
+ final long token = proto.start(fieldId);
+ proto.write(VibrationProto.START_TIME, mStartTimeDebug);
+ proto.end(token);
+ }
+ }
}
private static final class ScaleLevel {
@@ -1360,9 +1370,10 @@ public class VibratorService extends IVibratorService.Stub
private void noteVibratorOnLocked(int uid, long millis) {
try {
mBatteryStatsService.noteVibratorOn(uid, millis);
- StatsLog.write_non_chained(StatsLog.VIBRATOR_STATE_CHANGED, uid, null,
- StatsLog.VIBRATOR_STATE_CHANGED__STATE__ON, millis);
+ FrameworkStatsLog.write_non_chained(FrameworkStatsLog.VIBRATOR_STATE_CHANGED, uid, null,
+ FrameworkStatsLog.VIBRATOR_STATE_CHANGED__STATE__ON, millis);
mCurVibUid = uid;
+ mIsVibrating = true;
} catch (RemoteException e) {
}
}
@@ -1371,11 +1382,12 @@ public class VibratorService extends IVibratorService.Stub
if (mCurVibUid >= 0) {
try {
mBatteryStatsService.noteVibratorOff(mCurVibUid);
- StatsLog.write_non_chained(StatsLog.VIBRATOR_STATE_CHANGED, mCurVibUid, null,
- StatsLog.VIBRATOR_STATE_CHANGED__STATE__OFF, 0);
+ FrameworkStatsLog.write_non_chained(FrameworkStatsLog.VIBRATOR_STATE_CHANGED,
+ mCurVibUid, null, FrameworkStatsLog.VIBRATOR_STATE_CHANGED__STATE__OFF, 0);
} catch (RemoteException e) { }
mCurVibUid = -1;
}
+ mIsVibrating = false;
}
private void setVibratorUnderExternalControl(boolean externalControl) {
@@ -1390,6 +1402,93 @@ public class VibratorService extends IVibratorService.Stub
vibratorSetExternalControl(externalControl);
}
+ private void dumpInternal(PrintWriter pw) {
+ pw.println("Vibrator Service:");
+ synchronized (mLock) {
+ pw.print(" mCurrentVibration=");
+ if (mCurrentVibration != null) {
+ pw.println(mCurrentVibration.toInfo().toString());
+ } else {
+ pw.println("null");
+ }
+ pw.print(" mCurrentExternalVibration=" + mCurrentExternalVibration);
+ pw.println(" mVibratorUnderExternalControl=" + mVibratorUnderExternalControl);
+ pw.println(" mIsVibrating=" + mIsVibrating);
+ pw.println(" mLowPowerMode=" + mLowPowerMode);
+ pw.println(" mHapticFeedbackIntensity=" + mHapticFeedbackIntensity);
+ pw.println(" mNotificationIntensity=" + mNotificationIntensity);
+ pw.println(" mRingIntensity=" + mRingIntensity);
+ pw.println(" mSupportedEffects=" + mSupportedEffects);
+ pw.println();
+ pw.println(" Previous ring vibrations:");
+ for (VibrationInfo info : mPreviousRingVibrations) {
+ pw.print(" ");
+ pw.println(info.toString());
+ }
+
+ pw.println(" Previous notification vibrations:");
+ for (VibrationInfo info : mPreviousNotificationVibrations) {
+ pw.println(" " + info);
+ }
+
+ pw.println(" Previous alarm vibrations:");
+ for (VibrationInfo info : mPreviousAlarmVibrations) {
+ pw.println(" " + info);
+ }
+
+ pw.println(" Previous vibrations:");
+ for (VibrationInfo info : mPreviousVibrations) {
+ pw.println(" " + info);
+ }
+
+ pw.println(" Previous external vibrations:");
+ for (ExternalVibration vib : mPreviousExternalVibrations) {
+ pw.println(" " + vib);
+ }
+ }
+ }
+
+ private void dumpProto(FileDescriptor fd) {
+ final ProtoOutputStream proto = new ProtoOutputStream(fd);
+
+ synchronized (mLock) {
+ if (mCurrentVibration != null) {
+ mCurrentVibration.toInfo().dumpProto(proto,
+ VibratorServiceDumpProto.CURRENT_VIBRATION);
+ }
+ proto.write(VibratorServiceDumpProto.IS_VIBRATING, mIsVibrating);
+ proto.write(VibratorServiceDumpProto.VIBRATOR_UNDER_EXTERNAL_CONTROL,
+ mVibratorUnderExternalControl);
+ proto.write(VibratorServiceDumpProto.LOW_POWER_MODE, mLowPowerMode);
+ proto.write(VibratorServiceDumpProto.HAPTIC_FEEDBACK_INTENSITY,
+ mHapticFeedbackIntensity);
+ proto.write(VibratorServiceDumpProto.NOTIFICATION_INTENSITY,
+ mNotificationIntensity);
+ proto.write(VibratorServiceDumpProto.RING_INTENSITY, mRingIntensity);
+
+ for (VibrationInfo info : mPreviousRingVibrations) {
+ info.dumpProto(proto,
+ VibratorServiceDumpProto.PREVIOUS_RING_VIBRATIONS);
+ }
+
+ for (VibrationInfo info : mPreviousNotificationVibrations) {
+ info.dumpProto(proto,
+ VibratorServiceDumpProto.PREVIOUS_NOTIFICATION_VIBRATIONS);
+ }
+
+ for (VibrationInfo info : mPreviousAlarmVibrations) {
+ info.dumpProto(proto,
+ VibratorServiceDumpProto.PREVIOUS_ALARM_VIBRATIONS);
+ }
+
+ for (VibrationInfo info : mPreviousVibrations) {
+ info.dumpProto(proto,
+ VibratorServiceDumpProto.PREVIOUS_VIBRATIONS);
+ }
+ }
+ proto.flush();
+ }
+
private class VibrateThread extends Thread {
private final VibrationEffect.Waveform mWaveform;
private final int mUid;
@@ -1556,47 +1655,22 @@ public class VibratorService extends IVibratorService.Stub
protected void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
if (!DumpUtils.checkDumpPermission(mContext, TAG, pw)) return;
- pw.println("Vibrator Service:");
- synchronized (mLock) {
- pw.print(" mCurrentVibration=");
- if (mCurrentVibration != null) {
- pw.println(mCurrentVibration.toInfo().toString());
- } else {
- pw.println("null");
- }
- pw.print(" mCurrentExternalVibration=" + mCurrentExternalVibration);
- pw.println(" mVibratorUnderExternalControl=" + mVibratorUnderExternalControl);
- pw.println(" mLowPowerMode=" + mLowPowerMode);
- pw.println(" mHapticFeedbackIntensity=" + mHapticFeedbackIntensity);
- pw.println(" mNotificationIntensity=" + mNotificationIntensity);
- pw.println(" mRingIntensity=" + mRingIntensity);
- pw.println(" mSupportedEffects=" + mSupportedEffects);
- pw.println();
- pw.println(" Previous ring vibrations:");
- for (VibrationInfo info : mPreviousRingVibrations) {
- pw.print(" ");
- pw.println(info.toString());
- }
-
- pw.println(" Previous notification vibrations:");
- for (VibrationInfo info : mPreviousNotificationVibrations) {
- pw.println(" " + info);
- }
+ final long ident = Binder.clearCallingIdentity();
- pw.println(" Previous alarm vibrations:");
- for (VibrationInfo info : mPreviousAlarmVibrations) {
- pw.println(" " + info);
+ boolean isDumpProto = false;
+ for (String arg : args) {
+ if (arg.equals("--proto")) {
+ isDumpProto = true;
}
-
- pw.println(" Previous vibrations:");
- for (VibrationInfo info : mPreviousVibrations) {
- pw.println(" " + info);
- }
-
- pw.println(" Previous external vibrations:");
- for (ExternalVibration vib : mPreviousExternalVibrations) {
- pw.println(" " + vib);
+ }
+ try {
+ if (isDumpProto) {
+ dumpProto(fd);
+ } else {
+ dumpInternal(pw);
}
+ } finally {
+ Binder.restoreCallingIdentity(ident);
}
}
diff --git a/services/core/java/com/android/server/Watchdog.java b/services/core/java/com/android/server/Watchdog.java
index a60b09fda2e8..a1ccd8459c69 100644
--- a/services/core/java/com/android/server/Watchdog.java
+++ b/services/core/java/com/android/server/Watchdog.java
@@ -40,10 +40,10 @@ import android.util.EventLog;
import android.util.Log;
import android.util.Slog;
import android.util.SparseArray;
-import android.util.StatsLog;
import com.android.internal.os.ProcessCpuTracker;
import com.android.internal.os.ZygoteConnectionConstants;
+import com.android.internal.util.FrameworkStatsLog;
import com.android.server.am.ActivityManagerService;
import com.android.server.wm.SurfaceAnimationThread;
@@ -636,7 +636,8 @@ public class Watchdog extends Thread {
"watchdog", null, "system_server", null, null, null,
subject, cpuInfo, stack, null);
}
- StatsLog.write(StatsLog.SYSTEM_SERVER_WATCHDOG_OCCURRED, subject);
+ FrameworkStatsLog.write(FrameworkStatsLog.SYSTEM_SERVER_WATCHDOG_OCCURRED,
+ subject);
}
};
dropboxThread.start();
diff --git a/services/core/java/com/android/server/adb/AdbDebuggingManager.java b/services/core/java/com/android/server/adb/AdbDebuggingManager.java
index 143474bd5c94..10994133e265 100644
--- a/services/core/java/com/android/server/adb/AdbDebuggingManager.java
+++ b/services/core/java/com/android/server/adb/AdbDebuggingManager.java
@@ -46,12 +46,12 @@ import android.service.adb.AdbDebuggingManagerProto;
import android.util.AtomicFile;
import android.util.Base64;
import android.util.Slog;
-import android.util.StatsLog;
import android.util.Xml;
import com.android.internal.R;
import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.util.FastXmlSerializer;
+import com.android.internal.util.FrameworkStatsLog;
import com.android.internal.util.XmlUtils;
import com.android.internal.util.dump.DualDumpOutputStream;
import com.android.server.FgThread;
@@ -493,8 +493,8 @@ public class AdbDebuggingManager {
"Logging key " + key + ", state = " + state + ", alwaysAllow = " + alwaysAllow
+ ", lastConnectionTime = " + lastConnectionTime + ", authWindow = "
+ authWindow);
- StatsLog.write(StatsLog.ADB_CONNECTION_CHANGED, lastConnectionTime, authWindow, state,
- alwaysAllow);
+ FrameworkStatsLog.write(FrameworkStatsLog.ADB_CONNECTION_CHANGED, lastConnectionTime,
+ authWindow, state, alwaysAllow);
}
diff --git a/services/core/java/com/android/server/am/ActivityManagerService.java b/services/core/java/com/android/server/am/ActivityManagerService.java
index 6560777edd53..148f7de4449f 100644
--- a/services/core/java/com/android/server/am/ActivityManagerService.java
+++ b/services/core/java/com/android/server/am/ActivityManagerService.java
@@ -5143,14 +5143,12 @@ public class ActivityManagerService extends IActivityManager.Stub
checkTime(startTime, "attachApplicationLocked: after updateLruProcessLocked");
app.lastRequestedGc = app.lastLowMemory = SystemClock.uptimeMillis();
} catch (Exception e) {
- // todo: Yikes! What should we do? For now we will try to
- // start another process, but that could easily get us in
- // an infinite loop of restarting processes...
+ // We need kill the process group here. (b/148588589)
Slog.wtf(TAG, "Exception thrown during bind of " + app, e);
-
app.resetPackageList(mProcessStats);
app.unlinkDeathRecipient();
- mProcessList.startProcessLocked(app, new HostingRecord("bind-fail", processName));
+ app.kill("error during bind", ApplicationExitInfo.REASON_INITIALIZATION_FAILURE, true);
+ handleAppDiedLocked(app, false, true);
return false;
}
@@ -18924,19 +18922,16 @@ public class ActivityManagerService extends IActivityManager.Stub
}
// The arguments here are untyped because the base ActivityManagerInternal class
- // doesn't have compile-time visiblity into ActivityServiceConnectionHolder or
+ // doesn't have compile-time visibility into ActivityServiceConnectionHolder or
// ConnectionRecord.
@Override
- public void disconnectActivityFromServices(Object connectionHolder, Object conns) {
+ public void disconnectActivityFromServices(Object connectionHolder) {
// 'connectionHolder' is an untyped ActivityServiceConnectionsHolder
- // 'conns' is an untyped HashSet<ConnectionRecord>
final ActivityServiceConnectionsHolder holder =
(ActivityServiceConnectionsHolder) connectionHolder;
- final HashSet<ConnectionRecord> toDisconnect = (HashSet<ConnectionRecord>) conns;
- synchronized(ActivityManagerService.this) {
- for (ConnectionRecord cr : toDisconnect) {
- mServices.removeConnectionLocked(cr, null, holder);
- }
+ synchronized (ActivityManagerService.this) {
+ holder.forEachConnection(cr -> mServices.removeConnectionLocked(
+ (ConnectionRecord) cr, null /* skipApp */, holder /* skipAct */));
}
}
diff --git a/services/core/java/com/android/server/am/ProcessList.java b/services/core/java/com/android/server/am/ProcessList.java
index 3c7d6b80f557..71486d3686f3 100644
--- a/services/core/java/com/android/server/am/ProcessList.java
+++ b/services/core/java/com/android/server/am/ProcessList.java
@@ -1572,8 +1572,11 @@ public final class ProcessList {
if (userGid != UserHandle.ERR_GID) {
gidList.add(userGid);
}
- if (mountExternal == Zygote.MOUNT_EXTERNAL_ANDROID_WRITABLE) {
+ if (mountExternal == Zygote.MOUNT_EXTERNAL_ANDROID_WRITABLE
+ || mountExternal == Zygote.MOUNT_EXTERNAL_PASS_THROUGH) {
// For DownloadProviders and MTP: To grant access to /sdcard/Android/
+ // And a special case for the FUSE daemon since it runs an MTP server and should have
+ // access to Android/
gidList.add(UserHandle.getUid(UserHandle.getUserId(uid), Process.SDCARD_RW_GID));
}
if (mountExternal == Zygote.MOUNT_EXTERNAL_PASS_THROUGH) {
diff --git a/services/core/java/com/android/server/am/ProcessRecord.java b/services/core/java/com/android/server/am/ProcessRecord.java
index fc33c25c4eaf..2b4d15eda9f0 100644
--- a/services/core/java/com/android/server/am/ProcessRecord.java
+++ b/services/core/java/com/android/server/am/ProcessRecord.java
@@ -615,13 +615,20 @@ class ProcessRecord implements WindowProcessListener {
int _uid) {
mService = _service;
info = _info;
+ ProcessInfo procInfo = null;
if (_service.mPackageManagerInt != null) {
ArrayMap<String, ProcessInfo> processes =
_service.mPackageManagerInt.getProcessesForUid(_uid);
- processInfo = processes != null ? processes.get(_processName) : null;
- } else {
- processInfo = null;
+ if (processes != null) {
+ procInfo = processes.get(_processName);
+ if (procInfo != null && procInfo.deniedPermissions == null) {
+ // If this process hasn't asked for permissions to be denied, then
+ // we don't care about it.
+ procInfo = null;
+ }
+ }
}
+ processInfo = procInfo;
isolated = _info.uid != _uid;
appZygote = (UserHandle.getAppId(_uid) >= Process.FIRST_APP_ZYGOTE_ISOLATED_UID
&& UserHandle.getAppId(_uid) <= Process.LAST_APP_ZYGOTE_ISOLATED_UID);
diff --git a/services/core/java/com/android/server/am/TEST_MAPPING b/services/core/java/com/android/server/am/TEST_MAPPING
index bc4707f04724..4061df4f3f62 100644
--- a/services/core/java/com/android/server/am/TEST_MAPPING
+++ b/services/core/java/com/android/server/am/TEST_MAPPING
@@ -54,6 +54,14 @@
"include-filter": "com.android.server.am."
}
]
+ },
+ {
+ "name": "CtsAppSecurityHostTestCases",
+ "options": [
+ {
+ "include-filter": "android.appsecurity.cts.AppDataIsolationTests"
+ }
+ ]
}
]
}
diff --git a/services/core/java/com/android/server/am/UserController.java b/services/core/java/com/android/server/am/UserController.java
index c5d6bba27234..c75ee04543e3 100644
--- a/services/core/java/com/android/server/am/UserController.java
+++ b/services/core/java/com/android/server/am/UserController.java
@@ -130,6 +130,10 @@ class UserController implements Handler.Callback {
// giving up on them and unfreezing the screen.
static final int USER_SWITCH_TIMEOUT_MS = 3 * 1000;
+ // Amount of time we wait for observers to handle a user switch before we log a warning.
+ // Must be smaller than USER_SWITCH_TIMEOUT_MS.
+ private static final int USER_SWITCH_WARNING_TIMEOUT_MS = 500;
+
// ActivityManager thread message constants
static final int REPORT_USER_SWITCH_MSG = 10;
static final int CONTINUE_USER_SWITCH_MSG = 20;
@@ -1615,9 +1619,13 @@ class UserController implements Handler.Callback {
synchronized (mLock) {
long delay = SystemClock.elapsedRealtime() - dispatchStartedTime;
if (delay > USER_SWITCH_TIMEOUT_MS) {
- Slog.e(TAG, "User switch timeout: observer " + name
+ Slog.e(TAG, "User switch timeout: observer " + name
+ " sent result after " + delay + " ms");
+ } else if (delay > USER_SWITCH_WARNING_TIMEOUT_MS) {
+ Slog.w(TAG, "User switch slowed down by observer " + name
+ + ": result sent after " + delay + " ms");
}
+
curWaitingUserSwitchCallbacks.remove(name);
// Continue switching if all callbacks have been notified and
// user switching session is still valid
diff --git a/services/core/java/com/android/server/appop/AppOpsService.java b/services/core/java/com/android/server/appop/AppOpsService.java
index 531bc5d1c7df..784ce4a7d53b 100644
--- a/services/core/java/com/android/server/appop/AppOpsService.java
+++ b/services/core/java/com/android/server/appop/AppOpsService.java
@@ -88,6 +88,7 @@ import android.hardware.camera2.CameraDevice.CAMERA_AUDIO_RESTRICTION;
import android.net.Uri;
import android.os.AsyncTask;
import android.os.Binder;
+import android.os.Build;
import android.os.Bundle;
import android.os.Handler;
import android.os.IBinder;
@@ -846,7 +847,12 @@ public class AppOpsService extends IAppOpsService.Stub {
*/
public void started(@NonNull IBinder clientId, @AppOpsManager.UidState int uidState)
throws RemoteException {
- if (!parent.isRunning()) {
+ started(clientId, uidState, true);
+ }
+
+ private void started(@NonNull IBinder clientId, @AppOpsManager.UidState int uidState,
+ boolean triggerCallbackIfNeeded) throws RemoteException {
+ if (triggerCallbackIfNeeded && !parent.isRunning()) {
scheduleOpActiveChangedIfNeededLocked(parent.op, parent.uid,
parent.packageName, true);
}
@@ -965,8 +971,16 @@ public class AppOpsService extends IAppOpsService.Stub {
if (event.getUidState() != newState) {
try {
+ // Remove all but one unfinished start count and then call finished() to
+ // remove start event object
+ int numPreviousUnfinishedStarts = event.numUnfinishedStarts;
+ event.numUnfinishedStarts = 1;
finished(event.getClientId(), false);
- started(event.getClientId(), newState);
+
+ // Call started() to add a new start event object and then add the
+ // previously removed unfinished start counts back
+ started(event.getClientId(), newState, false);
+ event.numUnfinishedStarts += numPreviousUnfinishedStarts - 1;
} catch (RemoteException e) {
if (DEBUG) Slog.e(TAG, "Cannot switch to new uidState " + newState);
}
@@ -1948,6 +1962,11 @@ public class AppOpsService extends IAppOpsService.Stub {
@Override
public void setUidMode(int code, int uid, int mode) {
+ setUidMode(code, uid, mode, null);
+ }
+
+ private void setUidMode(int code, int uid, int mode,
+ @Nullable IAppOpsCallback callbackToIgnore) {
if (DEBUG) {
Slog.i(TAG, "uid " + uid + " OP_" + opToName(code) + " := " + modeToName(mode)
+ " by uid " + Binder.getCallingUid());
@@ -2031,6 +2050,10 @@ public class AppOpsService extends IAppOpsService.Stub {
}
}
}
+
+ if (callbackSpecs != null && callbackToIgnore != null) {
+ callbackSpecs.remove(mModeWatchers.get(callbackToIgnore.asBinder()));
+ }
}
if (callbackSpecs == null) {
@@ -2078,6 +2101,11 @@ public class AppOpsService extends IAppOpsService.Stub {
continue;
}
+ if (packageManager.checkPermission(permissionName, packageName)
+ != PackageManager.PERMISSION_GRANTED) {
+ continue;
+ }
+
PermissionInfo permissionInfo;
try {
permissionInfo = packageManager.getPermissionInfo(permissionName, 0);
@@ -2090,10 +2118,24 @@ public class AppOpsService extends IAppOpsService.Stub {
continue;
}
+ PackageManagerInternal packageManagerInternal = LocalServices.getService(
+ PackageManagerInternal.class);
+ boolean supportsRuntimePermissions = packageManagerInternal.getUidTargetSdkVersion(uid)
+ >= Build.VERSION_CODES.M;
+
UserHandle user = UserHandle.getUserHandleForUid(uid);
boolean isRevokedCompat;
if (permissionInfo.backgroundPermission != null) {
boolean isBackgroundRevokedCompat = mode != AppOpsManager.MODE_ALLOWED;
+
+ if (isBackgroundRevokedCompat && supportsRuntimePermissions) {
+ Slog.w(TAG, "setUidMode() called with a mode inconsistent with runtime"
+ + " permission state, this is discouraged and you should revoke the"
+ + " runtime permission instead: uid=" + uid + ", switchCode="
+ + switchCode + ", mode=" + mode + ", permission="
+ + permissionInfo.backgroundPermission);
+ }
+
long identity = Binder.clearCallingIdentity();
try {
packageManager.updatePermissionFlags(permissionInfo.backgroundPermission,
@@ -2110,6 +2152,13 @@ public class AppOpsService extends IAppOpsService.Stub {
isRevokedCompat = mode != AppOpsManager.MODE_ALLOWED;
}
+ if (isRevokedCompat && supportsRuntimePermissions) {
+ Slog.w(TAG, "setUidMode() called with a mode inconsistent with runtime"
+ + " permission state, this is discouraged and you should revoke the"
+ + " runtime permission instead: uid=" + uid + ", switchCode="
+ + switchCode + ", mode=" + mode + ", permission=" + permissionName);
+ }
+
long identity = Binder.clearCallingIdentity();
try {
packageManager.updatePermissionFlags(permissionName, packageName,
@@ -2158,6 +2207,11 @@ public class AppOpsService extends IAppOpsService.Stub {
*/
@Override
public void setMode(int code, int uid, @NonNull String packageName, int mode) {
+ setMode(code, uid, packageName, mode, null);
+ }
+
+ private void setMode(int code, int uid, @NonNull String packageName, int mode,
+ @Nullable IAppOpsCallback callbackToIgnore) {
enforceManageAppOpsModes(Binder.getCallingPid(), Binder.getCallingUid(), uid);
verifyIncomingOp(code);
ArraySet<ModeCallback> repCbs = null;
@@ -2201,6 +2255,9 @@ public class AppOpsService extends IAppOpsService.Stub {
}
repCbs.addAll(cbs);
}
+ if (repCbs != null && callbackToIgnore != null) {
+ repCbs.remove(mModeWatchers.get(callbackToIgnore.asBinder()));
+ }
if (mode == AppOpsManager.opToDefaultMode(op.op)) {
// If going into the default mode, prune this op
// if there is nothing else interesting in it.
@@ -5587,5 +5644,17 @@ public class AppOpsService extends IAppOpsService.Stub {
boolean visible) {
AppOpsService.this.updateAppWidgetVisibility(uidPackageNames, visible);
}
+
+ @Override
+ public void setUidModeIgnoringCallback(int code, int uid, int mode,
+ @Nullable IAppOpsCallback callbackToIgnore) {
+ setUidMode(code, uid, mode, callbackToIgnore);
+ }
+
+ @Override
+ public void setModeIgnoringCallback(int code, int uid, @NonNull String packageName,
+ int mode, @Nullable IAppOpsCallback callbackToIgnore) {
+ setMode(code, uid, packageName, mode, callbackToIgnore);
+ }
}
}
diff --git a/services/core/java/com/android/server/attention/AttentionManagerService.java b/services/core/java/com/android/server/attention/AttentionManagerService.java
index 5ac5b29b1475..0440de674e9e 100644
--- a/services/core/java/com/android/server/attention/AttentionManagerService.java
+++ b/services/core/java/com/android/server/attention/AttentionManagerService.java
@@ -56,13 +56,12 @@ import android.service.attention.IAttentionService;
import android.text.TextUtils;
import android.util.Slog;
import android.util.SparseArray;
-import android.util.StatsLog;
import com.android.internal.annotations.GuardedBy;
import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.util.DumpUtils;
+import com.android.internal.util.FrameworkStatsLog;
import com.android.internal.util.IndentingPrintWriter;
-import com.android.internal.util.Preconditions;
import com.android.server.SystemService;
import java.io.FileDescriptor;
@@ -274,8 +273,8 @@ public class AttentionManagerService extends SystemService {
userState.mAttentionCheckCacheBuffer.add(
new AttentionCheckCache(SystemClock.uptimeMillis(), result, timestamp));
}
- StatsLog.write(
- StatsLog.ATTENTION_MANAGER_SERVICE_RESULT_REPORTED,
+ FrameworkStatsLog.write(
+ FrameworkStatsLog.ATTENTION_MANAGER_SERVICE_RESULT_REPORTED,
result);
}
@@ -287,8 +286,8 @@ public class AttentionManagerService extends SystemService {
userState.mCurrentAttentionCheck.mIsFulfilled = true;
}
- StatsLog.write(
- StatsLog.ATTENTION_MANAGER_SERVICE_RESULT_REPORTED,
+ FrameworkStatsLog.write(
+ FrameworkStatsLog.ATTENTION_MANAGER_SERVICE_RESULT_REPORTED,
error);
}
};
diff --git a/services/core/java/com/android/server/camera/CameraServiceProxy.java b/services/core/java/com/android/server/camera/CameraServiceProxy.java
index 96af74a60b1d..61c99b88d113 100644
--- a/services/core/java/com/android/server/camera/CameraServiceProxy.java
+++ b/services/core/java/com/android/server/camera/CameraServiceProxy.java
@@ -36,23 +36,22 @@ import android.os.UserManager;
import android.util.ArrayMap;
import android.util.ArraySet;
import android.util.Slog;
-import android.util.StatsLog;
import com.android.internal.logging.MetricsLogger;
import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
+import com.android.internal.util.FrameworkStatsLog;
import com.android.server.LocalServices;
import com.android.server.ServiceThread;
import com.android.server.SystemService;
import com.android.server.wm.WindowManagerInternal;
-import java.util.concurrent.Executors;
-import java.util.concurrent.ScheduledThreadPoolExecutor;
-import java.util.concurrent.TimeUnit;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.List;
import java.util.Set;
+import java.util.concurrent.ScheduledThreadPoolExecutor;
+import java.util.concurrent.TimeUnit;
/**
* CameraServiceProxy is the system_server analog to the camera service running in cameraserver.
@@ -315,22 +314,22 @@ public class CameraServiceProxy extends SystemService
* Package-private
*/
private void logCameraUsageEvent(CameraUsageEvent e) {
- int facing = StatsLog.CAMERA_ACTION_EVENT__FACING__UNKNOWN;
+ int facing = FrameworkStatsLog.CAMERA_ACTION_EVENT__FACING__UNKNOWN;
switch(e.mCameraFacing) {
case ICameraServiceProxy.CAMERA_FACING_BACK:
- facing = StatsLog.CAMERA_ACTION_EVENT__FACING__BACK;
+ facing = FrameworkStatsLog.CAMERA_ACTION_EVENT__FACING__BACK;
break;
case ICameraServiceProxy.CAMERA_FACING_FRONT:
- facing = StatsLog.CAMERA_ACTION_EVENT__FACING__FRONT;
+ facing = FrameworkStatsLog.CAMERA_ACTION_EVENT__FACING__FRONT;
break;
case ICameraServiceProxy.CAMERA_FACING_EXTERNAL:
- facing = StatsLog.CAMERA_ACTION_EVENT__FACING__EXTERNAL;
+ facing = FrameworkStatsLog.CAMERA_ACTION_EVENT__FACING__EXTERNAL;
break;
default:
Slog.w(TAG, "Unknown camera facing: " + e.mCameraFacing);
}
- StatsLog.write(StatsLog.CAMERA_ACTION_EVENT, e.getDuration(), e.mAPILevel,
- e.mClientName, facing);
+ FrameworkStatsLog.write(FrameworkStatsLog.CAMERA_ACTION_EVENT, e.getDuration(),
+ e.mAPILevel, e.mClientName, facing);
}
}
diff --git a/services/core/java/com/android/server/connectivity/LingerMonitor.java b/services/core/java/com/android/server/connectivity/LingerMonitor.java
index 929dfc4d1511..707151059869 100644
--- a/services/core/java/com/android/server/connectivity/LingerMonitor.java
+++ b/services/core/java/com/android/server/connectivity/LingerMonitor.java
@@ -16,6 +16,10 @@
package com.android.server.connectivity;
+import static android.net.ConnectivityManager.NETID_UNSET;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
import android.app.PendingIntent;
import android.content.ComponentName;
import android.content.Context;
@@ -27,18 +31,16 @@ import android.text.TextUtils;
import android.text.format.DateUtils;
import android.util.Log;
import android.util.SparseArray;
-import android.util.SparseIntArray;
import android.util.SparseBooleanArray;
-import java.util.Arrays;
-import java.util.HashMap;
+import android.util.SparseIntArray;
import com.android.internal.R;
import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.util.MessageUtils;
-import com.android.server.connectivity.NetworkNotificationManager;
import com.android.server.connectivity.NetworkNotificationManager.NotificationType;
-import static android.net.ConnectivityManager.NETID_UNSET;
+import java.util.Arrays;
+import java.util.HashMap;
/**
* Class that monitors default network linger events and possibly notifies the user of network
@@ -206,8 +208,19 @@ public class LingerMonitor {
mEverNotified.put(fromNai.network.netId, true);
}
+ /**
+ * Put up or dismiss a notification or toast for of a change in the default network if needed.
+ *
+ * Putting up a notification when switching from no network to some network is not supported
+ * and as such this method can't be called with a null |fromNai|. It can be called with a
+ * null |toNai| if there isn't a default network any more.
+ *
+ * @param fromNai switching from this NAI
+ * @param toNai switching to this NAI
+ */
// The default network changed from fromNai to toNai due to a change in score.
- public void noteLingerDefaultNetwork(NetworkAgentInfo fromNai, NetworkAgentInfo toNai) {
+ public void noteLingerDefaultNetwork(@NonNull final NetworkAgentInfo fromNai,
+ @Nullable final NetworkAgentInfo toNai) {
if (VDBG) {
Log.d(TAG, "noteLingerDefaultNetwork from=" + fromNai.name() +
" everValidated=" + fromNai.everValidated +
@@ -221,6 +234,10 @@ public class LingerMonitor {
// Internet access).
maybeStopNotifying(fromNai);
+ // If the network was simply lost (either because it disconnected or because it stopped
+ // being the default with no replacement), then don't show a notification.
+ if (null == toNai) return;
+
// If this network never validated, don't notify. Otherwise, we could do things like:
//
// 1. Unvalidated wifi connects.
diff --git a/services/core/java/com/android/server/connectivity/Vpn.java b/services/core/java/com/android/server/connectivity/Vpn.java
index 423e0212dfd6..89af5b58ff72 100644
--- a/services/core/java/com/android/server/connectivity/Vpn.java
+++ b/services/core/java/com/android/server/connectivity/Vpn.java
@@ -24,6 +24,8 @@ import static android.net.NetworkCapabilities.NET_CAPABILITY_NOT_ROAMING;
import static android.net.RouteInfo.RTN_THROW;
import static android.net.RouteInfo.RTN_UNREACHABLE;
+import static com.android.internal.util.Preconditions.checkNotNull;
+
import android.Manifest;
import android.annotation.NonNull;
import android.annotation.Nullable;
@@ -60,6 +62,7 @@ import android.net.NetworkInfo.DetailedState;
import android.net.NetworkProvider;
import android.net.RouteInfo;
import android.net.UidRange;
+import android.net.VpnManager;
import android.net.VpnService;
import android.os.Binder;
import android.os.Build.VERSION_CODES;
@@ -157,6 +160,16 @@ public class Vpn {
// is actually O(n²)+O(n²).
private static final int MAX_ROUTES_TO_EVALUATE = 150;
+ /**
+ * Largest profile size allowable for Platform VPNs.
+ *
+ * <p>The largest platform VPN profiles use IKEv2 RSA Certificate Authentication and have two
+ * X509Certificates, and one RSAPrivateKey. This should lead to a max size of 2x 12kB for the
+ * certificates, plus a reasonable upper bound on the private key of 32kB. The rest of the
+ * profile is expected to be negligible in size.
+ */
+ @VisibleForTesting static final int MAX_VPN_PROFILE_SIZE_BYTES = 1 << 17; // 128kB
+
// TODO: create separate trackers for each unique VPN to support
// automated reconnection
@@ -507,8 +520,11 @@ public class Vpn {
}
if (packageName != null) {
- // Pre-authorize new always-on VPN package.
- if (!setPackageAuthorization(packageName, true)) {
+ // TODO: Give the minimum permission possible; if there is a Platform VPN profile, only
+ // grant ACTIVATE_PLATFORM_VPN.
+ // Pre-authorize new always-on VPN package. Grant the full ACTIVATE_VPN appop, allowing
+ // both VpnService and Platform VPNs.
+ if (!setPackageAuthorization(packageName, VpnManager.TYPE_VPN_SERVICE)) {
return false;
}
mAlwaysOn = true;
@@ -656,6 +672,11 @@ public class Vpn {
* It uses {@link VpnConfig#LEGACY_VPN} as its package name, and
* it can be revoked by itself.
*
+ * The permission checks to verify that the VPN has already been granted
+ * user consent are dependent on the type of the VPN being prepared. See
+ * {@link AppOpsManager#OP_ACTIVATE_VPN} and {@link
+ * AppOpsManager#OP_ACTIVATE_PLATFORM_VPN} for more information.
+ *
* Note: when we added VPN pre-consent in
* https://android.googlesource.com/platform/frameworks/base/+/0554260
* the names oldPackage and newPackage became misleading, because when
@@ -674,10 +695,12 @@ public class Vpn {
*
* @param oldPackage The package name of the old VPN application
* @param newPackage The package name of the new VPN application
- *
+ * @param vpnType The type of VPN being prepared. One of {@link VpnManager.VpnType} Preparing a
+ * platform VPN profile requires only the lesser ACTIVATE_PLATFORM_VPN appop.
* @return true if the operation succeeded.
*/
- public synchronized boolean prepare(String oldPackage, String newPackage) {
+ public synchronized boolean prepare(
+ String oldPackage, String newPackage, @VpnManager.VpnType int vpnType) {
if (oldPackage != null) {
// Stop an existing always-on VPN from being dethroned by other apps.
if (mAlwaysOn && !isCurrentPreparedPackage(oldPackage)) {
@@ -688,13 +711,14 @@ public class Vpn {
if (!isCurrentPreparedPackage(oldPackage)) {
// The package doesn't match. We return false (to obtain user consent) unless the
// user has already consented to that VPN package.
- if (!oldPackage.equals(VpnConfig.LEGACY_VPN) && isVpnUserPreConsented(oldPackage)) {
+ if (!oldPackage.equals(VpnConfig.LEGACY_VPN)
+ && isVpnPreConsented(mContext, oldPackage, vpnType)) {
prepareInternal(oldPackage);
return true;
}
return false;
} else if (!oldPackage.equals(VpnConfig.LEGACY_VPN)
- && !isVpnUserPreConsented(oldPackage)) {
+ && !isVpnPreConsented(mContext, oldPackage, vpnType)) {
// Currently prepared VPN is revoked, so unprepare it and return false.
prepareInternal(VpnConfig.LEGACY_VPN);
return false;
@@ -777,25 +801,49 @@ public class Vpn {
}
}
- /**
- * Set whether a package has the ability to launch VPNs without user intervention.
- */
- public boolean setPackageAuthorization(String packageName, boolean authorized) {
+ /** Set whether a package has the ability to launch VPNs without user intervention. */
+ public boolean setPackageAuthorization(String packageName, @VpnManager.VpnType int vpnType) {
// Check if the caller is authorized.
enforceControlPermissionOrInternalCaller();
- int uid = getAppUid(packageName, mUserHandle);
+ final int uid = getAppUid(packageName, mUserHandle);
if (uid == -1 || VpnConfig.LEGACY_VPN.equals(packageName)) {
// Authorization for nonexistent packages (or fake ones) can't be updated.
return false;
}
- long token = Binder.clearCallingIdentity();
+ final long token = Binder.clearCallingIdentity();
try {
- AppOpsManager appOps =
+ final int[] toChange;
+
+ // Clear all AppOps if the app is being unauthorized.
+ switch (vpnType) {
+ case VpnManager.TYPE_VPN_NONE:
+ toChange = new int[] {
+ AppOpsManager.OP_ACTIVATE_VPN, AppOpsManager.OP_ACTIVATE_PLATFORM_VPN
+ };
+ break;
+ case VpnManager.TYPE_VPN_PLATFORM:
+ toChange = new int[] {AppOpsManager.OP_ACTIVATE_PLATFORM_VPN};
+ break;
+ case VpnManager.TYPE_VPN_SERVICE:
+ toChange = new int[] {AppOpsManager.OP_ACTIVATE_VPN};
+ break;
+ default:
+ Log.wtf(TAG, "Unrecognized VPN type while granting authorization");
+ return false;
+ }
+
+ final AppOpsManager appOpMgr =
(AppOpsManager) mContext.getSystemService(Context.APP_OPS_SERVICE);
- appOps.setMode(AppOpsManager.OP_ACTIVATE_VPN, uid, packageName,
- authorized ? AppOpsManager.MODE_ALLOWED : AppOpsManager.MODE_IGNORED);
+ for (final int appOp : toChange) {
+ appOpMgr.setMode(
+ appOp,
+ uid,
+ packageName,
+ vpnType == VpnManager.TYPE_VPN_NONE
+ ? AppOpsManager.MODE_IGNORED : AppOpsManager.MODE_ALLOWED);
+ }
return true;
} catch (Exception e) {
Log.wtf(TAG, "Failed to set app ops for package " + packageName + ", uid " + uid, e);
@@ -805,13 +853,33 @@ public class Vpn {
return false;
}
- private boolean isVpnUserPreConsented(String packageName) {
- AppOpsManager appOps =
- (AppOpsManager) mContext.getSystemService(Context.APP_OPS_SERVICE);
+ private static boolean isVpnPreConsented(Context context, String packageName, int vpnType) {
+ switch (vpnType) {
+ case VpnManager.TYPE_VPN_SERVICE:
+ return isVpnServicePreConsented(context, packageName);
+ case VpnManager.TYPE_VPN_PLATFORM:
+ return isVpnProfilePreConsented(context, packageName);
+ default:
+ return false;
+ }
+ }
+
+ private static boolean doesPackageHaveAppop(Context context, String packageName, int appop) {
+ final AppOpsManager appOps =
+ (AppOpsManager) context.getSystemService(Context.APP_OPS_SERVICE);
+
+ // Verify that the caller matches the given package and has the required permission.
+ return appOps.noteOpNoThrow(appop, Binder.getCallingUid(), packageName)
+ == AppOpsManager.MODE_ALLOWED;
+ }
+
+ private static boolean isVpnServicePreConsented(Context context, String packageName) {
+ return doesPackageHaveAppop(context, packageName, AppOpsManager.OP_ACTIVATE_VPN);
+ }
- // Verify that the caller matches the given package and has permission to activate VPNs.
- return appOps.noteOpNoThrow(AppOpsManager.OP_ACTIVATE_VPN, Binder.getCallingUid(),
- packageName) == AppOpsManager.MODE_ALLOWED;
+ private static boolean isVpnProfilePreConsented(Context context, String packageName) {
+ return doesPackageHaveAppop(context, packageName, AppOpsManager.OP_ACTIVATE_PLATFORM_VPN)
+ || isVpnServicePreConsented(context, packageName);
}
private int getAppUid(String app, int userHandle) {
@@ -1005,6 +1073,9 @@ public class Vpn {
* Establish a VPN network and return the file descriptor of the VPN interface. This methods
* returns {@code null} if the application is revoked or not prepared.
*
+ * <p>This method supports ONLY VpnService-based VPNs. For Platform VPNs, see {@link
+ * provisionVpnProfile} and {@link startVpnProfile}
+ *
* @param config The parameters to configure the network.
* @return The file descriptor of the VPN interface.
*/
@@ -1015,7 +1086,7 @@ public class Vpn {
return null;
}
// Check to ensure consent hasn't been revoked since we were prepared.
- if (!isVpnUserPreConsented(mPackage)) {
+ if (!isVpnServicePreConsented(mContext, mPackage)) {
return null;
}
// Check if the service is properly declared.
@@ -1680,6 +1751,10 @@ public class Vpn {
public int settingsSecureGetIntForUser(String key, int def, int userId) {
return Settings.Secure.getIntForUser(mContext.getContentResolver(), key, def, userId);
}
+
+ public boolean isCallerSystem() {
+ return Binder.getCallingUid() == Process.SYSTEM_UID;
+ }
}
private native int jniCreate(int mtu);
@@ -2228,4 +2303,148 @@ public class Vpn {
}
}
}
+
+ private void verifyCallingUidAndPackage(String packageName) {
+ if (getAppUid(packageName, mUserHandle) != Binder.getCallingUid()) {
+ throw new SecurityException("Mismatched package and UID");
+ }
+ }
+
+ @VisibleForTesting
+ String getProfileNameForPackage(String packageName) {
+ return Credentials.PLATFORM_VPN + mUserHandle + "_" + packageName;
+ }
+
+ /**
+ * Stores an app-provisioned VPN profile and returns whether the app is already prepared.
+ *
+ * @param packageName the package name of the app provisioning this profile
+ * @param profile the profile to be stored and provisioned
+ * @param keyStore the System keystore instance to save VPN profiles
+ * @returns whether or not the app has already been granted user consent
+ */
+ public synchronized boolean provisionVpnProfile(
+ @NonNull String packageName, @NonNull VpnProfile profile, @NonNull KeyStore keyStore) {
+ checkNotNull(packageName, "No package name provided");
+ checkNotNull(profile, "No profile provided");
+ checkNotNull(keyStore, "KeyStore missing");
+
+ verifyCallingUidAndPackage(packageName);
+
+ final byte[] encodedProfile = profile.encode();
+ if (encodedProfile.length > MAX_VPN_PROFILE_SIZE_BYTES) {
+ throw new IllegalArgumentException("Profile too big");
+ }
+
+ // Permissions checked during startVpnProfile()
+ Binder.withCleanCallingIdentity(
+ () -> {
+ keyStore.put(
+ getProfileNameForPackage(packageName),
+ encodedProfile,
+ Process.SYSTEM_UID,
+ 0 /* flags */);
+ });
+
+ // TODO: if package has CONTROL_VPN, grant the ACTIVATE_PLATFORM_VPN appop.
+ // This mirrors the prepareAndAuthorize that is used by VpnService.
+
+ // Return whether the app is already pre-consented
+ return isVpnProfilePreConsented(mContext, packageName);
+ }
+
+ /**
+ * Deletes an app-provisioned VPN profile.
+ *
+ * @param packageName the package name of the app provisioning this profile
+ * @param keyStore the System keystore instance to save VPN profiles
+ */
+ public synchronized void deleteVpnProfile(
+ @NonNull String packageName, @NonNull KeyStore keyStore) {
+ checkNotNull(packageName, "No package name provided");
+ checkNotNull(keyStore, "KeyStore missing");
+
+ verifyCallingUidAndPackage(packageName);
+
+ Binder.withCleanCallingIdentity(
+ () -> {
+ keyStore.delete(getProfileNameForPackage(packageName), Process.SYSTEM_UID);
+ });
+ }
+
+ /**
+ * Retrieves the VpnProfile.
+ *
+ * <p>Must be used only as SYSTEM_UID, otherwise the key/UID pair will not match anything in the
+ * keystore.
+ */
+ @VisibleForTesting
+ @Nullable
+ VpnProfile getVpnProfilePrivileged(@NonNull String packageName, @NonNull KeyStore keyStore) {
+ if (!mSystemServices.isCallerSystem()) {
+ Log.wtf(TAG, "getVpnProfilePrivileged called as non-System UID ");
+ return null;
+ }
+
+ final byte[] encoded = keyStore.get(getProfileNameForPackage(packageName));
+ if (encoded == null) return null;
+
+ return VpnProfile.decode("" /* Key unused */, encoded);
+ }
+
+ /**
+ * Starts an already provisioned VPN Profile, keyed by package name.
+ *
+ * <p>This method is meant to be called by apps (via VpnManager and ConnectivityService).
+ * Privileged (system) callers should use startVpnProfilePrivileged instead. Otherwise the UIDs
+ * will not match during appop checks.
+ *
+ * @param packageName the package name of the app provisioning this profile
+ * @param keyStore the System keystore instance to retrieve VPN profiles
+ */
+ public synchronized void startVpnProfile(
+ @NonNull String packageName, @NonNull KeyStore keyStore) {
+ checkNotNull(packageName, "No package name provided");
+ checkNotNull(keyStore, "KeyStore missing");
+
+ // Prepare VPN for startup
+ if (!prepare(packageName, null /* newPackage */, VpnManager.TYPE_VPN_PLATFORM)) {
+ throw new SecurityException("User consent not granted for package " + packageName);
+ }
+
+ Binder.withCleanCallingIdentity(
+ () -> {
+ final VpnProfile profile = getVpnProfilePrivileged(packageName, keyStore);
+ if (profile == null) {
+ throw new IllegalArgumentException("No profile found for " + packageName);
+ }
+
+ startVpnProfilePrivileged(profile);
+ });
+ }
+
+ private void startVpnProfilePrivileged(@NonNull VpnProfile profile) {
+ // TODO: Start PlatformVpnRunner
+ }
+
+ /**
+ * Stops an already running VPN Profile for the given package.
+ *
+ * <p>This method is meant to be called by apps (via VpnManager and ConnectivityService).
+ * Privileged (system) callers should (re-)prepare the LEGACY_VPN instead.
+ *
+ * @param packageName the package name of the app provisioning this profile
+ */
+ public synchronized void stopVpnProfile(@NonNull String packageName) {
+ checkNotNull(packageName, "No package name provided");
+
+ // To stop the VPN profile, the caller must be the current prepared package. Otherwise,
+ // the app is not prepared, and we can just return.
+ if (!isCurrentPreparedPackage(packageName)) {
+ // TODO: Also check to make sure that the running VPN is a VPN profile.
+ return;
+ }
+
+ prepareInternal(VpnConfig.LEGACY_VPN);
+ }
}
diff --git a/services/core/java/com/android/server/content/SyncManager.java b/services/core/java/com/android/server/content/SyncManager.java
index 4ddc391bd889..6d130d91f639 100644
--- a/services/core/java/com/android/server/content/SyncManager.java
+++ b/services/core/java/com/android/server/content/SyncManager.java
@@ -212,8 +212,11 @@ public class SyncManager {
private static final int SYNC_OP_STATE_VALID = 0;
- private static final int SYNC_OP_STATE_INVALID = 1;
+ // "1" used to include errors 3, 4 and 5 but now it's split up.
private static final int SYNC_OP_STATE_INVALID_NO_ACCOUNT_ACCESS = 2;
+ private static final int SYNC_OP_STATE_INVALID_NO_ACCOUNT = 3;
+ private static final int SYNC_OP_STATE_INVALID_NOT_SYNCABLE = 4;
+ private static final int SYNC_OP_STATE_INVALID_SYNC_DISABLED = 5;
/** Flags used when connecting to a sync adapter service */
private static final int SYNC_ADAPTER_CONNECTION_FLAGS = Context.BIND_AUTO_CREATE
@@ -3206,12 +3209,10 @@ public class SyncManager {
}
final int syncOpState = computeSyncOpState(op);
- switch (syncOpState) {
- case SYNC_OP_STATE_INVALID_NO_ACCOUNT_ACCESS:
- case SYNC_OP_STATE_INVALID: {
- SyncJobService.callJobFinished(op.jobId, false,
- "invalid op state: " + syncOpState);
- } return;
+ if (syncOpState != SYNC_OP_STATE_VALID) {
+ SyncJobService.callJobFinished(op.jobId, false,
+ "invalid op state: " + syncOpState);
+ return;
}
if (!dispatchSyncOperation(op)) {
@@ -3354,27 +3355,27 @@ public class SyncManager {
pollFrequencyMillis, flexMillis, ContentResolver.SYNC_EXEMPTION_NONE);
final int syncOpState = computeSyncOpState(op);
- switch (syncOpState) {
- case SYNC_OP_STATE_INVALID_NO_ACCOUNT_ACCESS: {
- String packageName = op.owningPackage;
- final int userId = UserHandle.getUserId(op.owningUid);
- // If the app did not run and has no account access, done
- if (!wasPackageEverLaunched(packageName, userId)) {
- return;
- }
- mAccountManagerInternal.requestAccountAccess(op.target.account,
- packageName, userId, new RemoteCallback((Bundle result) -> {
- if (result != null
- && result.getBoolean(AccountManager.KEY_BOOLEAN_RESULT)) {
- updateOrAddPeriodicSync(target, pollFrequency, flex, extras);
- }
- }
- ));
- } return;
-
- case SYNC_OP_STATE_INVALID: {
+ if (syncOpState == SYNC_OP_STATE_INVALID_NO_ACCOUNT_ACCESS) {
+ String packageName = op.owningPackage;
+ final int userId = UserHandle.getUserId(op.owningUid);
+ // If the app did not run and has no account access, done
+ if (!wasPackageEverLaunched(packageName, userId)) {
return;
}
+ mLogger.log("requestAccountAccess for SYNC_OP_STATE_INVALID_NO_ACCOUNT_ACCESS");
+ mAccountManagerInternal.requestAccountAccess(op.target.account,
+ packageName, userId, new RemoteCallback((Bundle result) -> {
+ if (result != null
+ && result.getBoolean(AccountManager.KEY_BOOLEAN_RESULT)) {
+ updateOrAddPeriodicSync(target, pollFrequency, flex, extras);
+ }
+ }
+ ));
+ return;
+ }
+ if (syncOpState != SYNC_OP_STATE_VALID) {
+ mLogger.log("syncOpState=", syncOpState);
+ return;
}
scheduleSyncOperationH(op);
@@ -3452,7 +3453,7 @@ public class SyncManager {
Slog.v(TAG, " Dropping sync operation: account doesn't exist.");
}
Slog.wtf(TAG, "SYNC_OP_STATE_INVALID: account doesn't exist.");
- return SYNC_OP_STATE_INVALID;
+ return SYNC_OP_STATE_INVALID_NO_ACCOUNT;
}
// Drop this sync request if it isn't syncable.
state = computeSyncable(target.account, target.userId, target.provider, true);
@@ -3469,7 +3470,7 @@ public class SyncManager {
Slog.v(TAG, " Dropping sync operation: isSyncable == NOT_SYNCABLE");
}
Slog.wtf(TAG, "SYNC_OP_STATE_INVALID: NOT_SYNCABLE");
- return SYNC_OP_STATE_INVALID;
+ return SYNC_OP_STATE_INVALID_NOT_SYNCABLE;
}
final boolean syncEnabled = mSyncStorageEngine.getMasterSyncAutomatically(target.userId)
@@ -3488,7 +3489,7 @@ public class SyncManager {
Slog.v(TAG, " Dropping sync operation: disallowed by settings/network.");
}
Slog.wtf(TAG, "SYNC_OP_STATE_INVALID: disallowed by settings/network");
- return SYNC_OP_STATE_INVALID;
+ return SYNC_OP_STATE_INVALID_SYNC_DISABLED;
}
return SYNC_OP_STATE_VALID;
}
diff --git a/services/core/java/com/android/server/display/LocalDisplayAdapter.java b/services/core/java/com/android/server/display/LocalDisplayAdapter.java
index 7e8fe3ae8428..87a5730edc2e 100644
--- a/services/core/java/com/android/server/display/LocalDisplayAdapter.java
+++ b/services/core/java/com/android/server/display/LocalDisplayAdapter.java
@@ -91,8 +91,12 @@ final class LocalDisplayAdapter extends DisplayAdapter {
private void tryConnectDisplayLocked(long physicalDisplayId) {
final IBinder displayToken = SurfaceControl.getPhysicalDisplayToken(physicalDisplayId);
if (displayToken != null) {
- SurfaceControl.PhysicalDisplayInfo[] configs =
- SurfaceControl.getDisplayConfigs(displayToken);
+ SurfaceControl.DisplayInfo info = SurfaceControl.getDisplayInfo(displayToken);
+ if (info == null) {
+ Slog.w(TAG, "No valid info found for display device " + physicalDisplayId);
+ return;
+ }
+ SurfaceControl.DisplayConfig[] configs = SurfaceControl.getDisplayConfigs(displayToken);
if (configs == null) {
// There are no valid configs for this device, so we can't use it
Slog.w(TAG, "No valid configs found for display device " + physicalDisplayId);
@@ -115,19 +119,19 @@ final class LocalDisplayAdapter extends DisplayAdapter {
activeColorMode = Display.COLOR_MODE_INVALID;
}
int[] colorModes = SurfaceControl.getDisplayColorModes(displayToken);
- SurfaceControl.DesiredDisplayConfigSpecs desiredDisplayConfigSpecs =
+ SurfaceControl.DesiredDisplayConfigSpecs configSpecs =
SurfaceControl.getDesiredDisplayConfigSpecs(displayToken);
LocalDisplayDevice device = mDevices.get(physicalDisplayId);
if (device == null) {
// Display was added.
final boolean isInternal = mDevices.size() == 0;
- device = new LocalDisplayDevice(displayToken, physicalDisplayId, configs,
- activeConfig, desiredDisplayConfigSpecs, colorModes, activeColorMode,
+ device = new LocalDisplayDevice(displayToken, physicalDisplayId, info,
+ configs, activeConfig, configSpecs, colorModes, activeColorMode,
isInternal);
mDevices.put(physicalDisplayId, device);
sendDisplayDeviceEventLocked(device, DISPLAY_DEVICE_EVENT_ADDED);
- } else if (device.updatePhysicalDisplayInfoLocked(configs, activeConfig,
- desiredDisplayConfigSpecs, colorModes, activeColorMode)) {
+ } else if (device.updateDisplayConfigsLocked(configs, activeConfig, configSpecs,
+ colorModes, activeColorMode)) {
// Display properties changed.
sendDisplayDeviceEventLocked(device, DISPLAY_DEVICE_EVENT_CHANGED);
}
@@ -179,7 +183,7 @@ final class LocalDisplayAdapter extends DisplayAdapter {
private DisplayModeDirector.DesiredDisplayModeSpecs mDisplayModeSpecs =
new DisplayModeDirector.DesiredDisplayModeSpecs();
private boolean mDisplayModeSpecsInvalid;
- private int mActivePhysIndex;
+ private int mActiveConfigId;
private int mActiveColorMode;
private boolean mActiveColorModeInvalid;
private Display.HdrCapabilities mHdrCapabilities;
@@ -189,21 +193,25 @@ final class LocalDisplayAdapter extends DisplayAdapter {
private boolean mGameContentTypeRequested;
private boolean mSidekickActive;
private SidekickInternal mSidekickInternal;
- private SurfaceControl.PhysicalDisplayInfo[] mDisplayInfos;
+ private SurfaceControl.DisplayInfo mDisplayInfo;
+ private SurfaceControl.DisplayConfig[] mDisplayConfigs;
private Spline mSystemBrightnessToNits;
private Spline mNitsToHalBrightness;
private boolean mHalBrightnessSupport;
LocalDisplayDevice(IBinder displayToken, long physicalDisplayId,
- SurfaceControl.PhysicalDisplayInfo[] physicalDisplayInfos, int activeDisplayInfo,
- SurfaceControl.DesiredDisplayConfigSpecs physicalDisplayConfigSpecs,
+ SurfaceControl.DisplayInfo info, SurfaceControl.DisplayConfig[] configs,
+ int activeConfigId, SurfaceControl.DesiredDisplayConfigSpecs configSpecs,
int[] colorModes, int activeColorMode, boolean isInternal) {
super(LocalDisplayAdapter.this, displayToken, UNIQUE_ID_PREFIX + physicalDisplayId);
mPhysicalDisplayId = physicalDisplayId;
mIsInternal = isInternal;
- updatePhysicalDisplayInfoLocked(physicalDisplayInfos, activeDisplayInfo,
- physicalDisplayConfigSpecs, colorModes, activeColorMode);
+ mDisplayInfo = info;
+
+ updateDisplayConfigsLocked(configs, activeConfigId, configSpecs, colorModes,
+ activeColorMode);
updateColorModesLocked(colorModes, activeColorMode);
+
mSidekickInternal = LocalServices.getService(SidekickInternal.class);
if (mIsInternal) {
LightsManager lights = LocalServices.getService(LightsManager.class);
@@ -226,23 +234,24 @@ final class LocalDisplayAdapter extends DisplayAdapter {
return true;
}
- public boolean updatePhysicalDisplayInfoLocked(
- SurfaceControl.PhysicalDisplayInfo[] physicalDisplayInfos, int activeDisplayInfo,
- SurfaceControl.DesiredDisplayConfigSpecs physicalDisplayConfigSpecs,
- int[] colorModes, int activeColorMode) {
- mDisplayInfos = Arrays.copyOf(physicalDisplayInfos, physicalDisplayInfos.length);
- mActivePhysIndex = activeDisplayInfo;
+ public boolean updateDisplayConfigsLocked(
+ SurfaceControl.DisplayConfig[] configs, int activeConfigId,
+ SurfaceControl.DesiredDisplayConfigSpecs configSpecs, int[] colorModes,
+ int activeColorMode) {
+ mDisplayConfigs = Arrays.copyOf(configs, configs.length);
+ mActiveConfigId = activeConfigId;
+
// Build an updated list of all existing modes.
ArrayList<DisplayModeRecord> records = new ArrayList<DisplayModeRecord>();
boolean modesAdded = false;
- for (int i = 0; i < physicalDisplayInfos.length; i++) {
- SurfaceControl.PhysicalDisplayInfo info = physicalDisplayInfos[i];
+ for (int i = 0; i < configs.length; i++) {
+ SurfaceControl.DisplayConfig config = configs[i];
// First, check to see if we've already added a matching mode. Since not all
// configuration options are exposed via Display.Mode, it's possible that we have
- // multiple PhysicalDisplayInfos that would generate the same Display.Mode.
+ // multiple DisplayConfigs that would generate the same Display.Mode.
boolean existingMode = false;
for (int j = 0; j < records.size(); j++) {
- if (records.get(j).hasMatchingMode(info)) {
+ if (records.get(j).hasMatchingMode(config)) {
existingMode = true;
break;
}
@@ -253,9 +262,9 @@ final class LocalDisplayAdapter extends DisplayAdapter {
// If we haven't already added a mode for this configuration to the new set of
// supported modes then check to see if we have one in the prior set of supported
// modes to reuse.
- DisplayModeRecord record = findDisplayModeRecord(info);
+ DisplayModeRecord record = findDisplayModeRecord(config);
if (record == null) {
- record = new DisplayModeRecord(info);
+ record = new DisplayModeRecord(config);
modesAdded = true;
}
records.add(record);
@@ -265,7 +274,7 @@ final class LocalDisplayAdapter extends DisplayAdapter {
DisplayModeRecord activeRecord = null;
for (int i = 0; i < records.size(); i++) {
DisplayModeRecord record = records.get(i);
- if (record.hasMatchingMode(physicalDisplayInfos[activeDisplayInfo])){
+ if (record.hasMatchingMode(configs[activeConfigId])) {
activeRecord = record;
break;
}
@@ -282,17 +291,15 @@ final class LocalDisplayAdapter extends DisplayAdapter {
// Check whether surface flinger spontaneously changed display config specs out from
// under us. If so, schedule a traversal to reapply our display config specs.
if (mDisplayModeSpecs.baseModeId != 0) {
- int activeBaseMode =
- findMatchingModeIdLocked(physicalDisplayConfigSpecs.defaultConfig);
+ int activeBaseMode = findMatchingModeIdLocked(configSpecs.defaultConfig);
// If we can't map the defaultConfig index to a mode, then the physical display
// configs must have changed, and the code below for handling changes to the
// list of available modes will take care of updating display config specs.
if (activeBaseMode != 0) {
if (mDisplayModeSpecs.baseModeId != activeBaseMode
- || mDisplayModeSpecs.refreshRateRange.min
- != physicalDisplayConfigSpecs.minRefreshRate
+ || mDisplayModeSpecs.refreshRateRange.min != configSpecs.minRefreshRate
|| mDisplayModeSpecs.refreshRateRange.max
- != physicalDisplayConfigSpecs.maxRefreshRate) {
+ != configSpecs.maxRefreshRate) {
mDisplayModeSpecsInvalid = true;
sendTraversalRequestLocked();
}
@@ -313,7 +320,7 @@ final class LocalDisplayAdapter extends DisplayAdapter {
}
// Update the default mode, if needed.
- if (findDisplayInfoIndexLocked(mDefaultModeId) < 0) {
+ if (findDisplayConfigIdLocked(mDefaultModeId) < 0) {
if (mDefaultModeId != 0) {
Slog.w(TAG, "Default display mode no longer available, using currently"
+ " active mode as default.");
@@ -434,10 +441,10 @@ final class LocalDisplayAdapter extends DisplayAdapter {
return true;
}
- private DisplayModeRecord findDisplayModeRecord(SurfaceControl.PhysicalDisplayInfo info) {
+ private DisplayModeRecord findDisplayModeRecord(SurfaceControl.DisplayConfig config) {
for (int i = 0; i < mSupportedModes.size(); i++) {
DisplayModeRecord record = mSupportedModes.valueAt(i);
- if (record.hasMatchingMode(info)) {
+ if (record.hasMatchingMode(config)) {
return record;
}
}
@@ -455,10 +462,10 @@ final class LocalDisplayAdapter extends DisplayAdapter {
@Override
public DisplayDeviceInfo getDisplayDeviceInfoLocked() {
if (mInfo == null) {
- SurfaceControl.PhysicalDisplayInfo phys = mDisplayInfos[mActivePhysIndex];
+ SurfaceControl.DisplayConfig config = mDisplayConfigs[mActiveConfigId];
mInfo = new DisplayDeviceInfo();
- mInfo.width = phys.width;
- mInfo.height = phys.height;
+ mInfo.width = config.width;
+ mInfo.height = config.height;
mInfo.modeId = mActiveModeId;
mInfo.defaultModeId = mDefaultModeId;
mInfo.supportedModes = getDisplayModes(mSupportedModes);
@@ -471,20 +478,20 @@ final class LocalDisplayAdapter extends DisplayAdapter {
mInfo.supportedColorModes[i] = mSupportedColorModes.get(i);
}
mInfo.hdrCapabilities = mHdrCapabilities;
- mInfo.appVsyncOffsetNanos = phys.appVsyncOffsetNanos;
- mInfo.presentationDeadlineNanos = phys.presentationDeadlineNanos;
+ mInfo.appVsyncOffsetNanos = config.appVsyncOffsetNanos;
+ mInfo.presentationDeadlineNanos = config.presentationDeadlineNanos;
mInfo.state = mState;
mInfo.uniqueId = getUniqueId();
final DisplayAddress.Physical physicalAddress =
DisplayAddress.fromPhysicalDisplayId(mPhysicalDisplayId);
mInfo.address = physicalAddress;
- mInfo.densityDpi = (int) (phys.density * 160 + 0.5f);
- mInfo.xDpi = phys.xDpi;
- mInfo.yDpi = phys.yDpi;
+ mInfo.densityDpi = (int) (mDisplayInfo.density * 160 + 0.5f);
+ mInfo.xDpi = config.xDpi;
+ mInfo.yDpi = config.yDpi;
// Assume that all built-in displays that have secure output (eg. HDCP) also
// support compositing from gralloc protected buffers.
- if (phys.secure) {
+ if (mDisplayInfo.secure) {
mInfo.flags = DisplayDeviceInfo.FLAG_SECURE
| DisplayDeviceInfo.FLAG_SUPPORTS_PROTECTED_BUFFERS;
}
@@ -721,8 +728,8 @@ final class LocalDisplayAdapter extends DisplayAdapter {
// a valid mode.
return;
}
- int basePhysIndex = findDisplayInfoIndexLocked(displayModeSpecs.baseModeId);
- if (basePhysIndex < 0) {
+ int baseConfigId = findDisplayConfigIdLocked(displayModeSpecs.baseModeId);
+ if (baseConfigId < 0) {
// When a display is hotplugged, it's possible for a mode to be removed that was
// previously valid. Because of the way display changes are propagated through the
// framework, and the caching of the display mode specs in LogicalDisplay, it's
@@ -740,7 +747,7 @@ final class LocalDisplayAdapter extends DisplayAdapter {
getHandler().sendMessage(PooledLambda.obtainMessage(
LocalDisplayDevice::setDesiredDisplayModeSpecsAsync, this,
getDisplayTokenLocked(),
- new SurfaceControl.DesiredDisplayConfigSpecs(basePhysIndex,
+ new SurfaceControl.DesiredDisplayConfigSpecs(baseConfigId,
mDisplayModeSpecs.refreshRateRange.min,
mDisplayModeSpecs.refreshRateRange.max)));
}
@@ -764,22 +771,22 @@ final class LocalDisplayAdapter extends DisplayAdapter {
updateDeviceInfoLocked();
}
- public void onActivePhysicalDisplayModeChangedLocked(int physIndex) {
- if (updateActiveModeLocked(physIndex)) {
+ public void onActiveDisplayConfigChangedLocked(int configId) {
+ if (updateActiveModeLocked(configId)) {
updateDeviceInfoLocked();
}
}
- public boolean updateActiveModeLocked(int activePhysIndex) {
- if (mActivePhysIndex == activePhysIndex) {
+ public boolean updateActiveModeLocked(int activeConfigId) {
+ if (mActiveConfigId == activeConfigId) {
return false;
}
- mActivePhysIndex = activePhysIndex;
- mActiveModeId = findMatchingModeIdLocked(activePhysIndex);
+ mActiveConfigId = activeConfigId;
+ mActiveModeId = findMatchingModeIdLocked(activeConfigId);
mActiveModeInvalid = mActiveModeId == 0;
if (mActiveModeInvalid) {
Slog.w(TAG, "In unknown mode after setting allowed configs"
- + ", activePhysIndex=" + mActivePhysIndex);
+ + ", activeConfigId=" + mActiveConfigId);
}
return true;
}
@@ -850,7 +857,7 @@ final class LocalDisplayAdapter extends DisplayAdapter {
pw.println("mPhysicalDisplayId=" + mPhysicalDisplayId);
pw.println("mDisplayModeSpecs={" + mDisplayModeSpecs + "}");
pw.println("mDisplayModeSpecsInvalid=" + mDisplayModeSpecsInvalid);
- pw.println("mActivePhysIndex=" + mActivePhysIndex);
+ pw.println("mActiveConfigId=" + mActiveConfigId);
pw.println("mActiveModeId=" + mActiveModeId);
pw.println("mActiveColorMode=" + mActiveColorMode);
pw.println("mDefaultModeId=" + mDefaultModeId);
@@ -861,9 +868,10 @@ final class LocalDisplayAdapter extends DisplayAdapter {
pw.println("mAllmRequested=" + mAllmRequested);
pw.println("mGameContentTypeSupported" + mGameContentTypeSupported);
pw.println("mGameContentTypeRequested" + mGameContentTypeRequested);
- pw.println("mDisplayInfos=");
- for (int i = 0; i < mDisplayInfos.length; i++) {
- pw.println(" " + mDisplayInfos[i]);
+ pw.println("mDisplayInfo=" + mDisplayInfo);
+ pw.println("mDisplayConfigs=");
+ for (int i = 0; i < mDisplayConfigs.length; i++) {
+ pw.println(" " + mDisplayConfigs[i]);
}
pw.println("mSupportedModes=");
for (int i = 0; i < mSupportedModes.size(); i++) {
@@ -879,12 +887,12 @@ final class LocalDisplayAdapter extends DisplayAdapter {
pw.println("]");
}
- private int findDisplayInfoIndexLocked(int modeId) {
+ private int findDisplayConfigIdLocked(int modeId) {
DisplayModeRecord record = mSupportedModes.get(modeId);
if (record != null) {
- for (int i = 0; i < mDisplayInfos.length; i++) {
- SurfaceControl.PhysicalDisplayInfo info = mDisplayInfos[i];
- if (record.hasMatchingMode(info)){
+ for (int i = 0; i < mDisplayConfigs.length; i++) {
+ SurfaceControl.DisplayConfig config = mDisplayConfigs[i];
+ if (record.hasMatchingMode(config)) {
return i;
}
}
@@ -892,11 +900,11 @@ final class LocalDisplayAdapter extends DisplayAdapter {
return -1;
}
- private int findMatchingModeIdLocked(int physIndex) {
- SurfaceControl.PhysicalDisplayInfo info = mDisplayInfos[physIndex];
+ private int findMatchingModeIdLocked(int configId) {
+ SurfaceControl.DisplayConfig config = mDisplayConfigs[configId];
for (int i = 0; i < mSupportedModes.size(); i++) {
DisplayModeRecord record = mSupportedModes.valueAt(i);
- if (record.hasMatchingMode(info)) {
+ if (record.hasMatchingMode(config)) {
return record.mMode.getModeId();
}
}
@@ -948,23 +956,23 @@ final class LocalDisplayAdapter extends DisplayAdapter {
private static final class DisplayModeRecord {
public final Display.Mode mMode;
- public DisplayModeRecord(SurfaceControl.PhysicalDisplayInfo phys) {
- mMode = createMode(phys.width, phys.height, phys.refreshRate);
+ DisplayModeRecord(SurfaceControl.DisplayConfig config) {
+ mMode = createMode(config.width, config.height, config.refreshRate);
}
/**
- * Returns whether the mode generated by the given PhysicalDisplayInfo matches the mode
+ * Returns whether the mode generated by the given DisplayConfig matches the mode
* contained by the record modulo mode ID.
*
- * Note that this doesn't necessarily mean the the PhysicalDisplayInfos are identical, just
+ * Note that this doesn't necessarily mean that the DisplayConfigs are identical, just
* that they generate identical modes.
*/
- public boolean hasMatchingMode(SurfaceControl.PhysicalDisplayInfo info) {
+ public boolean hasMatchingMode(SurfaceControl.DisplayConfig config) {
int modeRefreshRate = Float.floatToIntBits(mMode.getRefreshRate());
- int displayInfoRefreshRate = Float.floatToIntBits(info.refreshRate);
- return mMode.getPhysicalWidth() == info.width
- && mMode.getPhysicalHeight() == info.height
- && modeRefreshRate == displayInfoRefreshRate;
+ int configRefreshRate = Float.floatToIntBits(config.refreshRate);
+ return mMode.getPhysicalWidth() == config.width
+ && mMode.getPhysicalHeight() == config.height
+ && modeRefreshRate == configRefreshRate;
}
public String toString() {
@@ -989,12 +997,12 @@ final class LocalDisplayAdapter extends DisplayAdapter {
}
@Override
- public void onConfigChanged(long timestampNanos, long physicalDisplayId, int physIndex) {
+ public void onConfigChanged(long timestampNanos, long physicalDisplayId, int configId) {
if (DEBUG) {
Slog.d(TAG, "onConfigChanged("
+ "timestampNanos=" + timestampNanos
+ ", physicalDisplayId=" + physicalDisplayId
- + ", physIndex=" + physIndex + ")");
+ + ", configId=" + configId + ")");
}
synchronized (getSyncRoot()) {
LocalDisplayDevice device = mDevices.get(physicalDisplayId);
@@ -1005,7 +1013,7 @@ final class LocalDisplayAdapter extends DisplayAdapter {
}
return;
}
- device.onActivePhysicalDisplayModeChangedLocked(physIndex);
+ device.onActiveDisplayConfigChangedLocked(configId);
}
}
}
diff --git a/services/core/java/com/android/server/incremental/IncrementalManagerService.java b/services/core/java/com/android/server/incremental/IncrementalManagerService.java
index 5876d433face..64f25dd6f9fd 100644
--- a/services/core/java/com/android/server/incremental/IncrementalManagerService.java
+++ b/services/core/java/com/android/server/incremental/IncrementalManagerService.java
@@ -32,7 +32,10 @@ import android.os.ShellCallback;
import android.os.incremental.IIncrementalManager;
import android.util.Slog;
+import com.android.internal.util.DumpUtils;
+
import java.io.FileDescriptor;
+import java.io.PrintWriter;
/**
* This service has the following purposes:
@@ -71,6 +74,13 @@ public class IncrementalManagerService extends IIncrementalManager.Stub {
mNativeInstance = nativeStartService();
}
+ @SuppressWarnings("resource")
+ @Override
+ protected void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
+ if (!DumpUtils.checkDumpAndUsageStatsPermission(mContext, TAG, pw)) return;
+ nativeDump(mNativeInstance, fd.getInt$());
+ }
+
/**
* Notifies native IIncrementalManager service that system is ready.
*/
@@ -158,4 +168,6 @@ public class IncrementalManagerService extends IIncrementalManager.Stub {
private static native long nativeStartService();
private static native void nativeSystemReady(long nativeInstance);
+
+ private static native void nativeDump(long nativeInstance, int fd);
}
diff --git a/services/core/java/com/android/server/integrity/AppIntegrityManagerServiceImpl.java b/services/core/java/com/android/server/integrity/AppIntegrityManagerServiceImpl.java
index 15dd6464e094..9754b6d4db02 100644
--- a/services/core/java/com/android/server/integrity/AppIntegrityManagerServiceImpl.java
+++ b/services/core/java/com/android/server/integrity/AppIntegrityManagerServiceImpl.java
@@ -46,11 +46,12 @@ import android.os.Binder;
import android.os.Bundle;
import android.os.Handler;
import android.os.HandlerThread;
+import android.provider.Settings;
import android.util.Slog;
-import android.util.StatsLog;
import com.android.internal.R;
import com.android.internal.annotations.VisibleForTesting;
+import com.android.internal.util.FrameworkStatsLog;
import com.android.server.LocalServices;
import com.android.server.integrity.engine.RuleEvaluationEngine;
import com.android.server.integrity.model.IntegrityCheckResult;
@@ -67,6 +68,7 @@ import java.security.cert.CertificateException;
import java.security.cert.CertificateFactory;
import java.security.cert.X509Certificate;
import java.util.Arrays;
+import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
@@ -105,6 +107,8 @@ public class AppIntegrityManagerServiceImpl extends IAppIntegrityManager.Stub {
private final RuleEvaluationEngine mEvaluationEngine;
private final IntegrityFileManager mIntegrityFileManager;
+ private final boolean mCheckIntegrityForRuleProviders;
+
/** Create an instance of {@link AppIntegrityManagerServiceImpl}. */
public static AppIntegrityManagerServiceImpl create(Context context) {
HandlerThread handlerThread = new HandlerThread("AppIntegrityManagerServiceHandler");
@@ -115,7 +119,13 @@ public class AppIntegrityManagerServiceImpl extends IAppIntegrityManager.Stub {
LocalServices.getService(PackageManagerInternal.class),
RuleEvaluationEngine.getRuleEvaluationEngine(),
IntegrityFileManager.getInstance(),
- handlerThread.getThreadHandler());
+ handlerThread.getThreadHandler(),
+ Settings.Global.getInt(
+ context.getContentResolver(),
+ Settings.Global.INTEGRITY_CHECK_INCLUDES_RULE_PROVIDER,
+ 0)
+ == 1
+ );
}
@VisibleForTesting
@@ -124,12 +134,14 @@ public class AppIntegrityManagerServiceImpl extends IAppIntegrityManager.Stub {
PackageManagerInternal packageManagerInternal,
RuleEvaluationEngine evaluationEngine,
IntegrityFileManager integrityFileManager,
- Handler handler) {
+ Handler handler,
+ boolean checkIntegrityForRuleProviders) {
mContext = context;
mPackageManagerInternal = packageManagerInternal;
mEvaluationEngine = evaluationEngine;
mIntegrityFileManager = integrityFileManager;
mHandler = handler;
+ mCheckIntegrityForRuleProviders = checkIntegrityForRuleProviders;
IntentFilter integrityVerificationFilter = new IntentFilter();
integrityVerificationFilter.addAction(ACTION_PACKAGE_NEEDS_INTEGRITY_VERIFICATION);
@@ -170,7 +182,8 @@ public class AppIntegrityManagerServiceImpl extends IAppIntegrityManager.Stub {
success = false;
}
- StatsLog.write(StatsLog.INTEGRITY_RULES_PUSHED, success, ruleProvider, version);
+ FrameworkStatsLog.write(FrameworkStatsLog.INTEGRITY_RULES_PUSHED, success,
+ ruleProvider, version);
Intent intent = new Intent();
intent.putExtra(EXTRA_STATUS, success ? STATUS_SUCCESS : STATUS_FAILURE);
@@ -207,6 +220,17 @@ public class AppIntegrityManagerServiceImpl extends IAppIntegrityManager.Stub {
: "";
}
+ @Override
+ public ParceledListSlice<Rule> getCurrentRules() {
+ List<Rule> rules = Collections.emptyList();
+ try {
+ rules = mIntegrityFileManager.readRules(/* appInstallMetadata= */ null);
+ } catch (Exception e) {
+ Slog.e(TAG, "Error getting current rules", e);
+ }
+ return new ParceledListSlice<>(rules);
+ }
+
private void handleIntegrityVerification(Intent intent) {
int verificationId = intent.getIntExtra(EXTRA_VERIFICATION_ID, -1);
@@ -228,7 +252,8 @@ public class AppIntegrityManagerServiceImpl extends IAppIntegrityManager.Stub {
String installerPackageName = getInstallerPackageName(intent);
// Skip integrity verification if the verifier is doing the install.
- if (isRuleProvider(installerPackageName)) {
+ if (!mCheckIntegrityForRuleProviders
+ && isRuleProvider(installerPackageName)) {
Slog.i(TAG, "Verifier doing the install. Skipping integrity check.");
mPackageManagerInternal.setIntegrityVerificationResult(
verificationId, PackageManagerInternal.INTEGRITY_VERIFICATION_ALLOW);
@@ -262,8 +287,8 @@ public class AppIntegrityManagerServiceImpl extends IAppIntegrityManager.Stub {
+ " due to "
+ result.getMatchedRules());
- StatsLog.write(
- StatsLog.INTEGRITY_CHECK_RESULT_REPORTED,
+ FrameworkStatsLog.write(
+ FrameworkStatsLog.INTEGRITY_CHECK_RESULT_REPORTED,
packageName,
appCert,
appInstallMetadata.getVersionCode(),
@@ -295,7 +320,7 @@ public class AppIntegrityManagerServiceImpl extends IAppIntegrityManager.Stub {
* Verify the UID and return the installer package name.
*
* @return the package name of the installer, or null if it cannot be determined or it is
- * installed via adb.
+ * installed via adb.
*/
@Nullable
private String getInstallerPackageName(Intent intent) {
@@ -584,12 +609,13 @@ public class AppIntegrityManagerServiceImpl extends IAppIntegrityManager.Stub {
}
private List<String> getAllowedRuleProviders() {
- return Arrays.asList(mContext.getResources().getStringArray(
- R.array.config_integrityRuleProviderPackages));
+ return Arrays.asList(
+ mContext.getResources()
+ .getStringArray(R.array.config_integrityRuleProviderPackages));
}
private boolean isRuleProvider(String installerPackageName) {
- return getAllowedRuleProviders().stream().anyMatch(
- ruleProvider -> ruleProvider.equals(installerPackageName));
+ return getAllowedRuleProviders().stream()
+ .anyMatch(ruleProvider -> ruleProvider.equals(installerPackageName));
}
}
diff --git a/services/core/java/com/android/server/integrity/IntegrityFileManager.java b/services/core/java/com/android/server/integrity/IntegrityFileManager.java
index fffe7d9030ff..7f0231e72373 100644
--- a/services/core/java/com/android/server/integrity/IntegrityFileManager.java
+++ b/services/core/java/com/android/server/integrity/IntegrityFileManager.java
@@ -147,19 +147,21 @@ public class IntegrityFileManager {
/**
* Read rules from persistent storage.
*
- * @param appInstallMetadata information about the install used to select rules to read
+ * @param appInstallMetadata information about the install used to select rules to read. If
+ * null, all rules will be read.
*/
- public List<Rule> readRules(AppInstallMetadata appInstallMetadata)
+ public List<Rule> readRules(@Nullable AppInstallMetadata appInstallMetadata)
throws IOException, RuleParseException {
synchronized (RULES_LOCK) {
// Try to identify indexes from the index file.
- List<RuleIndexRange> ruleReadingIndexes;
- try {
- ruleReadingIndexes =
- mRuleIndexingController.identifyRulesToEvaluate(appInstallMetadata);
- } catch (Exception e) {
- Slog.w(TAG, "Error identifying the rule indexes. Trying unindexed.", e);
- ruleReadingIndexes = Collections.emptyList();
+ List<RuleIndexRange> ruleReadingIndexes = Collections.emptyList();
+ if (appInstallMetadata != null) {
+ try {
+ ruleReadingIndexes =
+ mRuleIndexingController.identifyRulesToEvaluate(appInstallMetadata);
+ } catch (Exception e) {
+ Slog.w(TAG, "Error identifying the rule indexes. Trying unindexed.", e);
+ }
}
// Read the rules based on the index information when available.
diff --git a/services/core/java/com/android/server/integrity/model/IntegrityCheckResult.java b/services/core/java/com/android/server/integrity/model/IntegrityCheckResult.java
index 1b605c7c430c..1fa067065e1b 100644
--- a/services/core/java/com/android/server/integrity/model/IntegrityCheckResult.java
+++ b/services/core/java/com/android/server/integrity/model/IntegrityCheckResult.java
@@ -18,7 +18,8 @@ package com.android.server.integrity.model;
import android.annotation.Nullable;
import android.content.integrity.Rule;
-import android.util.StatsLog;
+
+import com.android.internal.util.FrameworkStatsLog;
import java.util.Collections;
import java.util.List;
@@ -86,11 +87,11 @@ public final class IntegrityCheckResult {
*/
public int getLoggingResponse() {
if (getEffect() == Effect.DENY) {
- return StatsLog.INTEGRITY_CHECK_RESULT_REPORTED__RESPONSE__REJECTED;
+ return FrameworkStatsLog.INTEGRITY_CHECK_RESULT_REPORTED__RESPONSE__REJECTED;
} else if (getEffect() == Effect.ALLOW && getMatchedRules().isEmpty()) {
- return StatsLog.INTEGRITY_CHECK_RESULT_REPORTED__RESPONSE__ALLOWED;
+ return FrameworkStatsLog.INTEGRITY_CHECK_RESULT_REPORTED__RESPONSE__ALLOWED;
} else if (getEffect() == Effect.ALLOW && !getMatchedRules().isEmpty()) {
- return StatsLog.INTEGRITY_CHECK_RESULT_REPORTED__RESPONSE__FORCE_ALLOWED;
+ return FrameworkStatsLog.INTEGRITY_CHECK_RESULT_REPORTED__RESPONSE__FORCE_ALLOWED;
} else {
throw new IllegalStateException("IntegrityCheckResult is not valid.");
}
diff --git a/services/core/java/com/android/server/integrity/serializer/RuleBinarySerializer.java b/services/core/java/com/android/server/integrity/serializer/RuleBinarySerializer.java
index d01499668e8c..00e054596cd7 100644
--- a/services/core/java/com/android/server/integrity/serializer/RuleBinarySerializer.java
+++ b/services/core/java/com/android/server/integrity/serializer/RuleBinarySerializer.java
@@ -87,7 +87,7 @@ public class RuleBinarySerializer implements RuleSerializer {
}
if (rules.size() > TOTAL_RULE_SIZE_LIMIT) {
- throw new IllegalArgumentException("Too many rules provided.");
+ throw new IllegalArgumentException("Too many rules provided: " + rules.size());
}
// Determine the indexing groups and the order of the rules within each indexed group.
@@ -134,7 +134,11 @@ public class RuleBinarySerializer implements RuleSerializer {
.map(list -> list.size())
.collect(Collectors.summingInt(Integer::intValue));
if (totalRuleCount > ruleSizeLimit) {
- throw new IllegalArgumentException("Too many rules provided in the indexing group.");
+ throw new IllegalArgumentException(
+ "Too many rules provided in the indexing group. Provided "
+ + totalRuleCount
+ + " limit "
+ + ruleSizeLimit);
}
}
diff --git a/services/core/java/com/android/server/locksettings/RebootEscrowData.java b/services/core/java/com/android/server/locksettings/RebootEscrowData.java
index aee608e8a544..2b1907985aeb 100644
--- a/services/core/java/com/android/server/locksettings/RebootEscrowData.java
+++ b/services/core/java/com/android/server/locksettings/RebootEscrowData.java
@@ -26,16 +26,12 @@ import java.io.IOException;
import java.security.InvalidAlgorithmParameterException;
import java.security.InvalidKeyException;
import java.security.NoSuchAlgorithmException;
-import java.security.SecureRandom;
import javax.crypto.BadPaddingException;
import javax.crypto.Cipher;
import javax.crypto.IllegalBlockSizeException;
-import javax.crypto.KeyGenerator;
import javax.crypto.NoSuchPaddingException;
-import javax.crypto.SecretKey;
import javax.crypto.spec.IvParameterSpec;
-import javax.crypto.spec.SecretKeySpec;
/**
* Holds the data necessary to complete a reboot escrow of the Synthetic Password.
@@ -47,17 +43,11 @@ class RebootEscrowData {
*/
private static final int CURRENT_VERSION = 1;
- /** The secret key will be of this format. */
- private static final String KEY_ALGO = "AES";
-
- /** The key size used for encrypting the reboot escrow data. */
- private static final int KEY_SIZE_BITS = 256;
-
/** The algorithm used for the encryption of the key blob. */
private static final String CIPHER_ALGO = "AES/GCM/NoPadding";
private RebootEscrowData(byte spVersion, byte[] iv, byte[] syntheticPassword, byte[] blob,
- byte[] key) {
+ RebootEscrowKey key) {
mSpVersion = spVersion;
mIv = iv;
mSyntheticPassword = syntheticPassword;
@@ -69,7 +59,7 @@ class RebootEscrowData {
private final byte[] mIv;
private final byte[] mSyntheticPassword;
private final byte[] mBlob;
- private final byte[] mKey;
+ private final RebootEscrowKey mKey;
public byte getSpVersion() {
return mSpVersion;
@@ -87,17 +77,13 @@ class RebootEscrowData {
return mBlob;
}
- public byte[] getKey() {
+ public RebootEscrowKey getKey() {
return mKey;
}
- static SecretKeySpec fromKeyBytes(byte[] keyBytes) {
- return new SecretKeySpec(keyBytes, KEY_ALGO);
- }
-
- static RebootEscrowData fromEncryptedData(SecretKeySpec keySpec, byte[] blob)
+ static RebootEscrowData fromEncryptedData(RebootEscrowKey key, byte[] blob)
throws IOException {
- Preconditions.checkNotNull(keySpec);
+ Preconditions.checkNotNull(key);
Preconditions.checkNotNull(blob);
DataInputStream dis = new DataInputStream(new ByteArrayInputStream(blob));
@@ -126,7 +112,7 @@ class RebootEscrowData {
final byte[] syntheticPassword;
try {
Cipher c = Cipher.getInstance(CIPHER_ALGO);
- c.init(Cipher.DECRYPT_MODE, keySpec, new IvParameterSpec(iv));
+ c.init(Cipher.DECRYPT_MODE, key.getKey(), new IvParameterSpec(iv));
syntheticPassword = c.doFinal(cipherText);
} catch (NoSuchAlgorithmException | InvalidKeyException | BadPaddingException
| IllegalBlockSizeException | NoSuchPaddingException
@@ -134,30 +120,22 @@ class RebootEscrowData {
throw new IOException("Could not decrypt ciphertext", e);
}
- return new RebootEscrowData(spVersion, iv, syntheticPassword, blob, keySpec.getEncoded());
+ return new RebootEscrowData(spVersion, iv, syntheticPassword, blob, key);
}
- static RebootEscrowData fromSyntheticPassword(byte spVersion, byte[] syntheticPassword)
+ static RebootEscrowData fromSyntheticPassword(RebootEscrowKey key, byte spVersion,
+ byte[] syntheticPassword)
throws IOException {
Preconditions.checkNotNull(syntheticPassword);
ByteArrayOutputStream bos = new ByteArrayOutputStream();
DataOutputStream dos = new DataOutputStream(bos);
- final SecretKey secretKey;
- try {
- KeyGenerator keyGenerator = KeyGenerator.getInstance(KEY_ALGO);
- keyGenerator.init(KEY_SIZE_BITS, new SecureRandom());
- secretKey = keyGenerator.generateKey();
- } catch (NoSuchAlgorithmException e) {
- throw new IOException("Could not generate new secret key", e);
- }
-
final byte[] cipherText;
final byte[] iv;
try {
Cipher cipher = Cipher.getInstance(CIPHER_ALGO);
- cipher.init(Cipher.ENCRYPT_MODE, secretKey);
+ cipher.init(Cipher.ENCRYPT_MODE, key.getKey());
cipherText = cipher.doFinal(syntheticPassword);
iv = cipher.getIV();
} catch (NoSuchAlgorithmException | BadPaddingException | IllegalBlockSizeException
@@ -173,6 +151,6 @@ class RebootEscrowData {
dos.write(cipherText);
return new RebootEscrowData(spVersion, iv, syntheticPassword, bos.toByteArray(),
- secretKey.getEncoded());
+ key);
}
}
diff --git a/services/core/java/com/android/server/locksettings/RebootEscrowKey.java b/services/core/java/com/android/server/locksettings/RebootEscrowKey.java
new file mode 100644
index 000000000000..5367319caa85
--- /dev/null
+++ b/services/core/java/com/android/server/locksettings/RebootEscrowKey.java
@@ -0,0 +1,67 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.locksettings;
+
+import java.io.IOException;
+import java.security.NoSuchAlgorithmException;
+import java.security.SecureRandom;
+
+import javax.crypto.KeyGenerator;
+import javax.crypto.SecretKey;
+import javax.crypto.spec.SecretKeySpec;
+
+/**
+ * Key used to encrypt and decrypt the {@link RebootEscrowData}.
+ */
+class RebootEscrowKey {
+
+ /** The secret key will be of this format. */
+ private static final String KEY_ALGO = "AES";
+
+ /** The key size used for encrypting the reboot escrow data. */
+ private static final int KEY_SIZE_BITS = 256;
+
+ private final SecretKey mKey;
+
+ private RebootEscrowKey(SecretKey key) {
+ mKey = key;
+ }
+
+ static RebootEscrowKey fromKeyBytes(byte[] keyBytes) {
+ return new RebootEscrowKey(new SecretKeySpec(keyBytes, KEY_ALGO));
+ }
+
+ static RebootEscrowKey generate() throws IOException {
+ final SecretKey secretKey;
+ try {
+ KeyGenerator keyGenerator = KeyGenerator.getInstance(KEY_ALGO);
+ keyGenerator.init(KEY_SIZE_BITS, new SecureRandom());
+ secretKey = keyGenerator.generateKey();
+ } catch (NoSuchAlgorithmException e) {
+ throw new IOException("Could not generate new secret key", e);
+ }
+ return new RebootEscrowKey(secretKey);
+ }
+
+ SecretKey getKey() {
+ return mKey;
+ }
+
+ byte[] getKeyBytes() {
+ return mKey.getEncoded();
+ }
+}
diff --git a/services/core/java/com/android/server/locksettings/RebootEscrowManager.java b/services/core/java/com/android/server/locksettings/RebootEscrowManager.java
index 46ea9d11d1dc..e991f96b208f 100644
--- a/services/core/java/com/android/server/locksettings/RebootEscrowManager.java
+++ b/services/core/java/com/android/server/locksettings/RebootEscrowManager.java
@@ -25,27 +25,26 @@ import android.os.RemoteException;
import android.os.ServiceManager;
import android.os.UserManager;
import android.util.Slog;
-import android.util.StatsLog;
+import com.android.internal.annotations.GuardedBy;
import com.android.internal.annotations.VisibleForTesting;
+import com.android.internal.util.FrameworkStatsLog;
import com.android.internal.widget.RebootEscrowListener;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
import java.util.NoSuchElementException;
-import java.util.concurrent.atomic.AtomicBoolean;
-
-import javax.crypto.spec.SecretKeySpec;
class RebootEscrowManager {
private static final String TAG = "RebootEscrowManager";
/**
- * Used to track when the reboot escrow is wanted. Set to false when mRebootEscrowReady is
- * true.
+ * Used to track when the reboot escrow is wanted. Should stay true once escrow is requested
+ * unless clearRebootEscrow is called. This will allow all the active users to be unlocked
+ * after reboot.
*/
- private final AtomicBoolean mRebootEscrowWanted = new AtomicBoolean(false);
+ private boolean mRebootEscrowWanted;
/** Used to track when reboot escrow is ready. */
private boolean mRebootEscrowReady;
@@ -54,10 +53,16 @@ class RebootEscrowManager {
private RebootEscrowListener mRebootEscrowListener;
/**
+ * Hold this lock when checking or generating the reboot escrow key.
+ */
+ private final Object mKeyGenerationLock = new Object();
+
+ /**
* Stores the reboot escrow data between when it's supplied and when
* {@link #armRebootEscrowIfNeeded()} is called.
*/
- private RebootEscrowData mPendingRebootEscrowData;
+ @GuardedBy("mKeyGenerationLock")
+ private RebootEscrowKey mPendingRebootEscrowKey;
private final UserManager mUserManager;
@@ -82,6 +87,7 @@ class RebootEscrowManager {
public Context getContext() {
return mContext;
}
+
public UserManager getUserManager() {
return (UserManager) mContext.getSystemService(Context.USER_SERVICE);
}
@@ -123,13 +129,13 @@ class RebootEscrowManager {
return;
}
- SecretKeySpec escrowKey = getAndClearRebootEscrowKey();
+ RebootEscrowKey escrowKey = getAndClearRebootEscrowKey();
if (escrowKey == null) {
Slog.w(TAG, "Had reboot escrow data for users, but no key; removing escrow storage.");
for (UserInfo user : users) {
mStorage.removeRebootEscrow(user.id);
}
- StatsLog.write(StatsLog.REBOOT_ESCROW_RECOVERY_REPORTED, false);
+ FrameworkStatsLog.write(FrameworkStatsLog.REBOOT_ESCROW_RECOVERY_REPORTED, false);
return;
}
@@ -137,10 +143,11 @@ class RebootEscrowManager {
for (UserInfo user : rebootEscrowUsers) {
allUsersUnlocked &= restoreRebootEscrowForUser(user.id, escrowKey);
}
- StatsLog.write(StatsLog.REBOOT_ESCROW_RECOVERY_REPORTED, allUsersUnlocked);
+ FrameworkStatsLog.write(FrameworkStatsLog.REBOOT_ESCROW_RECOVERY_REPORTED,
+ allUsersUnlocked);
}
- private SecretKeySpec getAndClearRebootEscrowKey() {
+ private RebootEscrowKey getAndClearRebootEscrowKey() {
IRebootEscrow rebootEscrow = mInjector.getRebootEscrow();
if (rebootEscrow == null) {
return null;
@@ -170,14 +177,14 @@ class RebootEscrowManager {
// Overwrite the existing key with the null key
rebootEscrow.storeKey(new byte[32]);
- return RebootEscrowData.fromKeyBytes(escrowKeyBytes);
+ return RebootEscrowKey.fromKeyBytes(escrowKeyBytes);
} catch (RemoteException e) {
Slog.w(TAG, "Could not retrieve escrow data");
return null;
}
}
- private boolean restoreRebootEscrowForUser(@UserIdInt int userId, SecretKeySpec escrowKey) {
+ private boolean restoreRebootEscrowForUser(@UserIdInt int userId, RebootEscrowKey key) {
if (!mStorage.hasRebootEscrow(userId)) {
return false;
}
@@ -186,7 +193,7 @@ class RebootEscrowManager {
byte[] blob = mStorage.readRebootEscrow(userId);
mStorage.removeRebootEscrow(userId);
- RebootEscrowData escrowData = RebootEscrowData.fromEncryptedData(escrowKey, blob);
+ RebootEscrowData escrowData = RebootEscrowData.fromEncryptedData(key, blob);
mCallbacks.onRebootEscrowRestored(escrowData.getSpVersion(),
escrowData.getSyntheticPassword(), userId);
@@ -199,33 +206,60 @@ class RebootEscrowManager {
void callToRebootEscrowIfNeeded(@UserIdInt int userId, byte spVersion,
byte[] syntheticPassword) {
- if (!mRebootEscrowWanted.compareAndSet(true, false)) {
+ if (!mRebootEscrowWanted) {
return;
}
IRebootEscrow rebootEscrow = mInjector.getRebootEscrow();
if (rebootEscrow == null) {
+ mRebootEscrowWanted = false;
+ setRebootEscrowReady(false);
+ return;
+ }
+
+ RebootEscrowKey escrowKey = generateEscrowKeyIfNeeded();
+ if (escrowKey == null) {
+ Slog.e(TAG, "Could not generate escrow key");
+ mRebootEscrowWanted = false;
setRebootEscrowReady(false);
return;
}
final RebootEscrowData escrowData;
try {
- escrowData = RebootEscrowData.fromSyntheticPassword(spVersion, syntheticPassword);
+ escrowData = RebootEscrowData.fromSyntheticPassword(escrowKey, spVersion,
+ syntheticPassword);
} catch (IOException e) {
setRebootEscrowReady(false);
Slog.w(TAG, "Could not escrow reboot data", e);
return;
}
- mPendingRebootEscrowData = escrowData;
mStorage.writeRebootEscrow(userId, escrowData.getBlob());
setRebootEscrowReady(true);
}
+ private RebootEscrowKey generateEscrowKeyIfNeeded() {
+ synchronized (mKeyGenerationLock) {
+ if (mPendingRebootEscrowKey != null) {
+ return mPendingRebootEscrowKey;
+ }
+
+ RebootEscrowKey key;
+ try {
+ key = RebootEscrowKey.generate();
+ } catch (IOException e) {
+ return null;
+ }
+
+ mPendingRebootEscrowKey = key;
+ return key;
+ }
+ }
+
private void clearRebootEscrowIfNeeded() {
- mRebootEscrowWanted.set(false);
+ mRebootEscrowWanted = false;
setRebootEscrowReady(false);
IRebootEscrow rebootEscrow = mInjector.getRebootEscrow();
@@ -255,14 +289,18 @@ class RebootEscrowManager {
return false;
}
- RebootEscrowData escrowData = mPendingRebootEscrowData;
- if (escrowData == null) {
+ RebootEscrowKey escrowKey;
+ synchronized (mKeyGenerationLock) {
+ escrowKey = mPendingRebootEscrowKey;
+ }
+
+ if (escrowKey == null) {
return false;
}
boolean armedRebootEscrow = false;
try {
- rebootEscrow.storeKey(escrowData.getKey());
+ rebootEscrow.storeKey(escrowKey.getKeyBytes());
armedRebootEscrow = true;
} catch (RemoteException e) {
Slog.w(TAG, "Failed escrow secret to RebootEscrow HAL", e);
@@ -283,7 +321,7 @@ class RebootEscrowManager {
}
clearRebootEscrowIfNeeded();
- mRebootEscrowWanted.set(true);
+ mRebootEscrowWanted = true;
return true;
}
diff --git a/services/core/java/com/android/server/media/MediaKeyDispatcher.java b/services/core/java/com/android/server/media/MediaKeyDispatcher.java
new file mode 100644
index 000000000000..16b9eb910ec1
--- /dev/null
+++ b/services/core/java/com/android/server/media/MediaKeyDispatcher.java
@@ -0,0 +1,39 @@
+/*
+ * Copyright 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.media;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.media.session.MediaSession;
+import android.view.KeyEvent;
+
+/**
+ * Provides a way to customize behavior for media key events.
+ */
+public interface MediaKeyDispatcher {
+ /**
+ * Implement this to customize the logic for which MediaSession should consume which key event.
+ *
+ * @param keyEvent a non-null KeyEvent whose key code is one of the supported media buttons.
+ * @param asSystemService {@code true} if the event came from the system service via hardware
+ * devices. {@code false} if the event came from the app process through key injection.
+ * @return a {@link MediaSession.Token} instance that should consume the given key event.
+ */
+ @Nullable
+ MediaSession.Token getSessionForKeyEvent(@NonNull KeyEvent keyEvent,
+ boolean asSystemService);
+}
diff --git a/services/core/java/com/android/server/media/MediaSession2Record.java b/services/core/java/com/android/server/media/MediaSession2Record.java
index b21d2e789555..820731d20c00 100644
--- a/services/core/java/com/android/server/media/MediaSession2Record.java
+++ b/services/core/java/com/android/server/media/MediaSession2Record.java
@@ -50,15 +50,18 @@ public class MediaSession2Record implements MediaSessionRecordImpl {
private final MediaSessionService mService;
@GuardedBy("mLock")
private boolean mIsConnected;
+ @GuardedBy("mLock")
+ private int mPolicies;
public MediaSession2Record(Session2Token sessionToken, MediaSessionService service,
- Looper handlerLooper) {
+ Looper handlerLooper, int policies) {
mSessionToken = sessionToken;
mService = service;
mHandlerExecutor = new HandlerExecutor(new Handler(handlerLooper));
mController = new MediaController2.Builder(service.getContext(), sessionToken)
.setControllerCallback(mHandlerExecutor, new Controller2Callback())
.build();
+ mPolicies = policies;
}
@Override
@@ -129,6 +132,21 @@ public class MediaSession2Record implements MediaSessionRecordImpl {
return false;
}
+
+ @Override
+ public int getSessionPolicies() {
+ synchronized (mLock) {
+ return mPolicies;
+ }
+ }
+
+ @Override
+ public void setSessionPolicies(int policies) {
+ synchronized (mLock) {
+ mPolicies = policies;
+ }
+ }
+
@Override
public void dump(PrintWriter pw, String prefix) {
pw.println(prefix + "token=" + mSessionToken);
diff --git a/services/core/java/com/android/server/media/MediaSessionRecord.java b/services/core/java/com/android/server/media/MediaSessionRecord.java
index 46fb24048a20..05f7e1d7c3a5 100644
--- a/services/core/java/com/android/server/media/MediaSessionRecord.java
+++ b/services/core/java/com/android/server/media/MediaSessionRecord.java
@@ -159,9 +159,12 @@ public class MediaSessionRecord implements IBinder.DeathRecipient, MediaSessionR
private long mDuration = -1;
private String mMetadataDescription;
+ private int mPolicies;
+
public MediaSessionRecord(int ownerPid, int ownerUid, int userId, String ownerPackageName,
ISessionCallback cb, String tag, Bundle sessionInfo,
- MediaSessionService service, Looper handlerLooper) throws RemoteException {
+ MediaSessionService service, Looper handlerLooper, int policies)
+ throws RemoteException {
mOwnerPid = ownerPid;
mOwnerUid = ownerUid;
mUserId = userId;
@@ -178,6 +181,7 @@ public class MediaSessionRecord implements IBinder.DeathRecipient, MediaSessionR
mAudioManager = (AudioManager) mContext.getSystemService(Context.AUDIO_SERVICE);
mAudioManagerInternal = LocalServices.getService(AudioManagerInternal.class);
mAudioAttrs = DEFAULT_ATTRIBUTES;
+ mPolicies = policies;
// May throw RemoteException if the session app is killed.
mSessionCb.mCb.asBinder().linkToDeath(this, 0);
@@ -438,6 +442,20 @@ public class MediaSessionRecord implements IBinder.DeathRecipient, MediaSessionR
}
@Override
+ public int getSessionPolicies() {
+ synchronized (mLock) {
+ return mPolicies;
+ }
+ }
+
+ @Override
+ public void setSessionPolicies(int policies) {
+ synchronized (mLock) {
+ mPolicies = policies;
+ }
+ }
+
+ @Override
public void dump(PrintWriter pw, String prefix) {
pw.println(prefix + mTag + " " + this);
@@ -808,6 +826,9 @@ public class MediaSessionRecord implements IBinder.DeathRecipient, MediaSessionR
@Override
public void setMediaButtonReceiver(PendingIntent pi) throws RemoteException {
+ if ((mPolicies & SessionPolicyProvider.SESSION_POLICY_IGNORE_BUTTON_RECEIVER) == 1) {
+ return;
+ }
mMediaButtonReceiver = pi;
final long token = Binder.clearCallingIdentity();
try {
diff --git a/services/core/java/com/android/server/media/MediaSessionRecordImpl.java b/services/core/java/com/android/server/media/MediaSessionRecordImpl.java
index 2cde89a7a6f6..6e1088088ced 100644
--- a/services/core/java/com/android/server/media/MediaSessionRecordImpl.java
+++ b/services/core/java/com/android/server/media/MediaSessionRecordImpl.java
@@ -20,6 +20,8 @@ import android.media.AudioManager;
import android.os.ResultReceiver;
import android.view.KeyEvent;
+import com.android.server.media.SessionPolicyProvider.SessionPolicy;
+
import java.io.PrintWriter;
/**
@@ -128,6 +130,18 @@ public interface MediaSessionRecordImpl extends AutoCloseable {
KeyEvent ke, int sequenceId, ResultReceiver cb);
/**
+ * Get session policies from custom policy provider set when MediaSessionRecord is instantiated.
+ * If custom policy does not exist, will return null.
+ */
+ @SessionPolicy
+ int getSessionPolicies();
+
+ /**
+ * Overwrite session policies that have been set when MediaSessionRecord is instantiated.
+ */
+ void setSessionPolicies(@SessionPolicy int policies);
+
+ /**
* Dumps internal state
*
* @param pw print writer
diff --git a/services/core/java/com/android/server/media/MediaSessionService.java b/services/core/java/com/android/server/media/MediaSessionService.java
index a6ad57a7ae3a..d0efef041180 100644
--- a/services/core/java/com/android/server/media/MediaSessionService.java
+++ b/services/core/java/com/android/server/media/MediaSessionService.java
@@ -141,6 +141,9 @@ public class MediaSessionService extends SystemService implements Monitor {
final RemoteCallbackList<IRemoteVolumeController> mRemoteVolumeControllers =
new RemoteCallbackList<>();
+ private SessionPolicyProvider mCustomSessionPolicyProvider;
+ private MediaKeyDispatcher mCustomMediaKeyDispatcher;
+
public MediaSessionService(Context context) {
super(context);
mContext = context;
@@ -179,6 +182,9 @@ public class MediaSessionService extends SystemService implements Monitor {
mSettingsObserver.observe();
mHasFeatureLeanback = mContext.getPackageManager().hasSystemFeature(
PackageManager.FEATURE_LEANBACK);
+
+ // TODO: (jinpark) check if config value for custom MediaKeyDispatcher and
+ // SessionPolicyProvider have been overlayed and instantiate using reflection.
updateUser();
}
@@ -555,7 +561,8 @@ public class MediaSessionService extends SystemService implements Monitor {
* 4. It needs to be added to the relevant user record.
*/
private MediaSessionRecord createSessionInternal(int callerPid, int callerUid, int userId,
- String callerPackageName, ISessionCallback cb, String tag, Bundle sessionInfo) {
+ String callerPackageName, ISessionCallback cb, String tag, Bundle sessionInfo,
+ int policies) {
synchronized (mLock) {
FullUserRecord user = getFullUserRecordLocked(userId);
if (user == null) {
@@ -566,7 +573,8 @@ public class MediaSessionService extends SystemService implements Monitor {
final MediaSessionRecord session;
try {
session = new MediaSessionRecord(callerPid, callerUid, userId,
- callerPackageName, cb, tag, sessionInfo, this, mHandler.getLooper());
+ callerPackageName, cb, tag, sessionInfo, this, mHandler.getLooper(),
+ policies);
} catch (RemoteException e) {
throw new RuntimeException("Media Session owner died prematurely.", e);
}
@@ -1127,8 +1135,11 @@ public class MediaSessionService extends SystemService implements Monitor {
if (cb == null) {
throw new IllegalArgumentException("Controller callback cannot be null");
}
+ int policies = (mCustomSessionPolicyProvider != null)
+ ? mCustomSessionPolicyProvider.getSessionPoliciesForApplication(
+ uid, packageName) : 0;
return createSessionInternal(pid, uid, resolvedUserId, packageName, cb, tag,
- sessionInfo).getSessionBinder();
+ sessionInfo, policies).getSessionBinder();
} finally {
Binder.restoreCallingIdentity(token);
}
@@ -1148,7 +1159,7 @@ public class MediaSessionService extends SystemService implements Monitor {
+ " but actually=" + sessionToken.getUid());
}
MediaSession2Record record = new MediaSession2Record(
- sessionToken, MediaSessionService.this, mHandler.getLooper());
+ sessionToken, MediaSessionService.this, mHandler.getLooper(), 0);
synchronized (mLock) {
FullUserRecord user = getFullUserRecordLocked(record.getUserId());
user.mPriorityStack.addSession(record);
@@ -1308,12 +1319,11 @@ public class MediaSessionService extends SystemService implements Monitor {
* ACTION_MEDIA_BUTTON intent to the rest of the system.
*
* @param packageName The caller package
- * @param asSystemService {@code true} if the event sent to the session as if it was come
- * from the system service instead of the app process. This helps sessions to
- * distinguish between the key injection by the app and key events from the
- * hardware devices. Should be used only when the volume key events aren't handled
- * by foreground activity. {@code false} otherwise to tell session about the real
- * caller.
+ * @param asSystemService {@code true} if the event sent to the session came from the
+ * service instead of the app process. This helps sessions to distinguish between
+ * the key injection by the app and key events from the hardware devices. Should be
+ * used only when the hardware key events aren't handled by foreground activity.
+ * {@code false} otherwise to tell session about the real caller.
* @param keyEvent a non-null KeyEvent whose key code is one of the
* supported media buttons
* @param needWakeLock true if a PARTIAL_WAKE_LOCK needs to be held
@@ -2115,14 +2125,27 @@ public class MediaSessionService extends SystemService implements Monitor {
// TODO(jaewan): Implement
return;
}
- MediaSessionRecord session =
- (MediaSessionRecord) mCurrentFullUserRecord.getMediaButtonSessionLocked();
+ MediaSessionRecord session = null;
+
+ // Retrieve custom session for key event if it exists.
+ if (mCustomMediaKeyDispatcher != null) {
+ MediaSession.Token token =
+ mCustomMediaKeyDispatcher.getSessionForKeyEvent(keyEvent, asSystemService);
+ if (token != null) {
+ session = getMediaSessionRecordLocked(token);
+ }
+ }
+
+ if (session == null) {
+ session = (MediaSessionRecord) mCurrentFullUserRecord.getMediaButtonSessionLocked();
+ }
+
if (session != null) {
if (DEBUG_KEY_EVENT) {
Log.d(TAG, "Sending " + keyEvent + " to " + session);
}
if (needWakeLock) {
- mKeyEventReceiver.aquireWakeLockLocked();
+ mKeyEventReceiver.acquireWakeLockLocked();
}
// If we don't need a wakelock use -1 as the id so we won't release it later.
session.sendMediaButton(packageName, pid, uid, asSystemService, keyEvent,
@@ -2140,7 +2163,7 @@ public class MediaSessionService extends SystemService implements Monitor {
} else if (mCurrentFullUserRecord.mLastMediaButtonReceiver != null
|| mCurrentFullUserRecord.mRestoredMediaButtonReceiver != null) {
if (needWakeLock) {
- mKeyEventReceiver.aquireWakeLockLocked();
+ mKeyEventReceiver.acquireWakeLockLocked();
}
Intent mediaButtonIntent = new Intent(Intent.ACTION_MEDIA_BUTTON);
mediaButtonIntent.addFlags(Intent.FLAG_RECEIVER_FOREGROUND);
@@ -2349,7 +2372,7 @@ public class MediaSessionService extends SystemService implements Monitor {
}
}
- public void aquireWakeLockLocked() {
+ public void acquireWakeLockLocked() {
if (mRefCount == 0) {
mMediaEventWakeLock.acquire();
}
diff --git a/services/core/java/com/android/server/media/MediaSessionStack.java b/services/core/java/com/android/server/media/MediaSessionStack.java
index 7bb7cf4b74ad..07b1a1acf466 100644
--- a/services/core/java/com/android/server/media/MediaSessionStack.java
+++ b/services/core/java/com/android/server/media/MediaSessionStack.java
@@ -274,6 +274,15 @@ class MediaSessionStack {
}
private void updateMediaButtonSession(MediaSessionRecordImpl newMediaButtonSession) {
+ // Check if the policy states that this session should not be updated as a media button
+ // session.
+ if (newMediaButtonSession != null) {
+ int policies = newMediaButtonSession.getSessionPolicies();
+ if ((policies & SessionPolicyProvider.SESSION_POLICY_IGNORE_BUTTON_SESSION) == 1) {
+ return;
+ }
+ }
+
MediaSessionRecordImpl oldMediaButtonSession = mMediaButtonSession;
mMediaButtonSession = newMediaButtonSession;
mOnMediaButtonSessionChangedListener.onMediaButtonSessionChanged(
diff --git a/services/core/java/com/android/server/media/SessionPolicyProvider.java b/services/core/java/com/android/server/media/SessionPolicyProvider.java
new file mode 100644
index 000000000000..6eb79ef8dd71
--- /dev/null
+++ b/services/core/java/com/android/server/media/SessionPolicyProvider.java
@@ -0,0 +1,63 @@
+/*
+ * Copyright 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.media;
+
+import android.annotation.IntDef;
+import android.annotation.NonNull;
+import android.media.session.MediaSession;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+
+/**
+ * Interface for customizing {@link MediaSessionService}
+ */
+public interface SessionPolicyProvider {
+ @IntDef(value = {
+ SESSION_POLICY_IGNORE_BUTTON_RECEIVER,
+ SESSION_POLICY_IGNORE_BUTTON_SESSION
+ })
+ @Retention(RetentionPolicy.SOURCE)
+ @interface SessionPolicy {}
+
+ /**
+ * Policy to ignore media button receiver, to not revive the media app when its media session is
+ * released or the app is dead.
+ *
+ * @see MediaSession#setMediaButtonReceiver
+ */
+ int SESSION_POLICY_IGNORE_BUTTON_RECEIVER = 1 << 0;
+
+ /**
+ * Policy to ignore sessions that should not respond to media key events via
+ * {@link MediaSessionService}. A typical use case is to explicitly
+ * ignore sessions that should not respond to media key events even if their playback state has
+ * changed most recently.
+ */
+ int SESSION_POLICY_IGNORE_BUTTON_SESSION = 1 << 1;
+
+ /**
+ * Use this to statically set policies for sessions when they are created.
+ * Use android.media.session.MediaSessionManager#setSessionPolicies(MediaSession.Token, int)
+ * to dynamically change policies at runtime.
+ *
+ * @param uid
+ * @param packageName
+ * @return list of policies
+ */
+ @SessionPolicy int getSessionPoliciesForApplication(int uid, @NonNull String packageName);
+}
diff --git a/services/core/java/com/android/server/notification/NotificationRecordLoggerImpl.java b/services/core/java/com/android/server/notification/NotificationRecordLoggerImpl.java
index 00b4c2b060ac..d61379921f2e 100644
--- a/services/core/java/com/android/server/notification/NotificationRecordLoggerImpl.java
+++ b/services/core/java/com/android/server/notification/NotificationRecordLoggerImpl.java
@@ -16,7 +16,7 @@
package com.android.server.notification;
-import android.util.StatsLog;
+import com.android.internal.util.FrameworkStatsLog;
/**
* Standard implementation of NotificationRecordLogger interface.
@@ -31,7 +31,7 @@ public class NotificationRecordLoggerImpl implements NotificationRecordLogger {
if (!p.shouldLog(buzzBeepBlink)) {
return;
}
- StatsLog.write(StatsLog.NOTIFICATION_REPORTED,
+ FrameworkStatsLog.write(FrameworkStatsLog.NOTIFICATION_REPORTED,
/* int32 event_id = 1 */ p.getUiEvent().getId(),
/* int32 uid = 2 */ r.getUid(),
/* string package_name = 3 */ r.sbn.getPackageName(),
diff --git a/services/core/java/com/android/server/pm/ApexManager.java b/services/core/java/com/android/server/pm/ApexManager.java
index 4ff37a2040dd..42bc464e943f 100644
--- a/services/core/java/com/android/server/pm/ApexManager.java
+++ b/services/core/java/com/android/server/pm/ApexManager.java
@@ -291,6 +291,14 @@ public abstract class ApexManager {
public abstract boolean restoreCeData(int userId, int rollbackId, String apexPackageName);
/**
+ * Deletes snapshots of the device encrypted apex data directories for the given
+ * {@code rollbackId}.
+ *
+ * @return boolean true if the delete was successful
+ */
+ public abstract boolean destroyDeSnapshots(int rollbackId);
+
+ /**
* Dumps various state information to the provided {@link PrintWriter} object.
*
* @param pw the {@link PrintWriter} object to send information to.
@@ -720,6 +728,17 @@ public abstract class ApexManager {
}
}
+ @Override
+ public boolean destroyDeSnapshots(int rollbackId) {
+ try {
+ mApexService.destroyDeSnapshots(rollbackId);
+ return true;
+ } catch (Exception e) {
+ Slog.e(TAG, e.getMessage(), e);
+ return false;
+ }
+ }
+
/**
* Dump information about the packages contained in a particular cache
* @param packagesCache the cache to print information about.
@@ -933,6 +952,11 @@ public abstract class ApexManager {
}
@Override
+ public boolean destroyDeSnapshots(int rollbackId) {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
void dump(PrintWriter pw, String packageName) {
// No-op
}
diff --git a/services/core/java/com/android/server/pm/AppsFilter.java b/services/core/java/com/android/server/pm/AppsFilter.java
index 89f24d86192c..3ad120705eb3 100644
--- a/services/core/java/com/android/server/pm/AppsFilter.java
+++ b/services/core/java/com/android/server/pm/AppsFilter.java
@@ -347,6 +347,7 @@ public class AppsFilter {
mForceQueryable.contains(newPkgSetting.appId)
/* shared user that is already force queryable */
|| newPkg.isForceQueryable()
+ || newPkgSetting.forceQueryableOverride
|| (newPkgSetting.isSystem() && (mSystemAppsQueryable
|| ArrayUtils.contains(mForceQueryableByDevicePackageNames,
newPkg.getPackageName())));
diff --git a/services/core/java/com/android/server/pm/DataLoaderManagerService.java b/services/core/java/com/android/server/pm/DataLoaderManagerService.java
index 50cd0fcbbb17..4fc9e909a774 100644
--- a/services/core/java/com/android/server/pm/DataLoaderManagerService.java
+++ b/services/core/java/com/android/server/pm/DataLoaderManagerService.java
@@ -225,9 +225,6 @@ public class DataLoaderManagerService extends SystemService {
private void remove() {
synchronized (mLock) {
mServiceConnections.remove(mId);
- if (mServiceConnections.size() == 0) {
- mServiceConnections = null;
- }
}
mParams.clear();
}
diff --git a/services/core/java/com/android/server/pm/PackageInstallerService.java b/services/core/java/com/android/server/pm/PackageInstallerService.java
index c17ad1119631..9116c4032ad5 100644
--- a/services/core/java/com/android/server/pm/PackageInstallerService.java
+++ b/services/core/java/com/android/server/pm/PackageInstallerService.java
@@ -635,6 +635,13 @@ public class PackageInstallerService extends IPackageInstaller.Stub implements
stageCid = buildExternalStageCid(sessionId);
}
}
+
+ // reset the force queryable param if it's not called by an approved caller.
+ if (params.forceQueryableOverride) {
+ if (callingUid != Process.SHELL_UID && callingUid != Process.ROOT_UID) {
+ params.forceQueryableOverride = false;
+ }
+ }
InstallSource installSource = InstallSource.create(installerPackageName,
originatingPackageName, requestedInstallerPackageName);
session = new PackageInstallerSession(mInternalCallback, mContext, mPm, this,
diff --git a/services/core/java/com/android/server/pm/PackageInstallerSession.java b/services/core/java/com/android/server/pm/PackageInstallerSession.java
index 38da8ab26962..bf7bebd10a13 100644
--- a/services/core/java/com/android/server/pm/PackageInstallerSession.java
+++ b/services/core/java/com/android/server/pm/PackageInstallerSession.java
@@ -101,7 +101,6 @@ import android.os.RevocableFileDescriptor;
import android.os.SystemProperties;
import android.os.UserHandle;
import android.os.incremental.IncrementalFileStorages;
-import android.os.incremental.IncrementalManager;
import android.os.storage.StorageManager;
import android.provider.Settings.Secure;
import android.stats.devicepolicy.DevicePolicyEnums;
@@ -559,17 +558,6 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub {
mStagedSessionErrorMessage =
stagedSessionErrorMessage != null ? stagedSessionErrorMessage : "";
- // TODO(b/136132412): sanity check if session should not be incremental
- if (!params.isStaged && isIncrementalInstallation()) {
- IncrementalManager incrementalManager = (IncrementalManager) mContext.getSystemService(
- Context.INCREMENTAL_SERVICE);
- if (incrementalManager != null) {
- mIncrementalFileStorages =
- new IncrementalFileStorages(mPackageName, stageDir, incrementalManager,
- params.dataLoaderParams);
- }
- }
-
if (isStreamingInstallation()
&& this.params.dataLoaderParams.getComponentName().getPackageName()
== SYSTEM_DATA_LOADER_PACKAGE) {
@@ -1040,10 +1028,6 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub {
}
}
- if (mIncrementalFileStorages != null) {
- mIncrementalFileStorages.finishSetUp();
- }
-
dispatchStreamValidateAndCommit();
}
@@ -1052,11 +1036,6 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub {
}
private void handleStreamValidateAndCommit() {
- // TODO(b/136132412): update with new APIs
- if (mIncrementalFileStorages != null) {
- mIncrementalFileStorages.startLoading();
- }
-
boolean success = streamValidateAndCommit();
if (isMultiPackage()) {
@@ -2476,17 +2455,15 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub {
}
}
- if (mIncrementalFileStorages != null) {
- for (InstallationFile file : addedFiles) {
- try {
- mIncrementalFileStorages.addFile(file);
- } catch (IOException ex) {
- // TODO(b/146080380): add incremental-specific error code
- throw new PackageManagerException(INSTALL_FAILED_INTERNAL_ERROR,
- "Failed to add and configure Incremental File: " + file.getName(), ex);
- }
+ // TODO(b/136132412): update with new APIs
+ if (isIncrementalInstallation()) {
+ try {
+ mIncrementalFileStorages = IncrementalFileStorages.initialize(mContext,
+ stageDir, params.dataLoaderParams, addedFiles);
+ return true;
+ } catch (IOException e) {
+ throw new PackageManagerException(e);
}
- return true;
}
final DataLoaderManager dataLoaderManager = mContext.getSystemService(
@@ -2761,13 +2738,14 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub {
bridge.forceClose();
}
}
+ if (mIncrementalFileStorages != null) {
+ mIncrementalFileStorages.cleanUp();
+ mIncrementalFileStorages = null;
+ }
// For staged sessions, we don't delete the directory where the packages have been copied,
// since these packages are supposed to be read on reboot.
// Those dirs are deleted when the staged session has reached a final state.
if (stageDir != null && !params.isStaged) {
- if (mIncrementalFileStorages != null) {
- mIncrementalFileStorages.cleanUp();
- }
try {
mPm.mInstaller.rmPackageDir(stageDir.getAbsolutePath());
} catch (InstallerException ignored) {
@@ -2783,6 +2761,7 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub {
} else {
if (mIncrementalFileStorages != null) {
mIncrementalFileStorages.cleanUp();
+ mIncrementalFileStorages = null;
}
try {
mPm.mInstaller.rmPackageDir(stageDir.getAbsolutePath());
diff --git a/services/core/java/com/android/server/pm/PackageManagerService.java b/services/core/java/com/android/server/pm/PackageManagerService.java
index 148c5adbdce0..93e724e3616c 100644
--- a/services/core/java/com/android/server/pm/PackageManagerService.java
+++ b/services/core/java/com/android/server/pm/PackageManagerService.java
@@ -55,6 +55,7 @@ import static android.content.pm.PackageManager.INSTALL_FAILED_INVALID_APK;
import static android.content.pm.PackageManager.INSTALL_FAILED_INVALID_INSTALL_LOCATION;
import static android.content.pm.PackageManager.INSTALL_FAILED_MISSING_SHARED_LIBRARY;
import static android.content.pm.PackageManager.INSTALL_FAILED_PACKAGE_CHANGED;
+import static android.content.pm.PackageManager.INSTALL_FAILED_PROCESS_NOT_DEFINED;
import static android.content.pm.PackageManager.INSTALL_FAILED_SHARED_USER_INCOMPATIBLE;
import static android.content.pm.PackageManager.INSTALL_FAILED_TEST_ONLY;
import static android.content.pm.PackageManager.INSTALL_FAILED_UPDATE_INCOMPATIBLE;
@@ -117,6 +118,7 @@ import static com.android.server.pm.PackageManagerServiceUtils.dumpCriticalInfo;
import static com.android.server.pm.PackageManagerServiceUtils.getCompressedFiles;
import static com.android.server.pm.PackageManagerServiceUtils.getLastModifiedTime;
import static com.android.server.pm.PackageManagerServiceUtils.logCriticalInfo;
+import static com.android.server.pm.PackageManagerServiceUtils.makeDirRecursive;
import static com.android.server.pm.PackageManagerServiceUtils.verifySignatures;
import android.Manifest;
@@ -205,6 +207,7 @@ import android.content.pm.dex.DexMetadataHelper;
import android.content.pm.dex.IArtManager;
import android.content.pm.parsing.AndroidPackage;
import android.content.pm.parsing.ApkParseUtils;
+import android.content.pm.parsing.ComponentParseUtils;
import android.content.pm.parsing.ComponentParseUtils.ParsedActivity;
import android.content.pm.parsing.ComponentParseUtils.ParsedActivityIntentInfo;
import android.content.pm.parsing.ComponentParseUtils.ParsedComponent;
@@ -361,7 +364,6 @@ import java.io.File;
import java.io.FileDescriptor;
import java.io.FileInputStream;
import java.io.FileOutputStream;
-import java.io.FilenameFilter;
import java.io.IOException;
import java.io.PrintWriter;
import java.lang.annotation.Retention;
@@ -651,6 +653,8 @@ public class PackageManagerService extends IPackageManager.Stub
private static final String[] INSTANT_APP_BROADCAST_PERMISSION =
new String[] { android.Manifest.permission.ACCESS_INSTANT_APPS };
+ private static final String RANDOM_DIR_PREFIX = "~~";
+
final ServiceThread mHandlerThread;
final PackageHandler mHandler;
@@ -1542,6 +1546,7 @@ public class PackageManagerService extends IPackageManager.Stub
final @Nullable String[] mTelephonyPackages;
final @NonNull String mServicesExtensionPackageName;
final @NonNull String mSharedSystemSharedLibraryPackageName;
+ final @Nullable String mRetailDemoPackage;
private final PackageUsage mPackageUsage = new PackageUsage();
private final CompilerStats mCompilerStats = new CompilerStats();
@@ -2996,9 +3001,6 @@ public class PackageManagerService extends IPackageManager.Stub
}
}
- //delete tmp files
- deleteTempPackageFiles();
-
final int cachedSystemApps = PackageParser.sCachedPackageReadCount.get();
// Remove any shared userIDs that have no associated packages
@@ -3161,6 +3163,7 @@ public class PackageManagerService extends IPackageManager.Stub
mAppPredictionServicePackage = getAppPredictionServicePackageName();
mIncidentReportApproverPackage = getIncidentReportApproverPackageName();
mTelephonyPackages = getTelephonyPackageNames();
+ mRetailDemoPackage = getRetailDemoPackageName();
// Now that we know all of the shared libraries, update all clients to have
// the correct library paths.
@@ -3591,8 +3594,7 @@ public class PackageManagerService extends IPackageManager.Stub
getNextCodePath(Environment.getDataAppDirectory(null), packageName);
int ret = PackageManager.INSTALL_SUCCEEDED;
try {
- Os.mkdir(dstCodePath.getAbsolutePath(), 0755);
- Os.chmod(dstCodePath.getAbsolutePath(), 0755);
+ makeDirRecursive(dstCodePath, 0755);
for (File srcFile : compressedFiles) {
final String srcFileName = srcFile.getName();
final String dstFileName = srcFileName.substring(
@@ -9852,8 +9854,12 @@ public class PackageManagerService extends IPackageManager.Stub
@GuardedBy("mInstallLock")
void removeCodePathLI(File codePath) {
if (codePath.isDirectory()) {
+ File codePathParent = codePath.getParentFile();
try {
mInstaller.rmPackageDir(codePath.getAbsolutePath());
+ if (codePathParent.getName().startsWith(RANDOM_DIR_PREFIX)) {
+ mInstaller.rmPackageDir(codePathParent.getAbsolutePath());
+ }
} catch (InstallerException e) {
Slog.w(TAG, "Failed to remove code path", e);
}
@@ -10570,6 +10576,10 @@ public class PackageManagerService extends IPackageManager.Stub
if (pkgSetting.sharedUser != null) {
pkgSetting.sharedUser.addPackage(pkgSetting);
}
+ if (reconciledPkg.installArgs != null && reconciledPkg.installArgs.forceQueryableOverride) {
+ pkgSetting.forceQueryableOverride = true;
+ }
+
// TODO(toddke): Consider a method specifically for modifying the Package object
// post scan; or, moving this stuff out of the Package object since it has nothing
// to do with the package on disk.
@@ -11246,6 +11256,26 @@ public class PackageManagerService extends IPackageManager.Stub
return object;
}
+ private <T extends ComponentParseUtils.ParsedMainComponent>
+ void assertPackageProcesses(AndroidPackage pkg, List<T> components,
+ ArrayMap<String, ComponentParseUtils.ParsedProcess> procs, String compName)
+ throws PackageManagerException {
+ if (components == null) {
+ return;
+ }
+ for (int i = components.size() - 1; i >= 0; i--) {
+ final ComponentParseUtils.ParsedMainComponent<?> component = components.get(i);
+ if (!procs.containsKey(component.getProcessName())) {
+ throw new PackageManagerException(
+ INSTALL_FAILED_PROCESS_NOT_DEFINED,
+ "Can't install because " + compName + " " + component.className
+ + "'s process attribute " + component.getProcessName()
+ + " (in package " + pkg.getPackageName()
+ + ") is not included in the <processes> list");
+ }
+ }
+ }
+
/**
* Asserts the parsed package is valid according to the given policy. If the
* package is invalid, for whatever reason, throws {@link PackageManagerException}.
@@ -11475,6 +11505,24 @@ public class PackageManagerService extends IPackageManager.Stub
mComponentResolver.assertProvidersNotDefined(pkg);
}
+ // If this package has defined explicit processes, then ensure that these are
+ // the only processes used by its components.
+ final ArrayMap<String, ComponentParseUtils.ParsedProcess> procs = pkg.getProcesses();
+ if (procs != null) {
+ if (!procs.containsKey(pkg.getProcessName())) {
+ throw new PackageManagerException(
+ INSTALL_FAILED_PROCESS_NOT_DEFINED,
+ "Can't install because application tag's process attribute "
+ + pkg.getProcessName()
+ + " (in package " + pkg.getPackageName()
+ + ") is not included in the <processes> list");
+ }
+ assertPackageProcesses(pkg, pkg.getActivities(), procs, "activity");
+ assertPackageProcesses(pkg, pkg.getServices(), procs, "service");
+ assertPackageProcesses(pkg, pkg.getReceivers(), procs, "receiver");
+ assertPackageProcesses(pkg, pkg.getProviders(), procs, "provider");
+ }
+
// Verify that packages sharing a user with a privileged app are marked as privileged.
if (!pkg.isPrivileged() && (pkg.getSharedUserId() != null)) {
SharedUserSetting sharedUserSetting = null;
@@ -14063,6 +14111,7 @@ public class PackageManagerService extends IPackageManager.Stub
@Nullable
MultiPackageInstallParams mParentInstallParams;
final long requiredInstalledVersionCode;
+ final boolean forceQueryableOverride;
InstallParams(OriginInfo origin, MoveInfo move, IPackageInstallObserver2 observer,
int installFlags, InstallSource installSource, String volumeUuid,
@@ -14084,6 +14133,7 @@ public class PackageManagerService extends IPackageManager.Stub
this.signingDetails = signingDetails;
this.installReason = installReason;
this.requiredInstalledVersionCode = requiredInstalledVersionCode;
+ this.forceQueryableOverride = false;
}
InstallParams(ActiveInstallSession activeInstallSession) {
@@ -14117,6 +14167,7 @@ public class PackageManagerService extends IPackageManager.Stub
signingDetails = activeInstallSession.getSigningDetails();
requiredInstalledVersionCode = activeInstallSession.getSessionParams()
.requiredInstalledVersionCode;
+ forceQueryableOverride = activeInstallSession.getSessionParams().forceQueryableOverride;
}
@Override
@@ -14717,6 +14768,7 @@ public class PackageManagerService extends IPackageManager.Stub
final int traceCookie;
final PackageParser.SigningDetails signingDetails;
final int installReason;
+ final boolean forceQueryableOverride;
@Nullable final MultiPackageInstallParams mMultiPackageInstallParams;
// The list of instruction sets supported by this app. This is currently
@@ -14730,7 +14782,7 @@ public class PackageManagerService extends IPackageManager.Stub
String abiOverride, String[] installGrantPermissions,
List<String> whitelistedRestrictedPermissions,
String traceMethod, int traceCookie, SigningDetails signingDetails,
- int installReason,
+ int installReason, boolean forceQueryableOverride,
MultiPackageInstallParams multiPackageInstallParams) {
this.origin = origin;
this.move = move;
@@ -14747,6 +14799,7 @@ public class PackageManagerService extends IPackageManager.Stub
this.traceCookie = traceCookie;
this.signingDetails = signingDetails;
this.installReason = installReason;
+ this.forceQueryableOverride = forceQueryableOverride;
this.mMultiPackageInstallParams = multiPackageInstallParams;
}
@@ -14757,7 +14810,7 @@ public class PackageManagerService extends IPackageManager.Stub
params.getUser(), null /*instructionSets*/, params.packageAbiOverride,
params.grantedRuntimePermissions, params.whitelistedRestrictedPermissions,
params.traceMethod, params.traceCookie, params.signingDetails,
- params.installReason, params.mParentInstallParams);
+ params.installReason, params.forceQueryableOverride, params.mParentInstallParams);
}
abstract int copyApk();
@@ -14848,7 +14901,7 @@ public class PackageManagerService extends IPackageManager.Stub
super(OriginInfo.fromNothing(), null, null, 0, InstallSource.EMPTY,
null, null, instructionSets, null, null, null, null, 0,
PackageParser.SigningDetails.UNKNOWN,
- PackageManager.INSTALL_REASON_UNKNOWN, null /* parent */);
+ PackageManager.INSTALL_REASON_UNKNOWN, false, null /* parent */);
this.codeFile = (codePath != null) ? new File(codePath) : null;
this.resourceFile = (resourcePath != null) ? new File(resourcePath) : null;
}
@@ -14926,7 +14979,9 @@ public class PackageManagerService extends IPackageManager.Stub
final boolean onIncremental = mIncrementalManager != null
&& isIncrementalPath(beforeCodeFile.getAbsolutePath());
try {
+ makeDirRecursive(afterCodeFile.getParentFile(), 0775);
if (onIncremental) {
+ // TODO(b/147371381): fix incremental installation
mIncrementalManager.rename(beforeCodeFile.getAbsolutePath(),
afterCodeFile.getAbsolutePath());
} else {
@@ -15138,16 +15193,29 @@ public class PackageManagerService extends IPackageManager.Stub
}
}
+ /**
+ * Given {@code targetDir}, returns {@code targetDir/~~[randomStrA]/[packageName]-[randomStrB].}
+ * Makes sure that {@code targetDir/~~[randomStrA]} directory doesn't exist.
+ * Notice that this method doesn't actually create any directory.
+ *
+ * @param targetDir Directory that is two-levels up from the result directory.
+ * @param packageName Name of the package whose code files are to be installed under the result
+ * directory.
+ * @return File object for the directory that should hold the code files of {@code packageName}.
+ */
private File getNextCodePath(File targetDir, String packageName) {
- File result;
SecureRandom random = new SecureRandom();
byte[] bytes = new byte[16];
+ File firstLevelDir;
do {
random.nextBytes(bytes);
- String suffix = Base64.encodeToString(bytes, Base64.URL_SAFE | Base64.NO_WRAP);
- result = new File(targetDir, packageName + "-" + suffix);
- } while (result.exists());
- return result;
+ String dirName = RANDOM_DIR_PREFIX
+ + Base64.encodeToString(bytes, Base64.URL_SAFE | Base64.NO_WRAP);
+ firstLevelDir = new File(targetDir, dirName);
+ } while (firstLevelDir.exists());
+ random.nextBytes(bytes);
+ String suffix = Base64.encodeToString(bytes, Base64.URL_SAFE | Base64.NO_WRAP);
+ return new File(firstLevelDir, packageName + "-" + suffix);
}
static class PackageInstalledInfo {
@@ -17142,12 +17210,6 @@ public class PackageManagerService extends IPackageManager.Stub
}
}
- private void deleteTempPackageFiles() {
- // TODO: Is this used?
- final FilenameFilter filter =
- (dir, name) -> name.startsWith("vmdl") && name.endsWith(".tmp");
- }
-
@Override
public void deletePackageAsUser(String packageName, int versionCode,
IPackageDeleteObserver observer, int userId, int flags) {
@@ -19703,6 +19765,41 @@ public class PackageManagerService extends IPackageManager.Stub
}
@Nullable
+ private String getRetailDemoPackageName() {
+ final String predefinedPkgName = mContext.getString(R.string.config_retailDemoPackage);
+ final String predefinedSignature = mContext.getString(
+ R.string.config_retailDemoPackageSignature);
+
+ if (TextUtils.isEmpty(predefinedPkgName) || TextUtils.isEmpty(predefinedSignature)) {
+ return null;
+ }
+
+ final AndroidPackage androidPkg = mPackages.get(predefinedPkgName);
+ if (androidPkg != null) {
+ final SigningDetails signingDetail = androidPkg.getSigningDetails();
+ if (signingDetail != null && signingDetail.signatures != null) {
+ try {
+ final MessageDigest msgDigest = MessageDigest.getInstance("SHA-256");
+ for (Signature signature : signingDetail.signatures) {
+ if (TextUtils.equals(predefinedSignature,
+ HexEncoding.encodeToString(msgDigest.digest(
+ signature.toByteArray()), false))) {
+ return predefinedPkgName;
+ }
+ }
+ } catch (NoSuchAlgorithmException e) {
+ Slog.e(
+ TAG,
+ "Unable to verify signatures as getting the retail demo package name",
+ e);
+ }
+ }
+ }
+
+ return null;
+ }
+
+ @Nullable
private String ensureSystemPackageName(@Nullable String packageName) {
if (packageName == null) {
return null;
@@ -21477,7 +21574,7 @@ public class PackageManagerService extends IPackageManager.Stub
final int absoluteCodePathCount = absoluteCodePaths.size();
for (int i = 0; i < absoluteCodePathCount; i++) {
String absoluteCodePath = absoluteCodePaths.get(i);
- if (absolutePath.startsWith(absoluteCodePath)) {
+ if (absoluteCodePath.startsWith(absolutePath)) {
pathValid = true;
break;
}
@@ -22990,6 +23087,10 @@ public class PackageManagerService extends IPackageManager.Stub
return filterOnlySystemPackages(mTelephonyPackages);
case PackageManagerInternal.PACKAGE_COMPANION:
return filterOnlySystemPackages("com.android.companiondevicemanager");
+ case PackageManagerInternal.PACKAGE_RETAIL_DEMO:
+ return TextUtils.isEmpty(mRetailDemoPackage)
+ ? ArrayUtils.emptyArray(String.class)
+ : new String[] {mRetailDemoPackage};
default:
return ArrayUtils.emptyArray(String.class);
}
@@ -23835,9 +23936,8 @@ public class PackageManagerService extends IPackageManager.Stub
@Override
public int getRuntimePermissionsVersion(@UserIdInt int userId) {
Preconditions.checkArgumentNonnegative(userId);
- mContext.enforceCallingOrSelfPermission(
- Manifest.permission.ADJUST_RUNTIME_PERMISSIONS_POLICY,
- "setRuntimePermissionVersion");
+ enforceAdjustRuntimePermissionsPolicyOrUpgradeRuntimePermissions(
+ "getRuntimePermissionVersion");
synchronized (mLock) {
return mSettings.getDefaultRuntimePermissionsVersionLPr(userId);
}
@@ -23847,14 +23947,27 @@ public class PackageManagerService extends IPackageManager.Stub
public void setRuntimePermissionsVersion(int version, @UserIdInt int userId) {
Preconditions.checkArgumentNonnegative(version);
Preconditions.checkArgumentNonnegative(userId);
- mContext.enforceCallingOrSelfPermission(
- Manifest.permission.ADJUST_RUNTIME_PERMISSIONS_POLICY,
+ enforceAdjustRuntimePermissionsPolicyOrUpgradeRuntimePermissions(
"setRuntimePermissionVersion");
synchronized (mLock) {
mSettings.setDefaultRuntimePermissionsVersionLPr(version, userId);
}
}
+ private void enforceAdjustRuntimePermissionsPolicyOrUpgradeRuntimePermissions(
+ @NonNull String message) {
+ if (mContext.checkCallingOrSelfPermission(
+ Manifest.permission.ADJUST_RUNTIME_PERMISSIONS_POLICY)
+ != PackageManager.PERMISSION_GRANTED
+ && mContext.checkCallingOrSelfPermission(
+ Manifest.permission.UPGRADE_RUNTIME_PERMISSIONS)
+ != PackageManager.PERMISSION_GRANTED) {
+ throw new SecurityException(message + " requires "
+ + Manifest.permission.ADJUST_RUNTIME_PERMISSIONS_POLICY + " or "
+ + Manifest.permission.UPGRADE_RUNTIME_PERMISSIONS);
+ }
+ }
+
@Nullable
public PackageSetting getPackageSetting(String packageName) {
synchronized (mLock) {
diff --git a/services/core/java/com/android/server/pm/PackageManagerServiceUtils.java b/services/core/java/com/android/server/pm/PackageManagerServiceUtils.java
index ded9a9c58c5e..71a5545c4131 100644
--- a/services/core/java/com/android/server/pm/PackageManagerServiceUtils.java
+++ b/services/core/java/com/android/server/pm/PackageManagerServiceUtils.java
@@ -83,6 +83,7 @@ import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.io.PrintWriter;
+import java.nio.file.Path;
import java.security.cert.CertificateEncodingException;
import java.security.cert.CertificateException;
import java.text.SimpleDateFormat;
@@ -918,4 +919,21 @@ public class PackageManagerServiceUtils {
}
return packageSetting.getPermissionsState();
}
+
+ /**
+ * Recursively create target directory
+ */
+ public static void makeDirRecursive(File targetDir, int mode) throws ErrnoException {
+ final Path targetDirPath = targetDir.toPath();
+ final int directoriesCount = targetDirPath.getNameCount();
+ File currentDir;
+ for (int i = 1; i <= directoriesCount; i++) {
+ currentDir = targetDirPath.subpath(0, i).toFile();
+ if (currentDir.exists()) {
+ continue;
+ }
+ Os.mkdir(currentDir.getAbsolutePath(), mode);
+ Os.chmod(currentDir.getAbsolutePath(), mode);
+ }
+ }
}
diff --git a/services/core/java/com/android/server/pm/PackageManagerShellCommand.java b/services/core/java/com/android/server/pm/PackageManagerShellCommand.java
index e7f6b8982b04..bb69680fb9f9 100644
--- a/services/core/java/com/android/server/pm/PackageManagerShellCommand.java
+++ b/services/core/java/com/android/server/pm/PackageManagerShellCommand.java
@@ -2730,6 +2730,9 @@ class PackageManagerShellCommand extends ShellCommand {
case "--staged":
sessionParams.setStaged();
break;
+ case "--force-queryable":
+ sessionParams.setForceQueryable();
+ break;
case "--enable-rollback":
if (params.installerPackageName == null) {
// com.android.shell has the TEST_MANAGE_ROLLBACKS
diff --git a/services/core/java/com/android/server/pm/PackageSettingBase.java b/services/core/java/com/android/server/pm/PackageSettingBase.java
index f1ac0afa5dfd..7d95b198d8ce 100644
--- a/services/core/java/com/android/server/pm/PackageSettingBase.java
+++ b/services/core/java/com/android/server/pm/PackageSettingBase.java
@@ -36,7 +36,6 @@ import android.util.SparseArray;
import android.util.proto.ProtoOutputStream;
import com.android.internal.annotations.VisibleForTesting;
-import com.android.internal.util.Preconditions;
import java.io.File;
import java.util.Arrays;
@@ -132,6 +131,8 @@ public abstract class PackageSettingBase extends SettingBase {
IntentFilterVerificationInfo verificationInfo;
+ boolean forceQueryableOverride;
+
PackageSettingBase(String name, String realName, File codePath, File resourcePath,
String legacyNativeLibraryPathString, String primaryCpuAbiString,
String secondaryCpuAbiString, String cpuAbiOverrideString,
@@ -261,6 +262,7 @@ public abstract class PackageSettingBase extends SettingBase {
? Arrays.copyOf(orig.usesStaticLibrariesVersions,
orig.usesStaticLibrariesVersions.length) : null;
updateAvailable = orig.updateAvailable;
+ forceQueryableOverride = orig.forceQueryableOverride;
}
@VisibleForTesting
@@ -693,6 +695,7 @@ public abstract class PackageSettingBase extends SettingBase {
this.categoryHint = other.categoryHint;
this.updateAvailable = other.updateAvailable;
this.verificationInfo = other.verificationInfo;
+ this.forceQueryableOverride = other.forceQueryableOverride;
if (mOldCodePaths != null) {
if (other.mOldCodePaths != null) {
diff --git a/services/core/java/com/android/server/pm/Settings.java b/services/core/java/com/android/server/pm/Settings.java
index 5d948b24256a..fbea59570ac0 100644
--- a/services/core/java/com/android/server/pm/Settings.java
+++ b/services/core/java/com/android/server/pm/Settings.java
@@ -2836,6 +2836,9 @@ public final class Settings {
if (pkg.updateAvailable) {
serializer.attribute(null, "updateAvailable", "true");
}
+ if (pkg.forceQueryableOverride) {
+ serializer.attribute(null, "forceQueryable", "true");
+ }
writeUsesStaticLibLPw(serializer, pkg.usesStaticLibraries, pkg.usesStaticLibrariesVersions);
@@ -3599,6 +3602,7 @@ public final class Settings {
PackageSetting packageSetting = null;
String version = null;
long versionCode = 0;
+ String installedForceQueryable = null;
try {
name = parser.getAttributeValue(null, ATTR_NAME);
realName = parser.getAttributeValue(null, "realName");
@@ -3615,6 +3619,7 @@ public final class Settings {
secondaryCpuAbiString = parser.getAttributeValue(null, "secondaryCpuAbi");
cpuAbiOverrideString = parser.getAttributeValue(null, "cpuAbiOverride");
updateAvailable = parser.getAttributeValue(null, "updateAvailable");
+ installedForceQueryable = parser.getAttributeValue(null, "forceQueryable");
if (primaryCpuAbiString == null && legacyCpuAbiString != null) {
primaryCpuAbiString = legacyCpuAbiString;
@@ -3798,6 +3803,7 @@ public final class Settings {
packageSetting.primaryCpuAbiString = primaryCpuAbiString;
packageSetting.secondaryCpuAbiString = secondaryCpuAbiString;
packageSetting.updateAvailable = "true".equals(updateAvailable);
+ packageSetting.forceQueryableOverride = "true".equals(installedForceQueryable);
// Handle legacy string here for single-user mode
final String enabledStr = parser.getAttributeValue(null, ATTR_ENABLED);
if (enabledStr != null) {
@@ -4564,7 +4570,7 @@ public final class Settings {
pw.print("anyDensity");
}
pw.println("]");
- List<String> libraryNames = pkg.getLibraryNames();
+ final List<String> libraryNames = pkg.getLibraryNames();
if (libraryNames != null && libraryNames.size() > 0) {
pw.print(prefix); pw.println(" dynamic libraries:");
for (int i = 0; i< libraryNames.size(); i++) {
@@ -4579,7 +4585,7 @@ public final class Settings {
pw.print(" version:"); pw.println(pkg.getStaticSharedLibVersion());
}
- List<String> usesLibraries = pkg.getUsesLibraries();
+ final List<String> usesLibraries = pkg.getUsesLibraries();
if (usesLibraries != null && usesLibraries.size() > 0) {
pw.print(prefix); pw.println(" usesLibraries:");
for (int i=0; i< usesLibraries.size(); i++) {
@@ -4587,8 +4593,8 @@ public final class Settings {
}
}
- List<String> usesStaticLibraries = pkg.getUsesStaticLibraries();
- long[] usesStaticLibrariesVersions = pkg.getUsesStaticLibrariesVersions();
+ final List<String> usesStaticLibraries = pkg.getUsesStaticLibraries();
+ final long[] usesStaticLibrariesVersions = pkg.getUsesStaticLibrariesVersions();
if (usesStaticLibraries != null
&& usesStaticLibraries.size() > 0) {
pw.print(prefix); pw.println(" usesStaticLibraries:");
@@ -4599,7 +4605,7 @@ public final class Settings {
}
}
- List<String> usesOptionalLibraries = pkg.getUsesOptionalLibraries();
+ final List<String> usesOptionalLibraries = pkg.getUsesOptionalLibraries();
if (usesOptionalLibraries != null
&& usesOptionalLibraries.size() > 0) {
pw.print(prefix); pw.println(" usesOptionalLibraries:");
@@ -4609,7 +4615,7 @@ public final class Settings {
}
}
- String[] usesLibraryFiles = pkg.getUsesLibraryFiles();
+ final String[] usesLibraryFiles = pkg.getUsesLibraryFiles();
if (usesLibraryFiles != null
&& usesLibraryFiles.length > 0) {
pw.print(prefix); pw.println(" usesLibraryFiles:");
@@ -4617,6 +4623,20 @@ public final class Settings {
pw.print(prefix); pw.print(" "); pw.println(usesLibraryFiles[i]);
}
}
+ final ArrayMap<String, ComponentParseUtils.ParsedProcess> procs = pkg.getProcesses();
+ if (procs != null) {
+ pw.print(prefix); pw.println(" processes:");
+ for (int i = 0; i < procs.size(); i++) {
+ final ComponentParseUtils.ParsedProcess proc = procs.valueAt(i);
+ pw.print(prefix); pw.print(" "); pw.println(proc.name);
+ if (proc.deniedPermissions != null) {
+ for (int j = 0; j < proc.deniedPermissions.size(); j++) {
+ pw.print(prefix); pw.print(" deny: ");
+ pw.println(proc.deniedPermissions.valueAt(j));
+ }
+ }
+ }
+ }
}
pw.print(prefix); pw.print(" timeStamp=");
date.setTime(ps.timeStamp);
diff --git a/services/core/java/com/android/server/pm/ShortcutService.java b/services/core/java/com/android/server/pm/ShortcutService.java
index f368666a06ba..f7889ea6141c 100644
--- a/services/core/java/com/android/server/pm/ShortcutService.java
+++ b/services/core/java/com/android/server/pm/ShortcutService.java
@@ -1977,10 +1977,15 @@ public class ShortcutService extends IShortcutService.Stub {
* After validating the caller, it passes the request to {@link #mShortcutRequestPinProcessor}.
* Either {@param shortcut} or {@param appWidget} should be non-null.
*/
- private boolean requestPinItem(String packageName, int userId, ShortcutInfo shortcut,
+ private boolean requestPinItem(String callingPackage, int userId, ShortcutInfo shortcut,
AppWidgetProviderInfo appWidget, Bundle extras, IntentSender resultIntent) {
- verifyCaller(packageName, userId);
- verifyShortcutInfoPackage(packageName, shortcut);
+ verifyCaller(callingPackage, userId);
+ if (shortcut == null || !injectHasAccessShortcutsPermission(
+ injectBinderCallingPid(), injectBinderCallingUid())) {
+ // Verify if caller is the shortcut owner, only if caller doesn't have ACCESS_SHORTCUTS.
+ verifyShortcutInfoPackage(callingPackage, shortcut);
+ }
+ final String shortcutPackage = shortcut.getPackage();
final boolean ret;
synchronized (mLock) {
@@ -1995,13 +2000,13 @@ public class ShortcutService extends IShortcutService.Stub {
// and then proceed the rest of the process.
if (shortcut != null) {
final ShortcutPackage ps = getPackageShortcutsForPublisherLocked(
- packageName, userId);
+ shortcutPackage, userId);
final String id = shortcut.getId();
if (ps.isShortcutExistsAndInvisibleToPublisher(id)) {
ps.updateInvisibleShortcutForPinRequestWith(shortcut);
- packageShortcutsChanged(packageName, userId);
+ packageShortcutsChanged(shortcutPackage, userId);
}
}
diff --git a/services/core/java/com/android/server/pm/permission/BasePermission.java b/services/core/java/com/android/server/pm/permission/BasePermission.java
index 565a85fd1ac1..e323c9869afb 100644
--- a/services/core/java/com/android/server/pm/permission/BasePermission.java
+++ b/services/core/java/com/android/server/pm/permission/BasePermission.java
@@ -284,6 +284,10 @@ public final class BasePermission {
return (protectionLevel & PermissionInfo.PROTECTION_FLAG_COMPANION) != 0;
}
+ public boolean isRetailDemo() {
+ return (protectionLevel & PermissionInfo.PROTECTION_FLAG_RETAIL_DEMO) != 0;
+ }
+
public void transfer(@NonNull String origPackageName, @NonNull String newPackageName) {
if (!origPackageName.equals(sourcePackageName)) {
return;
diff --git a/services/core/java/com/android/server/pm/permission/PermissionManagerService.java b/services/core/java/com/android/server/pm/permission/PermissionManagerService.java
index 6167a509b85f..1fc2dd5193e1 100644
--- a/services/core/java/com/android/server/pm/permission/PermissionManagerService.java
+++ b/services/core/java/com/android/server/pm/permission/PermissionManagerService.java
@@ -55,6 +55,8 @@ import android.annotation.UserIdInt;
import android.app.ActivityManager;
import android.app.ApplicationPackageManager;
import android.app.IActivityManager;
+import android.app.admin.DeviceAdminInfo;
+import android.app.admin.DevicePolicyManagerInternal;
import android.compat.annotation.ChangeId;
import android.compat.annotation.EnabledAfter;
import android.content.Context;
@@ -3355,10 +3357,27 @@ public class PermissionManagerService extends IPermissionManager.Stub {
// Special permissions for the system companion device manager.
allowed = true;
}
+ if (!allowed && bp.isRetailDemo()
+ && ArrayUtils.contains(mPackageManagerInt.getKnownPackageNames(
+ PackageManagerInternal.PACKAGE_RETAIL_DEMO, UserHandle.USER_SYSTEM),
+ pkg.getPackageName()) && isProfileOwner(pkg.getUid())) {
+ // Special permission granted only to the OEM specified retail demo app
+ allowed = true;
+ }
}
return allowed;
}
+ private static boolean isProfileOwner(int uid) {
+ DevicePolicyManagerInternal dpmInternal =
+ LocalServices.getService(DevicePolicyManagerInternal.class);
+ if (dpmInternal != null) {
+ return dpmInternal
+ .isActiveAdminWithPolicy(uid, DeviceAdminInfo.USES_POLICY_PROFILE_OWNER);
+ }
+ return false;
+ }
+
private static boolean canGrantOemPermission(PackageSetting ps, String permission) {
if (!ps.isOem()) {
return false;
diff --git a/services/core/java/com/android/server/policy/PermissionPolicyService.java b/services/core/java/com/android/server/policy/PermissionPolicyService.java
index a86c8d7545b1..2c7795a6274b 100644
--- a/services/core/java/com/android/server/policy/PermissionPolicyService.java
+++ b/services/core/java/com/android/server/policy/PermissionPolicyService.java
@@ -29,6 +29,7 @@ import android.annotation.NonNull;
import android.annotation.Nullable;
import android.annotation.UserIdInt;
import android.app.AppOpsManager;
+import android.app.AppOpsManagerInternal;
import android.content.Context;
import android.content.Intent;
import android.content.pm.ApplicationInfo;
@@ -84,6 +85,8 @@ public final class PermissionPolicyService extends SystemService {
private final Object mLock = new Object();
+ private IAppOpsCallback mAppOpsCallback;
+
/** Whether the user is started but not yet stopped */
@GuardedBy("mLock")
private final SparseBooleanArray mIsStarted = new SparseBooleanArray();
@@ -138,7 +141,7 @@ public final class PermissionPolicyService extends SystemService {
permManagerInternal.addOnRuntimePermissionStateChangedListener(
this::synchronizePackagePermissionsAndAppOpsAsyncForUser);
- IAppOpsCallback appOpsListener = new IAppOpsCallback.Stub() {
+ mAppOpsCallback = new IAppOpsCallback.Stub() {
public void opChanged(int op, int uid, String packageName) {
synchronizePackagePermissionsAndAppOpsAsyncForUser(packageName,
UserHandle.getUserId(uid));
@@ -155,7 +158,7 @@ public final class PermissionPolicyService extends SystemService {
PermissionInfo perm = dangerousPerms.get(i);
if (perm.isRuntime()) {
- appOpsService.startWatchingMode(getSwitchOp(perm.name), null, appOpsListener);
+ appOpsService.startWatchingMode(getSwitchOp(perm.name), null, mAppOpsCallback);
}
if (perm.isSoftRestricted()) {
SoftRestrictedPermissionPolicy policy =
@@ -163,7 +166,7 @@ public final class PermissionPolicyService extends SystemService {
perm.name);
int extraAppOp = policy.getExtraAppOpCode();
if (extraAppOp != OP_NONE) {
- appOpsService.startWatchingMode(extraAppOp, null, appOpsListener);
+ appOpsService.startWatchingMode(extraAppOp, null, mAppOpsCallback);
}
}
}
@@ -386,10 +389,11 @@ public final class PermissionPolicyService extends SystemService {
* Synchronizes permission to app ops. You *must* always sync all packages
* in a shared UID at the same time to ensure proper synchronization.
*/
- private static class PermissionToOpSynchroniser {
+ private class PermissionToOpSynchroniser {
private final @NonNull Context mContext;
private final @NonNull PackageManager mPackageManager;
private final @NonNull AppOpsManager mAppOpsManager;
+ private final @NonNull AppOpsManagerInternal mAppOpsManagerInternal;
private final @NonNull ArrayMap<String, PermissionInfo> mRuntimePermissionInfos;
@@ -429,6 +433,7 @@ public final class PermissionPolicyService extends SystemService {
mContext = context;
mPackageManager = context.getPackageManager();
mAppOpsManager = context.getSystemService(AppOpsManager.class);
+ mAppOpsManagerInternal = LocalServices.getService(AppOpsManagerInternal.class);
mRuntimePermissionInfos = new ArrayMap<>();
PermissionManagerServiceInternal permissionManagerInternal = LocalServices.getService(
@@ -668,7 +673,8 @@ public final class PermissionPolicyService extends SystemService {
opCode), uid, packageName);
if (currentMode != MODE_ALLOWED) {
if (currentMode != MODE_IGNORED) {
- mAppOpsManager.setUidMode(opCode, uid, MODE_IGNORED);
+ mAppOpsManagerInternal.setUidModeIgnoringCallback(opCode, uid, MODE_IGNORED,
+ mAppOpsCallback);
}
return true;
}
@@ -680,15 +686,16 @@ public final class PermissionPolicyService extends SystemService {
final int oldMode = mAppOpsManager.unsafeCheckOpRaw(AppOpsManager.opToPublicName(
opCode), uid, packageName);
if (oldMode != mode) {
- mAppOpsManager.setUidMode(opCode, uid, mode);
+ mAppOpsManagerInternal.setUidModeIgnoringCallback(opCode, uid, mode,
+ mAppOpsCallback);
final int newMode = mAppOpsManager.unsafeCheckOpRaw(AppOpsManager.opToPublicName(
opCode), uid, packageName);
if (newMode != mode) {
// Work around incorrectly-set package mode. It never makes sense for app ops
// related to runtime permissions, but can get in the way and we have to reset
// it.
- mAppOpsManager.setMode(opCode, uid, packageName, AppOpsManager.opToDefaultMode(
- opCode));
+ mAppOpsManagerInternal.setModeIgnoringCallback(opCode, uid, packageName,
+ AppOpsManager.opToDefaultMode(opCode), mAppOpsCallback);
}
}
}
diff --git a/services/core/java/com/android/server/policy/PhoneWindowManager.java b/services/core/java/com/android/server/policy/PhoneWindowManager.java
index 03cb283ce447..ede04f3bbb14 100644
--- a/services/core/java/com/android/server/policy/PhoneWindowManager.java
+++ b/services/core/java/com/android/server/policy/PhoneWindowManager.java
@@ -481,6 +481,8 @@ public class PhoneWindowManager implements WindowManagerPolicy {
int mVeryLongPressTimeout;
boolean mAllowStartActivityForLongPressOnPowerDuringSetup;
MetricsLogger mLogger;
+ boolean mWakeOnDpadKeyPress;
+ boolean mWakeOnAssistKeyPress;
private boolean mHandleVolumeKeysInWM;
@@ -1729,6 +1731,13 @@ public class PhoneWindowManager implements WindowManagerPolicy {
mAccessibilityShortcutController =
new AccessibilityShortcutController(mContext, new Handler(), mCurrentUserId);
mLogger = new MetricsLogger();
+
+ Resources res = mContext.getResources();
+ mWakeOnDpadKeyPress =
+ res.getBoolean(com.android.internal.R.bool.config_wakeOnDpadKeyPress);
+ mWakeOnAssistKeyPress =
+ res.getBoolean(com.android.internal.R.bool.config_wakeOnAssistKeyPress);
+
// Init display burn-in protection
boolean burnInProtectionEnabled = context.getResources().getBoolean(
com.android.internal.R.bool.config_enableBurnInProtection);
@@ -4072,13 +4081,11 @@ public class PhoneWindowManager implements WindowManagerPolicy {
*/
private boolean isWakeKeyWhenScreenOff(int keyCode) {
switch (keyCode) {
- // ignore volume keys unless docked
case KeyEvent.KEYCODE_VOLUME_UP:
case KeyEvent.KEYCODE_VOLUME_DOWN:
case KeyEvent.KEYCODE_VOLUME_MUTE:
return mDefaultDisplayPolicy.getDockMode() != Intent.EXTRA_DOCK_STATE_UNDOCKED;
- // ignore media keys
case KeyEvent.KEYCODE_MUTE:
case KeyEvent.KEYCODE_HEADSETHOOK:
case KeyEvent.KEYCODE_MEDIA_PLAY:
@@ -4092,7 +4099,18 @@ public class PhoneWindowManager implements WindowManagerPolicy {
case KeyEvent.KEYCODE_MEDIA_FAST_FORWARD:
case KeyEvent.KEYCODE_MEDIA_AUDIO_TRACK:
return false;
+
+ case KeyEvent.KEYCODE_DPAD_UP:
+ case KeyEvent.KEYCODE_DPAD_DOWN:
+ case KeyEvent.KEYCODE_DPAD_LEFT:
+ case KeyEvent.KEYCODE_DPAD_RIGHT:
+ case KeyEvent.KEYCODE_DPAD_CENTER:
+ return mWakeOnDpadKeyPress;
+
+ case KeyEvent.KEYCODE_ASSIST:
+ return mWakeOnAssistKeyPress;
}
+
return true;
}
diff --git a/services/core/java/com/android/server/power/AttentionDetector.java b/services/core/java/com/android/server/power/AttentionDetector.java
index eec0d5f35c5c..cc72dd69a5fa 100644
--- a/services/core/java/com/android/server/power/AttentionDetector.java
+++ b/services/core/java/com/android/server/power/AttentionDetector.java
@@ -38,9 +38,9 @@ import android.provider.DeviceConfig;
import android.provider.Settings;
import android.service.attention.AttentionService;
import android.util.Slog;
-import android.util.StatsLog;
import com.android.internal.annotations.VisibleForTesting;
+import com.android.internal.util.FrameworkStatsLog;
import com.android.server.LocalServices;
import com.android.server.wm.WindowManagerInternal;
@@ -285,7 +285,8 @@ public class AttentionDetector {
private void resetConsecutiveExtensionCount() {
final long previousCount = mConsecutiveTimeoutExtendedCount.getAndSet(0);
if (previousCount > 0) {
- StatsLog.write(StatsLog.SCREEN_TIMEOUT_EXTENSION_REPORTED, previousCount);
+ FrameworkStatsLog.write(FrameworkStatsLog.SCREEN_TIMEOUT_EXTENSION_REPORTED,
+ previousCount);
}
}
diff --git a/services/core/java/com/android/server/power/Notifier.java b/services/core/java/com/android/server/power/Notifier.java
index b45522d11a5d..0b95be15f157 100644
--- a/services/core/java/com/android/server/power/Notifier.java
+++ b/services/core/java/com/android/server/power/Notifier.java
@@ -49,13 +49,13 @@ import android.provider.Settings;
import android.telephony.TelephonyManager;
import android.util.EventLog;
import android.util.Slog;
-import android.util.StatsLog;
import android.view.WindowManagerPolicyConstants.OnReason;
import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.app.IBatteryStats;
import com.android.internal.logging.MetricsLogger;
import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
+import com.android.internal.util.FrameworkStatsLog;
import com.android.server.EventLogTags;
import com.android.server.LocalServices;
import com.android.server.inputmethod.InputMethodManagerInternal;
@@ -199,8 +199,8 @@ public class Notifier {
try {
mBatteryStats.noteInteractive(true);
} catch (RemoteException ex) { }
- StatsLog.write(StatsLog.INTERACTIVE_STATE_CHANGED,
- StatsLog.INTERACTIVE_STATE_CHANGED__STATE__ON);
+ FrameworkStatsLog.write(FrameworkStatsLog.INTERACTIVE_STATE_CHANGED,
+ FrameworkStatsLog.INTERACTIVE_STATE_CHANGED__STATE__ON);
}
/**
@@ -247,13 +247,15 @@ public class Notifier {
try {
if (workSource != null) {
mBatteryStats.noteLongPartialWakelockStartFromSource(tag, historyTag, workSource);
- StatsLog.write(StatsLog.LONG_PARTIAL_WAKELOCK_STATE_CHANGED, workSource,
- tag, historyTag, StatsLog.LONG_PARTIAL_WAKELOCK_STATE_CHANGED__STATE__ON);
+ FrameworkStatsLog.write(FrameworkStatsLog.LONG_PARTIAL_WAKELOCK_STATE_CHANGED,
+ workSource, tag, historyTag,
+ FrameworkStatsLog.LONG_PARTIAL_WAKELOCK_STATE_CHANGED__STATE__ON);
} else {
mBatteryStats.noteLongPartialWakelockStart(tag, historyTag, ownerUid);
- StatsLog.write_non_chained(StatsLog.LONG_PARTIAL_WAKELOCK_STATE_CHANGED,
- ownerUid, null, tag, historyTag,
- StatsLog.LONG_PARTIAL_WAKELOCK_STATE_CHANGED__STATE__ON);
+ FrameworkStatsLog.write_non_chained(
+ FrameworkStatsLog.LONG_PARTIAL_WAKELOCK_STATE_CHANGED, ownerUid, null, tag,
+ historyTag,
+ FrameworkStatsLog.LONG_PARTIAL_WAKELOCK_STATE_CHANGED__STATE__ON);
}
} catch (RemoteException ex) {
// Ignore
@@ -270,13 +272,15 @@ public class Notifier {
try {
if (workSource != null) {
mBatteryStats.noteLongPartialWakelockFinishFromSource(tag, historyTag, workSource);
- StatsLog.write(StatsLog.LONG_PARTIAL_WAKELOCK_STATE_CHANGED, workSource,
- tag, historyTag, StatsLog.LONG_PARTIAL_WAKELOCK_STATE_CHANGED__STATE__OFF);
+ FrameworkStatsLog.write(FrameworkStatsLog.LONG_PARTIAL_WAKELOCK_STATE_CHANGED,
+ workSource, tag, historyTag,
+ FrameworkStatsLog.LONG_PARTIAL_WAKELOCK_STATE_CHANGED__STATE__OFF);
} else {
mBatteryStats.noteLongPartialWakelockFinish(tag, historyTag, ownerUid);
- StatsLog.write_non_chained(StatsLog.LONG_PARTIAL_WAKELOCK_STATE_CHANGED,
- ownerUid, null, tag, historyTag,
- StatsLog.LONG_PARTIAL_WAKELOCK_STATE_CHANGED__STATE__OFF);
+ FrameworkStatsLog.write_non_chained(
+ FrameworkStatsLog.LONG_PARTIAL_WAKELOCK_STATE_CHANGED, ownerUid, null, tag,
+ historyTag,
+ FrameworkStatsLog.LONG_PARTIAL_WAKELOCK_STATE_CHANGED__STATE__OFF);
}
} catch (RemoteException ex) {
// Ignore
@@ -415,9 +419,9 @@ public class Notifier {
try {
mBatteryStats.noteInteractive(interactive);
} catch (RemoteException ex) { }
- StatsLog.write(StatsLog.INTERACTIVE_STATE_CHANGED,
- interactive ? StatsLog.INTERACTIVE_STATE_CHANGED__STATE__ON :
- StatsLog.INTERACTIVE_STATE_CHANGED__STATE__OFF);
+ FrameworkStatsLog.write(FrameworkStatsLog.INTERACTIVE_STATE_CHANGED,
+ interactive ? FrameworkStatsLog.INTERACTIVE_STATE_CHANGED__STATE__ON :
+ FrameworkStatsLog.INTERACTIVE_STATE_CHANGED__STATE__OFF);
// Handle early behaviors.
mInteractive = interactive;
diff --git a/services/core/java/com/android/server/rollback/Rollback.java b/services/core/java/com/android/server/rollback/Rollback.java
index cb583cd42412..bf7413b64588 100644
--- a/services/core/java/com/android/server/rollback/Rollback.java
+++ b/services/core/java/com/android/server/rollback/Rollback.java
@@ -44,6 +44,7 @@ import android.util.SparseLongArray;
import com.android.internal.annotations.GuardedBy;
import com.android.internal.util.ArrayUtils;
import com.android.internal.util.IndentingPrintWriter;
+import com.android.server.pm.ApexManager;
import java.io.File;
import java.io.IOException;
@@ -186,6 +187,14 @@ class Rollback {
private final int[] mPackageSessionIds;
/**
+ * The number of sessions in the install which are notified with success by
+ * {@link PackageInstaller.SessionCallback#onFinished(int, boolean)}.
+ * This rollback will be enabled only after all child sessions finished with success.
+ */
+ @GuardedBy("mLock")
+ private int mNumPackageSessionsWithSuccess;
+
+ /**
* Constructs a new, empty Rollback instance.
*
* @param rollbackId the id of the rollback.
@@ -651,15 +660,23 @@ class Rollback {
*/
void delete(AppDataRollbackHelper dataHelper) {
synchronized (mLock) {
+ boolean containsApex = false;
for (PackageRollbackInfo pkgInfo : info.getPackages()) {
- IntArray snapshottedUsers = pkgInfo.getSnapshottedUsers();
- for (int i = 0; i < snapshottedUsers.size(); i++) {
- // Destroy app data snapshot.
- int userId = snapshottedUsers.get(i);
-
- dataHelper.destroyAppDataSnapshot(info.getRollbackId(), pkgInfo, userId);
+ if (pkgInfo.isApex()) {
+ containsApex = true;
+ } else {
+ IntArray snapshottedUsers = pkgInfo.getSnapshottedUsers();
+ for (int i = 0; i < snapshottedUsers.size(); i++) {
+ // Destroy app data snapshot.
+ int userId = snapshottedUsers.get(i);
+
+ dataHelper.destroyAppDataSnapshot(info.getRollbackId(), pkgInfo, userId);
+ }
}
}
+ if (containsApex) {
+ ApexManager.getInstance().destroyDeSnapshots(info.getRollbackId());
+ }
RollbackStore.deleteRollback(this);
mState = ROLLBACK_STATE_DELETED;
@@ -831,6 +848,17 @@ class Rollback {
return mPackageSessionIds.length;
}
+ /**
+ * Called when a child session finished with success.
+ * Returns true when all child sessions are notified with success. This rollback will be
+ * enabled only after all child sessions finished with success.
+ */
+ boolean notifySessionWithSuccess() {
+ synchronized (mLock) {
+ return ++mNumPackageSessionsWithSuccess == mPackageSessionIds.length;
+ }
+ }
+
static String rollbackStateToString(@RollbackState int state) {
switch (state) {
case Rollback.ROLLBACK_STATE_ENABLING: return "enabling";
diff --git a/services/core/java/com/android/server/rollback/RollbackManagerServiceImpl.java b/services/core/java/com/android/server/rollback/RollbackManagerServiceImpl.java
index 4425c0acba2a..3fa114e3b7a3 100644
--- a/services/core/java/com/android/server/rollback/RollbackManagerServiceImpl.java
+++ b/services/core/java/com/android/server/rollback/RollbackManagerServiceImpl.java
@@ -123,7 +123,7 @@ class RollbackManagerServiceImpl extends IRollbackManager.Stub {
// Rollbacks we are in the process of enabling.
@GuardedBy("mLock")
- private final Set<NewRollback> mNewRollbacks = new ArraySet<>();
+ private final Set<Rollback> mNewRollbacks = new ArraySet<>();
// The list of all rollbacks, including available and committed rollbacks.
@GuardedBy("mLock")
@@ -240,16 +240,16 @@ class RollbackManagerServiceImpl extends IRollbackManager.Stub {
Slog.v(TAG, "broadcast=ACTION_CANCEL_ENABLE_ROLLBACK token=" + token);
}
synchronized (mLock) {
- NewRollback found = null;
- for (NewRollback newRollback : mNewRollbacks) {
- if (newRollback.rollback.hasToken(token)) {
+ Rollback found = null;
+ for (Rollback newRollback : mNewRollbacks) {
+ if (newRollback.hasToken(token)) {
found = newRollback;
break;
}
}
if (found != null) {
mNewRollbacks.remove(found);
- found.rollback.delete(mAppDataRollbackHelper);
+ found.delete(mAppDataRollbackHelper);
}
}
}
@@ -442,12 +442,12 @@ class RollbackManagerServiceImpl extends IRollbackManager.Stub {
rollback.delete(mAppDataRollbackHelper);
}
}
- Iterator<NewRollback> iter2 = mNewRollbacks.iterator();
+ Iterator<Rollback> iter2 = mNewRollbacks.iterator();
while (iter2.hasNext()) {
- NewRollback newRollback = iter2.next();
- if (newRollback.rollback.includesPackage(packageName)) {
+ Rollback newRollback = iter2.next();
+ if (newRollback.includesPackage(packageName)) {
iter2.remove();
- newRollback.rollback.delete(mAppDataRollbackHelper);
+ newRollback.delete(mAppDataRollbackHelper);
}
}
@@ -802,7 +802,7 @@ class RollbackManagerServiceImpl extends IRollbackManager.Stub {
}
}
- NewRollback newRollback;
+ Rollback newRollback;
synchronized (mLock) {
// See if we already have a NewRollback that contains this package
// session. If not, create a NewRollback for the parent session
@@ -813,9 +813,9 @@ class RollbackManagerServiceImpl extends IRollbackManager.Stub {
mNewRollbacks.add(newRollback);
}
}
- newRollback.rollback.addToken(token);
+ newRollback.addToken(token);
- return enableRollbackForPackageSession(newRollback.rollback, packageSession);
+ return enableRollbackForPackageSession(newRollback, packageSession);
}
@WorkerThread
@@ -825,12 +825,12 @@ class RollbackManagerServiceImpl extends IRollbackManager.Stub {
}
synchronized (mLock) {
- NewRollback newRollback = getNewRollbackForPackageSessionLocked(sessionId);
+ Rollback newRollback = getNewRollbackForPackageSessionLocked(sessionId);
if (newRollback != null) {
- Slog.w(TAG, "Delete new rollback id=" + newRollback.rollback.info.getRollbackId()
+ Slog.w(TAG, "Delete new rollback id=" + newRollback.info.getRollbackId()
+ " for session id=" + sessionId);
mNewRollbacks.remove(newRollback);
- newRollback.rollback.delete(mAppDataRollbackHelper);
+ newRollback.delete(mAppDataRollbackHelper);
}
Iterator<Rollback> iter = mRollbacks.iterator();
while (iter.hasNext()) {
@@ -972,9 +972,8 @@ class RollbackManagerServiceImpl extends IRollbackManager.Stub {
rollback.snapshotUserData(packageName, userIds, mAppDataRollbackHelper);
}
// non-staged installs
- for (NewRollback rollback : mNewRollbacks) {
- rollback.rollback.snapshotUserData(
- packageName, userIds, mAppDataRollbackHelper);
+ for (Rollback rollback : mNewRollbacks) {
+ rollback.snapshotUserData(packageName, userIds, mAppDataRollbackHelper);
}
}
}
@@ -1016,13 +1015,13 @@ class RollbackManagerServiceImpl extends IRollbackManager.Stub {
return;
}
- NewRollback newRollback;
+ Rollback newRollback;
synchronized (mLock) {
newRollback = createNewRollbackLocked(session);
}
if (!session.isMultiPackage()) {
- if (!enableRollbackForPackageSession(newRollback.rollback, session)) {
+ if (!enableRollbackForPackageSession(newRollback, session)) {
Slog.e(TAG, "Unable to enable rollback for session: " + sessionId);
result.offer(-1);
return;
@@ -1036,7 +1035,7 @@ class RollbackManagerServiceImpl extends IRollbackManager.Stub {
result.offer(-1);
return;
}
- if (!enableRollbackForPackageSession(newRollback.rollback, childSession)) {
+ if (!enableRollbackForPackageSession(newRollback, childSession)) {
Slog.e(TAG, "Unable to enable rollback for session: " + sessionId);
result.offer(-1);
return;
@@ -1197,7 +1196,7 @@ class RollbackManagerServiceImpl extends IRollbackManager.Stub {
}
if (success) {
- NewRollback newRollback;
+ Rollback newRollback;
synchronized (mLock) {
newRollback = getNewRollbackForPackageSessionLocked(sessionId);
if (newRollback != null && newRollback.notifySessionWithSuccess()) {
@@ -1229,8 +1228,7 @@ class RollbackManagerServiceImpl extends IRollbackManager.Stub {
* or null on error.
*/
@WorkerThread
- private Rollback completeEnableRollback(NewRollback newRollback) {
- Rollback rollback = newRollback.rollback;
+ private Rollback completeEnableRollback(Rollback rollback) {
if (LOCAL_LOGV) {
Slog.v(TAG, "completeEnableRollback id=" + rollback.info.getRollbackId());
}
@@ -1341,38 +1339,9 @@ class RollbackManagerServiceImpl extends IRollbackManager.Stub {
}
}
- private static class NewRollback {
- public final Rollback rollback;
-
- /**
- * The number of sessions in the install which are notified with success by
- * {@link PackageInstaller.SessionCallback#onFinished(int, boolean)}.
- * This NewRollback will be enabled only after all child sessions finished with success.
- */
- @GuardedBy("mNewRollbackLock")
- private int mNumPackageSessionsWithSuccess;
-
- private final Object mNewRollbackLock = new Object();
-
- NewRollback(Rollback rollback) {
- this.rollback = rollback;
- }
-
- /**
- * Called when a child session finished with success.
- * Returns true when all child sessions are notified with success. This NewRollback will be
- * enabled only after all child sessions finished with success.
- */
- boolean notifySessionWithSuccess() {
- synchronized (mNewRollbackLock) {
- return ++mNumPackageSessionsWithSuccess == rollback.getPackageSessionIdCount();
- }
- }
- }
-
@WorkerThread
@GuardedBy("mLock")
- private NewRollback createNewRollbackLocked(PackageInstaller.SessionInfo parentSession) {
+ private Rollback createNewRollbackLocked(PackageInstaller.SessionInfo parentSession) {
int rollbackId = allocateRollbackIdLocked();
final int userId;
if (parentSession.getUser() == UserHandle.ALL) {
@@ -1404,7 +1373,7 @@ class RollbackManagerServiceImpl extends IRollbackManager.Stub {
installerPackageName, packageSessionIds);
}
- return new NewRollback(rollback);
+ return rollback;
}
/**
@@ -1414,11 +1383,11 @@ class RollbackManagerServiceImpl extends IRollbackManager.Stub {
*/
@WorkerThread
@GuardedBy("mLock")
- NewRollback getNewRollbackForPackageSessionLocked(int packageSessionId) {
+ Rollback getNewRollbackForPackageSessionLocked(int packageSessionId) {
// We expect mNewRollbacks to be a very small list; linear search
// should be plenty fast.
- for (NewRollback newRollback: mNewRollbacks) {
- if (newRollback.rollback.containsSessionId(packageSessionId)) {
+ for (Rollback newRollback: mNewRollbacks) {
+ if (newRollback.containsSessionId(packageSessionId)) {
return newRollback;
}
}
diff --git a/services/core/java/com/android/server/rollback/RollbackPackageHealthObserver.java b/services/core/java/com/android/server/rollback/RollbackPackageHealthObserver.java
index 8893213d4a71..8d090f1b6fce 100644
--- a/services/core/java/com/android/server/rollback/RollbackPackageHealthObserver.java
+++ b/services/core/java/com/android/server/rollback/RollbackPackageHealthObserver.java
@@ -16,7 +16,7 @@
package com.android.server.rollback;
-import static android.util.StatsLog.WATCHDOG_ROLLBACK_OCCURRED__ROLLBACK_REASON__REASON_UNKNOWN;
+import static com.android.internal.util.FrameworkStatsLog.WATCHDOG_ROLLBACK_OCCURRED__ROLLBACK_REASON__REASON_UNKNOWN;
import android.annotation.Nullable;
import android.content.BroadcastReceiver;
@@ -38,9 +38,9 @@ import android.os.SystemProperties;
import android.text.TextUtils;
import android.util.ArraySet;
import android.util.Slog;
-import android.util.StatsLog;
import com.android.internal.annotations.GuardedBy;
+import com.android.internal.util.FrameworkStatsLog;
import com.android.server.PackageWatchdog;
import com.android.server.PackageWatchdog.FailureReasons;
import com.android.server.PackageWatchdog.PackageHealthObserver;
@@ -224,14 +224,15 @@ public final class RollbackPackageHealthObserver implements PackageHealthObserve
saveStagedRollbackId(rollbackId);
}
WatchdogRollbackLogger.logEvent(logPackage,
- StatsLog
+ FrameworkStatsLog
.WATCHDOG_ROLLBACK_OCCURRED__ROLLBACK_TYPE__ROLLBACK_BOOT_TRIGGERED,
WATCHDOG_ROLLBACK_OCCURRED__ROLLBACK_REASON__REASON_UNKNOWN,
"");
} else if (sessionInfo.isStagedSessionFailed()
&& markStagedSessionHandled(rollbackId)) {
WatchdogRollbackLogger.logEvent(logPackage,
- StatsLog.WATCHDOG_ROLLBACK_OCCURRED__ROLLBACK_TYPE__ROLLBACK_FAILURE,
+ FrameworkStatsLog
+ .WATCHDOG_ROLLBACK_OCCURRED__ROLLBACK_TYPE__ROLLBACK_FAILURE,
WATCHDOG_ROLLBACK_OCCURRED__ROLLBACK_REASON__REASON_UNKNOWN,
"");
mContext.unregisterReceiver(listener);
@@ -337,7 +338,7 @@ public final class RollbackPackageHealthObserver implements PackageHealthObserve
final VersionedPackage logPackage = logPackageTemp;
WatchdogRollbackLogger.logEvent(logPackage,
- StatsLog.WATCHDOG_ROLLBACK_OCCURRED__ROLLBACK_TYPE__ROLLBACK_INITIATE,
+ FrameworkStatsLog.WATCHDOG_ROLLBACK_OCCURRED__ROLLBACK_TYPE__ROLLBACK_INITIATE,
reasonToLog, failedPackageToLog);
final LocalIntentReceiver rollbackReceiver = new LocalIntentReceiver((Intent result) -> {
int status = result.getIntExtra(RollbackManager.EXTRA_STATUS,
@@ -355,7 +356,8 @@ public final class RollbackPackageHealthObserver implements PackageHealthObserve
logPackage);
} else {
WatchdogRollbackLogger.logEvent(logPackage,
- StatsLog.WATCHDOG_ROLLBACK_OCCURRED__ROLLBACK_TYPE__ROLLBACK_SUCCESS,
+ FrameworkStatsLog
+ .WATCHDOG_ROLLBACK_OCCURRED__ROLLBACK_TYPE__ROLLBACK_SUCCESS,
reasonToLog, failedPackageToLog);
}
} else {
@@ -363,7 +365,8 @@ public final class RollbackPackageHealthObserver implements PackageHealthObserve
markStagedSessionHandled(rollback.getRollbackId());
}
WatchdogRollbackLogger.logEvent(logPackage,
- StatsLog.WATCHDOG_ROLLBACK_OCCURRED__ROLLBACK_TYPE__ROLLBACK_FAILURE,
+ FrameworkStatsLog
+ .WATCHDOG_ROLLBACK_OCCURRED__ROLLBACK_TYPE__ROLLBACK_FAILURE,
reasonToLog, failedPackageToLog);
}
});
diff --git a/services/core/java/com/android/server/rollback/WatchdogRollbackLogger.java b/services/core/java/com/android/server/rollback/WatchdogRollbackLogger.java
index 3c923e7d2e31..46ec2f8258ca 100644
--- a/services/core/java/com/android/server/rollback/WatchdogRollbackLogger.java
+++ b/services/core/java/com/android/server/rollback/WatchdogRollbackLogger.java
@@ -16,11 +16,11 @@
package com.android.server.rollback;
-import static android.util.StatsLog.WATCHDOG_ROLLBACK_OCCURRED__ROLLBACK_REASON__REASON_APP_CRASH;
-import static android.util.StatsLog.WATCHDOG_ROLLBACK_OCCURRED__ROLLBACK_REASON__REASON_APP_NOT_RESPONDING;
-import static android.util.StatsLog.WATCHDOG_ROLLBACK_OCCURRED__ROLLBACK_REASON__REASON_EXPLICIT_HEALTH_CHECK;
-import static android.util.StatsLog.WATCHDOG_ROLLBACK_OCCURRED__ROLLBACK_REASON__REASON_NATIVE_CRASH;
-import static android.util.StatsLog.WATCHDOG_ROLLBACK_OCCURRED__ROLLBACK_REASON__REASON_UNKNOWN;
+import static com.android.internal.util.FrameworkStatsLog.WATCHDOG_ROLLBACK_OCCURRED__ROLLBACK_REASON__REASON_APP_CRASH;
+import static com.android.internal.util.FrameworkStatsLog.WATCHDOG_ROLLBACK_OCCURRED__ROLLBACK_REASON__REASON_APP_NOT_RESPONDING;
+import static com.android.internal.util.FrameworkStatsLog.WATCHDOG_ROLLBACK_OCCURRED__ROLLBACK_REASON__REASON_EXPLICIT_HEALTH_CHECK;
+import static com.android.internal.util.FrameworkStatsLog.WATCHDOG_ROLLBACK_OCCURRED__ROLLBACK_REASON__REASON_NATIVE_CRASH;
+import static com.android.internal.util.FrameworkStatsLog.WATCHDOG_ROLLBACK_OCCURRED__ROLLBACK_REASON__REASON_UNKNOWN;
import android.annotation.NonNull;
import android.annotation.Nullable;
@@ -36,6 +36,7 @@ import android.util.Slog;
import android.util.StatsLog;
import com.android.internal.annotations.VisibleForTesting;
+import com.android.internal.util.FrameworkStatsLog;
import com.android.server.PackageWatchdog;
import java.util.ArrayList;
@@ -69,20 +70,27 @@ public final class WatchdogRollbackLogger {
}
}
+ /**
+ * Returns the logging parent of a given package if it exists, {@code null} otherwise.
+ *
+ * The logging parent is defined by the {@code android.content.pm.LOGGING_PARENT} field in the
+ * metadata of a package's AndroidManifest.xml.
+ */
@VisibleForTesting
+ @Nullable
static VersionedPackage getLogPackage(Context context,
@NonNull VersionedPackage failingPackage) {
String logPackageName;
VersionedPackage loggingParent;
logPackageName = getLoggingParentName(context, failingPackage.getPackageName());
if (logPackageName == null) {
- return failingPackage;
+ return null;
}
try {
loggingParent = new VersionedPackage(logPackageName, context.getPackageManager()
.getPackageInfo(logPackageName, 0 /* flags */).getLongVersionCode());
} catch (PackageManager.NameNotFoundException e) {
- return failingPackage;
+ return null;
}
return loggingParent;
}
@@ -104,18 +112,14 @@ public final class WatchdogRollbackLogger {
return;
}
- // Identify the logging parent for this rollback. When all configurations are correct, each
- // package in the rollback refers to the same logging parent, except for the logging parent
- // itself. If a logging parent is missing for a package, we use the package itself for
- // logging. This might result in over-logging, but we prefer this over no logging.
+ // Identify the logging parent for this rollback. When all configurations are correct,
+ // each package in the rollback has a logging parent set in metadata.
final Set<String> loggingPackageNames = new ArraySet<>();
for (PackageRollbackInfo packageRollback : rollback.getPackages()) {
final String loggingParentName = getLoggingParentName(context,
packageRollback.getPackageName());
if (loggingParentName != null) {
loggingPackageNames.add(loggingParentName);
- } else {
- loggingPackageNames.add(packageRollback.getPackageName());
}
}
@@ -138,11 +142,13 @@ public final class WatchdogRollbackLogger {
for (VersionedPackage oldLoggingPackage : oldLoggingPackages) {
if (sessionInfo.isStagedSessionApplied()) {
logEvent(oldLoggingPackage,
- StatsLog.WATCHDOG_ROLLBACK_OCCURRED__ROLLBACK_TYPE__ROLLBACK_SUCCESS,
+ FrameworkStatsLog
+ .WATCHDOG_ROLLBACK_OCCURRED__ROLLBACK_TYPE__ROLLBACK_SUCCESS,
WATCHDOG_ROLLBACK_OCCURRED__ROLLBACK_REASON__REASON_UNKNOWN, "");
} else if (sessionInfo.isStagedSessionFailed()) {
logEvent(oldLoggingPackage,
- StatsLog.WATCHDOG_ROLLBACK_OCCURRED__ROLLBACK_TYPE__ROLLBACK_FAILURE,
+ FrameworkStatsLog
+ .WATCHDOG_ROLLBACK_OCCURRED__ROLLBACK_TYPE__ROLLBACK_FAILURE,
WATCHDOG_ROLLBACK_OCCURRED__ROLLBACK_REASON__REASON_UNKNOWN, "");
}
}
@@ -165,6 +171,10 @@ public final class WatchdogRollbackLogger {
if (logPackage != null) {
StatsLog.logWatchdogRollbackOccurred(type, logPackage.getPackageName(),
logPackage.getVersionCode(), rollbackReason, failingPackageName);
+ } else {
+ // In the case that the log package is null, still log an empty string as an
+ // indication that retrieving the logging parent failed.
+ StatsLog.logWatchdogRollbackOccurred(type, "", 0, rollbackReason, failingPackageName);
}
}
@@ -186,13 +196,14 @@ public final class WatchdogRollbackLogger {
private static String rollbackTypeToString(int type) {
switch (type) {
- case StatsLog.WATCHDOG_ROLLBACK_OCCURRED__ROLLBACK_TYPE__ROLLBACK_INITIATE:
+ case FrameworkStatsLog.WATCHDOG_ROLLBACK_OCCURRED__ROLLBACK_TYPE__ROLLBACK_INITIATE:
return "ROLLBACK_INITIATE";
- case StatsLog.WATCHDOG_ROLLBACK_OCCURRED__ROLLBACK_TYPE__ROLLBACK_SUCCESS:
+ case FrameworkStatsLog.WATCHDOG_ROLLBACK_OCCURRED__ROLLBACK_TYPE__ROLLBACK_SUCCESS:
return "ROLLBACK_SUCCESS";
- case StatsLog.WATCHDOG_ROLLBACK_OCCURRED__ROLLBACK_TYPE__ROLLBACK_FAILURE:
+ case FrameworkStatsLog.WATCHDOG_ROLLBACK_OCCURRED__ROLLBACK_TYPE__ROLLBACK_FAILURE:
return "ROLLBACK_FAILURE";
- case StatsLog.WATCHDOG_ROLLBACK_OCCURRED__ROLLBACK_TYPE__ROLLBACK_BOOT_TRIGGERED:
+ case FrameworkStatsLog
+ .WATCHDOG_ROLLBACK_OCCURRED__ROLLBACK_TYPE__ROLLBACK_BOOT_TRIGGERED:
return "ROLLBACK_BOOT_TRIGGERED";
default:
return "UNKNOWN";
@@ -201,13 +212,15 @@ public final class WatchdogRollbackLogger {
private static String rollbackReasonToString(int reason) {
switch (reason) {
- case StatsLog.WATCHDOG_ROLLBACK_OCCURRED__ROLLBACK_REASON__REASON_NATIVE_CRASH:
+ case FrameworkStatsLog.WATCHDOG_ROLLBACK_OCCURRED__ROLLBACK_REASON__REASON_NATIVE_CRASH:
return "REASON_NATIVE_CRASH";
- case StatsLog.WATCHDOG_ROLLBACK_OCCURRED__ROLLBACK_REASON__REASON_EXPLICIT_HEALTH_CHECK:
+ case FrameworkStatsLog
+ .WATCHDOG_ROLLBACK_OCCURRED__ROLLBACK_REASON__REASON_EXPLICIT_HEALTH_CHECK:
return "REASON_EXPLICIT_HEALTH_CHECK";
- case StatsLog.WATCHDOG_ROLLBACK_OCCURRED__ROLLBACK_REASON__REASON_APP_CRASH:
+ case FrameworkStatsLog.WATCHDOG_ROLLBACK_OCCURRED__ROLLBACK_REASON__REASON_APP_CRASH:
return "REASON_APP_CRASH";
- case StatsLog.WATCHDOG_ROLLBACK_OCCURRED__ROLLBACK_REASON__REASON_APP_NOT_RESPONDING:
+ case FrameworkStatsLog
+ .WATCHDOG_ROLLBACK_OCCURRED__ROLLBACK_REASON__REASON_APP_NOT_RESPONDING:
return "REASON_APP_NOT_RESPONDING";
default:
return "UNKNOWN";
diff --git a/services/core/java/com/android/server/security/KeyAttestationApplicationIdProviderService.java b/services/core/java/com/android/server/security/KeyAttestationApplicationIdProviderService.java
index a8c68c07231d..c908acdd1d6c 100644
--- a/services/core/java/com/android/server/security/KeyAttestationApplicationIdProviderService.java
+++ b/services/core/java/com/android/server/security/KeyAttestationApplicationIdProviderService.java
@@ -24,16 +24,16 @@ import android.content.pm.PackageManager.NameNotFoundException;
import android.os.Binder;
import android.os.RemoteException;
import android.os.UserHandle;
-import android.security.keymaster.KeyAttestationPackageInfo;
-import android.security.keymaster.KeyAttestationApplicationId;
import android.security.keymaster.IKeyAttestationApplicationIdProvider;
+import android.security.keymaster.KeyAttestationApplicationId;
+import android.security.keymaster.KeyAttestationPackageInfo;
/**
* @hide
* The KeyAttestationApplicationIdProviderService provides information describing the possible
* applications identified by a UID. Due to UID sharing, this KeyAttestationApplicationId can
- * comprise information about multiple packages. The Information is used by keystore to describe
- * the initiating application of a key attestation procedure.
+ * comprise information about multiple packages. The Information is used by keystore and credstore
+ * to describe the initiating application of a key attestation procedure.
*/
public class KeyAttestationApplicationIdProviderService
extends IKeyAttestationApplicationIdProvider.Stub {
@@ -46,8 +46,10 @@ public class KeyAttestationApplicationIdProviderService
public KeyAttestationApplicationId getKeyAttestationApplicationId(int uid)
throws RemoteException {
- if (Binder.getCallingUid() != android.os.Process.KEYSTORE_UID) {
- throw new SecurityException("This service can only be used by Keystore");
+ int callingUid = Binder.getCallingUid();
+ if (callingUid != android.os.Process.KEYSTORE_UID
+ && callingUid != android.os.Process.CREDSTORE_UID) {
+ throw new SecurityException("This service can only be used by Keystore or Credstore");
}
KeyAttestationPackageInfo[] keyAttestationPackageInfos = null;
final long token = Binder.clearCallingIdentity();
diff --git a/services/core/java/com/android/server/signedconfig/GlobalSettingsConfigApplicator.java b/services/core/java/com/android/server/signedconfig/GlobalSettingsConfigApplicator.java
index d77cf900a0a9..897aa14ebb64 100644
--- a/services/core/java/com/android/server/signedconfig/GlobalSettingsConfigApplicator.java
+++ b/services/core/java/com/android/server/signedconfig/GlobalSettingsConfigApplicator.java
@@ -23,7 +23,8 @@ import android.provider.Settings;
import android.util.ArrayMap;
import android.util.ArraySet;
import android.util.Slog;
-import android.util.StatsLog;
+
+import com.android.internal.util.FrameworkStatsLog;
import java.security.GeneralSecurityException;
import java.util.Arrays;
@@ -82,7 +83,7 @@ class GlobalSettingsConfigApplicator {
return mVerifier.verifySignature(data, signature);
} catch (GeneralSecurityException e) {
Slog.e(TAG, "Failed to verify signature", e);
- mEvent.status = StatsLog.SIGNED_CONFIG_REPORTED__STATUS__SECURITY_EXCEPTION;
+ mEvent.status = FrameworkStatsLog.SIGNED_CONFIG_REPORTED__STATUS__SECURITY_EXCEPTION;
return false;
}
}
@@ -116,14 +117,14 @@ class GlobalSettingsConfigApplicator {
mEvent.version = config.version;
} catch (InvalidConfigException e) {
Slog.e(TAG, "Failed to parse global settings from package " + mSourcePackage, e);
- mEvent.status = StatsLog.SIGNED_CONFIG_REPORTED__STATUS__INVALID_CONFIG;
+ mEvent.status = FrameworkStatsLog.SIGNED_CONFIG_REPORTED__STATUS__INVALID_CONFIG;
return;
}
int currentVersion = getCurrentConfigVersion();
if (currentVersion >= config.version) {
Slog.i(TAG, "Global settings from package " + mSourcePackage
+ " is older than existing: " + config.version + "<=" + currentVersion);
- mEvent.status = StatsLog.SIGNED_CONFIG_REPORTED__STATUS__OLD_CONFIG;
+ mEvent.status = FrameworkStatsLog.SIGNED_CONFIG_REPORTED__STATUS__OLD_CONFIG;
return;
}
// We have new config!
@@ -133,12 +134,12 @@ class GlobalSettingsConfigApplicator {
config.getMatchingConfig(Build.VERSION.SDK_INT);
if (matchedConfig == null) {
Slog.i(TAG, "Settings is not applicable to current SDK version; ignoring");
- mEvent.status = StatsLog.SIGNED_CONFIG_REPORTED__STATUS__NOT_APPLICABLE;
+ mEvent.status = FrameworkStatsLog.SIGNED_CONFIG_REPORTED__STATUS__NOT_APPLICABLE;
return;
}
Slog.i(TAG, "Updating global settings to version " + config.version);
updateCurrentConfig(config.version, matchedConfig.values);
- mEvent.status = StatsLog.SIGNED_CONFIG_REPORTED__STATUS__APPLIED;
+ mEvent.status = FrameworkStatsLog.SIGNED_CONFIG_REPORTED__STATUS__APPLIED;
}
}
diff --git a/services/core/java/com/android/server/signedconfig/SignatureVerifier.java b/services/core/java/com/android/server/signedconfig/SignatureVerifier.java
index 146c51688531..a85764221de5 100644
--- a/services/core/java/com/android/server/signedconfig/SignatureVerifier.java
+++ b/services/core/java/com/android/server/signedconfig/SignatureVerifier.java
@@ -18,7 +18,8 @@ package com.android.server.signedconfig;
import android.os.Build;
import android.util.Slog;
-import android.util.StatsLog;
+
+import com.android.internal.util.FrameworkStatsLog;
import java.nio.charset.StandardCharsets;
import java.security.InvalidKeyException;
@@ -96,7 +97,8 @@ public class SignatureVerifier {
try {
signature = Base64.getDecoder().decode(base64Signature);
} catch (IllegalArgumentException e) {
- mEvent.status = StatsLog.SIGNED_CONFIG_REPORTED__STATUS__BASE64_FAILURE_SIGNATURE;
+ mEvent.status =
+ FrameworkStatsLog.SIGNED_CONFIG_REPORTED__STATUS__BASE64_FAILURE_SIGNATURE;
Slog.e(TAG, "Failed to base64 decode signature");
return false;
}
@@ -108,7 +110,8 @@ public class SignatureVerifier {
if (DBG) Slog.w(TAG, "Trying to verify signature using debug key");
if (verifyWithPublicKey(mDebugKey, data, signature)) {
Slog.i(TAG, "Verified config using debug key");
- mEvent.verifiedWith = StatsLog.SIGNED_CONFIG_REPORTED__VERIFIED_WITH__DEBUG;
+ mEvent.verifiedWith =
+ FrameworkStatsLog.SIGNED_CONFIG_REPORTED__VERIFIED_WITH__DEBUG;
return true;
} else {
if (DBG) Slog.i(TAG, "Config verification failed using debug key");
@@ -120,16 +123,19 @@ public class SignatureVerifier {
if (mProdKey == null) {
Slog.e(TAG, "No prod key; construction failed?");
mEvent.status =
- StatsLog.SIGNED_CONFIG_REPORTED__STATUS__SIGNATURE_CHECK_FAILED_PROD_KEY_ABSENT;
+ FrameworkStatsLog
+ .SIGNED_CONFIG_REPORTED__STATUS__SIGNATURE_CHECK_FAILED_PROD_KEY_ABSENT;
return false;
}
if (verifyWithPublicKey(mProdKey, data, signature)) {
Slog.i(TAG, "Verified config using production key");
- mEvent.verifiedWith = StatsLog.SIGNED_CONFIG_REPORTED__VERIFIED_WITH__PRODUCTION;
+ mEvent.verifiedWith =
+ FrameworkStatsLog.SIGNED_CONFIG_REPORTED__VERIFIED_WITH__PRODUCTION;
return true;
} else {
if (DBG) Slog.i(TAG, "Verification failed using production key");
- mEvent.status = StatsLog.SIGNED_CONFIG_REPORTED__STATUS__SIGNATURE_CHECK_FAILED;
+ mEvent.status =
+ FrameworkStatsLog.SIGNED_CONFIG_REPORTED__STATUS__SIGNATURE_CHECK_FAILED;
return false;
}
}
diff --git a/services/core/java/com/android/server/signedconfig/SignedConfigEvent.java b/services/core/java/com/android/server/signedconfig/SignedConfigEvent.java
index 2f2062c6f2ee..c1adf936d79b 100644
--- a/services/core/java/com/android/server/signedconfig/SignedConfigEvent.java
+++ b/services/core/java/com/android/server/signedconfig/SignedConfigEvent.java
@@ -15,24 +15,24 @@
*/
package com.android.server.signedconfig;
-import android.util.StatsLog;
+import com.android.internal.util.FrameworkStatsLog;
/**
* Helper class to allow a SignedConfigReported event to be built up in stages.
*/
public class SignedConfigEvent {
- public int type = StatsLog.SIGNED_CONFIG_REPORTED__TYPE__UNKNOWN_TYPE;
- public int status = StatsLog.SIGNED_CONFIG_REPORTED__STATUS__UNKNOWN_STATUS;
+ public int type = FrameworkStatsLog.SIGNED_CONFIG_REPORTED__TYPE__UNKNOWN_TYPE;
+ public int status = FrameworkStatsLog.SIGNED_CONFIG_REPORTED__STATUS__UNKNOWN_STATUS;
public int version = 0;
public String fromPackage = null;
- public int verifiedWith = StatsLog.SIGNED_CONFIG_REPORTED__VERIFIED_WITH__NO_KEY;
+ public int verifiedWith = FrameworkStatsLog.SIGNED_CONFIG_REPORTED__VERIFIED_WITH__NO_KEY;
/**
* Write this event to statslog.
*/
public void send() {
- StatsLog.write(StatsLog.SIGNED_CONFIG_REPORTED,
+ FrameworkStatsLog.write(FrameworkStatsLog.SIGNED_CONFIG_REPORTED,
type, status, version, fromPackage, verifiedWith);
}
diff --git a/services/core/java/com/android/server/signedconfig/SignedConfigService.java b/services/core/java/com/android/server/signedconfig/SignedConfigService.java
index dc39542dc29f..037c82ad6593 100644
--- a/services/core/java/com/android/server/signedconfig/SignedConfigService.java
+++ b/services/core/java/com/android/server/signedconfig/SignedConfigService.java
@@ -26,8 +26,8 @@ import android.content.pm.PackageManagerInternal;
import android.net.Uri;
import android.os.Bundle;
import android.util.Slog;
-import android.util.StatsLog;
+import com.android.internal.util.FrameworkStatsLog;
import com.android.server.LocalServices;
import java.nio.charset.StandardCharsets;
@@ -85,7 +85,7 @@ public class SignedConfigService {
&& metaData.containsKey(KEY_GLOBAL_SETTINGS_SIGNATURE)) {
SignedConfigEvent event = new SignedConfigEvent();
try {
- event.type = StatsLog.SIGNED_CONFIG_REPORTED__TYPE__GLOBAL_SETTINGS;
+ event.type = FrameworkStatsLog.SIGNED_CONFIG_REPORTED__TYPE__GLOBAL_SETTINGS;
event.fromPackage = packageName;
String config = metaData.getString(KEY_GLOBAL_SETTINGS);
String signature = metaData.getString(KEY_GLOBAL_SETTINGS_SIGNATURE);
@@ -95,7 +95,8 @@ public class SignedConfigService {
} catch (IllegalArgumentException iae) {
Slog.e(TAG, "Failed to base64 decode global settings config from "
+ packageName);
- event.status = StatsLog.SIGNED_CONFIG_REPORTED__STATUS__BASE64_FAILURE_CONFIG;
+ event.status =
+ FrameworkStatsLog.SIGNED_CONFIG_REPORTED__STATUS__BASE64_FAILURE_CONFIG;
return;
}
if (DBG) {
diff --git a/services/core/java/com/android/server/storage/DeviceStorageMonitorService.java b/services/core/java/com/android/server/storage/DeviceStorageMonitorService.java
index b136ec754f59..734b71824490 100644
--- a/services/core/java/com/android/server/storage/DeviceStorageMonitorService.java
+++ b/services/core/java/com/android/server/storage/DeviceStorageMonitorService.java
@@ -41,11 +41,11 @@ import android.text.format.DateUtils;
import android.util.ArrayMap;
import android.util.DataUnit;
import android.util.Slog;
-import android.util.StatsLog;
import com.android.internal.messages.nano.SystemMessageProto.SystemMessage;
import com.android.internal.notification.SystemNotificationChannels;
import com.android.internal.util.DumpUtils;
+import com.android.internal.util.FrameworkStatsLog;
import com.android.internal.util.IndentingPrintWriter;
import com.android.server.EventLogTags;
import com.android.server.SystemService;
@@ -512,15 +512,15 @@ public class DeviceStorageMonitorService extends SystemService {
notification.flags |= Notification.FLAG_NO_CLEAR;
mNotifManager.notifyAsUser(uuid.toString(), SystemMessage.NOTE_LOW_STORAGE,
notification, UserHandle.ALL);
- StatsLog.write(StatsLog.LOW_STORAGE_STATE_CHANGED,
+ FrameworkStatsLog.write(FrameworkStatsLog.LOW_STORAGE_STATE_CHANGED,
Objects.toString(vol.getDescription()),
- StatsLog.LOW_STORAGE_STATE_CHANGED__STATE__ON);
+ FrameworkStatsLog.LOW_STORAGE_STATE_CHANGED__STATE__ON);
} else if (State.isLeaving(State.LEVEL_LOW, oldLevel, newLevel)) {
mNotifManager.cancelAsUser(uuid.toString(), SystemMessage.NOTE_LOW_STORAGE,
UserHandle.ALL);
- StatsLog.write(StatsLog.LOW_STORAGE_STATE_CHANGED,
+ FrameworkStatsLog.write(FrameworkStatsLog.LOW_STORAGE_STATE_CHANGED,
Objects.toString(vol.getDescription()),
- StatsLog.LOW_STORAGE_STATE_CHANGED__STATE__OFF);
+ FrameworkStatsLog.LOW_STORAGE_STATE_CHANGED__STATE__OFF);
}
}
diff --git a/services/core/java/com/android/server/wm/ActivityRecord.java b/services/core/java/com/android/server/wm/ActivityRecord.java
index df6dfc4d8ffe..ed38e9a73050 100644
--- a/services/core/java/com/android/server/wm/ActivityRecord.java
+++ b/services/core/java/com/android/server/wm/ActivityRecord.java
@@ -3037,6 +3037,8 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A
}
// Throw away any services that have been bound by this activity.
mServiceConnectionsHolder.disconnectActivityFromServices();
+ // This activity record is removing, make sure not to disconnect twice.
+ mServiceConnectionsHolder = null;
}
@Override
diff --git a/services/core/java/com/android/server/wm/ActivityServiceConnectionsHolder.java b/services/core/java/com/android/server/wm/ActivityServiceConnectionsHolder.java
index 6e75f9c9167f..5dfc261480f2 100644
--- a/services/core/java/com/android/server/wm/ActivityServiceConnectionsHolder.java
+++ b/services/core/java/com/android/server/wm/ActivityServiceConnectionsHolder.java
@@ -18,10 +18,13 @@ package com.android.server.wm;
import static com.android.server.wm.ActivityStack.ActivityState.PAUSING;
import static com.android.server.wm.ActivityStack.ActivityState.RESUMED;
+import static com.android.server.wm.ActivityTaskManagerDebugConfig.DEBUG_CLEANUP;
+import static com.android.server.wm.ActivityTaskManagerDebugConfig.TAG_ATM;
+
+import android.util.ArraySet;
+import android.util.Slog;
import java.io.PrintWriter;
-import java.util.HashSet;
-import java.util.Iterator;
import java.util.function.Consumer;
/**
@@ -30,6 +33,8 @@ import java.util.function.Consumer;
* instance of this per activity for tracking all services connected to that activity. AM will
* sometimes query this to bump the OOM score for the processes with services connected to visible
* activities.
+ * <p>
+ * Public methods are called in AM lock, otherwise in WM lock.
*/
public class ActivityServiceConnectionsHolder<T> {
@@ -44,7 +49,10 @@ public class ActivityServiceConnectionsHolder<T> {
* on the WM side since we don't perform operations on the object. Mainly here for communication
* and booking with the AM side.
*/
- private HashSet<T> mConnections;
+ private ArraySet<T> mConnections;
+
+ /** Whether all connections of {@link #mActivity} are being removed. */
+ private volatile boolean mIsDisconnecting;
ActivityServiceConnectionsHolder(ActivityTaskManagerService service, ActivityRecord activity) {
mService = service;
@@ -54,8 +62,16 @@ public class ActivityServiceConnectionsHolder<T> {
/** Adds a connection record that the activity has bound to a specific service. */
public void addConnection(T c) {
synchronized (mService.mGlobalLock) {
+ if (mIsDisconnecting) {
+ // This is unlikely to happen because the caller should create a new holder.
+ if (DEBUG_CLEANUP) {
+ Slog.e(TAG_ATM, "Skip adding connection " + c + " to a disconnecting holder of "
+ + mActivity);
+ }
+ return;
+ }
if (mConnections == null) {
- mConnections = new HashSet<>();
+ mConnections = new ArraySet<>();
}
mConnections.add(c);
}
@@ -67,6 +83,9 @@ public class ActivityServiceConnectionsHolder<T> {
if (mConnections == null) {
return;
}
+ if (DEBUG_CLEANUP && mIsDisconnecting) {
+ Slog.v(TAG_ATM, "Remove pending disconnecting " + c + " of " + mActivity);
+ }
mConnections.remove(c);
}
}
@@ -88,26 +107,33 @@ public class ActivityServiceConnectionsHolder<T> {
if (mConnections == null || mConnections.isEmpty()) {
return;
}
- final Iterator<T> it = mConnections.iterator();
- while (it.hasNext()) {
- T c = it.next();
- consumer.accept(c);
+ for (int i = mConnections.size() - 1; i >= 0; i--) {
+ consumer.accept(mConnections.valueAt(i));
}
}
}
- /** Removes the connection between the activity and all services that were connected to it. */
+ /**
+ * Removes the connection between the activity and all services that were connected to it. In
+ * general, this method is used to clean up if the activity didn't unbind services before it
+ * is destroyed.
+ */
void disconnectActivityFromServices() {
- if (mConnections == null || mConnections.isEmpty()) {
+ if (mConnections == null || mConnections.isEmpty() || mIsDisconnecting) {
return;
}
- // Capture and null out mConnections, to guarantee that we process
+ // Mark as disconnecting, to guarantee that we process
// disconnect of these specific connections exactly once even if
// we're racing with rapid activity lifecycle churn and this
// method is invoked more than once on this object.
- final Object disc = mConnections;
- mConnections = null;
- mService.mH.post(() -> mService.mAmInternal.disconnectActivityFromServices(this, disc));
+ // It is possible that {@link #removeConnection} is called while the disconnect-runnable is
+ // still in the message queue, so keep the reference of {@link #mConnections} to make sure
+ // the connection list is up-to-date.
+ mIsDisconnecting = true;
+ mService.mH.post(() -> {
+ mService.mAmInternal.disconnectActivityFromServices(this);
+ mIsDisconnecting = false;
+ });
}
public void dump(PrintWriter pw, String prefix) {
@@ -116,4 +142,9 @@ public class ActivityServiceConnectionsHolder<T> {
}
}
+ /** Used by {@link ActivityRecord#dump}. */
+ @Override
+ public String toString() {
+ return String.valueOf(mConnections);
+ }
}
diff --git a/services/core/java/com/android/server/wm/ActivityTaskManagerService.java b/services/core/java/com/android/server/wm/ActivityTaskManagerService.java
index 5a5976bf58ad..5f3e3a39490c 100644
--- a/services/core/java/com/android/server/wm/ActivityTaskManagerService.java
+++ b/services/core/java/com/android/server/wm/ActivityTaskManagerService.java
@@ -2247,11 +2247,11 @@ public class ActivityTaskManagerService extends IActivityTaskManager.Stub {
}
@Override
- public void setTaskWindowingMode(int taskId, int windowingMode, boolean toTop) {
+ public boolean setTaskWindowingMode(int taskId, int windowingMode, boolean toTop) {
if (windowingMode == WINDOWING_MODE_SPLIT_SCREEN_PRIMARY) {
- setTaskWindowingModeSplitScreenPrimary(taskId, SPLIT_SCREEN_CREATE_MODE_TOP_OR_LEFT,
+ return setTaskWindowingModeSplitScreenPrimary(taskId,
+ SPLIT_SCREEN_CREATE_MODE_TOP_OR_LEFT,
toTop, ANIMATE, null /* initialBounds */, true /* showRecents */);
- return;
}
enforceCallerIsRecentsOrHasPermission(MANAGE_ACTIVITY_STACKS, "setTaskWindowingMode()");
synchronized (mGlobalLock) {
@@ -2261,7 +2261,7 @@ public class ActivityTaskManagerService extends IActivityTaskManager.Stub {
MATCH_TASK_IN_STACKS_ONLY);
if (task == null) {
Slog.w(TAG, "setTaskWindowingMode: No task for id=" + taskId);
- return;
+ return false;
}
if (DEBUG_STACK) Slog.d(TAG_STACK, "setTaskWindowingMode: moving task=" + taskId
@@ -2278,6 +2278,7 @@ public class ActivityTaskManagerService extends IActivityTaskManager.Stub {
stack.moveToFront("setTaskWindowingMode", task);
}
stack.setWindowingMode(windowingMode);
+ return true;
} finally {
Binder.restoreCallingIdentity(ident);
}
@@ -2696,6 +2697,7 @@ public class ActivityTaskManagerService extends IActivityTaskManager.Stub {
* stack. Pass {@code null} to use default bounds.
* @param showRecents If the recents activity should be shown on the other side of the task
* going into split-screen mode.
+ * @return Whether the task was successfully put into splitscreen.
*/
@Override
public boolean setTaskWindowingModeSplitScreenPrimary(int taskId, int createMode,
@@ -2705,20 +2707,26 @@ public class ActivityTaskManagerService extends IActivityTaskManager.Stub {
synchronized (mGlobalLock) {
final long ident = Binder.clearCallingIdentity();
try {
+ if (isInLockTaskMode()) {
+ Slog.w(TAG, "setTaskWindowingModeSplitScreenPrimary: Is in lock task mode="
+ + getLockTaskModeState());
+ return false;
+ }
+
final Task task = mRootWindowContainer.anyTaskForId(taskId,
MATCH_TASK_IN_STACKS_ONLY);
if (task == null) {
Slog.w(TAG, "setTaskWindowingModeSplitScreenPrimary: No task for id=" + taskId);
return false;
}
- if (DEBUG_STACK) Slog.d(TAG_STACK,
- "setTaskWindowingModeSplitScreenPrimary: moving task=" + taskId
- + " to createMode=" + createMode + " toTop=" + toTop);
if (!task.isActivityTypeStandardOrUndefined()) {
throw new IllegalArgumentException("setTaskWindowingMode: Attempt to move"
+ " non-standard task " + taskId + " to split-screen windowing mode");
}
+ if (DEBUG_STACK) Slog.d(TAG_STACK,
+ "setTaskWindowingModeSplitScreenPrimary: moving task=" + taskId
+ + " to createMode=" + createMode + " toTop=" + toTop);
mWindowManager.setDockedStackCreateStateLocked(createMode, initialBounds);
final int windowingMode = task.getWindowingMode();
final ActivityStack stack = task.getStack();
diff --git a/services/core/java/com/android/server/wm/DisplayContent.java b/services/core/java/com/android/server/wm/DisplayContent.java
index 16ac9fb41f30..810aa3438ea0 100644
--- a/services/core/java/com/android/server/wm/DisplayContent.java
+++ b/services/core/java/com/android/server/wm/DisplayContent.java
@@ -194,6 +194,7 @@ import android.provider.Settings;
import android.util.ArraySet;
import android.util.DisplayMetrics;
import android.util.IntArray;
+import android.util.RotationUtils;
import android.util.Slog;
import android.util.SparseArray;
import android.util.SparseBooleanArray;
@@ -1603,17 +1604,18 @@ class DisplayContent extends WindowContainer<DisplayContent.DisplayChildWindowCo
if (cutout == null || cutout == DisplayCutout.NO_CUTOUT) {
return WmDisplayCutout.NO_CUTOUT;
}
+ final Insets waterfallInsets =
+ RotationUtils.rotateInsets(cutout.getWaterfallInsets(), rotation);
if (rotation == ROTATION_0) {
return WmDisplayCutout.computeSafeInsets(
cutout, mInitialDisplayWidth, mInitialDisplayHeight);
}
final boolean rotated = (rotation == ROTATION_90 || rotation == ROTATION_270);
final Rect[] newBounds = mRotationUtil.getRotatedBounds(
- WmDisplayCutout.computeSafeInsets(
- cutout, mInitialDisplayWidth, mInitialDisplayHeight)
- .getDisplayCutout().getBoundingRectsAll(),
+ cutout.getBoundingRectsAll(),
rotation, mInitialDisplayWidth, mInitialDisplayHeight);
- return WmDisplayCutout.computeSafeInsets(DisplayCutout.fromBounds(newBounds),
+ return WmDisplayCutout.computeSafeInsets(
+ DisplayCutout.fromBoundsAndWaterfall(newBounds, waterfallInsets),
rotated ? mInitialDisplayHeight : mInitialDisplayWidth,
rotated ? mInitialDisplayWidth : mInitialDisplayHeight);
}
diff --git a/services/core/java/com/android/server/wm/EmbeddedWindowController.java b/services/core/java/com/android/server/wm/EmbeddedWindowController.java
index 0291017cd039..884f7694d648 100644
--- a/services/core/java/com/android/server/wm/EmbeddedWindowController.java
+++ b/services/core/java/com/android/server/wm/EmbeddedWindowController.java
@@ -20,8 +20,10 @@ package com.android.server.wm;
import android.annotation.Nullable;
import android.os.IBinder;
import android.os.RemoteException;
+import android.os.UserHandle;
import android.util.ArrayMap;
import android.view.IWindow;
+import android.view.InputApplicationHandle;
/**
* Keeps track of embedded windows.
@@ -44,21 +46,12 @@ class EmbeddedWindowController {
*
* @param inputToken input channel token passed in by the embedding process when it requests
* the server to add an input channel to the embedded surface.
- * @param window client token used to clean up the map if the embedding process dies
- * @param hostWindowState input channel token belonging to the host window. This is needed to
- * handle input callbacks to wm. It's used when raising ANR and when
- * the user taps out side of the focused region on screen. This can be
- * null if there is no host window.
- * @param ownerUid calling uid
- * @param ownerPid calling pid used for anr blaming
+ * @param embeddedWindow An {@link EmbeddedWindow} object to add to this controller.
*/
- void add(IBinder inputToken, IWindow window, @Nullable WindowState hostWindowState,
- int ownerUid, int ownerPid) {
- EmbeddedWindow embeddedWindow = new EmbeddedWindow(window, hostWindowState, ownerUid,
- ownerPid);
+ void add(IBinder inputToken, EmbeddedWindow embeddedWindow) {
try {
mWindows.put(inputToken, embeddedWindow);
- window.asBinder().linkToDeath(()-> {
+ embeddedWindow.mClient.asBinder().linkToDeath(()-> {
synchronized (mWmLock) {
mWindows.remove(inputToken);
}
@@ -101,6 +94,15 @@ class EmbeddedWindowController {
final int mOwnerUid;
final int mOwnerPid;
+ /**
+ * @param clientToken client token used to clean up the map if the embedding process dies
+ * @param hostWindowState input channel token belonging to the host window. This is needed
+ * to handle input callbacks to wm. It's used when raising ANR and
+ * when the user taps out side of the focused region on screen. This
+ * can be null if there is no host window.
+ * @param ownerUid calling uid
+ * @param ownerPid calling pid used for anr blaming
+ */
EmbeddedWindow(IWindow clientToken, WindowState hostWindowState, int ownerUid,
int ownerPid) {
mClient = clientToken;
@@ -108,5 +110,21 @@ class EmbeddedWindowController {
mOwnerUid = ownerUid;
mOwnerPid = ownerPid;
}
+
+ String getName() {
+ final String hostWindowName = (mHostWindowState != null)
+ ? mHostWindowState.getWindowTag().toString() : "Internal";
+ return "EmbeddedWindow{ u" + UserHandle.getUserId(mOwnerUid) + " " + hostWindowName
+ + "}";
+ }
+
+ InputApplicationHandle getApplicationHandle() {
+ if (mHostWindowState == null
+ || mHostWindowState.mInputWindowHandle.inputApplicationHandle == null) {
+ return null;
+ }
+ return new InputApplicationHandle(
+ mHostWindowState.mInputWindowHandle.inputApplicationHandle);
+ }
}
}
diff --git a/services/core/java/com/android/server/wm/Session.java b/services/core/java/com/android/server/wm/Session.java
index ab1f34adbeeb..9df42486580b 100644
--- a/services/core/java/com/android/server/wm/Session.java
+++ b/services/core/java/com/android/server/wm/Session.java
@@ -622,7 +622,7 @@ class Session extends IWindowSession.Stub implements IBinder.DeathRecipient {
@Override
public void grantInputChannel(int displayId, SurfaceControl surface,
- IWindow window, IBinder hostInputToken, InputChannel outInputChannel) {
+ IWindow window, IBinder hostInputToken, int flags, InputChannel outInputChannel) {
if (hostInputToken == null && !mCanAddInternalSystemWindow) {
// Callers without INTERNAL_SYSTEM_WINDOW permission cannot grant input channel to
// embedded windows without providing a host window input token
@@ -631,8 +631,19 @@ class Session extends IWindowSession.Stub implements IBinder.DeathRecipient {
final long identity = Binder.clearCallingIdentity();
try {
- mService.grantInputChannel(mUid, mPid, displayId, surface, window,
- hostInputToken, outInputChannel);
+ mService.grantInputChannel(mUid, mPid, displayId, surface, window, hostInputToken,
+ flags, outInputChannel);
+ } finally {
+ Binder.restoreCallingIdentity(identity);
+ }
+ }
+
+ @Override
+ public void updateInputChannel(IBinder channelToken, int displayId, SurfaceControl surface,
+ int flags) {
+ final long identity = Binder.clearCallingIdentity();
+ try {
+ mService.updateInputChannel(channelToken, displayId, surface, flags);
} finally {
Binder.restoreCallingIdentity(identity);
}
diff --git a/services/core/java/com/android/server/wm/WindowManagerService.java b/services/core/java/com/android/server/wm/WindowManagerService.java
index 98c584fc743a..a5b99b0a26a3 100644
--- a/services/core/java/com/android/server/wm/WindowManagerService.java
+++ b/services/core/java/com/android/server/wm/WindowManagerService.java
@@ -7814,40 +7814,52 @@ public class WindowManagerService extends IWindowManager.Stub
* views.
*/
void grantInputChannel(int callingUid, int callingPid, int displayId, SurfaceControl surface,
- IWindow window, IBinder hostInputToken, InputChannel outInputChannel) {
- InputApplicationHandle applicationHandle = null;
+ IWindow window, IBinder hostInputToken, int flags, InputChannel outInputChannel) {
+ final InputApplicationHandle applicationHandle;
final String name;
final InputChannel[] inputChannels;
final InputChannel clientChannel;
final InputChannel serverChannel;
synchronized (mGlobalLock) {
- final WindowState hostWindow = mInputToWindowMap.get(hostInputToken);
- final String hostWindowName = (hostWindow != null)
- ? hostWindow.getWindowTag().toString() : "Internal";
- name = "EmbeddedWindow{ u" + UserHandle.getUserId(callingUid)
- + " " + hostWindowName + "}";
+ EmbeddedWindowController.EmbeddedWindow win =
+ new EmbeddedWindowController.EmbeddedWindow(window,
+ mInputToWindowMap.get(hostInputToken), callingUid, callingPid);
+ name = win.getName();
inputChannels = InputChannel.openInputChannelPair(name);
serverChannel = inputChannels[0];
clientChannel = inputChannels[1];
mInputManager.registerInputChannel(serverChannel);
- mEmbeddedWindowController.add(serverChannel.getToken(), window, hostWindow, callingUid,
- callingPid);
-
- if (hostWindow != null
- && hostWindow.mInputWindowHandle.inputApplicationHandle != null) {
- applicationHandle = new InputApplicationHandle(
- hostWindow.mInputWindowHandle.inputApplicationHandle);
+ mEmbeddedWindowController.add(clientChannel.getToken(), win);
+ if (serverChannel.getToken() != clientChannel.getToken()) {
+ throw new IllegalStateException("Client and Server channel are expected to"
+ + "be the same");
}
+
+ applicationHandle = win.getApplicationHandle();
}
+ updateInputChannel(clientChannel.getToken(), callingUid, callingPid, displayId, surface,
+ name, applicationHandle, flags);
+
clientChannel.transferTo(outInputChannel);
clientChannel.dispose();
+ // Prevent the java finalizer from breaking the input channel. But we won't
+ // do any further management so we just release the java ref and let the
+ // InputDispatcher hold the last ref.
+ serverChannel.release();
+ }
+ private void updateInputChannel(IBinder channelToken, int callingUid, int callingPid,
+ int displayId, SurfaceControl surface, String name,
+ InputApplicationHandle applicationHandle, int flags) {
InputWindowHandle h = new InputWindowHandle(applicationHandle, displayId);
- h.token = serverChannel.getToken();
+ h.token = channelToken;
h.name = name;
- h.layoutParamsFlags = WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL;
+
+ final int sanitizedFlags = flags & (LayoutParams.FLAG_NOT_TOUCHABLE
+ | LayoutParams.FLAG_SLIPPERY);
+ h.layoutParamsFlags = WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL | sanitizedFlags;
h.layoutParamsType = 0;
h.dispatchingTimeoutNanos = -1;
h.canReceiveKeys = false;
@@ -7862,15 +7874,34 @@ public class WindowManagerService extends IWindowManager.Stub
h.replaceTouchableRegionWithCrop(null);
- SurfaceSession s = new SurfaceSession();
SurfaceControl.Transaction t = mTransactionFactory.get();
t.setInputWindowInfo(surface, h);
t.apply();
+ t.close();
+ }
- // Prevent the java finalizer from breaking the input channel. But we won't
- // do any further management so we just release the java ref and let the
- // InputDispatcher hold the last ref.
- serverChannel.release();
+ /**
+ * Updates the flags on an existing surface's input channel. This assumes the surface provided
+ * is the one associated with the provided input-channel. If this isn't the case, behavior
+ * is undefined.
+ */
+ void updateInputChannel(IBinder channelToken, int displayId, SurfaceControl surface,
+ int flags) {
+ final InputApplicationHandle applicationHandle;
+ final String name;
+ final EmbeddedWindowController.EmbeddedWindow win;
+ synchronized (mGlobalLock) {
+ win = mEmbeddedWindowController.get(channelToken);
+ if (win == null) {
+ Slog.e(TAG, "Couldn't find window for provided channelToken.");
+ return;
+ }
+ name = win.getName();
+ applicationHandle = win.getApplicationHandle();
+ }
+
+ updateInputChannel(channelToken, win.mOwnerUid, win.mOwnerPid, displayId, surface, name,
+ applicationHandle, flags);
}
/** Return whether layer tracing is enabled */
diff --git a/services/core/java/com/android/server/wm/utils/WmDisplayCutout.java b/services/core/java/com/android/server/wm/utils/WmDisplayCutout.java
index 3be5d3176df5..46fff032e053 100644
--- a/services/core/java/com/android/server/wm/utils/WmDisplayCutout.java
+++ b/services/core/java/com/android/server/wm/utils/WmDisplayCutout.java
@@ -21,7 +21,6 @@ import android.util.Size;
import android.view.DisplayCutout;
import android.view.Gravity;
-import java.util.List;
import java.util.Objects;
/**
@@ -41,12 +40,17 @@ public class WmDisplayCutout {
mFrameSize = frameSize;
}
- public static WmDisplayCutout computeSafeInsets(DisplayCutout inner,
- int displayWidth, int displayHeight) {
- if (inner == DisplayCutout.NO_CUTOUT || inner.isBoundsEmpty()) {
+ /**
+ * Compute the safe insets according to the given DisplayCutout and the display size.
+ *
+ * @return return a WmDisplayCutout with calculated safe insets.
+ * @hide
+ */
+ public static WmDisplayCutout computeSafeInsets(
+ DisplayCutout inner, int displayWidth, int displayHeight) {
+ if (inner == DisplayCutout.NO_CUTOUT) {
return NO_CUTOUT;
}
-
final Size displaySize = new Size(displayWidth, displayHeight);
final Rect safeInsets = computeSafeInsets(displaySize, inner);
return new WmDisplayCutout(inner.replaceSafeInsets(safeInsets), displaySize);
@@ -112,57 +116,42 @@ public class WmDisplayCutout {
}
private static Rect computeSafeInsets(Size displaySize, DisplayCutout cutout) {
- if (displaySize.getWidth() < displaySize.getHeight()) {
- final List<Rect> boundingRects = cutout.replaceSafeInsets(
- new Rect(0, displaySize.getHeight() / 2, 0, displaySize.getHeight() / 2))
- .getBoundingRects();
- int topInset = findInsetForSide(displaySize, boundingRects, Gravity.TOP);
- int bottomInset = findInsetForSide(displaySize, boundingRects, Gravity.BOTTOM);
- return new Rect(0, topInset, 0, bottomInset);
- } else if (displaySize.getWidth() > displaySize.getHeight()) {
- final List<Rect> boundingRects = cutout.replaceSafeInsets(
- new Rect(displaySize.getWidth() / 2, 0, displaySize.getWidth() / 2, 0))
- .getBoundingRects();
- int leftInset = findInsetForSide(displaySize, boundingRects, Gravity.LEFT);
- int right = findInsetForSide(displaySize, boundingRects, Gravity.RIGHT);
- return new Rect(leftInset, 0, right, 0);
- } else {
+ if (displaySize.getWidth() == displaySize.getHeight()) {
throw new UnsupportedOperationException("not implemented: display=" + displaySize +
" cutout=" + cutout);
}
+
+ int leftInset = Math.max(cutout.getWaterfallInsets().left,
+ findCutoutInsetForSide(displaySize, cutout.getBoundingRectLeft(), Gravity.LEFT));
+ int topInset = Math.max(cutout.getWaterfallInsets().top,
+ findCutoutInsetForSide(displaySize, cutout.getBoundingRectTop(), Gravity.TOP));
+ int rightInset = Math.max(cutout.getWaterfallInsets().right,
+ findCutoutInsetForSide(displaySize, cutout.getBoundingRectRight(), Gravity.RIGHT));
+ int bottomInset = Math.max(cutout.getWaterfallInsets().bottom,
+ findCutoutInsetForSide(displaySize, cutout.getBoundingRectBottom(),
+ Gravity.BOTTOM));
+
+ return new Rect(leftInset, topInset, rightInset, bottomInset);
}
- private static int findInsetForSide(Size display, List<Rect> boundingRects, int gravity) {
+ private static int findCutoutInsetForSide(Size display, Rect boundingRect, int gravity) {
+ if (boundingRect.isEmpty()) {
+ return 0;
+ }
+
int inset = 0;
- final int size = boundingRects.size();
- for (int i = 0; i < size; i++) {
- Rect boundingRect = boundingRects.get(i);
- switch (gravity) {
- case Gravity.TOP:
- if (boundingRect.top == 0) {
- inset = Math.max(inset, boundingRect.bottom);
- }
- break;
- case Gravity.BOTTOM:
- if (boundingRect.bottom == display.getHeight()) {
- inset = Math.max(inset, display.getHeight() - boundingRect.top);
- }
- break;
- case Gravity.LEFT:
- if (boundingRect.left == 0) {
- inset = Math.max(inset, boundingRect.right);
- }
- break;
- case Gravity.RIGHT:
- if (boundingRect.right == display.getWidth()) {
- inset = Math.max(inset, display.getWidth() - boundingRect.left);
- }
- break;
- default:
- throw new IllegalArgumentException("unknown gravity: " + gravity);
- }
+ switch (gravity) {
+ case Gravity.TOP:
+ return Math.max(inset, boundingRect.bottom);
+ case Gravity.BOTTOM:
+ return Math.max(inset, display.getHeight() - boundingRect.top);
+ case Gravity.LEFT:
+ return Math.max(inset, boundingRect.right);
+ case Gravity.RIGHT:
+ return Math.max(inset, display.getWidth() - boundingRect.left);
+ default:
+ throw new IllegalArgumentException("unknown gravity: " + gravity);
}
- return inset;
}
public DisplayCutout getDisplayCutout() {
diff --git a/services/core/jni/com_android_server_incremental_IncrementalManagerService.cpp b/services/core/jni/com_android_server_incremental_IncrementalManagerService.cpp
index 5e255f46fa05..10bac94f77e2 100644
--- a/services/core/jni/com_android_server_incremental_IncrementalManagerService.cpp
+++ b/services/core/jni/com_android_server_incremental_IncrementalManagerService.cpp
@@ -33,9 +33,14 @@ static void nativeSystemReady(JNIEnv* env, jclass klass, jlong self) {
Incremental_IncrementalService_OnSystemReady(self);
}
+static void nativeDump(JNIEnv* env, jclass klass, jlong self, jint fd) {
+ Incremental_IncrementalService_OnDump(self, fd);
+}
+
static const JNINativeMethod method_table[] = {
{"nativeStartService", "()J", (void*)nativeStartService},
{"nativeSystemReady", "(J)V", (void*)nativeSystemReady},
+ {"nativeDump", "(JI)V", (void*)nativeDump},
};
int register_android_server_incremental_IncrementalManagerService(JNIEnv* env) {
diff --git a/services/core/jni/com_android_server_location_GnssLocationProvider.cpp b/services/core/jni/com_android_server_location_GnssLocationProvider.cpp
index 12de20cc3d51..1a8f1f9f0024 100644
--- a/services/core/jni/com_android_server_location_GnssLocationProvider.cpp
+++ b/services/core/jni/com_android_server_location_GnssLocationProvider.cpp
@@ -1218,19 +1218,21 @@ void GnssMeasurementCallback::translateSingleGnssMeasurement
SET(BasebandCn0DbHz, measurement_V2_1->basebandCN0DbHz);
if (measurement_V2_1->flags & GnssMeasurementFlags::HAS_RECEIVER_ISB) {
- SET(ReceiverInterSignalBiasNs, measurement_V2_1->receiverInterSignalBiasNs);
+ SET(ReceiverInterSignalBiasNanos, measurement_V2_1->receiverInterSignalBiasNs);
}
if (measurement_V2_1->flags & GnssMeasurementFlags::HAS_RECEIVER_ISB_UNCERTAINTY) {
- SET(ReceiverInterSignalBiasUncertaintyNs, measurement_V2_1->receiverInterSignalBiasUncertaintyNs);
+ SET(ReceiverInterSignalBiasUncertaintyNanos,
+ measurement_V2_1->receiverInterSignalBiasUncertaintyNs);
}
if (measurement_V2_1->flags & GnssMeasurementFlags::HAS_SATELLITE_ISB) {
- SET(SatelliteInterSignalBiasNs, measurement_V2_1->satelliteInterSignalBiasNs);
+ SET(SatelliteInterSignalBiasNanos, measurement_V2_1->satelliteInterSignalBiasNs);
}
if (measurement_V2_1->flags & GnssMeasurementFlags::HAS_SATELLITE_ISB_UNCERTAINTY) {
- SET(SatelliteInterSignalBiasUncertaintyNs, measurement_V2_1->satelliteInterSignalBiasUncertaintyNs);
+ SET(SatelliteInterSignalBiasUncertaintyNanos,
+ measurement_V2_1->satelliteInterSignalBiasUncertaintyNs);
}
}
diff --git a/services/incremental/BinderIncrementalService.cpp b/services/incremental/BinderIncrementalService.cpp
index 91d05723a605..0941831f5299 100644
--- a/services/incremental/BinderIncrementalService.cpp
+++ b/services/incremental/BinderIncrementalService.cpp
@@ -17,12 +17,14 @@
#include "BinderIncrementalService.h"
#include <binder/IResultReceiver.h>
+#include <binder/PermissionCache.h>
#include <incfs.h>
#include "ServiceWrappers.h"
#include "jni.h"
#include "nativehelper/JNIHelp.h"
#include "path.h"
+#include <android-base/logging.h>
using namespace std::literals;
using namespace android::incremental;
@@ -90,8 +92,13 @@ BinderIncrementalService* BinderIncrementalService::start() {
return self.get();
}
-status_t BinderIncrementalService::dump(int fd, const Vector<String16>& args) {
- return OK;
+status_t BinderIncrementalService::dump(int fd, const Vector<String16>&) {
+ static const String16 kDump("android.permission.DUMP");
+ if (!PermissionCache::checkCallingPermission(kDump)) {
+ return PERMISSION_DENIED;
+ }
+ mImpl.onDump(fd);
+ return NO_ERROR;
}
void BinderIncrementalService::onSystemReady() {
@@ -280,3 +287,10 @@ void Incremental_IncrementalService_OnSystemReady(jlong self) {
((android::os::incremental::BinderIncrementalService*)self)->onSystemReady();
}
}
+void Incremental_IncrementalService_OnDump(jlong self, jint fd) {
+ if (self) {
+ ((android::os::incremental::BinderIncrementalService*)self)->dump(fd, {});
+ } else {
+ dprintf(fd, "BinderIncrementalService is stopped.");
+ }
+}
diff --git a/services/incremental/IncrementalService.cpp b/services/incremental/IncrementalService.cpp
index e4a37dde7758..dbd97cfa039a 100644
--- a/services/incremental/IncrementalService.cpp
+++ b/services/incremental/IncrementalService.cpp
@@ -35,6 +35,7 @@
#include <uuid/uuid.h>
#include <zlib.h>
+#include <ctime>
#include <iterator>
#include <span>
#include <stack>
@@ -169,7 +170,7 @@ auto IncrementalService::IncFsMount::makeStorage(StorageId id) -> StorageMap::it
base::StringAppendF(&name, "%.*s_%d_%d", int(constants().storagePrefix.size()),
constants().storagePrefix.data(), id, no);
auto fullName = path::join(root, constants().mount, name);
- if (auto err = incrementalService.mIncFs->makeDir(control, fullName); !err) {
+ if (auto err = incrementalService.mIncFs->makeDir(control, fullName, 0755); !err) {
std::lock_guard l(lock);
return storages.insert_or_assign(id, Storage{std::move(fullName)}).first;
} else if (err != EEXIST) {
@@ -256,6 +257,68 @@ FileId IncrementalService::idFromMetadata(std::span<const uint8_t> metadata) {
IncrementalService::~IncrementalService() = default;
+inline const char* toString(TimePoint t) {
+ using SystemClock = std::chrono::system_clock;
+ time_t time = SystemClock::to_time_t(SystemClock::now() + std::chrono::duration_cast<SystemClock::duration>(t - Clock::now()));
+ return std::ctime(&time);
+}
+
+inline const char* toString(IncrementalService::BindKind kind) {
+ switch (kind) {
+ case IncrementalService::BindKind::Temporary:
+ return "Temporary";
+ case IncrementalService::BindKind::Permanent:
+ return "Permanent";
+ }
+}
+
+void IncrementalService::onDump(int fd) {
+ dprintf(fd, "Incremental is %s\n", incfs::enabled() ? "ENABLED" : "DISABLED");
+ dprintf(fd, "Incremental dir: %s\n", mIncrementalDir.c_str());
+
+ std::unique_lock l(mLock);
+
+ dprintf(fd, "Mounts (%d):\n", int(mMounts.size()));
+ for (auto&& [id, ifs] : mMounts) {
+ const IncFsMount& mnt = *ifs.get();
+ dprintf(fd, "\t[%d]:\n", id);
+ dprintf(fd, "\t\tmountId: %d\n", mnt.mountId);
+ dprintf(fd, "\t\tnextStorageDirNo: %d\n", mnt.nextStorageDirNo.load());
+ dprintf(fd, "\t\tdataLoaderStatus: %d\n", mnt.dataLoaderStatus.load());
+ dprintf(fd, "\t\tconnectionLostTime: %s\n", toString(mnt.connectionLostTime));
+ if (mnt.savedDataLoaderParams) {
+ const auto& params = mnt.savedDataLoaderParams.value();
+ dprintf(fd, "\t\tsavedDataLoaderParams:\n");
+ dprintf(fd, "\t\t\ttype: %s\n", toString(params.type).c_str());
+ dprintf(fd, "\t\t\tpackageName: %s\n", params.packageName.c_str());
+ dprintf(fd, "\t\t\tclassName: %s\n", params.className.c_str());
+ dprintf(fd, "\t\t\targuments: %s\n", params.arguments.c_str());
+ dprintf(fd, "\t\t\tdynamicArgs: %d\n", int(params.dynamicArgs.size()));
+ }
+ dprintf(fd, "\t\tstorages (%d):\n", int(mnt.storages.size()));
+ for (auto&& [storageId, storage] : mnt.storages) {
+ dprintf(fd, "\t\t\t[%d] -> [%s]\n", storageId, storage.name.c_str());
+ }
+
+ dprintf(fd, "\t\tbindPoints (%d):\n", int(mnt.bindPoints.size()));
+ for (auto&& [target, bind] : mnt.bindPoints) {
+ dprintf(fd, "\t\t\t[%s]->[%d]:\n", target.c_str(), bind.storage);
+ dprintf(fd, "\t\t\t\tsavedFilename: %s\n", bind.savedFilename.c_str());
+ dprintf(fd, "\t\t\t\tsourceDir: %s\n", bind.sourceDir.c_str());
+ dprintf(fd, "\t\t\t\tkind: %s\n", toString(bind.kind));
+ }
+ }
+
+ dprintf(fd, "Sorted binds (%d):\n", int(mBindsByPath.size()));
+ for (auto&& [target, mountPairIt] : mBindsByPath) {
+ const auto& bind = mountPairIt->second;
+ dprintf(fd, "\t\t[%s]->[%d]:\n", target.c_str(), bind.storage);
+ dprintf(fd, "\t\t\tsavedFilename: %s\n", bind.savedFilename.c_str());
+ dprintf(fd, "\t\t\tsourceDir: %s\n", bind.sourceDir.c_str());
+ dprintf(fd, "\t\t\tkind: %s\n", toString(bind.kind));
+ }
+}
+
std::optional<std::future<void>> IncrementalService::onSystemReady() {
std::promise<void> threadFinished;
if (mSystemReady.exchange(true)) {
@@ -617,15 +680,7 @@ int IncrementalService::bind(StorageId storage, std::string_view source, std::st
if (storageInfo == ifs->storages.end()) {
return -EINVAL;
}
- std::string normSource;
- if (path::isAbsolute(source)) {
- normSource = path::normalize(source);
- } else {
- normSource = path::normalize(path::join(storageInfo->second.name, source));
- }
- if (!path::startsWith(normSource, storageInfo->second.name)) {
- return -EINVAL;
- }
+ std::string normSource = normalizePathToStorage(ifs, storage, source);
l.unlock();
std::unique_lock l2(mLock, std::defer_lock);
return addBindMount(*ifs, storage, storageInfo->second.name, std::move(normSource),
@@ -674,20 +729,29 @@ int IncrementalService::unbind(StorageId storage, std::string_view target) {
return 0;
}
+std::string IncrementalService::normalizePathToStorage(const IncrementalService::IfsMountPtr ifs,
+ StorageId storage, std::string_view path) {
+ const auto storageInfo = ifs->storages.find(storage);
+ if (storageInfo == ifs->storages.end()) {
+ return {};
+ }
+ std::string normPath;
+ if (path::isAbsolute(path)) {
+ normPath = path::normalize(path);
+ } else {
+ normPath = path::normalize(path::join(storageInfo->second.name, path));
+ }
+ if (!path::startsWith(normPath, storageInfo->second.name)) {
+ return {};
+ }
+ return normPath;
+}
+
int IncrementalService::makeFile(StorageId storage, std::string_view path, int mode, FileId id,
incfs::NewFileParams params) {
if (auto ifs = getIfs(storage)) {
- const auto storageInfo = ifs->storages.find(storage);
- if (storageInfo == ifs->storages.end()) {
- return -EINVAL;
- }
- std::string normPath;
- if (path::isAbsolute(path)) {
- normPath = path::normalize(path);
- } else {
- normPath = path::normalize(path::join(storageInfo->second.name, path));
- }
- if (!path::startsWith(normPath, storageInfo->second.name)) {
+ std::string normPath = normalizePathToStorage(ifs, storage, path);
+ if (normPath.empty()) {
return -EINVAL;
}
auto err = mIncFs->makeFile(ifs->control, normPath, mode, id, params);
@@ -706,7 +770,11 @@ int IncrementalService::makeFile(StorageId storage, std::string_view path, int m
int IncrementalService::makeDir(StorageId storageId, std::string_view path, int mode) {
if (auto ifs = getIfs(storageId)) {
- return mIncFs->makeDir(ifs->control, path, mode);
+ std::string normPath = normalizePathToStorage(ifs, storageId, path);
+ if (normPath.empty()) {
+ return -EINVAL;
+ }
+ return mIncFs->makeDir(ifs->control, normPath, mode);
}
return -EINVAL;
}
@@ -716,31 +784,40 @@ int IncrementalService::makeDirs(StorageId storageId, std::string_view path, int
if (!ifs) {
return -EINVAL;
}
-
- auto err = mIncFs->makeDir(ifs->control, path, mode);
+ std::string normPath = normalizePathToStorage(ifs, storageId, path);
+ if (normPath.empty()) {
+ return -EINVAL;
+ }
+ auto err = mIncFs->makeDir(ifs->control, normPath, mode);
if (err == -EEXIST) {
return 0;
} else if (err != -ENOENT) {
return err;
}
- if (auto err = makeDirs(storageId, path::dirname(path), mode)) {
+ if (auto err = makeDirs(storageId, path::dirname(normPath), mode)) {
return err;
}
- return mIncFs->makeDir(ifs->control, path, mode);
+ return mIncFs->makeDir(ifs->control, normPath, mode);
}
int IncrementalService::link(StorageId sourceStorageId, std::string_view oldPath,
StorageId destStorageId, std::string_view newPath) {
if (auto ifsSrc = getIfs(sourceStorageId), ifsDest = getIfs(destStorageId);
ifsSrc && ifsSrc == ifsDest) {
- return mIncFs->link(ifsSrc->control, oldPath, newPath);
+ std::string normOldPath = normalizePathToStorage(ifsSrc, sourceStorageId, oldPath);
+ std::string normNewPath = normalizePathToStorage(ifsDest, destStorageId, newPath);
+ if (normOldPath.empty() || normNewPath.empty()) {
+ return -EINVAL;
+ }
+ return mIncFs->link(ifsSrc->control, normOldPath, normNewPath);
}
return -EINVAL;
}
int IncrementalService::unlink(StorageId storage, std::string_view path) {
if (auto ifs = getIfs(storage)) {
- return mIncFs->unlink(ifs->control, path);
+ std::string normOldPath = normalizePathToStorage(ifs, storage, path);
+ return mIncFs->unlink(ifs->control, normOldPath);
}
return -EINVAL;
}
diff --git a/services/incremental/IncrementalService.h b/services/incremental/IncrementalService.h
index ca5e4dbd9231..dec9f64f2084 100644
--- a/services/incremental/IncrementalService.h
+++ b/services/incremental/IncrementalService.h
@@ -18,8 +18,8 @@
#include <android-base/strings.h>
#include <android-base/unique_fd.h>
-#include <android/os/incremental/IIncrementalManager.h>
#include <android/content/pm/DataLoaderParamsParcel.h>
+#include <android/os/incremental/IIncrementalManager.h>
#include <binder/IServiceManager.h>
#include <utils/String16.h>
#include <utils/StrongPointer.h>
@@ -90,10 +90,11 @@ public:
return idFromMetadata({(const uint8_t*)metadata.data(), metadata.size()});
}
+ void onDump(int fd);
+
std::optional<std::future<void>> onSystemReady();
- StorageId createStorage(std::string_view mountPoint,
- DataLoaderParamsParcel&& dataLoaderParams,
+ StorageId createStorage(std::string_view mountPoint, DataLoaderParamsParcel&& dataLoaderParams,
CreateOptions options = CreateOptions::Default);
StorageId createLinkedStorage(std::string_view mountPoint, StorageId linkedStorage,
CreateOptions options = CreateOptions::Default);
@@ -109,8 +110,8 @@ public:
int makeFile(StorageId storage, std::string_view path, int mode, FileId id,
incfs::NewFileParams params);
- int makeDir(StorageId storage, std::string_view path, int mode = 0555);
- int makeDirs(StorageId storage, std::string_view path, int mode = 0555);
+ int makeDir(StorageId storage, std::string_view path, int mode = 0755);
+ int makeDirs(StorageId storage, std::string_view path, int mode = 0755);
int link(StorageId sourceStorageId, std::string_view oldPath, StorageId destStorageId,
std::string_view newPath);
@@ -207,6 +208,8 @@ private:
void deleteStorage(IncFsMount& ifs);
void deleteStorageLocked(IncFsMount& ifs, std::unique_lock<std::mutex>&& ifsLock);
MountMap::iterator getStorageSlotLocked();
+ std::string normalizePathToStorage(const IfsMountPtr incfs, StorageId storage,
+ std::string_view path);
// Member variables
std::unique_ptr<VoldServiceWrapper> mVold;
diff --git a/services/incremental/ServiceWrappers.h b/services/incremental/ServiceWrappers.h
index ae3739dba2f0..f0b56729e8d7 100644
--- a/services/incremental/ServiceWrappers.h
+++ b/services/incremental/ServiceWrappers.h
@@ -70,7 +70,7 @@ public:
virtual ~IncFsWrapper() = default;
virtual ErrorCode makeFile(Control control, std::string_view path, int mode, FileId id,
NewFileParams params) const = 0;
- virtual ErrorCode makeDir(Control control, std::string_view path, int mode = 0555) const = 0;
+ virtual ErrorCode makeDir(Control control, std::string_view path, int mode) const = 0;
virtual RawMetadata getMetadata(Control control, FileId fileid) const = 0;
virtual RawMetadata getMetadata(Control control, std::string_view path) const = 0;
virtual FileId getFileId(Control control, std::string_view path) const = 0;
diff --git a/services/incremental/include/incremental_service.h b/services/incremental/include/incremental_service.h
index 7109d953ba4d..4a34b11261b9 100644
--- a/services/incremental/include/incremental_service.h
+++ b/services/incremental/include/incremental_service.h
@@ -26,6 +26,7 @@ __BEGIN_DECLS
jlong Incremental_IncrementalService_Start();
void Incremental_IncrementalService_OnSystemReady(jlong self);
+void Incremental_IncrementalService_OnDump(jlong self, jint fd);
__END_DECLS
diff --git a/services/incremental/test/IncrementalServiceTest.cpp b/services/incremental/test/IncrementalServiceTest.cpp
index 28268181f173..9cdc83e75055 100644
--- a/services/incremental/test/IncrementalServiceTest.cpp
+++ b/services/incremental/test/IncrementalServiceTest.cpp
@@ -406,29 +406,16 @@ TEST_F(IncrementalServiceTest, testMakeDirectory) {
int storageId =
mIncrementalService->createStorage(tempDir.path, std::move(mDataLoaderParcel),
IncrementalService::CreateOptions::CreateNew);
- std::string_view dir_path("test");
- EXPECT_CALL(*mIncFs, makeDir(_, dir_path, _));
- auto res = mIncrementalService->makeDir(storageId, dir_path, 0555);
- ASSERT_EQ(res, 0);
-}
+ std::string dir_path("test");
-TEST_F(IncrementalServiceTest, testMakeDirectoryNested) {
- mVold->mountIncFsSuccess();
- mIncFs->makeFileSuccess();
- mVold->bindMountSuccess();
- mIncrementalManager->prepareDataLoaderSuccess();
- mIncrementalManager->startDataLoaderSuccess();
- TemporaryDir tempDir;
- int storageId =
- mIncrementalService->createStorage(tempDir.path, std::move(mDataLoaderParcel),
- IncrementalService::CreateOptions::CreateNew);
- auto first = "first"sv;
- auto second = "second"sv;
- std::string dir_path = std::string(first) + "/" + std::string(second);
- EXPECT_CALL(*mIncFs, makeDir(_, first, _)).Times(0);
- EXPECT_CALL(*mIncFs, makeDir(_, second, _)).Times(0);
- EXPECT_CALL(*mIncFs, makeDir(_, std::string_view(dir_path), _)).Times(1);
+ std::string tempPath(tempDir.path);
+ std::replace(tempPath.begin(), tempPath.end(), '/', '_');
+ std::string mount_dir = std::string(mRootDir.path) + "/" + tempPath.substr(1);
+ std::string normalized_dir_path = mount_dir + "/mount/st_1_0/" + dir_path;
+ // Expecting incfs to call makeDir on a path like:
+ // /data/local/tmp/TemporaryDir-06yixG/data_local_tmp_TemporaryDir-xwdFhT/mount/st_1_0/test
+ EXPECT_CALL(*mIncFs, makeDir(_, std::string_view(normalized_dir_path), _));
auto res = mIncrementalService->makeDir(storageId, dir_path, 0555);
ASSERT_EQ(res, 0);
}
@@ -446,15 +433,29 @@ TEST_F(IncrementalServiceTest, testMakeDirectories) {
auto first = "first"sv;
auto second = "second"sv;
auto third = "third"sv;
+
+ std::string tempPath(tempDir.path);
+ std::replace(tempPath.begin(), tempPath.end(), '/', '_');
+ std::string mount_dir = std::string(mRootDir.path) + "/" + tempPath.substr(1);
+
InSequence seq;
auto parent_path = std::string(first) + "/" + std::string(second);
auto dir_path = parent_path + "/" + std::string(third);
- EXPECT_CALL(*mIncFs, makeDir(_, std::string_view(dir_path), _)).WillOnce(Return(-ENOENT));
- EXPECT_CALL(*mIncFs, makeDir(_, std::string_view(parent_path), _)).WillOnce(Return(-ENOENT));
- EXPECT_CALL(*mIncFs, makeDir(_, first, _)).WillOnce(Return(0));
- EXPECT_CALL(*mIncFs, makeDir(_, std::string_view(parent_path), _)).WillOnce(Return(0));
- EXPECT_CALL(*mIncFs, makeDir(_, std::string_view(dir_path), _)).WillOnce(Return(0));
- auto res = mIncrementalService->makeDirs(storageId, dir_path, 0555);
+
+ std::string normalized_first_path = mount_dir + "/mount/st_1_0/" + std::string(first);
+ std::string normalized_parent_path = mount_dir + "/mount/st_1_0/" + parent_path;
+ std::string normalized_dir_path = mount_dir + "/mount/st_1_0/" + dir_path;
+
+ EXPECT_CALL(*mIncFs, makeDir(_, std::string_view(normalized_dir_path), _))
+ .WillOnce(Return(-ENOENT));
+ EXPECT_CALL(*mIncFs, makeDir(_, std::string_view(normalized_parent_path), _))
+ .WillOnce(Return(-ENOENT));
+ EXPECT_CALL(*mIncFs, makeDir(_, std::string_view(normalized_first_path), _))
+ .WillOnce(Return(0));
+ EXPECT_CALL(*mIncFs, makeDir(_, std::string_view(normalized_parent_path), _))
+ .WillOnce(Return(0));
+ EXPECT_CALL(*mIncFs, makeDir(_, std::string_view(normalized_dir_path), _)).WillOnce(Return(0));
+ auto res = mIncrementalService->makeDirs(storageId, normalized_dir_path, 0555);
ASSERT_EQ(res, 0);
}
} // namespace android::os::incremental
diff --git a/services/java/com/android/server/SystemServer.java b/services/java/com/android/server/SystemServer.java
index 6ba4330f3916..036335ca9011 100644
--- a/services/java/com/android/server/SystemServer.java
+++ b/services/java/com/android/server/SystemServer.java
@@ -71,7 +71,6 @@ import android.text.TextUtils;
import android.util.DisplayMetrics;
import android.util.EventLog;
import android.util.Slog;
-import android.util.StatsLog;
import android.view.contentcapture.ContentCaptureManager;
import com.android.internal.R;
@@ -79,6 +78,7 @@ import com.android.internal.notification.SystemNotificationChannels;
import com.android.internal.os.BinderInternal;
import com.android.internal.util.ConcurrentUtils;
import com.android.internal.util.EmergencyAffordanceManager;
+import com.android.internal.util.FrameworkStatsLog;
import com.android.internal.widget.ILockSettings;
import com.android.server.am.ActivityManagerService;
import com.android.server.appbinding.AppBindingService;
@@ -453,8 +453,9 @@ public final class SystemServer {
final long uptimeMillis = SystemClock.elapsedRealtime();
EventLog.writeEvent(EventLogTags.BOOT_PROGRESS_SYSTEM_RUN, uptimeMillis);
if (!mRuntimeRestart) {
- StatsLog.write(StatsLog.BOOT_TIME_EVENT_ELAPSED_TIME_REPORTED,
- StatsLog.BOOT_TIME_EVENT_ELAPSED_TIME__EVENT__SYSTEM_SERVER_INIT_START,
+ FrameworkStatsLog.write(FrameworkStatsLog.BOOT_TIME_EVENT_ELAPSED_TIME_REPORTED,
+ FrameworkStatsLog
+ .BOOT_TIME_EVENT_ELAPSED_TIME__EVENT__SYSTEM_SERVER_INIT_START,
uptimeMillis);
}
@@ -570,8 +571,8 @@ public final class SystemServer {
if (!mRuntimeRestart && !isFirstBootOrUpgrade()) {
final long uptimeMillis = SystemClock.elapsedRealtime();
- StatsLog.write(StatsLog.BOOT_TIME_EVENT_ELAPSED_TIME_REPORTED,
- StatsLog.BOOT_TIME_EVENT_ELAPSED_TIME__EVENT__SYSTEM_SERVER_READY,
+ FrameworkStatsLog.write(FrameworkStatsLog.BOOT_TIME_EVENT_ELAPSED_TIME_REPORTED,
+ FrameworkStatsLog.BOOT_TIME_EVENT_ELAPSED_TIME__EVENT__SYSTEM_SERVER_READY,
uptimeMillis);
final long maxUptimeMillis = 60 * 1000;
if (uptimeMillis > maxUptimeMillis) {
@@ -807,8 +808,9 @@ public final class SystemServer {
// Start the package manager.
if (!mRuntimeRestart) {
- StatsLog.write(StatsLog.BOOT_TIME_EVENT_ELAPSED_TIME_REPORTED,
- StatsLog.BOOT_TIME_EVENT_ELAPSED_TIME__EVENT__PACKAGE_MANAGER_INIT_START,
+ FrameworkStatsLog.write(FrameworkStatsLog.BOOT_TIME_EVENT_ELAPSED_TIME_REPORTED,
+ FrameworkStatsLog
+ .BOOT_TIME_EVENT_ELAPSED_TIME__EVENT__PACKAGE_MANAGER_INIT_START,
SystemClock.elapsedRealtime());
}
@@ -825,8 +827,9 @@ public final class SystemServer {
mPackageManager = mSystemContext.getPackageManager();
t.traceEnd();
if (!mRuntimeRestart && !isFirstBootOrUpgrade()) {
- StatsLog.write(StatsLog.BOOT_TIME_EVENT_ELAPSED_TIME_REPORTED,
- StatsLog.BOOT_TIME_EVENT_ELAPSED_TIME__EVENT__PACKAGE_MANAGER_INIT_READY,
+ FrameworkStatsLog.write(FrameworkStatsLog.BOOT_TIME_EVENT_ELAPSED_TIME_REPORTED,
+ FrameworkStatsLog
+ .BOOT_TIME_EVENT_ELAPSED_TIME__EVENT__PACKAGE_MANAGER_INIT_READY,
SystemClock.elapsedRealtime());
}
// Manages A/B OTA dexopting. This is a bootstrap service as we need it to rename
diff --git a/services/people/java/com/android/server/people/SessionInfo.java b/services/people/java/com/android/server/people/SessionInfo.java
index eb08e03c14de..eaa0781f12ef 100644
--- a/services/people/java/com/android/server/people/SessionInfo.java
+++ b/services/people/java/com/android/server/people/SessionInfo.java
@@ -25,7 +25,7 @@ import android.os.RemoteException;
import android.util.Slog;
import com.android.server.people.data.DataManager;
-import com.android.server.people.prediction.ConversationPredictor;
+import com.android.server.people.prediction.AppTargetPredictor;
import java.util.List;
@@ -34,12 +34,12 @@ class SessionInfo {
private static final String TAG = "SessionInfo";
- private final ConversationPredictor mConversationPredictor;
+ private final AppTargetPredictor mAppTargetPredictor;
private final RemoteCallbackList<IPredictionCallback> mCallbacks =
new RemoteCallbackList<>();
SessionInfo(AppPredictionContext predictionContext, DataManager dataManager) {
- mConversationPredictor = new ConversationPredictor(predictionContext,
+ mAppTargetPredictor = AppTargetPredictor.create(predictionContext,
this::updatePredictions, dataManager);
}
@@ -51,8 +51,8 @@ class SessionInfo {
mCallbacks.unregister(callback);
}
- ConversationPredictor getPredictor() {
- return mConversationPredictor;
+ AppTargetPredictor getPredictor() {
+ return mAppTargetPredictor;
}
void onDestroy() {
diff --git a/services/people/java/com/android/server/people/data/CallLogQueryHelper.java b/services/people/java/com/android/server/people/data/CallLogQueryHelper.java
new file mode 100644
index 000000000000..d825b6b2bd8f
--- /dev/null
+++ b/services/people/java/com/android/server/people/data/CallLogQueryHelper.java
@@ -0,0 +1,121 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.people.data;
+
+import android.annotation.WorkerThread;
+import android.content.Context;
+import android.database.Cursor;
+import android.provider.CallLog.Calls;
+import android.text.TextUtils;
+import android.util.Slog;
+import android.util.SparseIntArray;
+
+import java.util.function.BiConsumer;
+
+/** A helper class that queries the call log database. */
+class CallLogQueryHelper {
+
+ private static final String TAG = "CallLogQueryHelper";
+
+ private static final SparseIntArray CALL_TYPE_TO_EVENT_TYPE = new SparseIntArray();
+
+ static {
+ CALL_TYPE_TO_EVENT_TYPE.put(Calls.INCOMING_TYPE, Event.TYPE_CALL_INCOMING);
+ CALL_TYPE_TO_EVENT_TYPE.put(Calls.OUTGOING_TYPE, Event.TYPE_CALL_OUTGOING);
+ CALL_TYPE_TO_EVENT_TYPE.put(Calls.MISSED_TYPE, Event.TYPE_CALL_MISSED);
+ }
+
+ private final Context mContext;
+ private final BiConsumer<String, Event> mEventConsumer;
+ private long mLastCallTimestamp;
+
+ /**
+ * @param context Context for accessing the content resolver.
+ * @param eventConsumer Consumes the events created from the call log records. The first input
+ * param is the normalized phone number.
+ */
+ CallLogQueryHelper(Context context, BiConsumer<String, Event> eventConsumer) {
+ mContext = context;
+ mEventConsumer = eventConsumer;
+ }
+
+ /**
+ * Queries the call log database for the new data added since {@code sinceTime} and returns
+ * true if the query runs successfully and at least one call log entry is found.
+ */
+ @WorkerThread
+ boolean querySince(long sinceTime) {
+ String[] projection = new String[] {
+ Calls.CACHED_NORMALIZED_NUMBER, Calls.DATE, Calls.DURATION, Calls.TYPE };
+ String selection = Calls.DATE + " > ?";
+ String[] selectionArgs = new String[] { Long.toString(sinceTime) };
+ boolean hasResults = false;
+ try (Cursor cursor = mContext.getContentResolver().query(
+ Calls.CONTENT_URI, projection, selection, selectionArgs,
+ Calls.DEFAULT_SORT_ORDER)) {
+ if (cursor == null) {
+ Slog.w(TAG, "Cursor is null when querying call log.");
+ return false;
+ }
+ while (cursor.moveToNext()) {
+ // Phone number
+ int numberIndex = cursor.getColumnIndex(Calls.CACHED_NORMALIZED_NUMBER);
+ String phoneNumber = cursor.getString(numberIndex);
+
+ // Date
+ int dateIndex = cursor.getColumnIndex(Calls.DATE);
+ long date = cursor.getLong(dateIndex);
+
+ // Duration
+ int durationIndex = cursor.getColumnIndex(Calls.DURATION);
+ long durationSeconds = cursor.getLong(durationIndex);
+
+ // Type
+ int typeIndex = cursor.getColumnIndex(Calls.TYPE);
+ int callType = cursor.getInt(typeIndex);
+
+ mLastCallTimestamp = Math.max(mLastCallTimestamp, date);
+ if (addEvent(phoneNumber, date, durationSeconds, callType)) {
+ hasResults = true;
+ }
+ }
+ }
+ return hasResults;
+ }
+
+ long getLastCallTimestamp() {
+ return mLastCallTimestamp;
+ }
+
+ private boolean addEvent(String phoneNumber, long date, long durationSeconds, int callType) {
+ if (!validateEvent(phoneNumber, date, callType)) {
+ return false;
+ }
+ @Event.EventType int eventType = CALL_TYPE_TO_EVENT_TYPE.get(callType);
+ Event event = new Event.Builder(date, eventType)
+ .setCallDetails(new Event.CallDetails(durationSeconds))
+ .build();
+ mEventConsumer.accept(phoneNumber, event);
+ return true;
+ }
+
+ private boolean validateEvent(String phoneNumber, long date, int callType) {
+ return !TextUtils.isEmpty(phoneNumber)
+ && date > 0L
+ && CALL_TYPE_TO_EVENT_TYPE.indexOfKey(callType) >= 0;
+ }
+}
diff --git a/services/people/java/com/android/server/people/data/DataManager.java b/services/people/java/com/android/server/people/data/DataManager.java
index 4212072088e1..79503f797318 100644
--- a/services/people/java/com/android/server/people/data/DataManager.java
+++ b/services/people/java/com/android/server/people/data/DataManager.java
@@ -45,7 +45,9 @@ import android.os.Process;
import android.os.RemoteException;
import android.os.UserHandle;
import android.os.UserManager;
+import android.provider.CallLog;
import android.provider.ContactsContract.Contacts;
+import android.provider.Telephony.MmsSms;
import android.service.notification.NotificationListenerService;
import android.service.notification.StatusBarNotification;
import android.telecom.TelecomManager;
@@ -59,13 +61,13 @@ import com.android.internal.os.BackgroundThread;
import com.android.internal.telephony.SmsApplication;
import com.android.server.LocalServices;
-import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.ScheduledFuture;
import java.util.concurrent.TimeUnit;
+import java.util.function.BiConsumer;
import java.util.function.Consumer;
/**
@@ -77,7 +79,7 @@ public class DataManager {
private static final String PLATFORM_PACKAGE_NAME = "android";
private static final int MY_UID = Process.myUid();
private static final int MY_PID = Process.myPid();
- private static final long USAGE_STATS_QUERY_MAX_EVENT_AGE_MS = DateUtils.DAY_IN_MILLIS;
+ private static final long QUERY_EVENTS_MAX_AGE_MS = DateUtils.DAY_IN_MILLIS;
private static final long USAGE_STATS_QUERY_INTERVAL_SEC = 120L;
private final Context mContext;
@@ -90,6 +92,8 @@ public class DataManager {
private final SparseArray<ScheduledFuture<?>> mUsageStatsQueryFutures = new SparseArray<>();
private final SparseArray<NotificationListenerService> mNotificationListeners =
new SparseArray<>();
+ private final ContentObserver mCallLogContentObserver;
+ private final ContentObserver mMmsSmsContentObserver;
private ShortcutServiceInternal mShortcutServiceInternal;
private UsageStatsManagerInternal mUsageStatsManagerInternal;
@@ -97,9 +101,7 @@ public class DataManager {
private UserManager mUserManager;
public DataManager(Context context) {
- mContext = context;
- mInjector = new Injector();
- mUsageStatsQueryExecutor = mInjector.createScheduledExecutor();
+ this(context, new Injector());
}
@VisibleForTesting
@@ -107,6 +109,10 @@ public class DataManager {
mContext = context;
mInjector = injector;
mUsageStatsQueryExecutor = mInjector.createScheduledExecutor();
+ mCallLogContentObserver = new CallLogContentObserver(
+ BackgroundThread.getHandler());
+ mMmsSmsContentObserver = new MmsSmsContentObserver(
+ BackgroundThread.getHandler());
}
/** Initialization. Called when the system services are up running. */
@@ -159,6 +165,18 @@ public class DataManager {
} catch (RemoteException e) {
// Should never occur for local calls.
}
+
+ if (userId == UserHandle.USER_SYSTEM) {
+ // The call log and MMS/SMS messages are shared across user profiles. So only need to
+ // register the content observers once for the primary user.
+ // TODO: Register observers after the conversations and events being loaded from disk.
+ mContext.getContentResolver().registerContentObserver(
+ CallLog.CONTENT_URI, /* notifyForDescendants= */ true,
+ mCallLogContentObserver, UserHandle.USER_SYSTEM);
+ mContext.getContentResolver().registerContentObserver(
+ MmsSms.CONTENT_URI, /* notifyForDescendants= */ false,
+ mMmsSmsContentObserver, UserHandle.USER_SYSTEM);
+ }
}
/** This method is called when a user is stopped. */
@@ -183,6 +201,10 @@ public class DataManager {
// Should never occur for local calls.
}
}
+ if (userId == UserHandle.USER_SYSTEM) {
+ mContext.getContentResolver().unregisterContentObserver(mCallLogContentObserver);
+ mContext.getContentResolver().unregisterContentObserver(mMmsSmsContentObserver);
+ }
}
/**
@@ -199,6 +221,13 @@ public class DataManager {
}
}
+ /** Gets the {@link PackageData} for the given package and user. */
+ @Nullable
+ public PackageData getPackage(@NonNull String packageName, @UserIdInt int userId) {
+ UserData userData = getUnlockedUserData(userId);
+ return userData != null ? userData.getPackageData(packageName) : null;
+ }
+
/** Gets the {@link ShortcutInfo} for the given shortcut ID. */
@Nullable
public ShortcutInfo getShortcut(@NonNull String packageName, @UserIdInt int userId,
@@ -212,20 +241,11 @@ public class DataManager {
}
/**
- * Gets the conversation {@link ShareShortcutInfo}s from all packages owned by the calling user
- * that match the specified {@link IntentFilter}.
+ * Gets the {@link ShareShortcutInfo}s from all packages owned by the calling user that match
+ * the specified {@link IntentFilter}.
*/
- public List<ShareShortcutInfo> getConversationShareTargets(
- @NonNull IntentFilter intentFilter) {
- List<ShareShortcutInfo> shareShortcuts = mShortcutManager.getShareTargets(intentFilter);
- List<ShareShortcutInfo> result = new ArrayList<>();
- for (ShareShortcutInfo shareShortcut : shareShortcuts) {
- ShortcutInfo si = shareShortcut.getShortcutInfo();
- if (getConversationInfo(si.getPackage(), si.getUserId(), si.getId()) != null) {
- result.add(shareShortcut);
- }
- }
- return result;
+ public List<ShareShortcutInfo> getShareShortcuts(@NonNull IntentFilter intentFilter) {
+ return mShortcutManager.getShareTargets(intentFilter);
}
/** Reports the {@link AppTargetEvent} from App Prediction Manager. */
@@ -236,7 +256,7 @@ public class DataManager {
if (shortcutInfo == null || event.getAction() != AppTargetEvent.ACTION_LAUNCH) {
return;
}
- PackageData packageData = getPackageData(appTarget.getPackageName(),
+ PackageData packageData = getPackage(appTarget.getPackageName(),
appTarget.getUser().getIdentifier());
if (packageData == null) {
return;
@@ -277,26 +297,21 @@ public class DataManager {
userId, MY_PID, MY_UID);
}
+ private void forAllUnlockedUsers(Consumer<UserData> consumer) {
+ for (int i = 0; i < mUserDataArray.size(); i++) {
+ UserData userData = mUserDataArray.get(i);
+ if (userData.isUnlocked()) {
+ consumer.accept(userData);
+ }
+ }
+ }
+
@Nullable
private UserData getUnlockedUserData(int userId) {
UserData userData = mUserDataArray.get(userId);
return userData != null && userData.isUnlocked() ? userData : null;
}
- @Nullable
- private PackageData getPackageData(@NonNull String packageName, int userId) {
- UserData userData = getUnlockedUserData(userId);
- return userData != null ? userData.getPackageData(packageName) : null;
- }
-
- @Nullable
- private ConversationInfo getConversationInfo(@NonNull String packageName, @UserIdInt int userId,
- @NonNull String shortcutId) {
- PackageData packageData = getPackageData(packageName, userId);
- return packageData != null ? packageData.getConversationStore().getConversation(shortcutId)
- : null;
- }
-
private void updateDefaultDialer(@NonNull UserData userData) {
TelecomManager telecomManager = mContext.getSystemService(TelecomManager.class);
String defaultDialer = telecomManager != null
@@ -318,7 +333,7 @@ public class DataManager {
if (shortcutId == null) {
return null;
}
- PackageData packageData = getPackageData(sbn.getPackageName(),
+ PackageData packageData = getPackage(sbn.getPackageName(),
sbn.getUser().getIdentifier());
if (packageData == null
|| packageData.getConversationStore().getConversation(shortcutId) == null) {
@@ -382,7 +397,7 @@ public class DataManager {
usageEvents.getNextEvent(e);
String packageName = e.getPackageName();
- PackageData packageData = getPackageData(packageName, userId);
+ PackageData packageData = getPackage(packageName, userId);
if (packageData == null) {
continue;
}
@@ -405,10 +420,25 @@ public class DataManager {
}
@VisibleForTesting
+ ContentObserver getCallLogContentObserverForTesting() {
+ return mCallLogContentObserver;
+ }
+
+ @VisibleForTesting
+ ContentObserver getMmsSmsContentObserverForTesting() {
+ return mMmsSmsContentObserver;
+ }
+
+ @VisibleForTesting
NotificationListenerService getNotificationListenerServiceForTesting(@UserIdInt int userId) {
return mNotificationListeners.get(userId);
}
+ @VisibleForTesting
+ UserData getUserDataForTesting(@UserIdInt int userId) {
+ return mUserDataArray.get(userId);
+ }
+
/** Observer that observes the changes in the Contacts database. */
private class ContactsContentObserver extends ContentObserver {
@@ -459,6 +489,88 @@ public class DataManager {
}
}
+ /** Observer that observes the changes in the call log database. */
+ private class CallLogContentObserver extends ContentObserver implements
+ BiConsumer<String, Event> {
+
+ private final CallLogQueryHelper mCallLogQueryHelper;
+ private long mLastCallTimestamp;
+
+ private CallLogContentObserver(Handler handler) {
+ super(handler);
+ mCallLogQueryHelper = mInjector.createCallLogQueryHelper(mContext, this);
+ mLastCallTimestamp = System.currentTimeMillis() - QUERY_EVENTS_MAX_AGE_MS;
+ }
+
+ @Override
+ public void onChange(boolean selfChange) {
+ if (mCallLogQueryHelper.querySince(mLastCallTimestamp)) {
+ mLastCallTimestamp = mCallLogQueryHelper.getLastCallTimestamp();
+ }
+ }
+
+ @Override
+ public void accept(String phoneNumber, Event event) {
+ forAllUnlockedUsers(userData -> {
+ PackageData defaultDialer = userData.getDefaultDialer();
+ if (defaultDialer == null) {
+ return;
+ }
+ ConversationStore conversationStore = defaultDialer.getConversationStore();
+ if (conversationStore.getConversationByPhoneNumber(phoneNumber) == null) {
+ return;
+ }
+ EventStore eventStore = defaultDialer.getEventStore();
+ eventStore.getOrCreateCallEventHistory(phoneNumber).addEvent(event);
+ });
+ }
+ }
+
+ /** Observer that observes the changes in the MMS & SMS database. */
+ private class MmsSmsContentObserver extends ContentObserver implements
+ BiConsumer<String, Event> {
+
+ private final MmsQueryHelper mMmsQueryHelper;
+ private long mLastMmsTimestamp;
+
+ private final SmsQueryHelper mSmsQueryHelper;
+ private long mLastSmsTimestamp;
+
+ private MmsSmsContentObserver(Handler handler) {
+ super(handler);
+ mMmsQueryHelper = mInjector.createMmsQueryHelper(mContext, this);
+ mSmsQueryHelper = mInjector.createSmsQueryHelper(mContext, this);
+ mLastSmsTimestamp = mLastMmsTimestamp =
+ System.currentTimeMillis() - QUERY_EVENTS_MAX_AGE_MS;
+ }
+
+ @Override
+ public void onChange(boolean selfChange) {
+ if (mMmsQueryHelper.querySince(mLastMmsTimestamp)) {
+ mLastMmsTimestamp = mMmsQueryHelper.getLastMessageTimestamp();
+ }
+ if (mSmsQueryHelper.querySince(mLastSmsTimestamp)) {
+ mLastSmsTimestamp = mSmsQueryHelper.getLastMessageTimestamp();
+ }
+ }
+
+ @Override
+ public void accept(String phoneNumber, Event event) {
+ forAllUnlockedUsers(userData -> {
+ PackageData defaultSmsApp = userData.getDefaultSmsApp();
+ if (defaultSmsApp == null) {
+ return;
+ }
+ ConversationStore conversationStore = defaultSmsApp.getConversationStore();
+ if (conversationStore.getConversationByPhoneNumber(phoneNumber) == null) {
+ return;
+ }
+ EventStore eventStore = defaultSmsApp.getEventStore();
+ eventStore.getOrCreateSmsEventHistory(phoneNumber).addEvent(event);
+ });
+ }
+ }
+
/** Listener for the shortcut data changes. */
private class ShortcutServiceListener implements
ShortcutServiceInternal.ShortcutChangeListener {
@@ -504,7 +616,7 @@ public class DataManager {
private UsageStatsQueryRunnable(int userId) {
mUserId = userId;
- mLastQueryTime = System.currentTimeMillis() - USAGE_STATS_QUERY_MAX_EVENT_AGE_MS;
+ mLastQueryTime = System.currentTimeMillis() - QUERY_EVENTS_MAX_AGE_MS;
}
@Override
@@ -552,6 +664,21 @@ public class DataManager {
return new ContactsQueryHelper(context);
}
+ CallLogQueryHelper createCallLogQueryHelper(Context context,
+ BiConsumer<String, Event> eventConsumer) {
+ return new CallLogQueryHelper(context, eventConsumer);
+ }
+
+ MmsQueryHelper createMmsQueryHelper(Context context,
+ BiConsumer<String, Event> eventConsumer) {
+ return new MmsQueryHelper(context, eventConsumer);
+ }
+
+ SmsQueryHelper createSmsQueryHelper(Context context,
+ BiConsumer<String, Event> eventConsumer) {
+ return new SmsQueryHelper(context, eventConsumer);
+ }
+
int getCallingUserId() {
return Binder.getCallingUserHandle().getIdentifier();
}
diff --git a/services/people/java/com/android/server/people/data/MmsQueryHelper.java b/services/people/java/com/android/server/people/data/MmsQueryHelper.java
new file mode 100644
index 000000000000..1e485c082d18
--- /dev/null
+++ b/services/people/java/com/android/server/people/data/MmsQueryHelper.java
@@ -0,0 +1,151 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.people.data;
+
+import android.annotation.Nullable;
+import android.annotation.WorkerThread;
+import android.content.Context;
+import android.database.Cursor;
+import android.net.Uri;
+import android.provider.Telephony.BaseMmsColumns;
+import android.provider.Telephony.Mms;
+import android.telephony.PhoneNumberUtils;
+import android.text.TextUtils;
+import android.util.Slog;
+import android.util.SparseIntArray;
+
+import com.google.android.mms.pdu.PduHeaders;
+
+import java.util.function.BiConsumer;
+
+/** A helper class that queries the MMS database tables. */
+class MmsQueryHelper {
+
+ private static final String TAG = "MmsQueryHelper";
+ private static final long MILLIS_PER_SECONDS = 1000L;
+ private static final SparseIntArray MSG_BOX_TO_EVENT_TYPE = new SparseIntArray();
+
+ static {
+ MSG_BOX_TO_EVENT_TYPE.put(BaseMmsColumns.MESSAGE_BOX_INBOX, Event.TYPE_SMS_INCOMING);
+ MSG_BOX_TO_EVENT_TYPE.put(BaseMmsColumns.MESSAGE_BOX_SENT, Event.TYPE_SMS_OUTGOING);
+ }
+
+ private final Context mContext;
+ private final BiConsumer<String, Event> mEventConsumer;
+ private long mLastMessageTimestamp;
+ private String mCurrentCountryIso;
+
+ /**
+ * @param context Context for accessing the content resolver.
+ * @param eventConsumer Consumes the events created from the message records. The first input
+ * param is the normalized phone number.
+ */
+ MmsQueryHelper(Context context, BiConsumer<String, Event> eventConsumer) {
+ mContext = context;
+ mEventConsumer = eventConsumer;
+ mCurrentCountryIso = Utils.getCurrentCountryIso(mContext);
+ }
+
+ /**
+ * Queries the MMS database tables for the new data added since {@code sinceTime} (in millis)
+ * and returns true if the query runs successfully and at least one message entry is found.
+ */
+ @WorkerThread
+ boolean querySince(long sinceTime) {
+ String[] projection = new String[] { Mms._ID, Mms.DATE, Mms.MESSAGE_BOX };
+ String selection = Mms.DATE + " > ?";
+ // NOTE: The field Mms.DATE is stored in seconds, not milliseconds.
+ String[] selectionArgs = new String[] { Long.toString(sinceTime / MILLIS_PER_SECONDS) };
+ boolean hasResults = false;
+ try (Cursor cursor = mContext.getContentResolver().query(
+ Mms.CONTENT_URI, projection, selection, selectionArgs, null)) {
+ if (cursor == null) {
+ Slog.w(TAG, "Cursor is null when querying MMS table.");
+ return false;
+ }
+ while (cursor.moveToNext()) {
+ // ID
+ int msgIdIndex = cursor.getColumnIndex(Mms._ID);
+ String msgId = cursor.getString(msgIdIndex);
+
+ // Date
+ int dateIndex = cursor.getColumnIndex(Mms.DATE);
+ long date = cursor.getLong(dateIndex) * MILLIS_PER_SECONDS;
+
+ // Message box
+ int msgBoxIndex = cursor.getColumnIndex(Mms.MESSAGE_BOX);
+ int msgBox = cursor.getInt(msgBoxIndex);
+
+ mLastMessageTimestamp = Math.max(mLastMessageTimestamp, date);
+ String address = getMmsAddress(msgId, msgBox);
+ if (address != null && addEvent(address, date, msgBox)) {
+ hasResults = true;
+ }
+ }
+ }
+ return hasResults;
+ }
+
+ long getLastMessageTimestamp() {
+ return mLastMessageTimestamp;
+ }
+
+ @Nullable
+ private String getMmsAddress(String msgId, int msgBox) {
+ Uri addressUri = Mms.Addr.getAddrUriForMessage(msgId);
+ String[] projection = new String[] { Mms.Addr.ADDRESS, Mms.Addr.TYPE };
+ String address = null;
+ try (Cursor cursor = mContext.getContentResolver().query(
+ addressUri, projection, null, null, null)) {
+ if (cursor == null) {
+ Slog.w(TAG, "Cursor is null when querying MMS address table.");
+ return null;
+ }
+ while (cursor.moveToNext()) {
+ // Type
+ int typeIndex = cursor.getColumnIndex(Mms.Addr.TYPE);
+ int type = cursor.getInt(typeIndex);
+
+ if ((msgBox == BaseMmsColumns.MESSAGE_BOX_INBOX && type == PduHeaders.FROM)
+ || (msgBox == BaseMmsColumns.MESSAGE_BOX_SENT && type == PduHeaders.TO)) {
+ // Address
+ int addrIndex = cursor.getColumnIndex(Mms.Addr.ADDRESS);
+ address = cursor.getString(addrIndex);
+ }
+ }
+ }
+ if (!Mms.isPhoneNumber(address)) {
+ return null;
+ }
+ return PhoneNumberUtils.formatNumberToE164(address, mCurrentCountryIso);
+ }
+
+ private boolean addEvent(String phoneNumber, long date, int msgBox) {
+ if (!validateEvent(phoneNumber, date, msgBox)) {
+ return false;
+ }
+ @Event.EventType int eventType = MSG_BOX_TO_EVENT_TYPE.get(msgBox);
+ mEventConsumer.accept(phoneNumber, new Event(date, eventType));
+ return true;
+ }
+
+ private boolean validateEvent(String phoneNumber, long date, int msgBox) {
+ return !TextUtils.isEmpty(phoneNumber)
+ && date > 0L
+ && MSG_BOX_TO_EVENT_TYPE.indexOfKey(msgBox) >= 0;
+ }
+}
diff --git a/services/people/java/com/android/server/people/data/PackageData.java b/services/people/java/com/android/server/people/data/PackageData.java
index 9c22a7f1c484..75b870c74591 100644
--- a/services/people/java/com/android/server/people/data/PackageData.java
+++ b/services/people/java/com/android/server/people/data/PackageData.java
@@ -17,11 +17,13 @@
package com.android.server.people.data;
import android.annotation.NonNull;
+import android.annotation.Nullable;
import android.annotation.UserIdInt;
import android.content.LocusId;
import android.text.TextUtils;
import java.util.function.Consumer;
+import java.util.function.Predicate;
/** The data associated with a package. */
public class PackageData {
@@ -37,15 +39,19 @@ public class PackageData {
@NonNull
private final EventStore mEventStore;
- private boolean mIsDefaultDialer;
+ private final Predicate<String> mIsDefaultDialerPredicate;
- private boolean mIsDefaultSmsApp;
+ private final Predicate<String> mIsDefaultSmsAppPredicate;
- PackageData(@NonNull String packageName, @UserIdInt int userId) {
+ PackageData(@NonNull String packageName, @UserIdInt int userId,
+ @NonNull Predicate<String> isDefaultDialerPredicate,
+ @NonNull Predicate<String> isDefaultSmsAppPredicate) {
mPackageName = packageName;
mUserId = userId;
mConversationStore = new ConversationStore();
mEventStore = new EventStore();
+ mIsDefaultDialerPredicate = isDefaultDialerPredicate;
+ mIsDefaultSmsAppPredicate = isDefaultSmsAppPredicate;
}
@NonNull
@@ -68,6 +74,15 @@ public class PackageData {
}
/**
+ * Gets the {@link ConversationInfo} for a given shortcut ID. Returns null if such as {@link
+ * ConversationInfo} does not exist.
+ */
+ @Nullable
+ public ConversationInfo getConversationInfo(@NonNull String shortcutId) {
+ return getConversationStore().getConversation(shortcutId);
+ }
+
+ /**
* Gets the combined {@link EventHistory} for a given shortcut ID. This returned {@link
* EventHistory} has events of all types, no matter whether they're annotated with shortcut ID,
* Locus ID, or phone number etc.
@@ -114,11 +129,11 @@ public class PackageData {
}
public boolean isDefaultDialer() {
- return mIsDefaultDialer;
+ return mIsDefaultDialerPredicate.test(mPackageName);
}
public boolean isDefaultSmsApp() {
- return mIsDefaultSmsApp;
+ return mIsDefaultSmsAppPredicate.test(mPackageName);
}
@NonNull
@@ -131,14 +146,6 @@ public class PackageData {
return mEventStore;
}
- void setIsDefaultDialer(boolean value) {
- mIsDefaultDialer = value;
- }
-
- void setIsDefaultSmsApp(boolean value) {
- mIsDefaultSmsApp = value;
- }
-
void onDestroy() {
// TODO: STOPSHIP: Implements this method for the case of package being uninstalled.
}
diff --git a/services/people/java/com/android/server/people/data/SmsQueryHelper.java b/services/people/java/com/android/server/people/data/SmsQueryHelper.java
new file mode 100644
index 000000000000..c38c846bf461
--- /dev/null
+++ b/services/people/java/com/android/server/people/data/SmsQueryHelper.java
@@ -0,0 +1,119 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.people.data;
+
+import android.annotation.WorkerThread;
+import android.content.Context;
+import android.database.Cursor;
+import android.provider.Telephony.Sms;
+import android.provider.Telephony.TextBasedSmsColumns;
+import android.telephony.PhoneNumberUtils;
+import android.text.TextUtils;
+import android.util.Slog;
+import android.util.SparseIntArray;
+
+import java.util.function.BiConsumer;
+
+/** A helper class that queries the SMS database table. */
+class SmsQueryHelper {
+
+ private static final String TAG = "SmsQueryHelper";
+ private static final SparseIntArray SMS_TYPE_TO_EVENT_TYPE = new SparseIntArray();
+
+ static {
+ SMS_TYPE_TO_EVENT_TYPE.put(TextBasedSmsColumns.MESSAGE_TYPE_INBOX, Event.TYPE_SMS_INCOMING);
+ SMS_TYPE_TO_EVENT_TYPE.put(TextBasedSmsColumns.MESSAGE_TYPE_SENT, Event.TYPE_SMS_OUTGOING);
+ }
+
+ private final Context mContext;
+ private final BiConsumer<String, Event> mEventConsumer;
+ private final String mCurrentCountryIso;
+ private long mLastMessageTimestamp;
+
+ /**
+ * @param context Context for accessing the content resolver.
+ * @param eventConsumer Consumes the events created from the message records. The first input
+ * param is the normalized phone number.
+ */
+ SmsQueryHelper(Context context, BiConsumer<String, Event> eventConsumer) {
+ mContext = context;
+ mEventConsumer = eventConsumer;
+ mCurrentCountryIso = Utils.getCurrentCountryIso(mContext);
+ }
+
+ /**
+ * Queries the SMS database tables for the new data added since {@code sinceTime} (in millis)
+ * and returns true if the query runs successfully and at least one message entry is found.
+ */
+ @WorkerThread
+ boolean querySince(long sinceTime) {
+ String[] projection = new String[] { Sms._ID, Sms.DATE, Sms.TYPE, Sms.ADDRESS };
+ String selection = Sms.DATE + " > ?";
+ String[] selectionArgs = new String[] { Long.toString(sinceTime) };
+ boolean hasResults = false;
+ try (Cursor cursor = mContext.getContentResolver().query(
+ Sms.CONTENT_URI, projection, selection, selectionArgs, null)) {
+ if (cursor == null) {
+ Slog.w(TAG, "Cursor is null when querying SMS table.");
+ return false;
+ }
+ while (cursor.moveToNext()) {
+ // ID
+ int msgIdIndex = cursor.getColumnIndex(Sms._ID);
+ String msgId = cursor.getString(msgIdIndex);
+
+ // Date
+ int dateIndex = cursor.getColumnIndex(Sms.DATE);
+ long date = cursor.getLong(dateIndex);
+
+ // Type
+ int typeIndex = cursor.getColumnIndex(Sms.TYPE);
+ int type = cursor.getInt(typeIndex);
+
+ // Address
+ int addressIndex = cursor.getColumnIndex(Sms.ADDRESS);
+ String address = PhoneNumberUtils.formatNumberToE164(
+ cursor.getString(addressIndex), mCurrentCountryIso);
+
+ mLastMessageTimestamp = Math.max(mLastMessageTimestamp, date);
+ if (address != null && addEvent(address, date, type)) {
+ hasResults = true;
+ }
+ }
+ }
+ return hasResults;
+ }
+
+ long getLastMessageTimestamp() {
+ return mLastMessageTimestamp;
+ }
+
+ private boolean addEvent(String phoneNumber, long date, int type) {
+ if (!validateEvent(phoneNumber, date, type)) {
+ return false;
+ }
+ @Event.EventType int eventType = SMS_TYPE_TO_EVENT_TYPE.get(type);
+ mEventConsumer.accept(phoneNumber, new Event(date, eventType));
+ return true;
+ }
+
+ private boolean validateEvent(String phoneNumber, long date, int type) {
+ return !TextUtils.isEmpty(phoneNumber)
+ && date > 0L
+ && SMS_TYPE_TO_EVENT_TYPE.indexOfKey(type) >= 0;
+ }
+}
diff --git a/services/people/java/com/android/server/people/data/UserData.java b/services/people/java/com/android/server/people/data/UserData.java
index 2c16059e89ba..4e8fd16d05fd 100644
--- a/services/people/java/com/android/server/people/data/UserData.java
+++ b/services/people/java/com/android/server/people/data/UserData.java
@@ -34,6 +34,12 @@ class UserData {
private Map<String, PackageData> mPackageDataMap = new ArrayMap<>();
+ @Nullable
+ private String mDefaultDialer;
+
+ @Nullable
+ private String mDefaultSmsApp;
+
UserData(@UserIdInt int userId) {
mUserId = userId;
}
@@ -66,8 +72,7 @@ class UserData {
*/
@NonNull
PackageData getOrCreatePackageData(String packageName) {
- return mPackageDataMap.computeIfAbsent(
- packageName, key -> new PackageData(packageName, mUserId));
+ return mPackageDataMap.computeIfAbsent(packageName, key -> createPackageData(packageName));
}
/**
@@ -80,24 +85,32 @@ class UserData {
}
void setDefaultDialer(@Nullable String packageName) {
- for (PackageData packageData : mPackageDataMap.values()) {
- if (packageData.isDefaultDialer()) {
- packageData.setIsDefaultDialer(false);
- }
- if (TextUtils.equals(packageName, packageData.getPackageName())) {
- packageData.setIsDefaultDialer(true);
- }
- }
+ mDefaultDialer = packageName;
+ }
+
+ @Nullable
+ PackageData getDefaultDialer() {
+ return mDefaultDialer != null ? getPackageData(mDefaultDialer) : null;
}
void setDefaultSmsApp(@Nullable String packageName) {
- for (PackageData packageData : mPackageDataMap.values()) {
- if (packageData.isDefaultSmsApp()) {
- packageData.setIsDefaultSmsApp(false);
- }
- if (TextUtils.equals(packageName, packageData.getPackageName())) {
- packageData.setIsDefaultSmsApp(true);
- }
- }
+ mDefaultSmsApp = packageName;
+ }
+
+ @Nullable
+ PackageData getDefaultSmsApp() {
+ return mDefaultSmsApp != null ? getPackageData(mDefaultSmsApp) : null;
+ }
+
+ private PackageData createPackageData(String packageName) {
+ return new PackageData(packageName, mUserId, this::isDefaultDialer, this::isDefaultSmsApp);
+ }
+
+ private boolean isDefaultDialer(String packageName) {
+ return TextUtils.equals(mDefaultDialer, packageName);
+ }
+
+ private boolean isDefaultSmsApp(String packageName) {
+ return TextUtils.equals(mDefaultSmsApp, packageName);
}
}
diff --git a/services/people/java/com/android/server/people/data/Utils.java b/services/people/java/com/android/server/people/data/Utils.java
new file mode 100644
index 000000000000..b7529605f462
--- /dev/null
+++ b/services/people/java/com/android/server/people/data/Utils.java
@@ -0,0 +1,49 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.people.data;
+
+import android.content.Context;
+import android.location.Country;
+import android.location.CountryDetector;
+
+import java.util.Locale;
+
+/** The utilities static methods for people service data package. */
+class Utils {
+
+ /**
+ * @return The ISO 3166-1 two letters country code of the country the user is in.
+ */
+ static String getCurrentCountryIso(Context context) {
+ String countryIso = null;
+ CountryDetector detector = (CountryDetector) context.getSystemService(
+ Context.COUNTRY_DETECTOR);
+ if (detector != null) {
+ Country country = detector.detectCountry();
+ if (country != null) {
+ countryIso = country.getCountryIso();
+ }
+ }
+ if (countryIso == null) {
+ countryIso = Locale.getDefault().getCountry();
+ }
+ return countryIso;
+ }
+
+ private Utils() {
+ }
+}
diff --git a/services/people/java/com/android/server/people/prediction/AppTargetPredictor.java b/services/people/java/com/android/server/people/prediction/AppTargetPredictor.java
new file mode 100644
index 000000000000..44f3e35833d9
--- /dev/null
+++ b/services/people/java/com/android/server/people/prediction/AppTargetPredictor.java
@@ -0,0 +1,128 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.people.prediction;
+
+import android.annotation.MainThread;
+import android.annotation.NonNull;
+import android.annotation.WorkerThread;
+import android.app.prediction.AppPredictionContext;
+import android.app.prediction.AppTarget;
+import android.app.prediction.AppTargetEvent;
+import android.app.prediction.AppTargetId;
+
+import com.android.internal.annotations.VisibleForTesting;
+import com.android.server.people.data.DataManager;
+
+import java.util.List;
+import java.util.concurrent.ExecutorService;
+import java.util.concurrent.Executors;
+import java.util.function.Consumer;
+
+/**
+ * Predictor that predicts the {@link AppTarget} the user is most likely to open.
+ */
+public class AppTargetPredictor {
+
+ private static final String UI_SURFACE_SHARE = "share";
+
+ /** Creates a {@link AppTargetPredictor} instance based on the prediction context. */
+ public static AppTargetPredictor create(@NonNull AppPredictionContext predictionContext,
+ @NonNull Consumer<List<AppTarget>> updatePredictionsMethod,
+ @NonNull DataManager dataManager) {
+ if (UI_SURFACE_SHARE.equals(predictionContext.getUiSurface())) {
+ return new ShareTargetPredictor(
+ predictionContext, updatePredictionsMethod, dataManager);
+ }
+ return new AppTargetPredictor(predictionContext, updatePredictionsMethod, dataManager);
+ }
+
+ private final AppPredictionContext mPredictionContext;
+ private final Consumer<List<AppTarget>> mUpdatePredictionsMethod;
+ private final DataManager mDataManager;
+ private final ExecutorService mCallbackExecutor;
+
+ AppTargetPredictor(@NonNull AppPredictionContext predictionContext,
+ @NonNull Consumer<List<AppTarget>> updatePredictionsMethod,
+ @NonNull DataManager dataManager) {
+ mPredictionContext = predictionContext;
+ mUpdatePredictionsMethod = updatePredictionsMethod;
+ mDataManager = dataManager;
+ mCallbackExecutor = Executors.newSingleThreadExecutor();
+ }
+
+ /**
+ * Called by the client app to indicate a target launch.
+ */
+ @MainThread
+ public void onAppTargetEvent(AppTargetEvent event) {
+ }
+
+ /**
+ * Called by the client app to indicate a particular location has been shown to the user.
+ */
+ @MainThread
+ public void onLaunchLocationShown(String launchLocation, List<AppTargetId> targetIds) {
+ }
+
+ /**
+ * Called by the client app to request sorting of the provided targets based on the prediction
+ * ranking.
+ */
+ @MainThread
+ public void onSortAppTargets(List<AppTarget> targets, Consumer<List<AppTarget>> callback) {
+ mCallbackExecutor.execute(() -> sortTargets(targets, callback));
+ }
+
+ /**
+ * Called by the client app to request target predictions.
+ */
+ @MainThread
+ public void onRequestPredictionUpdate() {
+ mCallbackExecutor.execute(this::predictTargets);
+ }
+
+ @VisibleForTesting
+ public Consumer<List<AppTarget>> getUpdatePredictionsMethod() {
+ return mUpdatePredictionsMethod;
+ }
+
+ /** To be overridden by the subclass to predict the targets. */
+ @WorkerThread
+ void predictTargets() {
+ }
+
+ /**
+ * To be overridden by the subclass to sort the provided targets based on the prediction
+ * ranking.
+ */
+ @WorkerThread
+ void sortTargets(List<AppTarget> targets, Consumer<List<AppTarget>> callback) {
+ callback.accept(targets);
+ }
+
+ AppPredictionContext getPredictionContext() {
+ return mPredictionContext;
+ }
+
+ DataManager getDataManager() {
+ return mDataManager;
+ }
+
+ void updatePredictions(List<AppTarget> targets) {
+ mUpdatePredictionsMethod.accept(targets);
+ }
+}
diff --git a/services/people/java/com/android/server/people/prediction/ConversationPredictor.java b/services/people/java/com/android/server/people/prediction/ConversationPredictor.java
deleted file mode 100644
index ed8a56bb6435..000000000000
--- a/services/people/java/com/android/server/people/prediction/ConversationPredictor.java
+++ /dev/null
@@ -1,142 +0,0 @@
-/*
- * Copyright (C) 2019 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.server.people.prediction;
-
-import android.annotation.MainThread;
-import android.annotation.NonNull;
-import android.annotation.Nullable;
-import android.app.prediction.AppPredictionContext;
-import android.app.prediction.AppTarget;
-import android.app.prediction.AppTargetEvent;
-import android.app.prediction.AppTargetId;
-import android.content.IntentFilter;
-import android.content.pm.ShortcutInfo;
-import android.content.pm.ShortcutManager.ShareShortcutInfo;
-
-import com.android.internal.annotations.VisibleForTesting;
-import com.android.internal.app.ChooserActivity;
-import com.android.server.people.data.DataManager;
-import com.android.server.people.data.EventHistory;
-
-import java.util.ArrayList;
-import java.util.List;
-import java.util.concurrent.ExecutorService;
-import java.util.concurrent.Executors;
-import java.util.function.Consumer;
-
-/**
- * Predictor that predicts the conversations or apps the user is most likely to open.
- */
-public class ConversationPredictor {
-
- private static final String UI_SURFACE_SHARE = "share";
-
- private final AppPredictionContext mPredictionContext;
- private final Consumer<List<AppTarget>> mUpdatePredictionsMethod;
- private final DataManager mDataManager;
- private final ExecutorService mCallbackExecutor;
- @Nullable
- private final IntentFilter mIntentFilter;
-
- public ConversationPredictor(@NonNull AppPredictionContext predictionContext,
- @NonNull Consumer<List<AppTarget>> updatePredictionsMethod,
- @NonNull DataManager dataManager) {
- mPredictionContext = predictionContext;
- mUpdatePredictionsMethod = updatePredictionsMethod;
- mDataManager = dataManager;
- mCallbackExecutor = Executors.newSingleThreadExecutor();
- if (UI_SURFACE_SHARE.equals(mPredictionContext.getUiSurface())) {
- mIntentFilter = mPredictionContext.getExtras().getParcelable(
- ChooserActivity.APP_PREDICTION_INTENT_FILTER_KEY);
- } else {
- mIntentFilter = null;
- }
- }
-
- /**
- * Called by the client app to indicate a target launch.
- */
- @MainThread
- public void onAppTargetEvent(AppTargetEvent event) {
- mDataManager.reportAppTargetEvent(event, mIntentFilter);
- }
-
- /**
- * Called by the client app to indicate a particular location has been shown to the user.
- */
- @MainThread
- public void onLaunchLocationShown(String launchLocation, List<AppTargetId> targetIds) {}
-
- /**
- * Called by the client app to request sorting of the provided targets based on the prediction
- * ranking.
- */
- @MainThread
- public void onSortAppTargets(List<AppTarget> targets, Consumer<List<AppTarget>> callback) {
- mCallbackExecutor.execute(() -> callback.accept(targets));
- }
-
- /**
- * Called by the client app to request target predictions.
- */
- @MainThread
- public void onRequestPredictionUpdate() {
- // TODO: Re-route the call to different ranking classes for different surfaces.
- mCallbackExecutor.execute(() -> {
- List<AppTarget> targets = new ArrayList<>();
- if (mIntentFilter != null) {
- List<ShareShortcutInfo> shareShortcuts =
- mDataManager.getConversationShareTargets(mIntentFilter);
- for (ShareShortcutInfo shareShortcut : shareShortcuts) {
- ShortcutInfo shortcutInfo = shareShortcut.getShortcutInfo();
- AppTargetId appTargetId = new AppTargetId(shortcutInfo.getId());
- String shareTargetClass = shareShortcut.getTargetComponent().getClassName();
- targets.add(new AppTarget.Builder(appTargetId, shortcutInfo)
- .setClassName(shareTargetClass)
- .build());
- }
- } else {
- List<ConversationData> conversationDataList = new ArrayList<>();
- mDataManager.forAllPackages(packageData ->
- packageData.forAllConversations(conversationInfo -> {
- EventHistory eventHistory = packageData.getEventHistory(
- conversationInfo.getShortcutId());
- ConversationData conversationData = new ConversationData(
- packageData.getPackageName(), packageData.getUserId(),
- conversationInfo, eventHistory);
- conversationDataList.add(conversationData);
- }));
- for (ConversationData conversationData : conversationDataList) {
- String shortcutId = conversationData.getConversationInfo().getShortcutId();
- ShortcutInfo shortcut = mDataManager.getShortcut(
- conversationData.getPackageName(), conversationData.getUserId(),
- shortcutId);
- if (shortcut != null) {
- AppTargetId appTargetId = new AppTargetId(shortcut.getId());
- targets.add(new AppTarget.Builder(appTargetId, shortcut).build());
- }
- }
- }
- mUpdatePredictionsMethod.accept(targets);
- });
- }
-
- @VisibleForTesting
- public Consumer<List<AppTarget>> getUpdatePredictionsMethod() {
- return mUpdatePredictionsMethod;
- }
-}
diff --git a/services/people/java/com/android/server/people/prediction/ShareTargetPredictor.java b/services/people/java/com/android/server/people/prediction/ShareTargetPredictor.java
new file mode 100644
index 000000000000..280ced3a07c5
--- /dev/null
+++ b/services/people/java/com/android/server/people/prediction/ShareTargetPredictor.java
@@ -0,0 +1,139 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.people.prediction;
+
+import android.annotation.MainThread;
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.annotation.WorkerThread;
+import android.app.prediction.AppPredictionContext;
+import android.app.prediction.AppTarget;
+import android.app.prediction.AppTargetEvent;
+import android.app.prediction.AppTargetId;
+import android.content.IntentFilter;
+import android.content.pm.ShortcutInfo;
+import android.content.pm.ShortcutManager.ShareShortcutInfo;
+
+import com.android.internal.annotations.VisibleForTesting;
+import com.android.internal.app.ChooserActivity;
+import com.android.server.people.data.ConversationInfo;
+import com.android.server.people.data.DataManager;
+import com.android.server.people.data.EventHistory;
+import com.android.server.people.data.PackageData;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.function.Consumer;
+
+class ShareTargetPredictor extends AppTargetPredictor {
+
+ private final IntentFilter mIntentFilter;
+
+ ShareTargetPredictor(@NonNull AppPredictionContext predictionContext,
+ @NonNull Consumer<List<AppTarget>> updatePredictionsMethod,
+ @NonNull DataManager dataManager) {
+ super(predictionContext, updatePredictionsMethod, dataManager);
+ mIntentFilter = predictionContext.getExtras().getParcelable(
+ ChooserActivity.APP_PREDICTION_INTENT_FILTER_KEY);
+ }
+
+ @MainThread
+ @Override
+ public void onAppTargetEvent(AppTargetEvent event) {
+ getDataManager().reportAppTargetEvent(event, mIntentFilter);
+ }
+
+ @WorkerThread
+ @Override
+ protected void predictTargets() {
+ List<ShareTarget> shareTargets = getShareTargets();
+ // TODO: Rank the share targets with the data in ShareTarget.mConversationData.
+ List<AppTarget> appTargets = new ArrayList<>();
+ for (ShareTarget shareTarget : shareTargets) {
+
+ ShortcutInfo shortcutInfo = shareTarget.getShareShortcutInfo().getShortcutInfo();
+ AppTargetId appTargetId = new AppTargetId(shortcutInfo.getId());
+ String shareTargetClassName =
+ shareTarget.getShareShortcutInfo().getTargetComponent().getClassName();
+ AppTarget appTarget = new AppTarget.Builder(appTargetId, shortcutInfo)
+ .setClassName(shareTargetClassName)
+ .build();
+ appTargets.add(appTarget);
+ if (appTargets.size() >= getPredictionContext().getPredictedTargetCount()) {
+ break;
+ }
+ }
+ updatePredictions(appTargets);
+ }
+
+ @VisibleForTesting
+ List<ShareTarget> getShareTargets() {
+ List<ShareTarget> shareTargets = new ArrayList<>();
+ List<ShareShortcutInfo> shareShortcuts =
+ getDataManager().getShareShortcuts(mIntentFilter);
+
+ for (ShareShortcutInfo shareShortcut : shareShortcuts) {
+ ShortcutInfo shortcutInfo = shareShortcut.getShortcutInfo();
+ String packageName = shortcutInfo.getPackage();
+ int userId = shortcutInfo.getUserId();
+ PackageData packageData = getDataManager().getPackage(packageName, userId);
+
+ ConversationData conversationData = null;
+ if (packageData != null) {
+ String shortcutId = shortcutInfo.getId();
+ ConversationInfo conversationInfo =
+ packageData.getConversationInfo(shortcutId);
+
+ if (conversationInfo != null) {
+ EventHistory eventHistory = packageData.getEventHistory(shortcutId);
+ conversationData = new ConversationData(
+ packageName, userId, conversationInfo, eventHistory);
+ }
+ }
+ shareTargets.add(new ShareTarget(shareShortcut, conversationData));
+ }
+
+ return shareTargets;
+ }
+
+ @VisibleForTesting
+ static class ShareTarget {
+
+ @NonNull
+ private final ShareShortcutInfo mShareShortcutInfo;
+ @Nullable
+ private final ConversationData mConversationData;
+
+ private ShareTarget(@NonNull ShareShortcutInfo shareShortcutInfo,
+ @Nullable ConversationData conversationData) {
+ mShareShortcutInfo = shareShortcutInfo;
+ mConversationData = conversationData;
+ }
+
+ @NonNull
+ @VisibleForTesting
+ ShareShortcutInfo getShareShortcutInfo() {
+ return mShareShortcutInfo;
+ }
+
+ @Nullable
+ @VisibleForTesting
+ ConversationData getConversationData() {
+ return mConversationData;
+ }
+ }
+}
diff --git a/services/tests/mockingservicestests/src/com/android/server/AlarmManagerServiceTest.java b/services/tests/mockingservicestests/src/com/android/server/AlarmManagerServiceTest.java
index 6a5de84266e2..bb149cf327b8 100644
--- a/services/tests/mockingservicestests/src/com/android/server/AlarmManagerServiceTest.java
+++ b/services/tests/mockingservicestests/src/com/android/server/AlarmManagerServiceTest.java
@@ -22,6 +22,7 @@ import static android.app.AlarmManager.RTC_WAKEUP;
import static android.app.usage.UsageStatsManager.STANDBY_BUCKET_ACTIVE;
import static android.app.usage.UsageStatsManager.STANDBY_BUCKET_FREQUENT;
import static android.app.usage.UsageStatsManager.STANDBY_BUCKET_RARE;
+import static android.app.usage.UsageStatsManager.STANDBY_BUCKET_RESTRICTED;
import static android.app.usage.UsageStatsManager.STANDBY_BUCKET_WORKING_SET;
import static com.android.dx.mockito.inline.extended.ExtendedMockito.doCallRealMethod;
@@ -45,6 +46,7 @@ import static com.android.server.AlarmManagerService.Constants.KEY_MAX_INTERVAL;
import static com.android.server.AlarmManagerService.Constants.KEY_MIN_FUTURITY;
import static com.android.server.AlarmManagerService.Constants.KEY_MIN_INTERVAL;
import static com.android.server.AlarmManagerService.IS_WAKEUP_MASK;
+import static com.android.server.AlarmManagerService.MILLIS_IN_DAY;
import static com.android.server.AlarmManagerService.TIME_CHANGED_MASK;
import static com.android.server.AlarmManagerService.WORKING_INDEX;
@@ -107,6 +109,7 @@ public class AlarmManagerServiceTest {
private static final String TEST_CALLING_PACKAGE = "com.android.framework.test-package";
private static final int SYSTEM_UI_UID = 123456789;
private static final int TEST_CALLING_UID = 12345;
+ private static final long RESTRICTED_WINDOW_MS = MILLIS_IN_DAY;
private long mAppStandbyWindow;
private AlarmManagerService mService;
@@ -485,13 +488,13 @@ public class AlarmManagerServiceTest {
anyLong())).thenReturn(standbyBucket);
final long firstTrigger = mNowElapsedTest + 10;
for (int i = 0; i < quota; i++) {
- setTestAlarm(ELAPSED_REALTIME_WAKEUP, mNowElapsedTest + 10 + i,
+ setTestAlarm(ELAPSED_REALTIME_WAKEUP, firstTrigger + i,
getNewMockPendingIntent());
mNowElapsedTest = mTestTimer.getElapsed();
mTestTimer.expire();
}
// This one should get deferred on set
- setTestAlarm(ELAPSED_REALTIME_WAKEUP, mNowElapsedTest + quota + 10,
+ setTestAlarm(ELAPSED_REALTIME_WAKEUP, firstTrigger + quota,
getNewMockPendingIntent());
final long expectedNextTrigger = firstTrigger + 1 + mAppStandbyWindow;
assertEquals("Incorrect next alarm trigger", expectedNextTrigger, mTestTimer.getElapsed());
@@ -503,11 +506,11 @@ public class AlarmManagerServiceTest {
anyLong())).thenReturn(standbyBucket);
final long firstTrigger = mNowElapsedTest + 10;
for (int i = 0; i < quota; i++) {
- setTestAlarm(ELAPSED_REALTIME_WAKEUP, mNowElapsedTest + 10 + i,
+ setTestAlarm(ELAPSED_REALTIME_WAKEUP, firstTrigger + i,
getNewMockPendingIntent());
}
// This one should get deferred after the latest alarm expires
- setTestAlarm(ELAPSED_REALTIME_WAKEUP, mNowElapsedTest + quota + 10,
+ setTestAlarm(ELAPSED_REALTIME_WAKEUP, firstTrigger + quota,
getNewMockPendingIntent());
for (int i = 0; i < quota; i++) {
mNowElapsedTest = mTestTimer.getElapsed();
@@ -596,6 +599,43 @@ public class AlarmManagerServiceTest {
testQuotasNoDeferral(STANDBY_BUCKET_RARE);
}
+ @Test
+ public void testRestrictedBucketAlarmsDeferredOnSet() throws Exception {
+ when(mUsageStatsManagerInternal
+ .getAppStandbyBucket(eq(TEST_CALLING_PACKAGE), anyInt(), anyLong()))
+ .thenReturn(STANDBY_BUCKET_RESTRICTED);
+ // This one should go off
+ final long firstTrigger = mNowElapsedTest + 10;
+ setTestAlarm(ELAPSED_REALTIME_WAKEUP, firstTrigger, getNewMockPendingIntent());
+ mNowElapsedTest = mTestTimer.getElapsed();
+ mTestTimer.expire();
+
+ // This one should get deferred on set
+ setTestAlarm(ELAPSED_REALTIME_WAKEUP, firstTrigger + 1, getNewMockPendingIntent());
+ final long expectedNextTrigger =
+ firstTrigger + mService.mConstants.APP_STANDBY_RESTRICTED_WINDOW;
+ assertEquals("Incorrect next alarm trigger", expectedNextTrigger, mTestTimer.getElapsed());
+ }
+
+ @Test
+ public void testRestrictedBucketAlarmsDeferredOnExpiration() throws Exception {
+ when(mUsageStatsManagerInternal
+ .getAppStandbyBucket(eq(TEST_CALLING_PACKAGE), anyInt(), anyLong()))
+ .thenReturn(STANDBY_BUCKET_RESTRICTED);
+ // This one should go off
+ final long firstTrigger = mNowElapsedTest + 10;
+ setTestAlarm(ELAPSED_REALTIME_WAKEUP, firstTrigger, getNewMockPendingIntent());
+
+ // This one should get deferred after the latest alarm expires
+ setTestAlarm(ELAPSED_REALTIME_WAKEUP, firstTrigger + 1, getNewMockPendingIntent());
+
+ mNowElapsedTest = mTestTimer.getElapsed();
+ mTestTimer.expire();
+ final long expectedNextTrigger =
+ firstTrigger + mService.mConstants.APP_STANDBY_RESTRICTED_WINDOW;
+ assertEquals("Incorrect next alarm trigger", expectedNextTrigger, mTestTimer.getElapsed());
+ }
+
private void assertAndHandleBucketChanged(int bucket) {
when(mUsageStatsManagerInternal.getAppStandbyBucket(eq(TEST_CALLING_PACKAGE), anyInt(),
anyLong())).thenReturn(bucket);
diff --git a/services/tests/mockingservicestests/src/com/android/server/am/CachedAppOptimizerTest.java b/services/tests/mockingservicestests/src/com/android/server/am/CachedAppOptimizerTest.java
index 1985513c4290..e5ec1f76c554 100644
--- a/services/tests/mockingservicestests/src/com/android/server/am/CachedAppOptimizerTest.java
+++ b/services/tests/mockingservicestests/src/com/android/server/am/CachedAppOptimizerTest.java
@@ -36,6 +36,7 @@ import com.android.server.appop.AppOpsService;
import com.android.server.testables.TestableDeviceConfig;
import org.junit.After;
+import org.junit.Assume;
import org.junit.Before;
import org.junit.Rule;
import org.junit.Test;
@@ -122,6 +123,8 @@ public final class CachedAppOptimizerTest {
CachedAppOptimizer.DEFAULT_COMPACT_THROTTLE_4);
assertThat(mCachedAppOptimizerUnderTest.mCompactStatsdSampleRate).isEqualTo(
CachedAppOptimizer.DEFAULT_STATSD_SAMPLE_RATE);
+ assertThat(mCachedAppOptimizerUnderTest.mFreezerStatsdSampleRate).isEqualTo(
+ CachedAppOptimizer.DEFAULT_STATSD_SAMPLE_RATE);
assertThat(mCachedAppOptimizerUnderTest.mFullAnonRssThrottleKb).isEqualTo(
CachedAppOptimizer.DEFAULT_COMPACT_FULL_RSS_THROTTLE_KB);
assertThat(mCachedAppOptimizerUnderTest.mCompactThrottleBFGS).isEqualTo(
@@ -132,6 +135,8 @@ public final class CachedAppOptimizerTest {
CachedAppOptimizer.DEFAULT_COMPACT_FULL_RSS_THROTTLE_KB);
assertThat(mCachedAppOptimizerUnderTest.mFullDeltaRssThrottleKb).isEqualTo(
CachedAppOptimizer.DEFAULT_COMPACT_FULL_DELTA_RSS_THROTTLE_KB);
+ assertThat(mCachedAppOptimizerUnderTest.useFreezer()).isEqualTo(
+ CachedAppOptimizer.DEFAULT_USE_FREEZER);
Set<Integer> expected = new HashSet<>();
for (String s : TextUtils.split(
@@ -177,6 +182,9 @@ public final class CachedAppOptimizerTest {
CachedAppOptimizer.KEY_COMPACT_STATSD_SAMPLE_RATE,
Float.toString(CachedAppOptimizer.DEFAULT_STATSD_SAMPLE_RATE + 0.1f), false);
DeviceConfig.setProperty(DeviceConfig.NAMESPACE_ACTIVITY_MANAGER,
+ CachedAppOptimizer.KEY_FREEZER_STATSD_SAMPLE_RATE,
+ Float.toString(CachedAppOptimizer.DEFAULT_STATSD_SAMPLE_RATE + 0.1f), false);
+ DeviceConfig.setProperty(DeviceConfig.NAMESPACE_ACTIVITY_MANAGER,
CachedAppOptimizer.KEY_COMPACT_FULL_RSS_THROTTLE_KB,
Long.toString(CachedAppOptimizer.DEFAULT_COMPACT_FULL_RSS_THROTTLE_KB + 1), false);
DeviceConfig.setProperty(DeviceConfig.NAMESPACE_ACTIVITY_MANAGER,
@@ -185,6 +193,11 @@ public final class CachedAppOptimizerTest {
CachedAppOptimizer.DEFAULT_COMPACT_FULL_DELTA_RSS_THROTTLE_KB + 1), false);
DeviceConfig.setProperty(DeviceConfig.NAMESPACE_ACTIVITY_MANAGER,
CachedAppOptimizer.KEY_COMPACT_PROC_STATE_THROTTLE, "1,2,3", false);
+ assertThat(mCachedAppOptimizerUnderTest.useFreezer()).isEqualTo(
+ CachedAppOptimizer.DEFAULT_USE_FREEZER);
+ DeviceConfig.setProperty(DeviceConfig.NAMESPACE_ACTIVITY_MANAGER,
+ CachedAppOptimizer.KEY_USE_FREEZER, CachedAppOptimizer.DEFAULT_USE_FREEZER
+ ? "false" : "true" , false);
// Then calling init will read and set that flag.
mCachedAppOptimizerUnderTest.init();
@@ -211,6 +224,8 @@ public final class CachedAppOptimizerTest {
CachedAppOptimizer.DEFAULT_COMPACT_THROTTLE_6 + 1);
assertThat(mCachedAppOptimizerUnderTest.mCompactStatsdSampleRate).isEqualTo(
CachedAppOptimizer.DEFAULT_STATSD_SAMPLE_RATE + 0.1f);
+ assertThat(mCachedAppOptimizerUnderTest.mFreezerStatsdSampleRate).isEqualTo(
+ CachedAppOptimizer.DEFAULT_STATSD_SAMPLE_RATE + 0.1f);
assertThat(mCachedAppOptimizerUnderTest.mCompactThrottleBFGS).isEqualTo(
CachedAppOptimizer.DEFAULT_COMPACT_THROTTLE_5 + 1);
assertThat(mCachedAppOptimizerUnderTest.mCompactThrottlePersistent).isEqualTo(
@@ -218,6 +233,14 @@ public final class CachedAppOptimizerTest {
assertThat(mCachedAppOptimizerUnderTest.mFullAnonRssThrottleKb).isEqualTo(
CachedAppOptimizer.DEFAULT_COMPACT_FULL_RSS_THROTTLE_KB + 1);
assertThat(mCachedAppOptimizerUnderTest.mProcStateThrottle).containsExactly(1, 2, 3);
+
+ if (mCachedAppOptimizerUnderTest.isFreezerSupported()) {
+ if (CachedAppOptimizer.DEFAULT_USE_FREEZER) {
+ assertThat(mCachedAppOptimizerUnderTest.useFreezer()).isFalse();
+ } else {
+ assertThat(mCachedAppOptimizerUnderTest.useFreezer()).isTrue();
+ }
+ }
}
@Test
@@ -244,6 +267,44 @@ public final class CachedAppOptimizerTest {
}
@Test
+ public void useFreeze_doesNotListenToDeviceConfigChanges() throws InterruptedException {
+ Assume.assumeTrue(mCachedAppOptimizerUnderTest.isFreezerSupported());
+
+ assertThat(mCachedAppOptimizerUnderTest.useFreezer()).isEqualTo(
+ CachedAppOptimizer.DEFAULT_USE_FREEZER);
+
+ // The freezer DeviceConfig property is read at boot only
+ DeviceConfig.setProperty(DeviceConfig.NAMESPACE_ACTIVITY_MANAGER,
+ CachedAppOptimizer.KEY_USE_FREEZER, "true", false);
+ mCachedAppOptimizerUnderTest.init();
+ mCountDown = new CountDownLatch(1);
+
+ // No notifications should get to the cached app optimizer.
+ assertThat(mCountDown.await(5, TimeUnit.SECONDS)).isFalse();
+
+ // The flag value has to be set correctly.
+ assertThat(mCachedAppOptimizerUnderTest.useFreezer()).isTrue();
+ // The cached app optimizer thread must be running.
+ assertThat(mCachedAppOptimizerUnderTest.mCachedAppOptimizerThread.isAlive()).isTrue();
+
+ // Set the flag the other way without rebooting. It shall not change.
+ mCountDown = new CountDownLatch(1);
+ DeviceConfig.setProperty(DeviceConfig.NAMESPACE_ACTIVITY_MANAGER,
+ CachedAppOptimizer.KEY_USE_FREEZER, "false", false);
+ assertThat(mCountDown.await(5, TimeUnit.SECONDS)).isTrue();
+ assertThat(mCachedAppOptimizerUnderTest.useFreezer()).isTrue();
+
+
+ // Now, set the flag to false and restart the cached app optimizer
+ DeviceConfig.setProperty(DeviceConfig.NAMESPACE_ACTIVITY_MANAGER,
+ CachedAppOptimizer.KEY_USE_FREEZER, "false", false);
+ mCachedAppOptimizerUnderTest.init();
+
+ // The flag value has to be set correctly.
+ assertThat(mCachedAppOptimizerUnderTest.useFreezer()).isFalse();
+ }
+
+ @Test
public void useCompaction_listensToDeviceConfigChangesBadValues() throws InterruptedException {
assertThat(mCachedAppOptimizerUnderTest.useCompaction()).isEqualTo(
CachedAppOptimizer.DEFAULT_USE_COMPACTION);
@@ -261,6 +322,22 @@ public final class CachedAppOptimizerTest {
}
@Test
+ public void useFreeze_listensToDeviceConfigChangesBadValues() throws InterruptedException {
+ assertThat(mCachedAppOptimizerUnderTest.useFreezer()).isEqualTo(
+ CachedAppOptimizer.DEFAULT_USE_FREEZER);
+
+ // When we push an invalid flag value...
+ DeviceConfig.setProperty(DeviceConfig.NAMESPACE_ACTIVITY_MANAGER,
+ CachedAppOptimizer.KEY_USE_FREEZER, "foobar", false);
+
+ mCachedAppOptimizerUnderTest.init();
+
+ // Then we set the default.
+ assertThat(mCachedAppOptimizerUnderTest.useFreezer()).isEqualTo(
+ CachedAppOptimizer.DEFAULT_USE_FREEZER);
+ }
+
+ @Test
public void compactAction_listensToDeviceConfigChanges() throws InterruptedException {
mCachedAppOptimizerUnderTest.init();
@@ -482,6 +559,17 @@ public final class CachedAppOptimizerTest {
// Then that override is reflected in the compactor.
assertThat(mCachedAppOptimizerUnderTest.mCompactStatsdSampleRate).isEqualTo(
CachedAppOptimizer.DEFAULT_STATSD_SAMPLE_RATE + 0.1f);
+
+ // When we override mFreezerStatsdSampleRate with a reasonable value ...
+ mCountDown = new CountDownLatch(1);
+ DeviceConfig.setProperty(DeviceConfig.NAMESPACE_ACTIVITY_MANAGER,
+ CachedAppOptimizer.KEY_FREEZER_STATSD_SAMPLE_RATE,
+ Float.toString(CachedAppOptimizer.DEFAULT_STATSD_SAMPLE_RATE + 0.1f), false);
+ assertThat(mCountDown.await(5, TimeUnit.SECONDS)).isTrue();
+
+ // Then that override is reflected in the compactor.
+ assertThat(mCachedAppOptimizerUnderTest.mFreezerStatsdSampleRate).isEqualTo(
+ CachedAppOptimizer.DEFAULT_STATSD_SAMPLE_RATE + 0.1f);
}
@Test
@@ -498,6 +586,16 @@ public final class CachedAppOptimizerTest {
// Then that override is reflected in the compactor.
assertThat(mCachedAppOptimizerUnderTest.mCompactStatsdSampleRate).isEqualTo(
CachedAppOptimizer.DEFAULT_STATSD_SAMPLE_RATE);
+
+ // When we override mFreezerStatsdSampleRate with an unreasonable value ...
+ mCountDown = new CountDownLatch(1);
+ DeviceConfig.setProperty(DeviceConfig.NAMESPACE_ACTIVITY_MANAGER,
+ CachedAppOptimizer.KEY_FREEZER_STATSD_SAMPLE_RATE, "foo", false);
+ assertThat(mCountDown.await(5, TimeUnit.SECONDS)).isTrue();
+
+ // Then that override is reflected in the freezer.
+ assertThat(mCachedAppOptimizerUnderTest.mFreezerStatsdSampleRate).isEqualTo(
+ CachedAppOptimizer.DEFAULT_STATSD_SAMPLE_RATE);
}
@Test
@@ -523,6 +621,25 @@ public final class CachedAppOptimizerTest {
// Then the values is capped in the range.
assertThat(mCachedAppOptimizerUnderTest.mCompactStatsdSampleRate).isEqualTo(1.0f);
+
+ // When we override mFreezerStatsdSampleRate with an value outside of [0..1]...
+ mCountDown = new CountDownLatch(1);
+ DeviceConfig.setProperty(DeviceConfig.NAMESPACE_ACTIVITY_MANAGER,
+ CachedAppOptimizer.KEY_FREEZER_STATSD_SAMPLE_RATE,
+ Float.toString(-1.0f), false);
+ assertThat(mCountDown.await(5, TimeUnit.SECONDS)).isTrue();
+
+ // Then the values is capped in the range.
+ assertThat(mCachedAppOptimizerUnderTest.mFreezerStatsdSampleRate).isEqualTo(0.0f);
+
+ mCountDown = new CountDownLatch(1);
+ DeviceConfig.setProperty(DeviceConfig.NAMESPACE_ACTIVITY_MANAGER,
+ CachedAppOptimizer.KEY_FREEZER_STATSD_SAMPLE_RATE,
+ Float.toString(1.01f), false);
+ assertThat(mCountDown.await(5, TimeUnit.SECONDS)).isTrue();
+
+ // Then the values is capped in the range.
+ assertThat(mCachedAppOptimizerUnderTest.mFreezerStatsdSampleRate).isEqualTo(1.0f);
}
@Test
diff --git a/services/tests/mockingservicestests/src/com/android/server/display/LocalDisplayAdapterTest.java b/services/tests/mockingservicestests/src/com/android/server/display/LocalDisplayAdapterTest.java
index 9e1ddb1f8a80..4a40b80a85c1 100644
--- a/services/tests/mockingservicestests/src/com/android/server/display/LocalDisplayAdapterTest.java
+++ b/services/tests/mockingservicestests/src/com/android/server/display/LocalDisplayAdapterTest.java
@@ -109,10 +109,11 @@ public class LocalDisplayAdapterTest {
*/
@Test
public void testPrivateDisplay() throws Exception {
- setUpDisplay(new DisplayConfig(createDisplayAddress(PORT_A), createDummyDisplayInfo()));
- setUpDisplay(new DisplayConfig(createDisplayAddress(PORT_B), createDummyDisplayInfo()));
- setUpDisplay(new DisplayConfig(createDisplayAddress(PORT_C), createDummyDisplayInfo()));
+ setUpDisplay(new FakeDisplay(PORT_A));
+ setUpDisplay(new FakeDisplay(PORT_B));
+ setUpDisplay(new FakeDisplay(PORT_C));
updateAvailableDisplays();
+
doReturn(new int[]{ PORT_B }).when(mMockedResources)
.getIntArray(com.android.internal.R.array.config_localPrivateDisplayPorts);
mAdapter.registerLocked();
@@ -135,9 +136,10 @@ public class LocalDisplayAdapterTest {
*/
@Test
public void testPublicDisplaysForNoConfigLocalPrivateDisplayPorts() throws Exception {
- setUpDisplay(new DisplayConfig(createDisplayAddress(PORT_A), createDummyDisplayInfo()));
- setUpDisplay(new DisplayConfig(createDisplayAddress(PORT_C), createDummyDisplayInfo()));
+ setUpDisplay(new FakeDisplay(PORT_A));
+ setUpDisplay(new FakeDisplay(PORT_C));
updateAvailableDisplays();
+
// config_localPrivateDisplayPorts is null
mAdapter.registerLocked();
@@ -165,9 +167,8 @@ public class LocalDisplayAdapterTest {
*/
@Test
public void testDpiValues() throws Exception {
- // needs default one always
- setUpDisplay(new DisplayConfig(createDisplayAddress(PORT_A), createDummyDisplayInfo()));
- setUpDisplay(new DisplayConfig(createDisplayAddress(PORT_B), createDummyDisplayInfo()));
+ setUpDisplay(new FakeDisplay(PORT_A));
+ setUpDisplay(new FakeDisplay(PORT_B));
updateAvailableDisplays();
mAdapter.registerLocked();
@@ -193,32 +194,32 @@ public class LocalDisplayAdapterTest {
assertEquals(expectedDensityDpi, info.densityDpi);
}
- private class DisplayConfig {
+ private static class FakeDisplay {
public final DisplayAddress.Physical address;
- public final IBinder displayToken = new Binder();
- public final SurfaceControl.PhysicalDisplayInfo displayInfo;
-
- private DisplayConfig(
- DisplayAddress.Physical address, SurfaceControl.PhysicalDisplayInfo displayInfo) {
- this.address = address;
- this.displayInfo = displayInfo;
+ public final IBinder token = new Binder();
+ public final SurfaceControl.DisplayInfo info;
+ public final SurfaceControl.DisplayConfig config;
+
+ private FakeDisplay(int port) {
+ this.address = createDisplayAddress(port);
+ this.info = createFakeDisplayInfo();
+ this.config = createFakeDisplayConfig();
}
}
- private void setUpDisplay(DisplayConfig config) {
- mAddresses.add(config.address);
- doReturn(config.displayToken).when(() ->
- SurfaceControl.getPhysicalDisplayToken(config.address.getPhysicalDisplayId()));
- doReturn(new SurfaceControl.PhysicalDisplayInfo[]{
- config.displayInfo
- }).when(() -> SurfaceControl.getDisplayConfigs(config.displayToken));
- doReturn(0).when(() -> SurfaceControl.getActiveConfig(config.displayToken));
- doReturn(0).when(() -> SurfaceControl.getActiveColorMode(config.displayToken));
- doReturn(new int[]{
- 0
- }).when(() -> SurfaceControl.getDisplayColorModes(config.displayToken));
+ private void setUpDisplay(FakeDisplay display) {
+ mAddresses.add(display.address);
+ doReturn(display.token).when(() ->
+ SurfaceControl.getPhysicalDisplayToken(display.address.getPhysicalDisplayId()));
+ doReturn(display.info).when(() -> SurfaceControl.getDisplayInfo(display.token));
+ doReturn(new SurfaceControl.DisplayConfig[] { display.config }).when(
+ () -> SurfaceControl.getDisplayConfigs(display.token));
+ doReturn(0).when(() -> SurfaceControl.getActiveConfig(display.token));
+ doReturn(0).when(() -> SurfaceControl.getActiveColorMode(display.token));
+ doReturn(new int[] { 0 }).when(
+ () -> SurfaceControl.getDisplayColorModes(display.token));
doReturn(new SurfaceControl.DesiredDisplayConfigSpecs(0, 60.f, 60.f))
- .when(() -> SurfaceControl.getDesiredDisplayConfigSpecs(config.displayToken));
+ .when(() -> SurfaceControl.getDesiredDisplayConfigSpecs(display.token));
}
private void updateAvailableDisplays() {
@@ -235,18 +236,21 @@ public class LocalDisplayAdapterTest {
return DisplayAddress.fromPortAndModel((byte) port, DISPLAY_MODEL);
}
- private static SurfaceControl.PhysicalDisplayInfo createDummyDisplayInfo() {
- SurfaceControl.PhysicalDisplayInfo info = new SurfaceControl.PhysicalDisplayInfo();
+ private static SurfaceControl.DisplayInfo createFakeDisplayInfo() {
+ final SurfaceControl.DisplayInfo info = new SurfaceControl.DisplayInfo();
info.density = 100;
- info.xDpi = 100;
- info.yDpi = 100;
- info.secure = false;
- info.width = 800;
- info.height = 600;
-
return info;
}
+ private static SurfaceControl.DisplayConfig createFakeDisplayConfig() {
+ final SurfaceControl.DisplayConfig config = new SurfaceControl.DisplayConfig();
+ config.width = 800;
+ config.height = 600;
+ config.xDpi = 100;
+ config.yDpi = 100;
+ return config;
+ }
+
private void waitForHandlerToComplete(Handler handler, long waitTimeMs)
throws InterruptedException {
final Object lock = new Object();
diff --git a/services/tests/servicestests/src/com/android/server/accessibility/gestures/TouchExplorerTest.java b/services/tests/servicestests/src/com/android/server/accessibility/gestures/TouchExplorerTest.java
index a4ceadb3028b..9053234aa220 100644
--- a/services/tests/servicestests/src/com/android/server/accessibility/gestures/TouchExplorerTest.java
+++ b/services/tests/servicestests/src/com/android/server/accessibility/gestures/TouchExplorerTest.java
@@ -89,9 +89,12 @@ public class TouchExplorerTest {
@Override
public void onMotionEvent(MotionEvent event, MotionEvent rawEvent, int policyFlags) {
- MotionEventMatcher lastEventMatcher = new MotionEventMatcher(mLastEvent);
mEvents.add(0, event.copy());
- assertThat(rawEvent, lastEventMatcher);
+ // LastEvent may not match if we're clearing the state
+ if (mLastEvent != null) {
+ MotionEventMatcher lastEventMatcher = new MotionEventMatcher(mLastEvent);
+ assertThat(rawEvent, lastEventMatcher);
+ }
}
@Override
@@ -126,6 +129,31 @@ public class TouchExplorerTest {
}
@Test
+ public void upEventWhenInTwoFingerMove_clearsState() {
+ goFromStateClearTo(STATE_MOVING_2FINGERS);
+
+ send(upEvent());
+ assertState(STATE_CLEAR);
+ }
+
+ @Test
+ public void clearEventsWhenInTwoFingerMove_clearsStateAndSendsUp() {
+ goFromStateClearTo(STATE_MOVING_2FINGERS);
+
+ // Clear last event so we don't try to match against anything when cleanup events are sent
+ // for the clear
+ mLastEvent = null;
+ mTouchExplorer.clearEvents(InputDevice.SOURCE_TOUCHSCREEN);
+ assertState(STATE_CLEAR);
+ List<MotionEvent> events = getCapturedEvents();
+ assertCapturedEvents(
+ MotionEvent.ACTION_DOWN,
+ MotionEvent.ACTION_POINTER_DOWN,
+ MotionEvent.ACTION_POINTER_UP,
+ MotionEvent.ACTION_UP);
+ }
+
+ @Test
public void testTwoFingersDrag_shouldDraggingAndActionDown() {
goFromStateClearTo(STATE_DRAGGING_2FINGERS);
@@ -268,6 +296,12 @@ public class TouchExplorerTest {
DEFAULT_Y, 0));
}
+ private MotionEvent upEvent() {
+ MotionEvent event = downEvent();
+ event.setAction(MotionEvent.ACTION_UP);
+ return event;
+ }
+
private MotionEvent pointerDownEvent() {
final int secondPointerId = 0x0100;
final int action = MotionEvent.ACTION_POINTER_DOWN | secondPointerId;
diff --git a/services/tests/servicestests/src/com/android/server/integrity/AppIntegrityManagerServiceImplTest.java b/services/tests/servicestests/src/com/android/server/integrity/AppIntegrityManagerServiceImplTest.java
index 49ad866a56a4..4a7636a179b1 100644
--- a/services/tests/servicestests/src/com/android/server/integrity/AppIntegrityManagerServiceImplTest.java
+++ b/services/tests/servicestests/src/com/android/server/integrity/AppIntegrityManagerServiceImplTest.java
@@ -23,12 +23,15 @@ import static android.content.pm.PackageManager.EXTRA_VERIFICATION_ID;
import static android.content.pm.PackageManager.EXTRA_VERIFICATION_INSTALLER_PACKAGE;
import static android.content.pm.PackageManager.EXTRA_VERIFICATION_INSTALLER_UID;
+import static com.google.common.truth.Truth.assertThat;
+
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.anyInt;
import static org.mockito.ArgumentMatchers.anyLong;
import static org.mockito.ArgumentMatchers.eq;
+import static org.mockito.Mockito.atLeastOnce;
import static org.mockito.Mockito.doReturn;
import static org.mockito.Mockito.doThrow;
import static org.mockito.Mockito.mock;
@@ -43,6 +46,7 @@ import android.content.IntentFilter;
import android.content.IntentSender;
import android.content.integrity.AppInstallMetadata;
import android.content.integrity.AtomicFormula;
+import android.content.integrity.IntegrityFormula;
import android.content.integrity.Rule;
import android.content.pm.ApplicationInfo;
import android.content.pm.PackageInfo;
@@ -103,19 +107,24 @@ public class AppIntegrityManagerServiceImplTest {
private static final String PLAY_STORE_PKG = "com.android.vending";
private static final String ADB_INSTALLER = "adb";
- private static final String PLAY_STORE_CERT =
- "play_store_cert";
+ private static final String PLAY_STORE_CERT = "play_store_cert";
private static final String ADB_CERT = "";
@org.junit.Rule
public MockitoRule mMockitoRule = MockitoJUnit.rule();
- @Mock PackageManagerInternal mPackageManagerInternal;
- @Mock Context mMockContext;
- @Mock Resources mMockResources;
- @Mock RuleEvaluationEngine mRuleEvaluationEngine;
- @Mock IntegrityFileManager mIntegrityFileManager;
- @Mock Handler mHandler;
+ @Mock
+ PackageManagerInternal mPackageManagerInternal;
+ @Mock
+ Context mMockContext;
+ @Mock
+ Resources mMockResources;
+ @Mock
+ RuleEvaluationEngine mRuleEvaluationEngine;
+ @Mock
+ IntegrityFileManager mIntegrityFileManager;
+ @Mock
+ Handler mHandler;
private PackageManager mSpyPackageManager;
private File mTestApk;
@@ -134,7 +143,8 @@ public class AppIntegrityManagerServiceImplTest {
mPackageManagerInternal,
mRuleEvaluationEngine,
mIntegrityFileManager,
- mHandler);
+ mHandler,
+ /* checkIntegrityForRuleProviders= */ true);
mSpyPackageManager = spy(mRealContext.getPackageManager());
// setup mocks to prevent NPE
@@ -144,16 +154,12 @@ public class AppIntegrityManagerServiceImplTest {
when(mIntegrityFileManager.initialized()).thenReturn(true);
}
- // TODO(b/148370598): Implement a test to validate that allow response is retuned when the test
- // request times out.
-
@Test
public void updateRuleSet_notAuthorized() throws Exception {
makeUsSystemApp();
Rule rule =
new Rule(
- new AtomicFormula.BooleanAtomicFormula(
- AtomicFormula.PRE_INSTALLED, true),
+ new AtomicFormula.BooleanAtomicFormula(AtomicFormula.PRE_INSTALLED, true),
Rule.DENY);
TestUtils.assertExpectException(
SecurityException.class,
@@ -171,8 +177,7 @@ public class AppIntegrityManagerServiceImplTest {
whitelistUsAsRuleProvider();
Rule rule =
new Rule(
- new AtomicFormula.BooleanAtomicFormula(
- AtomicFormula.PRE_INSTALLED, true),
+ new AtomicFormula.BooleanAtomicFormula(AtomicFormula.PRE_INSTALLED, true),
Rule.DENY);
TestUtils.assertExpectException(
SecurityException.class,
@@ -191,8 +196,7 @@ public class AppIntegrityManagerServiceImplTest {
makeUsSystemApp();
Rule rule =
new Rule(
- new AtomicFormula.BooleanAtomicFormula(
- AtomicFormula.PRE_INSTALLED, true),
+ new AtomicFormula.BooleanAtomicFormula(AtomicFormula.PRE_INSTALLED, true),
Rule.DENY);
// no SecurityException
@@ -207,12 +211,7 @@ public class AppIntegrityManagerServiceImplTest {
IntentSender mockReceiver = mock(IntentSender.class);
List<Rule> rules =
Arrays.asList(
- new Rule(
- new AtomicFormula.StringAtomicFormula(
- AtomicFormula.PACKAGE_NAME,
- PACKAGE_NAME,
- /* isHashedValue= */ false),
- Rule.DENY));
+ new Rule(IntegrityFormula.PACKAGE_NAME.equalTo(PACKAGE_NAME), Rule.DENY));
mService.updateRuleSet(VERSION, new ParceledListSlice<>(rules), mockReceiver);
runJobInHandler();
@@ -231,12 +230,7 @@ public class AppIntegrityManagerServiceImplTest {
IntentSender mockReceiver = mock(IntentSender.class);
List<Rule> rules =
Arrays.asList(
- new Rule(
- new AtomicFormula.StringAtomicFormula(
- AtomicFormula.PACKAGE_NAME,
- PACKAGE_NAME,
- /* isHashedValue= */ false),
- Rule.DENY));
+ new Rule(IntegrityFormula.PACKAGE_NAME.equalTo(PACKAGE_NAME), Rule.DENY));
mService.updateRuleSet(VERSION, new ParceledListSlice<>(rules), mockReceiver);
runJobInHandler();
@@ -368,13 +362,21 @@ public class AppIntegrityManagerServiceImplTest {
public void verifierAsInstaller_skipIntegrityVerification() throws Exception {
whitelistUsAsRuleProvider();
makeUsSystemApp();
+ mService =
+ new AppIntegrityManagerServiceImpl(
+ mMockContext,
+ mPackageManagerInternal,
+ mRuleEvaluationEngine,
+ mIntegrityFileManager,
+ mHandler,
+ /* checkIntegrityForRuleProviders= */ false);
ArgumentCaptor<BroadcastReceiver> broadcastReceiverCaptor =
ArgumentCaptor.forClass(BroadcastReceiver.class);
- verify(mMockContext)
+ verify(mMockContext, atLeastOnce())
.registerReceiver(broadcastReceiverCaptor.capture(), any(), any(), any());
Intent intent = makeVerificationIntent(TEST_FRAMEWORK_PACKAGE);
- when(mRuleEvaluationEngine.evaluate(any(), any())).thenReturn(
- IntegrityCheckResult.deny(/* rule= */ null));
+ when(mRuleEvaluationEngine.evaluate(any(), any()))
+ .thenReturn(IntegrityCheckResult.deny(/* rule= */ null));
broadcastReceiverCaptor.getValue().onReceive(mMockContext, intent);
runJobInHandler();
@@ -384,6 +386,16 @@ public class AppIntegrityManagerServiceImplTest {
1, PackageManagerInternal.INTEGRITY_VERIFICATION_ALLOW);
}
+ @Test
+ public void getCurrentRules() throws Exception {
+ whitelistUsAsRuleProvider();
+ makeUsSystemApp();
+ Rule rule = new Rule(IntegrityFormula.PACKAGE_NAME.equalTo("package"), Rule.DENY);
+ when(mIntegrityFileManager.readRules(any())).thenReturn(Arrays.asList(rule));
+
+ assertThat(mService.getCurrentRules().getList()).containsExactly(rule);
+ }
+
private void whitelistUsAsRuleProvider() {
Resources mockResources = mock(Resources.class);
when(mockResources.getStringArray(R.array.config_integrityRuleProviderPackages))
@@ -409,14 +421,11 @@ public class AppIntegrityManagerServiceImplTest {
private Intent makeVerificationIntent() throws Exception {
PackageInfo packageInfo =
- mRealContext.getPackageManager()
+ mRealContext
+ .getPackageManager()
.getPackageInfo(TEST_FRAMEWORK_PACKAGE, PackageManager.GET_SIGNATURES);
- doReturn(packageInfo)
- .when(mSpyPackageManager)
- .getPackageInfo(eq(INSTALLER), anyInt());
- doReturn(1)
- .when(mSpyPackageManager)
- .getPackageUid(eq(INSTALLER), anyInt());
+ doReturn(packageInfo).when(mSpyPackageManager).getPackageInfo(eq(INSTALLER), anyInt());
+ doReturn(1).when(mSpyPackageManager).getPackageUid(eq(INSTALLER), anyInt());
return makeVerificationIntent(INSTALLER);
}
diff --git a/services/tests/servicestests/src/com/android/server/integrity/model/IntegrityCheckResultTest.java b/services/tests/servicestests/src/com/android/server/integrity/model/IntegrityCheckResultTest.java
index ec1423958f8f..6c23ff6fcb3c 100644
--- a/services/tests/servicestests/src/com/android/server/integrity/model/IntegrityCheckResultTest.java
+++ b/services/tests/servicestests/src/com/android/server/integrity/model/IntegrityCheckResultTest.java
@@ -21,7 +21,8 @@ import static com.google.common.truth.Truth.assertThat;
import android.content.integrity.AtomicFormula;
import android.content.integrity.CompoundFormula;
import android.content.integrity.Rule;
-import android.util.StatsLog;
+
+import com.android.internal.util.FrameworkStatsLog;
import org.junit.Test;
import org.junit.runner.RunWith;
@@ -40,7 +41,7 @@ public class IntegrityCheckResultTest {
assertThat(allowResult.getEffect()).isEqualTo(IntegrityCheckResult.Effect.ALLOW);
assertThat(allowResult.getMatchedRules()).isEmpty();
assertThat(allowResult.getLoggingResponse())
- .isEqualTo(StatsLog.INTEGRITY_CHECK_RESULT_REPORTED__RESPONSE__ALLOWED);
+ .isEqualTo(FrameworkStatsLog.INTEGRITY_CHECK_RESULT_REPORTED__RESPONSE__ALLOWED);
}
@Test
@@ -58,7 +59,8 @@ public class IntegrityCheckResultTest {
assertThat(allowResult.getEffect()).isEqualTo(IntegrityCheckResult.Effect.ALLOW);
assertThat(allowResult.getMatchedRules()).containsExactly(forceAllowRule);
assertThat(allowResult.getLoggingResponse())
- .isEqualTo(StatsLog.INTEGRITY_CHECK_RESULT_REPORTED__RESPONSE__FORCE_ALLOWED);
+ .isEqualTo(
+ FrameworkStatsLog.INTEGRITY_CHECK_RESULT_REPORTED__RESPONSE__FORCE_ALLOWED);
}
@Test
@@ -76,7 +78,7 @@ public class IntegrityCheckResultTest {
assertThat(denyResult.getEffect()).isEqualTo(IntegrityCheckResult.Effect.DENY);
assertThat(denyResult.getMatchedRules()).containsExactly(failedRule);
assertThat(denyResult.getLoggingResponse())
- .isEqualTo(StatsLog.INTEGRITY_CHECK_RESULT_REPORTED__RESPONSE__REJECTED);
+ .isEqualTo(FrameworkStatsLog.INTEGRITY_CHECK_RESULT_REPORTED__RESPONSE__REJECTED);
}
@Test
diff --git a/services/tests/servicestests/src/com/android/server/integrity/serializer/RuleBinarySerializerTest.java b/services/tests/servicestests/src/com/android/server/integrity/serializer/RuleBinarySerializerTest.java
index b3d949395d5c..b87877907d4f 100644
--- a/services/tests/servicestests/src/com/android/server/integrity/serializer/RuleBinarySerializerTest.java
+++ b/services/tests/servicestests/src/com/android/server/integrity/serializer/RuleBinarySerializerTest.java
@@ -203,8 +203,8 @@ public class RuleBinarySerializerTest {
+ getBits(DEFAULT_FORMAT_VERSION_BYTES.length, /* numOfBits= */ 32)
+ SERIALIZED_END_INDEXING_KEY
+ getBits(
- DEFAULT_FORMAT_VERSION_BYTES.length + getBytes(expectedBits).length,
- /* numOfBits= */ 32);
+ DEFAULT_FORMAT_VERSION_BYTES.length + getBytes(expectedBits).length,
+ /* numOfBits= */ 32);
expectedIndexingOutputStream.write(
getBytes(
expectedIndexingBitsForIndexed
@@ -427,8 +427,7 @@ public class RuleBinarySerializerTest {
Rule rule =
new Rule(
new AtomicFormula.LongAtomicFormula(
- AtomicFormula.VERSION_CODE, AtomicFormula.EQ,
- versionCode),
+ AtomicFormula.VERSION_CODE, AtomicFormula.EQ, versionCode),
Rule.DENY);
RuleSerializer binarySerializer = new RuleBinarySerializer();
String expectedBits =
@@ -456,8 +455,7 @@ public class RuleBinarySerializerTest {
String preInstalled = "1";
Rule rule =
new Rule(
- new AtomicFormula.BooleanAtomicFormula(
- AtomicFormula.PRE_INSTALLED, true),
+ new AtomicFormula.BooleanAtomicFormula(AtomicFormula.PRE_INSTALLED, true),
Rule.DENY);
RuleSerializer binarySerializer = new RuleBinarySerializer();
String expectedBits =
diff --git a/services/tests/servicestests/src/com/android/server/locksettings/RebootEscrowDataTest.java b/services/tests/servicestests/src/com/android/server/locksettings/RebootEscrowDataTest.java
index 54c552b06c23..b5c6d0425bb1 100644
--- a/services/tests/servicestests/src/com/android/server/locksettings/RebootEscrowDataTest.java
+++ b/services/tests/servicestests/src/com/android/server/locksettings/RebootEscrowDataTest.java
@@ -21,16 +21,22 @@ import static org.junit.Assert.assertThat;
import androidx.test.runner.AndroidJUnit4;
+import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
-import javax.crypto.spec.SecretKeySpec;
-
/**
* atest FrameworksServicesTests:RebootEscrowDataTest
*/
@RunWith(AndroidJUnit4.class)
public class RebootEscrowDataTest {
+ private RebootEscrowKey mKey;
+
+ @Before
+ public void generateKey() throws Exception {
+ mKey = RebootEscrowKey.generate();
+ }
+
private static byte[] getTestSp() {
byte[] testSp = new byte[10];
for (int i = 0; i < testSp.length; i++) {
@@ -41,30 +47,30 @@ public class RebootEscrowDataTest {
@Test(expected = NullPointerException.class)
public void fromEntries_failsOnNull() throws Exception {
- RebootEscrowData.fromSyntheticPassword((byte) 2, null);
+ RebootEscrowData.fromSyntheticPassword(mKey, (byte) 2, null);
}
@Test(expected = NullPointerException.class)
public void fromEncryptedData_failsOnNullData() throws Exception {
byte[] testSp = getTestSp();
- RebootEscrowData expected = RebootEscrowData.fromSyntheticPassword((byte) 2, testSp);
- SecretKeySpec key = RebootEscrowData.fromKeyBytes(expected.getKey());
+ RebootEscrowData expected = RebootEscrowData.fromSyntheticPassword(mKey, (byte) 2, testSp);
+ RebootEscrowKey key = RebootEscrowKey.fromKeyBytes(expected.getKey().getKeyBytes());
RebootEscrowData.fromEncryptedData(key, null);
}
@Test(expected = NullPointerException.class)
public void fromEncryptedData_failsOnNullKey() throws Exception {
byte[] testSp = getTestSp();
- RebootEscrowData expected = RebootEscrowData.fromSyntheticPassword((byte) 2, testSp);
+ RebootEscrowData expected = RebootEscrowData.fromSyntheticPassword(mKey, (byte) 2, testSp);
RebootEscrowData.fromEncryptedData(null, expected.getBlob());
}
@Test
public void fromEntries_loopback_success() throws Exception {
byte[] testSp = getTestSp();
- RebootEscrowData expected = RebootEscrowData.fromSyntheticPassword((byte) 2, testSp);
+ RebootEscrowData expected = RebootEscrowData.fromSyntheticPassword(mKey, (byte) 2, testSp);
- SecretKeySpec key = RebootEscrowData.fromKeyBytes(expected.getKey());
+ RebootEscrowKey key = RebootEscrowKey.fromKeyBytes(expected.getKey().getKeyBytes());
RebootEscrowData actual = RebootEscrowData.fromEncryptedData(key, expected.getBlob());
assertThat(actual.getSpVersion(), is(expected.getSpVersion()));
diff --git a/services/tests/servicestests/src/com/android/server/locksettings/RebootEscrowManagerTests.java b/services/tests/servicestests/src/com/android/server/locksettings/RebootEscrowManagerTests.java
index 78a5a0b69fff..44d88d2b44d3 100644
--- a/services/tests/servicestests/src/com/android/server/locksettings/RebootEscrowManagerTests.java
+++ b/services/tests/servicestests/src/com/android/server/locksettings/RebootEscrowManagerTests.java
@@ -18,6 +18,7 @@ package com.android.server.locksettings;
import static android.content.pm.UserInfo.FLAG_FULL;
import static android.content.pm.UserInfo.FLAG_PRIMARY;
+import static android.content.pm.UserInfo.FLAG_PROFILE;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertTrue;
@@ -27,6 +28,7 @@ import static org.mockito.Mockito.clearInvocations;
import static org.mockito.Mockito.doThrow;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.never;
+import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.verifyNoMoreInteractions;
import static org.mockito.Mockito.when;
@@ -56,7 +58,9 @@ import java.util.ArrayList;
@RunWith(AndroidJUnit4.class)
public class RebootEscrowManagerTests {
protected static final int PRIMARY_USER_ID = 0;
- protected static final int NONSECURE_USER_ID = 10;
+ protected static final int WORK_PROFILE_USER_ID = 10;
+ protected static final int NONSECURE_SECONDARY_USER_ID = 20;
+ protected static final int SECURE_SECONDARY_USER_ID = 21;
private static final byte FAKE_SP_VERSION = 1;
private static final byte[] FAKE_AUTH_TOKEN = new byte[] {
0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08,
@@ -107,10 +111,14 @@ public class RebootEscrowManagerTests {
ArrayList<UserInfo> users = new ArrayList<>();
users.add(new UserInfo(PRIMARY_USER_ID, "primary", FLAG_PRIMARY));
- users.add(new UserInfo(NONSECURE_USER_ID, "non-secure", FLAG_FULL));
+ users.add(new UserInfo(WORK_PROFILE_USER_ID, "work", FLAG_PROFILE));
+ users.add(new UserInfo(NONSECURE_SECONDARY_USER_ID, "non-secure", FLAG_FULL));
+ users.add(new UserInfo(SECURE_SECONDARY_USER_ID, "secure", FLAG_FULL));
when(mUserManager.getUsers()).thenReturn(users);
when(mCallbacks.isUserSecure(PRIMARY_USER_ID)).thenReturn(true);
- when(mCallbacks.isUserSecure(NONSECURE_USER_ID)).thenReturn(false);
+ when(mCallbacks.isUserSecure(WORK_PROFILE_USER_ID)).thenReturn(true);
+ when(mCallbacks.isUserSecure(NONSECURE_SECONDARY_USER_ID)).thenReturn(false);
+ when(mCallbacks.isUserSecure(SECURE_SECONDARY_USER_ID)).thenReturn(true);
mService = new RebootEscrowManager(new MockInjector(mContext, mUserManager, mRebootEscrow),
mCallbacks, mStorage);
}
@@ -154,6 +162,30 @@ public class RebootEscrowManagerTests {
assertTrue(mService.armRebootEscrowIfNeeded());
verify(mRebootEscrow).storeKey(any());
+
+ assertTrue(mStorage.hasRebootEscrow(PRIMARY_USER_ID));
+ assertFalse(mStorage.hasRebootEscrow(NONSECURE_SECONDARY_USER_ID));
+ }
+
+ @Test
+ public void armService_MultipleUsers_Success() throws Exception {
+ RebootEscrowListener mockListener = mock(RebootEscrowListener.class);
+ mService.setRebootEscrowListener(mockListener);
+ mService.prepareRebootEscrow();
+
+ clearInvocations(mRebootEscrow);
+ mService.callToRebootEscrowIfNeeded(PRIMARY_USER_ID, FAKE_SP_VERSION, FAKE_AUTH_TOKEN);
+ verify(mockListener).onPreparedForReboot(eq(true));
+ mService.callToRebootEscrowIfNeeded(SECURE_SECONDARY_USER_ID, FAKE_SP_VERSION,
+ FAKE_AUTH_TOKEN);
+ verify(mRebootEscrow, never()).storeKey(any());
+
+ assertTrue(mService.armRebootEscrowIfNeeded());
+ verify(mRebootEscrow, times(1)).storeKey(any());
+
+ assertTrue(mStorage.hasRebootEscrow(PRIMARY_USER_ID));
+ assertTrue(mStorage.hasRebootEscrow(SECURE_SECONDARY_USER_ID));
+ assertFalse(mStorage.hasRebootEscrow(NONSECURE_SECONDARY_USER_ID));
}
@Test
diff --git a/services/tests/servicestests/src/com/android/server/people/PeopleServiceTest.java b/services/tests/servicestests/src/com/android/server/people/PeopleServiceTest.java
index d3166b91dc9e..4ae374abb7c2 100644
--- a/services/tests/servicestests/src/com/android/server/people/PeopleServiceTest.java
+++ b/services/tests/servicestests/src/com/android/server/people/PeopleServiceTest.java
@@ -28,6 +28,7 @@ import android.app.prediction.IPredictionCallback;
import android.content.Context;
import android.content.pm.ParceledListSlice;
import android.os.Binder;
+import android.os.Bundle;
import android.os.RemoteException;
import com.android.server.LocalServices;
@@ -76,6 +77,7 @@ public final class PeopleServiceTest {
mPredictionContext = new AppPredictionContext.Builder(mContext)
.setUiSurface(APP_PREDICTION_SHARE_UI_SURFACE)
.setPredictedTargetCount(APP_PREDICTION_TARGET_COUNT)
+ .setExtras(new Bundle())
.build();
}
diff --git a/services/tests/servicestests/src/com/android/server/people/data/CallLogQueryHelperTest.java b/services/tests/servicestests/src/com/android/server/people/data/CallLogQueryHelperTest.java
new file mode 100644
index 000000000000..7a16d171b475
--- /dev/null
+++ b/services/tests/servicestests/src/com/android/server/people/data/CallLogQueryHelperTest.java
@@ -0,0 +1,169 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.people.data;
+
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
+import static org.mockito.Mockito.when;
+
+import android.database.Cursor;
+import android.database.MatrixCursor;
+import android.net.Uri;
+import android.provider.CallLog.Calls;
+import android.test.mock.MockContentProvider;
+import android.test.mock.MockContentResolver;
+import android.test.mock.MockContext;
+import android.util.ArrayMap;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.JUnit4;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Map;
+import java.util.function.BiConsumer;
+
+@RunWith(JUnit4.class)
+public final class CallLogQueryHelperTest {
+
+ private static final String CALL_LOG_AUTHORITY = "call_log";
+ private static final String NORMALIZED_PHONE_NUMBER = "+16505551111";
+
+ private static final String[] CALL_LOG_COLUMNS = new String[] {
+ Calls.CACHED_NORMALIZED_NUMBER, Calls.DATE, Calls.DURATION, Calls.TYPE };
+
+ @Mock
+ private MockContext mContext;
+
+ private MatrixCursor mCursor;
+ private EventConsumer mEventConsumer;
+ private CallLogQueryHelper mHelper;
+
+ @Before
+ public void setUp() {
+ MockitoAnnotations.initMocks(this);
+
+ mCursor = new MatrixCursor(CALL_LOG_COLUMNS);
+
+ MockContentResolver contentResolver = new MockContentResolver();
+ contentResolver.addProvider(CALL_LOG_AUTHORITY, new CallLogContentProvider());
+ when(mContext.getContentResolver()).thenReturn(contentResolver);
+
+ mEventConsumer = new EventConsumer();
+ mHelper = new CallLogQueryHelper(mContext, mEventConsumer);
+ }
+
+ @Test
+ public void testQueryNoCalls() {
+ assertFalse(mHelper.querySince(50L));
+ assertFalse(mEventConsumer.mEventMap.containsKey(NORMALIZED_PHONE_NUMBER));
+ }
+
+ @Test
+ public void testQueryIncomingCall() {
+ mCursor.addRow(new Object[] {
+ NORMALIZED_PHONE_NUMBER, /* date= */ 100L, /* duration= */ 30L,
+ /* type= */ Calls.INCOMING_TYPE });
+
+ assertTrue(mHelper.querySince(50L));
+ List<Event> events = mEventConsumer.mEventMap.get(NORMALIZED_PHONE_NUMBER);
+
+ assertEquals(100L, mHelper.getLastCallTimestamp());
+ assertEquals(1, events.size());
+ assertEquals(Event.TYPE_CALL_INCOMING, events.get(0).getType());
+ assertEquals(100L, events.get(0).getTimestamp());
+ assertEquals(30L, events.get(0).getCallDetails().getDurationSeconds());
+ }
+
+ @Test
+ public void testQueryOutgoingCall() {
+ mCursor.addRow(new Object[] {
+ NORMALIZED_PHONE_NUMBER, /* date= */ 100L, /* duration= */ 40L,
+ /* type= */ Calls.OUTGOING_TYPE });
+
+ assertTrue(mHelper.querySince(50L));
+ List<Event> events = mEventConsumer.mEventMap.get(NORMALIZED_PHONE_NUMBER);
+
+ assertEquals(100L, mHelper.getLastCallTimestamp());
+ assertEquals(1, events.size());
+ assertEquals(Event.TYPE_CALL_OUTGOING, events.get(0).getType());
+ assertEquals(100L, events.get(0).getTimestamp());
+ assertEquals(40L, events.get(0).getCallDetails().getDurationSeconds());
+ }
+
+ @Test
+ public void testQueryMissedCall() {
+ mCursor.addRow(new Object[] {
+ NORMALIZED_PHONE_NUMBER, /* date= */ 100L, /* duration= */ 0L,
+ /* type= */ Calls.MISSED_TYPE });
+
+ assertTrue(mHelper.querySince(50L));
+ List<Event> events = mEventConsumer.mEventMap.get(NORMALIZED_PHONE_NUMBER);
+
+ assertEquals(100L, mHelper.getLastCallTimestamp());
+ assertEquals(1, events.size());
+ assertEquals(Event.TYPE_CALL_MISSED, events.get(0).getType());
+ assertEquals(100L, events.get(0).getTimestamp());
+ assertEquals(0L, events.get(0).getCallDetails().getDurationSeconds());
+ }
+
+ @Test
+ public void testQueryMultipleCalls() {
+ mCursor.addRow(new Object[] {
+ NORMALIZED_PHONE_NUMBER, /* date= */ 100L, /* duration= */ 0L,
+ /* type= */ Calls.MISSED_TYPE });
+ mCursor.addRow(new Object[] {
+ NORMALIZED_PHONE_NUMBER, /* date= */ 110L, /* duration= */ 40L,
+ /* type= */ Calls.OUTGOING_TYPE });
+
+ assertTrue(mHelper.querySince(50L));
+ List<Event> events = mEventConsumer.mEventMap.get(NORMALIZED_PHONE_NUMBER);
+
+ assertEquals(110L, mHelper.getLastCallTimestamp());
+ assertEquals(2, events.size());
+ assertEquals(Event.TYPE_CALL_MISSED, events.get(0).getType());
+ assertEquals(100L, events.get(0).getTimestamp());
+ assertEquals(Event.TYPE_CALL_OUTGOING, events.get(1).getType());
+ assertEquals(110L, events.get(1).getTimestamp());
+ assertEquals(40L, events.get(1).getCallDetails().getDurationSeconds());
+ }
+
+ private class EventConsumer implements BiConsumer<String, Event> {
+
+ private final Map<String, List<Event>> mEventMap = new ArrayMap<>();
+
+ @Override
+ public void accept(String phoneNumber, Event event) {
+ mEventMap.computeIfAbsent(phoneNumber, key -> new ArrayList<>()).add(event);
+ }
+ }
+
+ private class CallLogContentProvider extends MockContentProvider {
+
+ @Override
+ public Cursor query(Uri uri, String[] projection, String selection, String[] selectionArgs,
+ String sortOrder) {
+ return mCursor;
+ }
+ }
+}
diff --git a/services/tests/servicestests/src/com/android/server/people/data/DataManagerTest.java b/services/tests/servicestests/src/com/android/server/people/data/DataManagerTest.java
index 91da761ce229..62ea425a54a7 100644
--- a/services/tests/servicestests/src/com/android/server/people/data/DataManagerTest.java
+++ b/services/tests/servicestests/src/com/android/server/people/data/DataManagerTest.java
@@ -18,6 +18,8 @@ package com.android.server.people.data;
import static android.app.usage.UsageEvents.Event.SHORTCUT_INVOCATION;
+import static androidx.test.platform.app.InstrumentationRegistry.getInstrumentation;
+
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertTrue;
@@ -40,14 +42,12 @@ import android.app.prediction.AppTargetEvent;
import android.app.prediction.AppTargetId;
import android.app.usage.UsageEvents;
import android.app.usage.UsageStatsManagerInternal;
-import android.content.ComponentName;
import android.content.ContentResolver;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
import android.content.pm.ShortcutInfo;
import android.content.pm.ShortcutManager;
-import android.content.pm.ShortcutManager.ShareShortcutInfo;
import android.content.pm.ShortcutServiceInternal;
import android.content.pm.UserInfo;
import android.database.ContentObserver;
@@ -58,6 +58,7 @@ import android.os.UserManager;
import android.provider.ContactsContract;
import android.service.notification.NotificationListenerService;
import android.service.notification.StatusBarNotification;
+import android.telecom.TelecomManager;
import android.telephony.TelephonyManager;
import android.util.Range;
@@ -79,6 +80,7 @@ import java.util.List;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.ScheduledFuture;
import java.util.concurrent.TimeUnit;
+import java.util.function.BiConsumer;
@RunWith(JUnit4.class)
public final class DataManagerTest {
@@ -90,6 +92,7 @@ public final class DataManagerTest {
private static final String TEST_SHORTCUT_ID = "sc";
private static final String CONTACT_URI = "content://com.android.contacts/contacts/lookup/123";
private static final String PHONE_NUMBER = "+1234567890";
+ private static final long MILLIS_PER_MINUTE = 1000L * 60L;
@Mock private Context mContext;
@Mock private ShortcutServiceInternal mShortcutServiceInternal;
@@ -97,6 +100,7 @@ public final class DataManagerTest {
@Mock private ShortcutManager mShortcutManager;
@Mock private UserManager mUserManager;
@Mock private TelephonyManager mTelephonyManager;
+ @Mock private TelecomManager mTelecomManager;
@Mock private ContentResolver mContentResolver;
@Mock private ScheduledExecutorService mExecutorService;
@Mock private ScheduledFuture mScheduledFuture;
@@ -117,6 +121,9 @@ public final class DataManagerTest {
when(mContext.getMainLooper()).thenReturn(Looper.getMainLooper());
+ Context originalContext = getInstrumentation().getTargetContext();
+ when(mContext.getApplicationInfo()).thenReturn(originalContext.getApplicationInfo());
+
when(mContext.getSystemService(Context.SHORTCUT_SERVICE)).thenReturn(mShortcutManager);
when(mContext.getSystemServiceName(ShortcutManager.class)).thenReturn(
Context.SHORTCUT_SERVICE);
@@ -127,6 +134,11 @@ public final class DataManagerTest {
when(mContext.getSystemService(Context.TELEPHONY_SERVICE)).thenReturn(mTelephonyManager);
+ when(mContext.getSystemService(Context.TELECOM_SERVICE)).thenReturn(mTelecomManager);
+ when(mContext.getSystemServiceName(TelecomManager.class)).thenReturn(
+ Context.TELECOM_SERVICE);
+ when(mTelecomManager.getDefaultDialerPackage(anyInt())).thenReturn(TEST_PKG_NAME);
+
when(mExecutorService.scheduleAtFixedRate(any(Runnable.class), anyLong(), anyLong(), any(
TimeUnit.class))).thenReturn(mScheduledFuture);
@@ -224,31 +236,6 @@ public final class DataManagerTest {
}
@Test
- public void testGetShareTargets() {
- mDataManager.onUserUnlocked(USER_ID_PRIMARY);
-
- ShortcutInfo shortcut1 =
- buildShortcutInfo("pkg_1", USER_ID_PRIMARY, "sc_1", buildPerson());
- ShareShortcutInfo shareShortcut1 =
- new ShareShortcutInfo(shortcut1, new ComponentName("pkg_1", "activity"));
-
- ShortcutInfo shortcut2 =
- buildShortcutInfo("pkg_2", USER_ID_PRIMARY, "sc_2", buildPerson());
- ShareShortcutInfo shareShortcut2 =
- new ShareShortcutInfo(shortcut2, new ComponentName("pkg_2", "activity"));
- mDataManager.onShortcutAddedOrUpdated(shortcut2);
-
- when(mShortcutManager.getShareTargets(any(IntentFilter.class)))
- .thenReturn(Arrays.asList(shareShortcut1, shareShortcut2));
-
- List<ShareShortcutInfo> shareShortcuts =
- mDataManager.getConversationShareTargets(new IntentFilter());
- // Only "sc_2" is stored as a conversation.
- assertEquals(1, shareShortcuts.size());
- assertEquals("sc_2", shareShortcuts.get(0).getShortcutInfo().getId());
- }
-
- @Test
public void testReportAppTargetEvent() throws IntentFilter.MalformedMimeTypeException {
mDataManager.onUserUnlocked(USER_ID_PRIMARY);
ShortcutInfo shortcut = buildShortcutInfo(TEST_PKG_NAME, USER_ID_PRIMARY, TEST_SHORTCUT_ID,
@@ -351,6 +338,61 @@ public final class DataManagerTest {
assertEquals(1, activeShortcutInvocationTimeSlots.size());
}
+ @Test
+ public void testCallLogContentObserver() {
+ mDataManager.onUserUnlocked(USER_ID_PRIMARY);
+
+ ShortcutInfo shortcut = buildShortcutInfo(TEST_PKG_NAME, USER_ID_PRIMARY, TEST_SHORTCUT_ID,
+ buildPerson());
+ mDataManager.onShortcutAddedOrUpdated(shortcut);
+
+ ContentObserver contentObserver = mDataManager.getCallLogContentObserverForTesting();
+ contentObserver.onChange(false);
+ long currentTimestamp = System.currentTimeMillis();
+ mInjector.mCallLogQueryHelper.mEventConsumer.accept(PHONE_NUMBER,
+ new Event(currentTimestamp - MILLIS_PER_MINUTE * 15L, Event.TYPE_CALL_OUTGOING));
+ mInjector.mCallLogQueryHelper.mEventConsumer.accept(PHONE_NUMBER,
+ new Event(currentTimestamp - MILLIS_PER_MINUTE * 10L, Event.TYPE_CALL_INCOMING));
+ mInjector.mCallLogQueryHelper.mEventConsumer.accept(PHONE_NUMBER,
+ new Event(currentTimestamp - MILLIS_PER_MINUTE * 5L, Event.TYPE_CALL_MISSED));
+
+ List<Range<Long>> activeTimeSlots = new ArrayList<>();
+ mDataManager.forAllPackages(packageData ->
+ activeTimeSlots.addAll(
+ packageData.getEventHistory(TEST_SHORTCUT_ID)
+ .getEventIndex(Event.CALL_EVENT_TYPES)
+ .getActiveTimeSlots()));
+ assertEquals(3, activeTimeSlots.size());
+ }
+
+ @Test
+ public void testMmsSmsContentObserver() {
+ mDataManager.onUserUnlocked(USER_ID_PRIMARY);
+
+ ShortcutInfo shortcut = buildShortcutInfo(TEST_PKG_NAME, USER_ID_PRIMARY, TEST_SHORTCUT_ID,
+ buildPerson());
+ mDataManager.onShortcutAddedOrUpdated(shortcut);
+ mDataManager.getUserDataForTesting(USER_ID_PRIMARY).setDefaultSmsApp(TEST_PKG_NAME);
+
+ ContentObserver contentObserver = mDataManager.getMmsSmsContentObserverForTesting();
+ contentObserver.onChange(false);
+ long currentTimestamp = System.currentTimeMillis();
+ Event outgoingSmsEvent =
+ new Event(currentTimestamp - MILLIS_PER_MINUTE * 10L, Event.TYPE_SMS_OUTGOING);
+ Event incomingSmsEvent =
+ new Event(currentTimestamp - MILLIS_PER_MINUTE * 5L, Event.TYPE_SMS_INCOMING);
+ mInjector.mMmsQueryHelper.mEventConsumer.accept(PHONE_NUMBER, outgoingSmsEvent);
+ mInjector.mSmsQueryHelper.mEventConsumer.accept(PHONE_NUMBER, incomingSmsEvent);
+
+ List<Range<Long>> activeTimeSlots = new ArrayList<>();
+ mDataManager.forAllPackages(packageData ->
+ activeTimeSlots.addAll(
+ packageData.getEventHistory(TEST_SHORTCUT_ID)
+ .getEventIndex(Event.SMS_EVENT_TYPES)
+ .getActiveTimeSlots()));
+ assertEquals(2, activeTimeSlots.size());
+ }
+
private static <T> void addLocalServiceMock(Class<T> clazz, T mock) {
LocalServices.removeServiceForTest(clazz);
LocalServices.addService(clazz, mock);
@@ -428,10 +470,73 @@ public final class DataManagerTest {
}
}
+ private class TestCallLogQueryHelper extends CallLogQueryHelper {
+
+ private final BiConsumer<String, Event> mEventConsumer;
+
+ TestCallLogQueryHelper(Context context, BiConsumer<String, Event> eventConsumer) {
+ super(context, eventConsumer);
+ mEventConsumer = eventConsumer;
+ }
+
+ @Override
+ boolean querySince(long sinceTime) {
+ return true;
+ }
+
+ @Override
+ long getLastCallTimestamp() {
+ return 100L;
+ }
+ }
+
+ private class TestSmsQueryHelper extends SmsQueryHelper {
+
+ private final BiConsumer<String, Event> mEventConsumer;
+
+ TestSmsQueryHelper(Context context, BiConsumer<String, Event> eventConsumer) {
+ super(context, eventConsumer);
+ mEventConsumer = eventConsumer;
+ }
+
+ @Override
+ boolean querySince(long sinceTime) {
+ return true;
+ }
+
+ @Override
+ long getLastMessageTimestamp() {
+ return 100L;
+ }
+ }
+
+ private class TestMmsQueryHelper extends MmsQueryHelper {
+
+ private final BiConsumer<String, Event> mEventConsumer;
+
+ TestMmsQueryHelper(Context context, BiConsumer<String, Event> eventConsumer) {
+ super(context, eventConsumer);
+ mEventConsumer = eventConsumer;
+ }
+
+ @Override
+ boolean querySince(long sinceTime) {
+ return true;
+ }
+
+ @Override
+ long getLastMessageTimestamp() {
+ return 100L;
+ }
+ }
+
private class TestInjector extends DataManager.Injector {
private final TestContactsQueryHelper mContactsQueryHelper =
new TestContactsQueryHelper(mContext);
+ private TestCallLogQueryHelper mCallLogQueryHelper;
+ private TestMmsQueryHelper mMmsQueryHelper;
+ private TestSmsQueryHelper mSmsQueryHelper;
@Override
ScheduledExecutorService createScheduledExecutor() {
@@ -444,6 +549,27 @@ public final class DataManagerTest {
}
@Override
+ CallLogQueryHelper createCallLogQueryHelper(Context context,
+ BiConsumer<String, Event> eventConsumer) {
+ mCallLogQueryHelper = new TestCallLogQueryHelper(context, eventConsumer);
+ return mCallLogQueryHelper;
+ }
+
+ @Override
+ MmsQueryHelper createMmsQueryHelper(Context context,
+ BiConsumer<String, Event> eventConsumer) {
+ mMmsQueryHelper = new TestMmsQueryHelper(context, eventConsumer);
+ return mMmsQueryHelper;
+ }
+
+ @Override
+ SmsQueryHelper createSmsQueryHelper(Context context,
+ BiConsumer<String, Event> eventConsumer) {
+ mSmsQueryHelper = new TestSmsQueryHelper(context, eventConsumer);
+ return mSmsQueryHelper;
+ }
+
+ @Override
int getCallingUserId() {
return mCallingUserId;
}
diff --git a/services/tests/servicestests/src/com/android/server/people/data/MmsQueryHelperTest.java b/services/tests/servicestests/src/com/android/server/people/data/MmsQueryHelperTest.java
new file mode 100644
index 000000000000..7730890e1486
--- /dev/null
+++ b/services/tests/servicestests/src/com/android/server/people/data/MmsQueryHelperTest.java
@@ -0,0 +1,174 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.people.data;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
+import static org.mockito.Mockito.when;
+
+import android.database.Cursor;
+import android.database.MatrixCursor;
+import android.net.Uri;
+import android.provider.Telephony.BaseMmsColumns;
+import android.provider.Telephony.Mms;
+import android.test.mock.MockContentProvider;
+import android.test.mock.MockContentResolver;
+import android.test.mock.MockContext;
+import android.util.ArrayMap;
+
+import com.google.android.mms.pdu.PduHeaders;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.JUnit4;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Map;
+import java.util.function.BiConsumer;
+
+@RunWith(JUnit4.class)
+public final class MmsQueryHelperTest {
+
+ private static final String MMS_AUTHORITY = "mms";
+ private static final String PHONE_NUMBER = "650-555-1111";
+ private static final String NORMALIZED_PHONE_NUMBER = "+16505551111";
+ private static final String OWN_PHONE_NUMBER = "650-555-9999";
+
+ private static final String[] MMS_COLUMNS = new String[] { Mms._ID, Mms.DATE, Mms.MESSAGE_BOX };
+ private static final String[] ADDR_COLUMNS = new String[] { Mms.Addr.ADDRESS, Mms.Addr.TYPE };
+
+ @Mock
+ private MockContext mContext;
+
+ private MatrixCursor mMmsCursor;
+ private final List<MatrixCursor> mAddrCursors = new ArrayList<>();
+ private EventConsumer mEventConsumer;
+ private MmsQueryHelper mHelper;
+
+ @Before
+ public void setUp() {
+ MockitoAnnotations.initMocks(this);
+
+ mMmsCursor = new MatrixCursor(MMS_COLUMNS);
+ mAddrCursors.add(new MatrixCursor(ADDR_COLUMNS));
+ mAddrCursors.add(new MatrixCursor(ADDR_COLUMNS));
+
+ MockContentResolver contentResolver = new MockContentResolver();
+ contentResolver.addProvider(MMS_AUTHORITY, new MmsContentProvider());
+ when(mContext.getContentResolver()).thenReturn(contentResolver);
+
+ mEventConsumer = new EventConsumer();
+ mHelper = new MmsQueryHelper(mContext, mEventConsumer);
+ }
+
+ @Test
+ public void testQueryNoMessages() {
+ assertFalse(mHelper.querySince(50_000L));
+ assertFalse(mEventConsumer.mEventMap.containsKey(NORMALIZED_PHONE_NUMBER));
+ }
+
+ @Test
+ public void testQueryIncomingMessage() {
+ mMmsCursor.addRow(new Object[] {
+ /* id= */ 0, /* date= */ 100L, /* msgBox= */ BaseMmsColumns.MESSAGE_BOX_INBOX });
+ mAddrCursors.get(0).addRow(new Object[] {
+ /* address= */ PHONE_NUMBER, /* type= */ PduHeaders.FROM });
+ mAddrCursors.get(0).addRow(new Object[] {
+ /* address= */ OWN_PHONE_NUMBER, /* type= */ PduHeaders.TO });
+
+ assertTrue(mHelper.querySince(50_000L));
+ List<Event> events = mEventConsumer.mEventMap.get(NORMALIZED_PHONE_NUMBER);
+
+ assertEquals(100_000L, mHelper.getLastMessageTimestamp());
+ assertEquals(1, events.size());
+ assertEquals(Event.TYPE_SMS_INCOMING, events.get(0).getType());
+ assertEquals(100_000L, events.get(0).getTimestamp());
+ }
+
+ @Test
+ public void testQueryOutgoingMessage() {
+ mMmsCursor.addRow(new Object[] {
+ /* id= */ 0, /* date= */ 100L, /* msgBox= */ BaseMmsColumns.MESSAGE_BOX_SENT });
+ mAddrCursors.get(0).addRow(new Object[] {
+ /* address= */ OWN_PHONE_NUMBER, /* type= */ PduHeaders.FROM });
+ mAddrCursors.get(0).addRow(new Object[] {
+ /* address= */ PHONE_NUMBER, /* type= */ PduHeaders.TO });
+
+ assertTrue(mHelper.querySince(50_000L));
+ List<Event> events = mEventConsumer.mEventMap.get(NORMALIZED_PHONE_NUMBER);
+
+ assertEquals(100_000L, mHelper.getLastMessageTimestamp());
+ assertEquals(1, events.size());
+ assertEquals(Event.TYPE_SMS_OUTGOING, events.get(0).getType());
+ assertEquals(100_000L, events.get(0).getTimestamp());
+ }
+
+ @Test
+ public void testQueryMultipleMessages() {
+ mMmsCursor.addRow(new Object[] {
+ /* id= */ 0, /* date= */ 100L, /* msgBox= */ BaseMmsColumns.MESSAGE_BOX_SENT });
+ mMmsCursor.addRow(new Object[] {
+ /* id= */ 1, /* date= */ 110L, /* msgBox= */ BaseMmsColumns.MESSAGE_BOX_INBOX });
+ mAddrCursors.get(0).addRow(new Object[] {
+ /* address= */ OWN_PHONE_NUMBER, /* type= */ PduHeaders.FROM });
+ mAddrCursors.get(0).addRow(new Object[] {
+ /* address= */ PHONE_NUMBER, /* type= */ PduHeaders.TO });
+ mAddrCursors.get(1).addRow(new Object[] {
+ /* address= */ PHONE_NUMBER, /* type= */ PduHeaders.FROM });
+ mAddrCursors.get(1).addRow(new Object[] {
+ /* address= */ OWN_PHONE_NUMBER, /* type= */ PduHeaders.TO });
+
+ assertTrue(mHelper.querySince(50_000L));
+ List<Event> events = mEventConsumer.mEventMap.get(NORMALIZED_PHONE_NUMBER);
+
+ assertEquals(110_000L, mHelper.getLastMessageTimestamp());
+ assertEquals(2, events.size());
+ assertEquals(Event.TYPE_SMS_OUTGOING, events.get(0).getType());
+ assertEquals(100_000L, events.get(0).getTimestamp());
+ assertEquals(Event.TYPE_SMS_INCOMING, events.get(1).getType());
+ assertEquals(110_000L, events.get(1).getTimestamp());
+ }
+
+ private class EventConsumer implements BiConsumer<String, Event> {
+
+ private final Map<String, List<Event>> mEventMap = new ArrayMap<>();
+
+ @Override
+ public void accept(String phoneNumber, Event event) {
+ mEventMap.computeIfAbsent(phoneNumber, key -> new ArrayList<>()).add(event);
+ }
+ }
+
+ private class MmsContentProvider extends MockContentProvider {
+
+ @Override
+ public Cursor query(Uri uri, String[] projection, String selection, String[] selectionArgs,
+ String sortOrder) {
+ List<String> segments = uri.getPathSegments();
+ if (segments.size() == 2 && "addr".equals(segments.get(1))) {
+ int messageId = Integer.valueOf(segments.get(0));
+ return mAddrCursors.get(messageId);
+ }
+ return mMmsCursor;
+ }
+ }
+}
diff --git a/services/tests/servicestests/src/com/android/server/people/data/PackageDataTest.java b/services/tests/servicestests/src/com/android/server/people/data/PackageDataTest.java
index 1b80d6fc3a2d..ec4789ad0cdf 100644
--- a/services/tests/servicestests/src/com/android/server/people/data/PackageDataTest.java
+++ b/services/tests/servicestests/src/com/android/server/people/data/PackageDataTest.java
@@ -16,7 +16,6 @@
package com.android.server.people.data;
-
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertTrue;
@@ -46,12 +45,15 @@ public final class PackageDataTest {
private Event mE2;
private Event mE3;
private Event mE4;
+ private boolean mIsDefaultDialer;
+ private boolean mIsDefaultSmsApp;
private PackageData mPackageData;
@Before
public void setUp() {
- mPackageData = new PackageData(PACKAGE_NAME, USER_ID);
+ mPackageData = new PackageData(
+ PACKAGE_NAME, USER_ID, pkg -> mIsDefaultDialer, pkg -> mIsDefaultSmsApp);
ConversationInfo conversationInfo = new ConversationInfo.Builder()
.setShortcutId(SHORTCUT_ID)
.setLocusId(LOCUS_ID)
@@ -83,8 +85,8 @@ public final class PackageDataTest {
@Test
public void testGetEventHistoryDefaultDialerAndSmsApp() {
- mPackageData.setIsDefaultDialer(true);
- mPackageData.setIsDefaultSmsApp(true);
+ mIsDefaultDialer = true;
+ mIsDefaultSmsApp = true;
EventStore eventStore = mPackageData.getEventStore();
eventStore.getOrCreateShortcutEventHistory(SHORTCUT_ID).addEvent(mE1);
eventStore.getOrCreateCallEventHistory(PHONE_NUMBER).addEvent(mE3);
diff --git a/services/tests/servicestests/src/com/android/server/people/data/SmsQueryHelperTest.java b/services/tests/servicestests/src/com/android/server/people/data/SmsQueryHelperTest.java
new file mode 100644
index 000000000000..5cb8cb4fe9f1
--- /dev/null
+++ b/services/tests/servicestests/src/com/android/server/people/data/SmsQueryHelperTest.java
@@ -0,0 +1,151 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.people.data;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
+import static org.mockito.Mockito.when;
+
+import android.database.Cursor;
+import android.database.MatrixCursor;
+import android.net.Uri;
+import android.provider.Telephony.Sms;
+import android.provider.Telephony.TextBasedSmsColumns;
+import android.test.mock.MockContentProvider;
+import android.test.mock.MockContentResolver;
+import android.test.mock.MockContext;
+import android.util.ArrayMap;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.JUnit4;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Map;
+import java.util.function.BiConsumer;
+
+@RunWith(JUnit4.class)
+public final class SmsQueryHelperTest {
+
+ private static final String SMS_AUTHORITY = "sms";
+ private static final String PHONE_NUMBER = "650-555-1111";
+ private static final String NORMALIZED_PHONE_NUMBER = "+16505551111";
+
+ private static final String[] SMS_COLUMNS = new String[] {
+ Sms._ID, Sms.DATE, Sms.TYPE, Sms.ADDRESS };
+
+ @Mock
+ private MockContext mContext;
+
+ private MatrixCursor mSmsCursor;
+ private EventConsumer mEventConsumer;
+ private SmsQueryHelper mHelper;
+
+ @Before
+ public void setUp() {
+ MockitoAnnotations.initMocks(this);
+
+ mSmsCursor = new MatrixCursor(SMS_COLUMNS);
+
+ MockContentResolver contentResolver = new MockContentResolver();
+ contentResolver.addProvider(SMS_AUTHORITY, new SmsContentProvider());
+ when(mContext.getContentResolver()).thenReturn(contentResolver);
+
+ mEventConsumer = new EventConsumer();
+ mHelper = new SmsQueryHelper(mContext, mEventConsumer);
+ }
+
+ @Test
+ public void testQueryNoMessages() {
+ assertFalse(mHelper.querySince(50_000L));
+ assertFalse(mEventConsumer.mEventMap.containsKey(NORMALIZED_PHONE_NUMBER));
+ }
+
+ @Test
+ public void testQueryIncomingMessage() {
+ mSmsCursor.addRow(new Object[] {
+ /* id= */ 0, /* date= */ 100L, /* type= */ TextBasedSmsColumns.MESSAGE_TYPE_INBOX,
+ /* address= */ PHONE_NUMBER });
+
+ assertTrue(mHelper.querySince(50L));
+ List<Event> events = mEventConsumer.mEventMap.get(NORMALIZED_PHONE_NUMBER);
+
+ assertEquals(100L, mHelper.getLastMessageTimestamp());
+ assertEquals(1, events.size());
+ assertEquals(Event.TYPE_SMS_INCOMING, events.get(0).getType());
+ assertEquals(100L, events.get(0).getTimestamp());
+ }
+
+ @Test
+ public void testQueryOutgoingMessage() {
+ mSmsCursor.addRow(new Object[] {
+ /* id= */ 0, /* date= */ 100L, /* type= */ TextBasedSmsColumns.MESSAGE_TYPE_SENT,
+ /* address= */ PHONE_NUMBER });
+
+ assertTrue(mHelper.querySince(50L));
+ List<Event> events = mEventConsumer.mEventMap.get(NORMALIZED_PHONE_NUMBER);
+
+ assertEquals(100L, mHelper.getLastMessageTimestamp());
+ assertEquals(1, events.size());
+ assertEquals(Event.TYPE_SMS_OUTGOING, events.get(0).getType());
+ assertEquals(100L, events.get(0).getTimestamp());
+ }
+
+ @Test
+ public void testQueryMultipleMessages() {
+ mSmsCursor.addRow(new Object[] {
+ /* id= */ 0, /* date= */ 100L, /* type= */ TextBasedSmsColumns.MESSAGE_TYPE_SENT,
+ /* address= */ PHONE_NUMBER });
+ mSmsCursor.addRow(new Object[] {
+ /* id= */ 0, /* date= */ 110L, /* type= */ TextBasedSmsColumns.MESSAGE_TYPE_INBOX,
+ /* address= */ PHONE_NUMBER });
+
+ assertTrue(mHelper.querySince(50L));
+ List<Event> events = mEventConsumer.mEventMap.get(NORMALIZED_PHONE_NUMBER);
+
+ assertEquals(110L, mHelper.getLastMessageTimestamp());
+ assertEquals(2, events.size());
+ assertEquals(Event.TYPE_SMS_OUTGOING, events.get(0).getType());
+ assertEquals(100L, events.get(0).getTimestamp());
+ assertEquals(Event.TYPE_SMS_INCOMING, events.get(1).getType());
+ assertEquals(110L, events.get(1).getTimestamp());
+ }
+
+ private class EventConsumer implements BiConsumer<String, Event> {
+
+ private final Map<String, List<Event>> mEventMap = new ArrayMap<>();
+
+ @Override
+ public void accept(String phoneNumber, Event event) {
+ mEventMap.computeIfAbsent(phoneNumber, key -> new ArrayList<>()).add(event);
+ }
+ }
+
+ private class SmsContentProvider extends MockContentProvider {
+
+ @Override
+ public Cursor query(Uri uri, String[] projection, String selection, String[] selectionArgs,
+ String sortOrder) {
+ return mSmsCursor;
+ }
+ }
+}
diff --git a/services/tests/servicestests/src/com/android/server/people/prediction/ShareTargetPredictorTest.java b/services/tests/servicestests/src/com/android/server/people/prediction/ShareTargetPredictorTest.java
new file mode 100644
index 000000000000..808906e3a06a
--- /dev/null
+++ b/services/tests/servicestests/src/com/android/server/people/prediction/ShareTargetPredictorTest.java
@@ -0,0 +1,137 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.people.prediction;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertNull;
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.anyString;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.when;
+
+import android.app.prediction.AppPredictionContext;
+import android.content.ComponentName;
+import android.content.Context;
+import android.content.Intent;
+import android.content.pm.ShortcutInfo;
+import android.content.pm.ShortcutManager.ShareShortcutInfo;
+import android.os.Bundle;
+import android.os.UserHandle;
+
+import com.android.server.people.data.ConversationInfo;
+import com.android.server.people.data.DataManager;
+import com.android.server.people.data.EventHistory;
+import com.android.server.people.data.PackageData;
+import com.android.server.people.prediction.ShareTargetPredictor.ShareTarget;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.JUnit4;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+
+import java.util.ArrayList;
+import java.util.List;
+
+@RunWith(JUnit4.class)
+public final class ShareTargetPredictorTest {
+
+ private static final String UI_SURFACE_SHARE = "share";
+ private static final int NUM_PREDICTED_TARGETS = 5;
+ private static final int USER_ID = 0;
+ private static final String PACKAGE_1 = "pkg1";
+ private static final String CLASS_1 = "cls1";
+ private static final String PACKAGE_2 = "pkg2";
+ private static final String CLASS_2 = "cls2";
+
+ @Mock private Context mContext;
+ @Mock private DataManager mDataManager;
+ @Mock private PackageData mPackageData1;
+ @Mock private PackageData mPackageData2;
+
+ private List<ShareShortcutInfo> mShareShortcuts = new ArrayList<>();
+
+ private ShareTargetPredictor mPredictor;
+
+ @Before
+ public void setUp() {
+ MockitoAnnotations.initMocks(this);
+
+ when(mDataManager.getShareShortcuts(any())).thenReturn(mShareShortcuts);
+ when(mDataManager.getPackage(PACKAGE_1, USER_ID)).thenReturn(mPackageData1);
+ when(mDataManager.getPackage(PACKAGE_2, USER_ID)).thenReturn(mPackageData2);
+
+ AppPredictionContext predictionContext = new AppPredictionContext.Builder(mContext)
+ .setUiSurface(UI_SURFACE_SHARE)
+ .setPredictedTargetCount(NUM_PREDICTED_TARGETS)
+ .setExtras(new Bundle())
+ .build();
+ mPredictor = new ShareTargetPredictor(predictionContext, targets -> { }, mDataManager);
+ }
+
+ @Test
+ public void testGetShareTargets() {
+ mShareShortcuts.add(buildShareShortcut(PACKAGE_1, CLASS_1, "sc1"));
+ mShareShortcuts.add(buildShareShortcut(PACKAGE_1, CLASS_1, "sc2"));
+ mShareShortcuts.add(buildShareShortcut(PACKAGE_2, CLASS_2, "sc3"));
+ mShareShortcuts.add(buildShareShortcut(PACKAGE_2, CLASS_2, "sc4"));
+
+ when(mPackageData1.getConversationInfo("sc1")).thenReturn(mock(ConversationInfo.class));
+ when(mPackageData1.getConversationInfo("sc2")).thenReturn(mock(ConversationInfo.class));
+ when(mPackageData2.getConversationInfo("sc3")).thenReturn(mock(ConversationInfo.class));
+ // "sc4" does not have a ConversationInfo.
+
+ when(mPackageData1.getEventHistory(anyString())).thenReturn(mock(EventHistory.class));
+ when(mPackageData2.getEventHistory(anyString())).thenReturn(mock(EventHistory.class));
+
+ List<ShareTarget> shareTargets = mPredictor.getShareTargets();
+
+ assertEquals(4, shareTargets.size());
+
+ assertEquals("sc1", shareTargets.get(0).getShareShortcutInfo().getShortcutInfo().getId());
+ assertNotNull(shareTargets.get(0).getConversationData());
+
+ assertEquals("sc2", shareTargets.get(1).getShareShortcutInfo().getShortcutInfo().getId());
+ assertNotNull(shareTargets.get(1).getConversationData());
+
+ assertEquals("sc3", shareTargets.get(2).getShareShortcutInfo().getShortcutInfo().getId());
+ assertNotNull(shareTargets.get(2).getConversationData());
+
+ assertEquals("sc4", shareTargets.get(3).getShareShortcutInfo().getShortcutInfo().getId());
+ assertNull(shareTargets.get(3).getConversationData());
+ }
+
+ private ShareShortcutInfo buildShareShortcut(
+ String packageName, String className, String shortcutId) {
+ ShortcutInfo shortcutInfo = buildShortcut(packageName, shortcutId);
+ ComponentName componentName = new ComponentName(packageName, className);
+ return new ShareShortcutInfo(shortcutInfo, componentName);
+ }
+
+ private ShortcutInfo buildShortcut(String packageName, String shortcutId) {
+ Context mockContext = mock(Context.class);
+ when(mockContext.getPackageName()).thenReturn(packageName);
+ when(mockContext.getUserId()).thenReturn(USER_ID);
+ when(mockContext.getUser()).thenReturn(UserHandle.of(USER_ID));
+ ShortcutInfo.Builder builder = new ShortcutInfo.Builder(mockContext, shortcutId)
+ .setShortLabel(shortcutId)
+ .setIntent(new Intent("TestIntent"));
+ return builder.build();
+ }
+}
diff --git a/services/tests/servicestests/src/com/android/server/rollback/RollbackUnitTest.java b/services/tests/servicestests/src/com/android/server/rollback/RollbackUnitTest.java
index 164c88382828..9719509659ec 100644
--- a/services/tests/servicestests/src/com/android/server/rollback/RollbackUnitTest.java
+++ b/services/tests/servicestests/src/com/android/server/rollback/RollbackUnitTest.java
@@ -291,6 +291,17 @@ public class RollbackUnitTest {
verify(mMockDataHelper).restoreAppData(123, pkgInfo1, 7, 333, "blah");
}
+ @Test
+ public void notifySessionWithSuccess() {
+ int[] sessionIds = new int[]{ 7777, 8888 };
+ Rollback rollback = new Rollback(123, new File("/test/testing"), -1, USER, INSTALLER,
+ sessionIds);
+ // The 1st invocation returns false because not all child sessions are notified.
+ assertThat(rollback.notifySessionWithSuccess()).isFalse();
+ // The 2nd invocation returns true because now all child sessions are notified.
+ assertThat(rollback.notifySessionWithSuccess()).isTrue();
+ }
+
private static PackageRollbackInfo newPkgInfoFor(
String packageName, long fromVersion, long toVersion, boolean isApex) {
return new PackageRollbackInfo(new VersionedPackage(packageName, fromVersion),
diff --git a/services/tests/servicestests/src/com/android/server/rollback/WatchdogRollbackLoggerTest.java b/services/tests/servicestests/src/com/android/server/rollback/WatchdogRollbackLoggerTest.java
index 61117f18445b..ba493d4f9646 100644
--- a/services/tests/servicestests/src/com/android/server/rollback/WatchdogRollbackLoggerTest.java
+++ b/services/tests/servicestests/src/com/android/server/rollback/WatchdogRollbackLoggerTest.java
@@ -60,18 +60,18 @@ public class WatchdogRollbackLoggerTest {
}
/**
- * Ensures that the original package is returned if the application info has no metadata.
+ * Ensures that null is returned if the application info has no metadata.
*/
@Test
public void testLogPackageHasNoMetadata() throws Exception {
when(mMockPm.getApplicationInfo(anyString(), anyInt())).thenReturn(mApplicationInfo);
VersionedPackage logPackage = WatchdogRollbackLogger.getLogPackage(mMockContext,
sTestPackageV1);
- assertThat(logPackage).isEqualTo(sTestPackageV1);
+ assertThat(logPackage).isNull();
}
/**
- * Ensures the original package is returned if the application info does not contain a logging
+ * Ensures that null is returned if the application info does not contain a logging
* parent key.
*/
@Test
@@ -81,7 +81,7 @@ public class WatchdogRollbackLoggerTest {
bundle.putString(LOGGING_PARENT_KEY, null);
VersionedPackage logPackage = WatchdogRollbackLogger.getLogPackage(mMockContext,
sTestPackageV1);
- assertThat(logPackage).isEqualTo(sTestPackageV1);
+ assertThat(logPackage).isNull();
}
/**
@@ -102,8 +102,7 @@ public class WatchdogRollbackLoggerTest {
}
/**
- * Ensures that the original package is returned if Package Manager does not know about the
- * logging parent.
+ * Ensures that null is returned if Package Manager does not know about the logging parent.
*/
@Test
public void testLogPackageNameNotFound() throws Exception {
@@ -114,6 +113,6 @@ public class WatchdogRollbackLoggerTest {
new PackageManager.NameNotFoundException());
VersionedPackage logPackage = WatchdogRollbackLogger.getLogPackage(mMockContext,
sTestPackageV1);
- assertThat(logPackage).isEqualTo(sTestPackageV1);
+ assertThat(logPackage).isNull();
}
}
diff --git a/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java b/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java
index 2cf5eaefcc58..34872db5cf7e 100755
--- a/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java
+++ b/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java
@@ -159,6 +159,7 @@ import com.android.server.lights.LightsManager;
import com.android.server.lights.LogicalLight;
import com.android.server.notification.NotificationManagerService.NotificationAssistants;
import com.android.server.notification.NotificationManagerService.NotificationListeners;
+import com.android.server.statusbar.StatusBarManagerInternal;
import com.android.server.uri.UriGrantsManagerInternal;
import com.android.server.wm.WindowManagerInternal;
@@ -261,6 +262,8 @@ public class NotificationManagerServiceTest extends UiServiceTestCase {
NotificationRecordLoggerFake mNotificationRecordLogger = new NotificationRecordLoggerFake();
private InstanceIdSequence mNotificationInstanceIdSequence = new InstanceIdSequenceFake(
1 << 30);
+ @Mock
+ StatusBarManagerInternal mStatusBar;
// Use a Testable subclass so we can simulate calls from the system without failing.
private static class TestableNotificationManagerService extends NotificationManagerService {
@@ -356,6 +359,8 @@ public class NotificationManagerServiceTest extends UiServiceTestCase {
LocalServices.addService(UriGrantsManagerInternal.class, mUgmInternal);
LocalServices.removeServiceForTest(WindowManagerInternal.class);
LocalServices.addService(WindowManagerInternal.class, mWindowManagerInternal);
+ LocalServices.removeServiceForTest(StatusBarManagerInternal.class);
+ LocalServices.addService(StatusBarManagerInternal.class, mStatusBar);
doNothing().when(mContext).sendBroadcastAsUser(any(), any(), any());
@@ -4483,7 +4488,7 @@ public class NotificationManagerServiceTest extends UiServiceTestCase {
}
@Test
- public void testAllowForegroundToasts() throws Exception {
+ public void testAllowForegroundCustomToasts() throws Exception {
final String testPackage = "testPackageName";
assertEquals(0, mService.mToastQueue.size());
mService.isSystemUid = false;
@@ -4506,6 +4511,79 @@ public class NotificationManagerServiceTest extends UiServiceTestCase {
}
@Test
+ public void testDisallowBackgroundCustomToasts() throws Exception {
+ final String testPackage = "testPackageName";
+ assertEquals(0, mService.mToastQueue.size());
+ mService.isSystemUid = false;
+
+ // package is not suspended
+ when(mPackageManager.isPackageSuspendedForUser(testPackage, UserHandle.getUserId(mUid)))
+ .thenReturn(false);
+
+ // this app is NOT in the foreground
+ when(mActivityManager.getUidImportance(mUid)).thenReturn(IMPORTANCE_NONE);
+
+ // enqueue toast -> no toasts enqueued
+ ((INotificationManager) mService.mService).enqueueToast(testPackage, new Binder(),
+ new TestableToastCallback(), 2000, 0);
+ assertEquals(0, mService.mToastQueue.size());
+ }
+
+ @Test
+ public void testAllowForegroundTextToasts() throws Exception {
+ final String testPackage = "testPackageName";
+ assertEquals(0, mService.mToastQueue.size());
+ mService.isSystemUid = false;
+
+ // package is not suspended
+ when(mPackageManager.isPackageSuspendedForUser(testPackage, UserHandle.getUserId(mUid)))
+ .thenReturn(false);
+
+ // this app is in the foreground
+ when(mActivityManager.getUidImportance(mUid)).thenReturn(IMPORTANCE_FOREGROUND);
+
+ // enqueue toast -> toast should still enqueue
+ ((INotificationManager) mService.mService).enqueueTextToast(testPackage, new Binder(),
+ "Text", 2000, 0, null);
+ assertEquals(1, mService.mToastQueue.size());
+ }
+
+ @Test
+ public void testAllowBackgroundTextToasts() throws Exception {
+ final String testPackage = "testPackageName";
+ assertEquals(0, mService.mToastQueue.size());
+ mService.isSystemUid = false;
+
+ // package is not suspended
+ when(mPackageManager.isPackageSuspendedForUser(testPackage, UserHandle.getUserId(mUid)))
+ .thenReturn(false);
+
+ // this app is NOT in the foreground
+ when(mActivityManager.getUidImportance(mUid)).thenReturn(IMPORTANCE_NONE);
+
+ // enqueue toast -> toast should still enqueue
+ ((INotificationManager) mService.mService).enqueueTextToast(testPackage, new Binder(),
+ "Text", 2000, 0, null);
+ assertEquals(1, mService.mToastQueue.size());
+ }
+
+ @Test
+ public void testTextToastsCallStatusBar() throws Exception {
+ final String testPackage = "testPackageName";
+ assertEquals(0, mService.mToastQueue.size());
+ mService.isSystemUid = false;
+
+ // package is not suspended
+ when(mPackageManager.isPackageSuspendedForUser(testPackage, UserHandle.getUserId(mUid)))
+ .thenReturn(false);
+
+ // enqueue toast -> no toasts enqueued
+ ((INotificationManager) mService.mService).enqueueTextToast(testPackage, new Binder(),
+ "Text", 2000, 0, null);
+ verify(mStatusBar).showToast(any(), any(), any(), any(), anyInt(), any());
+ }
+
+ @Test
public void testDisallowToastsFromSuspendedPackages() throws Exception {
final String testPackage = "testPackageName";
assertEquals(0, mService.mToastQueue.size());
diff --git a/services/tests/wmtests/src/com/android/server/wm/utils/WmDisplayCutoutTest.java b/services/tests/wmtests/src/com/android/server/wm/utils/WmDisplayCutoutTest.java
index fb8ba7bffd4c..a283476bfdf0 100644
--- a/services/tests/wmtests/src/com/android/server/wm/utils/WmDisplayCutoutTest.java
+++ b/services/tests/wmtests/src/com/android/server/wm/utils/WmDisplayCutoutTest.java
@@ -47,6 +47,7 @@ import org.junit.Test;
@SmallTest
@Presubmit
public class WmDisplayCutoutTest {
+ private static final Rect ZERO_RECT = new Rect();
private final DisplayCutout mCutoutTop = new DisplayCutout(
Insets.of(0, 100, 0, 0),
null /* boundLeft */, new Rect(50, 0, 75, 100) /* boundTop */,
@@ -99,41 +100,204 @@ public class WmDisplayCutoutTest {
}
@Test
- public void computeSafeInsets_top() {
+ public void computeSafeInsets_cutoutTop() {
WmDisplayCutout cutout = WmDisplayCutout.computeSafeInsets(
- fromBoundingRect(0, 0, 100, 20, BOUNDS_POSITION_TOP), 200, 400);
+ fromBoundingRect(80, 0, 120, 20, BOUNDS_POSITION_TOP), 200, 400);
assertEquals(new Rect(0, 20, 0, 0), cutout.getDisplayCutout().getSafeInsets());
}
@Test
- public void computeSafeInsets_left() {
+ public void computeSafeInsets_cutoutLeft() {
WmDisplayCutout cutout = WmDisplayCutout.computeSafeInsets(
- fromBoundingRect(0, 0, 20, 100, BOUNDS_POSITION_LEFT), 400, 200);
+ fromBoundingRect(0, 180, 20, 220, BOUNDS_POSITION_LEFT), 200, 400);
assertEquals(new Rect(20, 0, 0, 0), cutout.getDisplayCutout().getSafeInsets());
}
@Test
- public void computeSafeInsets_bottom() {
+ public void computeSafeInsets_cutoutBottom() {
WmDisplayCutout cutout = WmDisplayCutout.computeSafeInsets(
- fromBoundingRect(0, 180, 100, 200, BOUNDS_POSITION_BOTTOM), 100, 200);
+ fromBoundingRect(80, 380, 120, 400, BOUNDS_POSITION_BOTTOM), 200, 400);
assertEquals(new Rect(0, 0, 0, 20), cutout.getDisplayCutout().getSafeInsets());
}
@Test
- public void computeSafeInsets_right() {
+ public void computeSafeInsets_cutoutRight() {
WmDisplayCutout cutout = WmDisplayCutout.computeSafeInsets(
- fromBoundingRect(180, 0, 200, 100, BOUNDS_POSITION_RIGHT), 200, 100);
+ fromBoundingRect(180, 180, 200, 220, BOUNDS_POSITION_RIGHT), 200, 400);
assertEquals(new Rect(0, 0, 20, 0), cutout.getDisplayCutout().getSafeInsets());
}
@Test
+ public void computeSafeInsets_topLeftCornerCutout_portrait() {
+ WmDisplayCutout cutout = WmDisplayCutout.computeSafeInsets(
+ fromBoundingRect(0, 0, 20, 20, BOUNDS_POSITION_TOP), 200, 400);
+
+ assertEquals(new Rect(0, 20, 0, 0), cutout.getDisplayCutout().getSafeInsets());
+ }
+
+ @Test
+ public void computeSafeInsets_topRightCornerCutout_portrait() {
+ WmDisplayCutout cutout = WmDisplayCutout.computeSafeInsets(
+ fromBoundingRect(180, 0, 200, 20, BOUNDS_POSITION_TOP), 200, 400);
+
+ assertEquals(new Rect(0, 20, 0, 0), cutout.getDisplayCutout().getSafeInsets());
+ }
+
+ @Test
+ public void computeSafeInsets_bottomLeftCornerCutout_portrait() {
+ WmDisplayCutout cutout = WmDisplayCutout.computeSafeInsets(
+ fromBoundingRect(0, 380, 20, 400, BOUNDS_POSITION_BOTTOM), 200, 400);
+
+ assertEquals(new Rect(0, 0, 0, 20), cutout.getDisplayCutout().getSafeInsets());
+ }
+
+ @Test
+ public void computeSafeInsets_bottomRightCornerCutout_portrait() {
+ WmDisplayCutout cutout = WmDisplayCutout.computeSafeInsets(
+ fromBoundingRect(180, 380, 200, 400, BOUNDS_POSITION_BOTTOM), 200, 400);
+
+ assertEquals(new Rect(0, 0, 0, 20), cutout.getDisplayCutout().getSafeInsets());
+ }
+
+ @Test
+ public void computeSafeInsets_topLeftCornerCutout_landscape() {
+ WmDisplayCutout cutout = WmDisplayCutout.computeSafeInsets(
+ fromBoundingRect(0, 0, 20, 20, BOUNDS_POSITION_LEFT), 400, 200);
+
+ assertEquals(new Rect(20, 0, 0, 0), cutout.getDisplayCutout().getSafeInsets());
+ }
+
+ @Test
+ public void computeSafeInsets_topRightCornerCutout_landscape() {
+ WmDisplayCutout cutout = WmDisplayCutout.computeSafeInsets(
+ fromBoundingRect(380, 0, 400, 20, BOUNDS_POSITION_RIGHT), 400, 200);
+
+ assertEquals(new Rect(0, 0, 20, 0), cutout.getDisplayCutout().getSafeInsets());
+ }
+
+ @Test
+ public void computeSafeInsets_bottomLeftCornerCutout_landscape() {
+ WmDisplayCutout cutout = WmDisplayCutout.computeSafeInsets(
+ fromBoundingRect(0, 180, 20, 200, BOUNDS_POSITION_LEFT), 400, 200);
+
+ assertEquals(new Rect(20, 0, 0, 0), cutout.getDisplayCutout().getSafeInsets());
+ }
+
+ @Test
+ public void computeSafeInsets_bottomRightCornerCutout_landscape() {
+ WmDisplayCutout cutout = WmDisplayCutout.computeSafeInsets(
+ fromBoundingRect(380, 180, 400, 200, BOUNDS_POSITION_RIGHT), 400, 200);
+
+ assertEquals(new Rect(0, 0, 20, 0), cutout.getDisplayCutout().getSafeInsets());
+ }
+
+ @Test
+ public void computeSafeInsets_waterfall() {
+ WmDisplayCutout cutout = WmDisplayCutout.computeSafeInsets(
+ DisplayCutout.fromBoundsAndWaterfall(
+ new Rect[] {ZERO_RECT, ZERO_RECT, ZERO_RECT, ZERO_RECT},
+ Insets.of(1, 2, 3, 4)),
+ 200, 400);
+
+ assertEquals(new Rect(1, 2, 3, 4), cutout.getDisplayCutout().getSafeInsets());
+ }
+
+ @Test
+ public void computeSafeInsets_cutoutTop_greaterThan_waterfallTop() {
+ WmDisplayCutout cutout = WmDisplayCutout.computeSafeInsets(
+ DisplayCutout.fromBoundsAndWaterfall(
+ new Rect[] {ZERO_RECT, new Rect(80, 0, 120, 30), ZERO_RECT, ZERO_RECT},
+ Insets.of(0, 20, 0, 0)),
+ 200, 400);
+
+ assertEquals(new Rect(0, 30, 0, 0), cutout.getDisplayCutout().getSafeInsets());
+ }
+
+ @Test
+ public void computeSafeInsets_cutoutTop_lessThan_waterfallTop() {
+ WmDisplayCutout cutout = WmDisplayCutout.computeSafeInsets(
+ DisplayCutout.fromBoundsAndWaterfall(
+ new Rect[] {ZERO_RECT, new Rect(80, 0, 120, 30), ZERO_RECT, ZERO_RECT},
+ Insets.of(0, 40, 0, 0)),
+ 200, 400);
+
+ assertEquals(new Rect(0, 40, 0, 0), cutout.getDisplayCutout().getSafeInsets());
+ }
+
+ @Test
+ public void computeSafeInsets_cutoutLeft_greaterThan_waterfallLeft() {
+ WmDisplayCutout cutout = WmDisplayCutout.computeSafeInsets(
+ DisplayCutout.fromBoundsAndWaterfall(
+ new Rect[] {new Rect(0, 180, 30, 220), ZERO_RECT, ZERO_RECT, ZERO_RECT},
+ Insets.of(20, 0, 0, 0)),
+ 200, 400);
+
+ assertEquals(new Rect(30, 0, 0, 0), cutout.getDisplayCutout().getSafeInsets());
+ }
+
+ @Test
+ public void computeSafeInsets_cutoutLeft_lessThan_waterfallLeft() {
+ WmDisplayCutout cutout = WmDisplayCutout.computeSafeInsets(
+ DisplayCutout.fromBoundsAndWaterfall(
+ new Rect[] {new Rect(0, 180, 30, 220), ZERO_RECT, ZERO_RECT, ZERO_RECT},
+ Insets.of(40, 0, 0, 0)),
+ 200, 400);
+
+ assertEquals(new Rect(40, 0, 0, 0), cutout.getDisplayCutout().getSafeInsets());
+ }
+
+ @Test
+ public void computeSafeInsets_cutoutBottom_greaterThan_waterfallBottom() {
+ WmDisplayCutout cutout = WmDisplayCutout.computeSafeInsets(
+ DisplayCutout.fromBoundsAndWaterfall(
+ new Rect[] {ZERO_RECT, ZERO_RECT, ZERO_RECT, new Rect(80, 370, 120, 400)},
+ Insets.of(0, 0, 0, 20)),
+ 200, 400);
+
+ assertEquals(new Rect(0, 0, 0, 30), cutout.getDisplayCutout().getSafeInsets());
+ }
+
+ @Test
+ public void computeSafeInsets_cutoutBottom_lessThan_waterfallBottom() {
+ WmDisplayCutout cutout = WmDisplayCutout.computeSafeInsets(
+ DisplayCutout.fromBoundsAndWaterfall(
+ new Rect[] {ZERO_RECT, ZERO_RECT, ZERO_RECT, new Rect(80, 370, 120, 400)},
+ Insets.of(0, 0, 0, 40)),
+ 200, 400);
+
+ assertEquals(new Rect(0, 0, 0, 40), cutout.getDisplayCutout().getSafeInsets());
+ }
+
+ @Test
+ public void computeSafeInsets_cutoutRight_greaterThan_waterfallRight() {
+ WmDisplayCutout cutout = WmDisplayCutout.computeSafeInsets(
+ DisplayCutout.fromBoundsAndWaterfall(
+ new Rect[] {ZERO_RECT, ZERO_RECT, new Rect(170, 180, 200, 220), ZERO_RECT},
+ Insets.of(0, 0, 20, 0)),
+ 200, 400);
+
+ assertEquals(new Rect(0, 0, 30, 0), cutout.getDisplayCutout().getSafeInsets());
+ }
+
+ @Test
+ public void computeSafeInsets_cutoutRight_lessThan_waterfallRight() {
+ WmDisplayCutout cutout = WmDisplayCutout.computeSafeInsets(
+ DisplayCutout.fromBoundsAndWaterfall(
+ new Rect[] {ZERO_RECT, ZERO_RECT, new Rect(170, 180, 200, 220), ZERO_RECT},
+ Insets.of(0, 0, 40, 0)),
+ 200, 400);
+
+ assertEquals(new Rect(0, 0, 40, 0), cutout.getDisplayCutout().getSafeInsets());
+ }
+
+ @Test
public void computeSafeInsets_bounds() {
- DisplayCutout cutout = WmDisplayCutout.computeSafeInsets(mCutoutTop, 1000,
- 2000).getDisplayCutout();
+ DisplayCutout cutout =
+ WmDisplayCutout.computeSafeInsets(mCutoutTop, 1000, 2000).getDisplayCutout();
assertEquals(mCutoutTop.getBoundingRects(), cutout.getBoundingRects());
}
diff --git a/telephony/java/android/telephony/BarringInfo.java b/telephony/java/android/telephony/BarringInfo.java
index 9baa66fcd87e..92423a2f2218 100644
--- a/telephony/java/android/telephony/BarringInfo.java
+++ b/telephony/java/android/telephony/BarringInfo.java
@@ -62,38 +62,38 @@ public final class BarringInfo implements Parcelable {
/* Applicabe to UTRAN */
/** Barring indicator for circuit-switched service; applicable to UTRAN */
public static final int BARRING_SERVICE_TYPE_CS_SERVICE =
- android.hardware.radio.V1_5.BarringServiceType.CS_SERVICE;
+ android.hardware.radio.V1_5.BarringInfo.ServiceType.CS_SERVICE;
/** Barring indicator for packet-switched service; applicable to UTRAN */
public static final int BARRING_SERVICE_TYPE_PS_SERVICE =
- android.hardware.radio.V1_5.BarringServiceType.PS_SERVICE;
+ android.hardware.radio.V1_5.BarringInfo.ServiceType.PS_SERVICE;
/** Barring indicator for circuit-switched voice service; applicable to UTRAN */
public static final int BARRING_SERVICE_TYPE_CS_VOICE =
- android.hardware.radio.V1_5.BarringServiceType.CS_VOICE;
+ android.hardware.radio.V1_5.BarringInfo.ServiceType.CS_VOICE;
/* Applicable to EUTRAN, NGRAN */
/** Barring indicator for mobile-originated signalling; applicable to EUTRAN and NGRAN */
public static final int BARRING_SERVICE_TYPE_MO_SIGNALLING =
- android.hardware.radio.V1_5.BarringServiceType.MO_SIGNALLING;
+ android.hardware.radio.V1_5.BarringInfo.ServiceType.MO_SIGNALLING;
/** Barring indicator for mobile-originated data traffic; applicable to EUTRAN and NGRAN */
public static final int BARRING_SERVICE_TYPE_MO_DATA =
- android.hardware.radio.V1_5.BarringServiceType.MO_DATA;
+ android.hardware.radio.V1_5.BarringInfo.ServiceType.MO_DATA;
/** Barring indicator for circuit-switched fallback for voice; applicable to EUTRAN and NGRAN */
public static final int BARRING_SERVICE_TYPE_CS_FALLBACK =
- android.hardware.radio.V1_5.BarringServiceType.CS_FALLBACK;
+ android.hardware.radio.V1_5.BarringInfo.ServiceType.CS_FALLBACK;
/** Barring indicator for MMTEL (IMS) voice; applicable to EUTRAN and NGRAN */
public static final int BARRING_SERVICE_TYPE_MMTEL_VOICE =
- android.hardware.radio.V1_5.BarringServiceType.MMTEL_VOICE;
+ android.hardware.radio.V1_5.BarringInfo.ServiceType.MMTEL_VOICE;
/** Barring indicator for MMTEL (IMS) video; applicable to EUTRAN and NGRAN */
public static final int BARRING_SERVICE_TYPE_MMTEL_VIDEO =
- android.hardware.radio.V1_5.BarringServiceType.MMTEL_VIDEO;
+ android.hardware.radio.V1_5.BarringInfo.ServiceType.MMTEL_VIDEO;
/* Applicable to UTRAN, EUTRAN, NGRAN */
/** Barring indicator for emergency services; applicable to UTRAN, EUTRAN, and NGRAN */
public static final int BARRING_SERVICE_TYPE_EMERGENCY =
- android.hardware.radio.V1_5.BarringServiceType.EMERGENCY;
+ android.hardware.radio.V1_5.BarringInfo.ServiceType.EMERGENCY;
/** Barring indicator for SMS sending; applicable to UTRAN, EUTRAN, and NGRAN */
public static final int BARRING_SERVICE_TYPE_SMS =
- android.hardware.radio.V1_5.BarringServiceType.SMS;
+ android.hardware.radio.V1_5.BarringInfo.ServiceType.SMS;
//TODO: add barring constants for Operator-Specific barring codes
@@ -112,13 +112,14 @@ public final class BarringInfo implements Parcelable {
public @interface BarringType {}
/** Barring is inactive */
- public static final int BARRING_TYPE_NONE = android.hardware.radio.V1_5.BarringType.NONE;
+ public static final int BARRING_TYPE_NONE =
+ android.hardware.radio.V1_5.BarringInfo.BarringType.NONE;
/** The service is barred */
public static final int BARRING_TYPE_UNCONDITIONAL =
- android.hardware.radio.V1_5.BarringType.UNCONDITIONAL;
+ android.hardware.radio.V1_5.BarringInfo.BarringType.UNCONDITIONAL;
/** The service may be barred based on additional factors */
public static final int BARRING_TYPE_CONDITIONAL =
- android.hardware.radio.V1_5.BarringType.CONDITIONAL;
+ android.hardware.radio.V1_5.BarringInfo.BarringType.CONDITIONAL;
/** If a modem does not report barring info, then the barring type will be UNKNOWN */
public static final int BARRING_TYPE_UNKNOWN = -1;
@@ -277,27 +278,29 @@ public final class BarringInfo implements Parcelable {
SparseArray<BarringServiceInfo> serviceInfos = new SparseArray<>();
for (android.hardware.radio.V1_5.BarringInfo halBarringInfo : halBarringInfos) {
- if (halBarringInfo.type == android.hardware.radio.V1_5.BarringType.CONDITIONAL) {
- if (halBarringInfo.typeSpecificInfo.getDiscriminator()
- != android.hardware.radio.V1_5.BarringTypeSpecificInfo
- .hidl_discriminator.conditionalBarringInfo) {
+ if (halBarringInfo.barringType
+ == android.hardware.radio.V1_5.BarringInfo.BarringType.CONDITIONAL) {
+ if (halBarringInfo.barringTypeSpecificInfo.getDiscriminator()
+ != android.hardware.radio.V1_5.BarringInfo.BarringTypeSpecificInfo
+ .hidl_discriminator.conditional) {
// this is an error case where the barring info is conditional but the
// conditional barring fields weren't included
continue;
}
- android.hardware.radio.V1_5.ConditionalBarringInfo conditionalInfo =
- halBarringInfo.typeSpecificInfo.conditionalBarringInfo();
+ android.hardware.radio.V1_5.BarringInfo.BarringTypeSpecificInfo
+ .Conditional conditionalInfo =
+ halBarringInfo.barringTypeSpecificInfo.conditional();
serviceInfos.put(
- halBarringInfo.service, new BarringServiceInfo(
- halBarringInfo.type, // will always be CONDITIONAL here
+ halBarringInfo.serviceType, new BarringServiceInfo(
+ halBarringInfo.barringType, // will always be CONDITIONAL here
conditionalInfo.isBarred,
- conditionalInfo.barringFactor,
- conditionalInfo.barringTimeSeconds));
+ conditionalInfo.factor,
+ conditionalInfo.timeSeconds));
} else {
// Barring type is either NONE or UNCONDITIONAL
serviceInfos.put(
- halBarringInfo.service, new BarringServiceInfo(halBarringInfo.type,
- false, 0, 0));
+ halBarringInfo.serviceType, new BarringServiceInfo(
+ halBarringInfo.barringType, false, 0, 0));
}
}
return new BarringInfo(ci, serviceInfos);
@@ -321,6 +324,9 @@ public final class BarringInfo implements Parcelable {
/** @hide */
@SystemApi
public @NonNull BarringInfo createLocationInfoSanitizedCopy() {
+ // The only thing that would need sanitizing is the CellIdentity
+ if (mCellIdentity == null) return this;
+
return new BarringInfo(mCellIdentity.sanitizeLocationInfo(), mBarringServiceInfos);
}
diff --git a/telephony/java/android/telephony/PreciseDataConnectionState.java b/telephony/java/android/telephony/PreciseDataConnectionState.java
index 54c22ae282fb..708adebb6db3 100644
--- a/telephony/java/android/telephony/PreciseDataConnectionState.java
+++ b/telephony/java/android/telephony/PreciseDataConnectionState.java
@@ -265,10 +265,10 @@ public final class PreciseDataConnectionState implements Parcelable {
/**
* Return the APN Settings for this data connection.
*
- * Returns the ApnSetting that was used to configure this data connection.
+ * @return the ApnSetting that was used to configure this data connection.
*/
// FIXME: This shouldn't be nullable; update once the ApnSetting is supplied correctly
- @Nullable ApnSetting getApnSetting() {
+ public @Nullable ApnSetting getApnSetting() {
return mApnSetting;
}
diff --git a/telephony/java/android/telephony/SmsCbMessage.java b/telephony/java/android/telephony/SmsCbMessage.java
index c0dfec9a1172..752707e5a5dc 100644
--- a/telephony/java/android/telephony/SmsCbMessage.java
+++ b/telephony/java/android/telephony/SmsCbMessage.java
@@ -586,6 +586,7 @@ public final class SmsCbMessage implements Parcelable {
cv.put(CellBroadcasts.SERIAL_NUMBER, getSerialNumber());
cv.put(CellBroadcasts.SERVICE_CATEGORY, getServiceCategory());
cv.put(CellBroadcasts.LANGUAGE_CODE, getLanguageCode());
+ cv.put(CellBroadcasts.DATA_CODING_SCHEME, getDataCodingScheme());
cv.put(CellBroadcasts.MESSAGE_BODY, getMessageBody());
cv.put(CellBroadcasts.MESSAGE_FORMAT, getMessageFormat());
cv.put(CellBroadcasts.MESSAGE_PRIORITY, getMessagePriority());
diff --git a/telephony/java/android/telephony/SubscriptionManager.java b/telephony/java/android/telephony/SubscriptionManager.java
index 7406c8d5a33c..97f50fd314b8 100644
--- a/telephony/java/android/telephony/SubscriptionManager.java
+++ b/telephony/java/android/telephony/SubscriptionManager.java
@@ -55,7 +55,6 @@ import android.os.RemoteException;
import android.provider.Telephony.SimInfo;
import android.telephony.euicc.EuiccManager;
import android.telephony.ims.ImsMmTelManager;
-import android.util.DisplayMetrics;
import android.util.Log;
import android.util.Pair;
@@ -2500,23 +2499,24 @@ public class SubscriptionManager {
final SubscriptionInfo subInfo =
SubscriptionManager.from(context).getActiveSubscriptionInfo(subId);
- Configuration config = context.getResources().getConfiguration();
- Configuration newConfig = new Configuration();
- newConfig.setTo(config);
+ Configuration overrideConfig = new Configuration();
if (subInfo != null) {
- newConfig.mcc = subInfo.getMcc();
- newConfig.mnc = subInfo.getMnc();
- if (newConfig.mnc == 0) newConfig.mnc = Configuration.MNC_ZERO;
+ overrideConfig.mcc = subInfo.getMcc();
+ overrideConfig.mnc = subInfo.getMnc();
+ if (overrideConfig.mnc == 0) overrideConfig.mnc = Configuration.MNC_ZERO;
}
if (useRootLocale) {
- newConfig.setLocale(Locale.ROOT);
+ overrideConfig.setLocale(Locale.ROOT);
}
- DisplayMetrics metrics = context.getResources().getDisplayMetrics();
- DisplayMetrics newMetrics = new DisplayMetrics();
- newMetrics.setTo(metrics);
- Resources res = new Resources(context.getResources().getAssets(), newMetrics, newConfig);
+ // Create new context with new configuration so that we can avoid modifying the passed in
+ // context.
+ // Note that if the original context configuration changes, the resources here will also
+ // change for all values except those overridden by newConfig (e.g. if the device has an
+ // orientation change).
+ Context newContext = context.createConfigurationContext(overrideConfig);
+ Resources res = newContext.getResources();
if (cacheKey != null) {
// Save the newly created Resources in the resource cache.
diff --git a/tests/RollbackTest/StagedRollbackTest/src/com/android/tests/rollback/host/StagedRollbackTest.java b/tests/RollbackTest/StagedRollbackTest/src/com/android/tests/rollback/host/StagedRollbackTest.java
index ce2f7c50e581..82a524be5c1e 100644
--- a/tests/RollbackTest/StagedRollbackTest/src/com/android/tests/rollback/host/StagedRollbackTest.java
+++ b/tests/RollbackTest/StagedRollbackTest/src/com/android/tests/rollback/host/StagedRollbackTest.java
@@ -329,6 +329,46 @@ public class StagedRollbackTest extends BaseHostJUnit4Test {
}
/**
+ * Tests that data in DE (user) apex data directory is restored when apex is rolled back.
+ */
+ @Test
+ public void testRollbackApexDataDirectories_DeUser() throws Exception {
+ pushTestApex();
+
+ // Push files to apex data directory
+ String oldFilePath1 = apexDataDirDeUser(
+ APK_IN_APEX_TESTAPEX_NAME, 0) + "/" + TEST_FILENAME_1;
+ String oldFilePath2 =
+ apexDataDirDeUser(APK_IN_APEX_TESTAPEX_NAME, 0) + TEST_SUBDIR + TEST_FILENAME_2;
+ assertTrue(getDevice().pushString(TEST_STRING_1, oldFilePath1));
+ assertTrue(getDevice().pushString(TEST_STRING_2, oldFilePath2));
+
+ // Install new version of the APEX with rollback enabled
+ runPhase("testRollbackApexDataDirectories_Phase1");
+ getDevice().reboot();
+
+ // Replace files in data directory
+ getDevice().deleteFile(oldFilePath1);
+ getDevice().deleteFile(oldFilePath2);
+ String newFilePath3 =
+ apexDataDirDeUser(APK_IN_APEX_TESTAPEX_NAME, 0) + "/" + TEST_FILENAME_3;
+ String newFilePath4 =
+ apexDataDirDeUser(APK_IN_APEX_TESTAPEX_NAME, 0) + TEST_SUBDIR + TEST_FILENAME_4;
+ assertTrue(getDevice().pushString(TEST_STRING_3, newFilePath3));
+ assertTrue(getDevice().pushString(TEST_STRING_4, newFilePath4));
+
+ // Roll back the APEX
+ runPhase("testRollbackApexDataDirectories_Phase2");
+ getDevice().reboot();
+
+ // Verify that old files have been restored and new files are gone
+ assertEquals(TEST_STRING_1, getDevice().pullFileContents(oldFilePath1));
+ assertEquals(TEST_STRING_2, getDevice().pullFileContents(oldFilePath2));
+ assertNull(getDevice().pullFile(newFilePath3));
+ assertNull(getDevice().pullFile(newFilePath4));
+ }
+
+ /**
* Tests that data in CE apex data directory is restored when apex is rolled back.
*/
@Test
@@ -382,6 +422,10 @@ public class StagedRollbackTest extends BaseHostJUnit4Test {
return String.format("/data/misc/apexdata/%s", apexName);
}
+ private static String apexDataDirDeUser(String apexName, int userId) {
+ return String.format("/data/misc_de/%d/apexdata/%s", userId, apexName);
+ }
+
private static String apexDataDirCe(String apexName, int userId) {
return String.format("/data/misc_ce/%d/apexdata/%s", userId, apexName);
}
diff --git a/tests/net/java/android/net/VpnManagerTest.java b/tests/net/java/android/net/VpnManagerTest.java
index 655c4d118592..95a794235a2e 100644
--- a/tests/net/java/android/net/VpnManagerTest.java
+++ b/tests/net/java/android/net/VpnManagerTest.java
@@ -16,13 +16,24 @@
package android.net;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertNull;
+import static org.mockito.Matchers.any;
+import static org.mockito.Matchers.eq;
import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+import android.content.ComponentName;
+import android.content.Intent;
import android.test.mock.MockContext;
import androidx.test.filters.SmallTest;
import androidx.test.runner.AndroidJUnit4;
+import com.android.internal.net.VpnProfile;
+
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
@@ -31,7 +42,12 @@ import org.junit.runner.RunWith;
@SmallTest
@RunWith(AndroidJUnit4.class)
public class VpnManagerTest {
- private static final String VPN_PROFILE_KEY = "KEY";
+ private static final String PKG_NAME = "fooPackage";
+
+ private static final String SESSION_NAME_STRING = "testSession";
+ private static final String SERVER_ADDR_STRING = "1.2.3.4";
+ private static final String IDENTITY_STRING = "Identity";
+ private static final byte[] PSK_BYTES = "preSharedKey".getBytes();
private IConnectivityManager mMockCs;
private VpnManager mVpnManager;
@@ -39,7 +55,7 @@ public class VpnManagerTest {
new MockContext() {
@Override
public String getOpPackageName() {
- return "fooPackage";
+ return PKG_NAME;
}
};
@@ -50,34 +66,55 @@ public class VpnManagerTest {
}
@Test
- public void testProvisionVpnProfile() throws Exception {
- try {
- mVpnManager.provisionVpnProfile(mock(PlatformVpnProfile.class));
- } catch (UnsupportedOperationException expected) {
- }
+ public void testProvisionVpnProfilePreconsented() throws Exception {
+ final PlatformVpnProfile profile = getPlatformVpnProfile();
+ when(mMockCs.provisionVpnProfile(any(VpnProfile.class), eq(PKG_NAME))).thenReturn(true);
+
+ // Expect there to be no intent returned, as consent has already been granted.
+ assertNull(mVpnManager.provisionVpnProfile(profile));
+ verify(mMockCs).provisionVpnProfile(eq(profile.toVpnProfile()), eq(PKG_NAME));
+ }
+
+ @Test
+ public void testProvisionVpnProfileNeedsConsent() throws Exception {
+ final PlatformVpnProfile profile = getPlatformVpnProfile();
+ when(mMockCs.provisionVpnProfile(any(VpnProfile.class), eq(PKG_NAME))).thenReturn(false);
+
+ // Expect intent to be returned, as consent has not already been granted.
+ final Intent intent = mVpnManager.provisionVpnProfile(profile);
+ assertNotNull(intent);
+
+ final ComponentName expectedComponentName =
+ ComponentName.unflattenFromString(
+ "com.android.vpndialogs/com.android.vpndialogs.PlatformVpnConfirmDialog");
+ assertEquals(expectedComponentName, intent.getComponent());
+ verify(mMockCs).provisionVpnProfile(eq(profile.toVpnProfile()), eq(PKG_NAME));
}
@Test
public void testDeleteProvisionedVpnProfile() throws Exception {
- try {
- mVpnManager.deleteProvisionedVpnProfile();
- } catch (UnsupportedOperationException expected) {
- }
+ mVpnManager.deleteProvisionedVpnProfile();
+ verify(mMockCs).deleteVpnProfile(eq(PKG_NAME));
}
@Test
public void testStartProvisionedVpnProfile() throws Exception {
- try {
- mVpnManager.startProvisionedVpnProfile();
- } catch (UnsupportedOperationException expected) {
- }
+ mVpnManager.startProvisionedVpnProfile();
+ verify(mMockCs).startVpnProfile(eq(PKG_NAME));
}
@Test
public void testStopProvisionedVpnProfile() throws Exception {
- try {
- mVpnManager.stopProvisionedVpnProfile();
- } catch (UnsupportedOperationException expected) {
- }
+ mVpnManager.stopProvisionedVpnProfile();
+ verify(mMockCs).stopVpnProfile(eq(PKG_NAME));
+ }
+
+ private Ikev2VpnProfile getPlatformVpnProfile() throws Exception {
+ return new Ikev2VpnProfile.Builder(SERVER_ADDR_STRING, IDENTITY_STRING)
+ .setBypassable(true)
+ .setMaxMtu(1300)
+ .setMetered(true)
+ .setAuthPsk(PSK_BYTES)
+ .build();
}
}
diff --git a/tests/net/java/com/android/server/ConnectivityServiceTest.java b/tests/net/java/com/android/server/ConnectivityServiceTest.java
index 5592cd7c2f9f..028c1dad82e4 100644
--- a/tests/net/java/com/android/server/ConnectivityServiceTest.java
+++ b/tests/net/java/com/android/server/ConnectivityServiceTest.java
@@ -5741,6 +5741,40 @@ public class ConnectivityServiceTest {
mCm.unregisterNetworkCallback(defaultCallback);
}
+ @Test
+ public final void testLoseTrusted() throws Exception {
+ final NetworkRequest trustedRequest = new NetworkRequest.Builder()
+ .addCapability(NET_CAPABILITY_TRUSTED)
+ .build();
+ final TestNetworkCallback trustedCallback = new TestNetworkCallback();
+ mCm.requestNetwork(trustedRequest, trustedCallback);
+
+ mCellNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_CELLULAR);
+ mCellNetworkAgent.connect(true);
+ trustedCallback.expectAvailableThenValidatedCallbacks(mCellNetworkAgent);
+ verify(mNetworkManagementService).setDefaultNetId(eq(mCellNetworkAgent.getNetwork().netId));
+
+ mWiFiNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_WIFI);
+ mWiFiNetworkAgent.connect(true);
+ trustedCallback.expectAvailableDoubleValidatedCallbacks(mWiFiNetworkAgent);
+ verify(mNetworkManagementService).setDefaultNetId(eq(mWiFiNetworkAgent.getNetwork().netId));
+
+ mWiFiNetworkAgent.removeCapability(NET_CAPABILITY_TRUSTED);
+ // There is currently a bug where losing the TRUSTED capability will send a LOST
+ // callback to requests before the available callback, in spite of the semantics
+ // of the requests dictating this should not happen. This is considered benign, but
+ // ideally should be fixed in the future.
+ trustedCallback.expectCallback(CallbackEntry.LOST, mWiFiNetworkAgent);
+ trustedCallback.expectAvailableCallbacksValidated(mCellNetworkAgent);
+ verify(mNetworkManagementService).setDefaultNetId(eq(mCellNetworkAgent.getNetwork().netId));
+
+ mCellNetworkAgent.removeCapability(NET_CAPABILITY_TRUSTED);
+ trustedCallback.expectCallback(CallbackEntry.LOST, mCellNetworkAgent);
+ verify(mNetworkManagementService).clearDefaultNetId();
+
+ mCm.unregisterNetworkCallback(trustedCallback);
+ }
+
@Ignore // 40%+ flakiness : figure out why and re-enable.
@Test
public final void testBatteryStatsNetworkType() throws Exception {
diff --git a/tests/net/java/com/android/server/connectivity/VpnTest.java b/tests/net/java/com/android/server/connectivity/VpnTest.java
index ce50bef53d75..155c61f3f8c7 100644
--- a/tests/net/java/com/android/server/connectivity/VpnTest.java
+++ b/tests/net/java/com/android/server/connectivity/VpnTest.java
@@ -28,11 +28,11 @@ import static android.net.NetworkCapabilities.NET_CAPABILITY_NOT_ROAMING;
import static android.net.NetworkCapabilities.TRANSPORT_CELLULAR;
import static android.net.NetworkCapabilities.TRANSPORT_VPN;
import static android.net.NetworkCapabilities.TRANSPORT_WIFI;
-import static android.net.RouteInfo.RTN_UNREACHABLE;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertTrue;
+import static org.junit.Assert.fail;
import static org.mockito.AdditionalMatchers.aryEq;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.anyBoolean;
@@ -43,6 +43,7 @@ import static org.mockito.Mockito.atLeastOnce;
import static org.mockito.Mockito.doAnswer;
import static org.mockito.Mockito.doNothing;
import static org.mockito.Mockito.inOrder;
+import static org.mockito.Mockito.never;
import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
@@ -58,21 +59,21 @@ import android.content.pm.ServiceInfo;
import android.content.pm.UserInfo;
import android.content.res.Resources;
import android.net.ConnectivityManager;
-import android.net.IpPrefix;
-import android.net.LinkProperties;
import android.net.Network;
import android.net.NetworkCapabilities;
import android.net.NetworkInfo.DetailedState;
-import android.net.RouteInfo;
import android.net.UidRange;
+import android.net.VpnManager;
import android.net.VpnService;
import android.os.Build.VERSION_CODES;
import android.os.Bundle;
import android.os.INetworkManagementService;
import android.os.Looper;
-import android.os.SystemClock;
+import android.os.Process;
import android.os.UserHandle;
import android.os.UserManager;
+import android.security.Credentials;
+import android.security.KeyStore;
import android.util.ArrayMap;
import android.util.ArraySet;
@@ -81,6 +82,7 @@ import androidx.test.runner.AndroidJUnit4;
import com.android.internal.R;
import com.android.internal.net.VpnConfig;
+import com.android.internal.net.VpnProfile;
import org.junit.Before;
import org.junit.Test;
@@ -90,9 +92,6 @@ import org.mockito.InOrder;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
-import java.net.Inet4Address;
-import java.net.Inet6Address;
-import java.net.UnknownHostException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
@@ -124,6 +123,8 @@ public class VpnTest {
managedProfileA.profileGroupId = primaryUser.id;
}
+ static final String TEST_VPN_PKG = "com.dummy.vpn";
+
/**
* Names and UIDs for some fake packages. Important points:
* - UID is ordered increasing.
@@ -148,6 +149,8 @@ public class VpnTest {
@Mock private NotificationManager mNotificationManager;
@Mock private Vpn.SystemServices mSystemServices;
@Mock private ConnectivityManager mConnectivityManager;
+ @Mock private KeyStore mKeyStore;
+ private final VpnProfile mVpnProfile = new VpnProfile("key");
@Before
public void setUp() throws Exception {
@@ -166,6 +169,7 @@ public class VpnTest {
when(mContext.getString(R.string.config_customVpnAlwaysOnDisconnectedDialogComponent))
.thenReturn(Resources.getSystem().getString(
R.string.config_customVpnAlwaysOnDisconnectedDialogComponent));
+ when(mSystemServices.isCallerSystem()).thenReturn(true);
// Used by {@link Notification.Builder}
ApplicationInfo applicationInfo = new ApplicationInfo();
@@ -175,6 +179,10 @@ public class VpnTest {
.thenReturn(applicationInfo);
doNothing().when(mNetService).registerObserver(any());
+
+ // Deny all appops by default.
+ when(mAppOps.noteOpNoThrow(anyInt(), anyInt(), anyString()))
+ .thenReturn(AppOpsManager.MODE_IGNORED);
}
@Test
@@ -464,12 +472,12 @@ public class VpnTest {
order.verify(mNetService).setAllowOnlyVpnForUids(eq(true), aryEq(entireUser));
// When a new VPN package is set the rules should change to cover that package.
- vpn.prepare(null, PKGS[0]);
+ vpn.prepare(null, PKGS[0], VpnManager.TYPE_VPN_SERVICE);
order.verify(mNetService).setAllowOnlyVpnForUids(eq(false), aryEq(entireUser));
order.verify(mNetService).setAllowOnlyVpnForUids(eq(true), aryEq(exceptPkg0));
// When that VPN package is unset, everything should be undone again in reverse.
- vpn.prepare(null, VpnConfig.LEGACY_VPN);
+ vpn.prepare(null, VpnConfig.LEGACY_VPN, VpnManager.TYPE_VPN_SERVICE);
order.verify(mNetService).setAllowOnlyVpnForUids(eq(false), aryEq(exceptPkg0));
order.verify(mNetService).setAllowOnlyVpnForUids(eq(true), aryEq(entireUser));
}
@@ -632,6 +640,230 @@ public class VpnTest {
}
/**
+ * The profile name should NOT change between releases for backwards compatibility
+ *
+ * <p>If this is changed between releases, the {@link Vpn#getVpnProfilePrivileged()} method MUST
+ * be updated to ensure backward compatibility.
+ */
+ @Test
+ public void testGetProfileNameForPackage() throws Exception {
+ final Vpn vpn = createVpn(primaryUser.id);
+ setMockedUsers(primaryUser);
+
+ final String expected = Credentials.PLATFORM_VPN + primaryUser.id + "_" + TEST_VPN_PKG;
+ assertEquals(expected, vpn.getProfileNameForPackage(TEST_VPN_PKG));
+ }
+
+ private Vpn createVpnAndSetupUidChecks(int... grantedOps) throws Exception {
+ final Vpn vpn = createVpn(primaryUser.id);
+ setMockedUsers(primaryUser);
+
+ when(mPackageManager.getPackageUidAsUser(eq(TEST_VPN_PKG), anyInt()))
+ .thenReturn(Process.myUid());
+
+ for (final int op : grantedOps) {
+ when(mAppOps.noteOpNoThrow(op, Process.myUid(), TEST_VPN_PKG))
+ .thenReturn(AppOpsManager.MODE_ALLOWED);
+ }
+
+ return vpn;
+ }
+
+ private void checkProvisionVpnProfile(Vpn vpn, boolean expectedResult, int... checkedOps) {
+ assertEquals(expectedResult, vpn.provisionVpnProfile(TEST_VPN_PKG, mVpnProfile, mKeyStore));
+
+ // The profile should always be stored, whether or not consent has been previously granted.
+ verify(mKeyStore)
+ .put(
+ eq(vpn.getProfileNameForPackage(TEST_VPN_PKG)),
+ eq(mVpnProfile.encode()),
+ eq(Process.SYSTEM_UID),
+ eq(0));
+
+ for (final int checkedOp : checkedOps) {
+ verify(mAppOps).noteOpNoThrow(checkedOp, Process.myUid(), TEST_VPN_PKG);
+ }
+ }
+
+ @Test
+ public void testProvisionVpnProfilePreconsented() throws Exception {
+ final Vpn vpn = createVpnAndSetupUidChecks(AppOpsManager.OP_ACTIVATE_PLATFORM_VPN);
+
+ checkProvisionVpnProfile(
+ vpn, true /* expectedResult */, AppOpsManager.OP_ACTIVATE_PLATFORM_VPN);
+ }
+
+ @Test
+ public void testProvisionVpnProfileNotPreconsented() throws Exception {
+ final Vpn vpn = createVpnAndSetupUidChecks();
+
+ // Expect that both the ACTIVATE_VPN and ACTIVATE_PLATFORM_VPN were tried, but the caller
+ // had neither.
+ checkProvisionVpnProfile(vpn, false /* expectedResult */,
+ AppOpsManager.OP_ACTIVATE_PLATFORM_VPN, AppOpsManager.OP_ACTIVATE_VPN);
+ }
+
+ @Test
+ public void testProvisionVpnProfileVpnServicePreconsented() throws Exception {
+ final Vpn vpn = createVpnAndSetupUidChecks(AppOpsManager.OP_ACTIVATE_VPN);
+
+ checkProvisionVpnProfile(vpn, true /* expectedResult */, AppOpsManager.OP_ACTIVATE_VPN);
+ }
+
+ @Test
+ public void testProvisionVpnProfileTooLarge() throws Exception {
+ final Vpn vpn = createVpnAndSetupUidChecks(AppOpsManager.OP_ACTIVATE_PLATFORM_VPN);
+
+ final VpnProfile bigProfile = new VpnProfile("");
+ bigProfile.name = new String(new byte[Vpn.MAX_VPN_PROFILE_SIZE_BYTES + 1]);
+
+ try {
+ vpn.provisionVpnProfile(TEST_VPN_PKG, bigProfile, mKeyStore);
+ fail("Expected IAE due to profile size");
+ } catch (IllegalArgumentException expected) {
+ }
+ }
+
+ @Test
+ public void testDeleteVpnProfile() throws Exception {
+ final Vpn vpn = createVpnAndSetupUidChecks();
+
+ vpn.deleteVpnProfile(TEST_VPN_PKG, mKeyStore);
+
+ verify(mKeyStore)
+ .delete(eq(vpn.getProfileNameForPackage(TEST_VPN_PKG)), eq(Process.SYSTEM_UID));
+ }
+
+ @Test
+ public void testGetVpnProfilePrivileged() throws Exception {
+ final Vpn vpn = createVpnAndSetupUidChecks();
+
+ when(mKeyStore.get(vpn.getProfileNameForPackage(TEST_VPN_PKG)))
+ .thenReturn(new VpnProfile("").encode());
+
+ vpn.getVpnProfilePrivileged(TEST_VPN_PKG, mKeyStore);
+
+ verify(mKeyStore).get(eq(vpn.getProfileNameForPackage(TEST_VPN_PKG)));
+ }
+
+ @Test
+ public void testStartVpnProfile() throws Exception {
+ final Vpn vpn = createVpnAndSetupUidChecks(AppOpsManager.OP_ACTIVATE_PLATFORM_VPN);
+
+ when(mKeyStore.get(vpn.getProfileNameForPackage(TEST_VPN_PKG)))
+ .thenReturn(mVpnProfile.encode());
+
+ vpn.startVpnProfile(TEST_VPN_PKG, mKeyStore);
+
+ verify(mKeyStore).get(eq(vpn.getProfileNameForPackage(TEST_VPN_PKG)));
+ verify(mAppOps)
+ .noteOpNoThrow(
+ eq(AppOpsManager.OP_ACTIVATE_PLATFORM_VPN),
+ eq(Process.myUid()),
+ eq(TEST_VPN_PKG));
+ }
+
+ @Test
+ public void testStartVpnProfileVpnServicePreconsented() throws Exception {
+ final Vpn vpn = createVpnAndSetupUidChecks(AppOpsManager.OP_ACTIVATE_VPN);
+
+ when(mKeyStore.get(vpn.getProfileNameForPackage(TEST_VPN_PKG)))
+ .thenReturn(mVpnProfile.encode());
+
+ vpn.startVpnProfile(TEST_VPN_PKG, mKeyStore);
+
+ // Verify that the the ACTIVATE_VPN appop was checked, but no error was thrown.
+ verify(mAppOps).noteOpNoThrow(AppOpsManager.OP_ACTIVATE_VPN, Process.myUid(), TEST_VPN_PKG);
+ }
+
+ @Test
+ public void testStartVpnProfileNotConsented() throws Exception {
+ final Vpn vpn = createVpnAndSetupUidChecks();
+
+ try {
+ vpn.startVpnProfile(TEST_VPN_PKG, mKeyStore);
+ fail("Expected failure due to no user consent");
+ } catch (SecurityException expected) {
+ }
+
+ // Verify both appops were checked.
+ verify(mAppOps)
+ .noteOpNoThrow(
+ eq(AppOpsManager.OP_ACTIVATE_PLATFORM_VPN),
+ eq(Process.myUid()),
+ eq(TEST_VPN_PKG));
+ verify(mAppOps).noteOpNoThrow(AppOpsManager.OP_ACTIVATE_VPN, Process.myUid(), TEST_VPN_PKG);
+
+ // Keystore should never have been accessed.
+ verify(mKeyStore, never()).get(any());
+ }
+
+ @Test
+ public void testStartVpnProfileMissingProfile() throws Exception {
+ final Vpn vpn = createVpnAndSetupUidChecks(AppOpsManager.OP_ACTIVATE_PLATFORM_VPN);
+
+ when(mKeyStore.get(vpn.getProfileNameForPackage(TEST_VPN_PKG))).thenReturn(null);
+
+ try {
+ vpn.startVpnProfile(TEST_VPN_PKG, mKeyStore);
+ fail("Expected failure due to missing profile");
+ } catch (IllegalArgumentException expected) {
+ }
+
+ verify(mKeyStore).get(vpn.getProfileNameForPackage(TEST_VPN_PKG));
+ verify(mAppOps)
+ .noteOpNoThrow(
+ eq(AppOpsManager.OP_ACTIVATE_PLATFORM_VPN),
+ eq(Process.myUid()),
+ eq(TEST_VPN_PKG));
+ }
+
+ @Test
+ public void testSetPackageAuthorizationVpnService() throws Exception {
+ final Vpn vpn = createVpnAndSetupUidChecks();
+
+ assertTrue(vpn.setPackageAuthorization(TEST_VPN_PKG, VpnManager.TYPE_VPN_SERVICE));
+ verify(mAppOps)
+ .setMode(
+ eq(AppOpsManager.OP_ACTIVATE_VPN),
+ eq(Process.myUid()),
+ eq(TEST_VPN_PKG),
+ eq(AppOpsManager.MODE_ALLOWED));
+ }
+
+ @Test
+ public void testSetPackageAuthorizationPlatformVpn() throws Exception {
+ final Vpn vpn = createVpnAndSetupUidChecks();
+
+ assertTrue(vpn.setPackageAuthorization(TEST_VPN_PKG, VpnManager.TYPE_VPN_PLATFORM));
+ verify(mAppOps)
+ .setMode(
+ eq(AppOpsManager.OP_ACTIVATE_PLATFORM_VPN),
+ eq(Process.myUid()),
+ eq(TEST_VPN_PKG),
+ eq(AppOpsManager.MODE_ALLOWED));
+ }
+
+ @Test
+ public void testSetPackageAuthorizationRevokeAuthorization() throws Exception {
+ final Vpn vpn = createVpnAndSetupUidChecks();
+
+ assertTrue(vpn.setPackageAuthorization(TEST_VPN_PKG, VpnManager.TYPE_VPN_NONE));
+ verify(mAppOps)
+ .setMode(
+ eq(AppOpsManager.OP_ACTIVATE_VPN),
+ eq(Process.myUid()),
+ eq(TEST_VPN_PKG),
+ eq(AppOpsManager.MODE_IGNORED));
+ verify(mAppOps)
+ .setMode(
+ eq(AppOpsManager.OP_ACTIVATE_PLATFORM_VPN),
+ eq(Process.myUid()),
+ eq(TEST_VPN_PKG),
+ eq(AppOpsManager.MODE_IGNORED));
+ }
+
+ /**
* Mock some methods of vpn object.
*/
private Vpn createVpn(@UserIdInt int userId) {
diff --git a/tools/aapt2/link/ManifestFixer.cpp b/tools/aapt2/link/ManifestFixer.cpp
index 954d4010d181..2af118c40138 100644
--- a/tools/aapt2/link/ManifestFixer.cpp
+++ b/tools/aapt2/link/ManifestFixer.cpp
@@ -428,6 +428,12 @@ bool ManifestFixer::BuildRules(xml::XmlActionExecutor* executor,
application_action["meta-data"] = meta_data_action;
+ application_action["processes"];
+ application_action["processes"]["deny-permission"];
+ application_action["processes"]["allow-permission"];
+ application_action["processes"]["process"]["deny-permission"];
+ application_action["processes"]["process"]["allow-permission"];
+
application_action["activity"] = component_action;
application_action["activity"]["layout"];
diff --git a/tools/stats_log_api_gen/Android.bp b/tools/stats_log_api_gen/Android.bp
index d3958a65c704..0e32aee93ebd 100644
--- a/tools/stats_log_api_gen/Android.bp
+++ b/tools/stats_log_api_gen/Android.bp
@@ -30,7 +30,6 @@ cc_binary_host {
"utils.cpp",
],
cflags: [
- "-DSTATS_SCHEMA_LEGACY",
"-Wall",
"-Werror",
],
diff --git a/tools/stats_log_api_gen/Collation.cpp b/tools/stats_log_api_gen/Collation.cpp
index d290acae1967..8bccd7150050 100644
--- a/tools/stats_log_api_gen/Collation.cpp
+++ b/tools/stats_log_api_gen/Collation.cpp
@@ -40,18 +40,21 @@ AtomDecl::AtomDecl()
{
}
-AtomDecl::AtomDecl(const AtomDecl& that)
- : code(that.code),
- name(that.name),
- message(that.message),
- fields(that.fields),
- primaryFields(that.primaryFields),
- exclusiveField(that.exclusiveField),
- uidField(that.uidField),
- whitelisted(that.whitelisted),
- binaryFields(that.binaryFields),
- hasModule(that.hasModule),
- moduleName(that.moduleName) {}
+AtomDecl::AtomDecl(const AtomDecl &that)
+ : code(that.code),
+ name(that.name),
+ message(that.message),
+ fields(that.fields),
+ primaryFields(that.primaryFields),
+ exclusiveField(that.exclusiveField),
+ defaultState(that.defaultState),
+ resetState(that.resetState),
+ nested(that.nested),
+ uidField(that.uidField),
+ whitelisted(that.whitelisted),
+ binaryFields(that.binaryFields),
+ hasModule(that.hasModule),
+ moduleName(that.moduleName) {}
AtomDecl::AtomDecl(int c, const string& n, const string& m)
:code(c),
@@ -281,7 +284,7 @@ int collate_atom(const Descriptor *atom, AtomDecl *atomDecl,
atomDecl->fields.push_back(atField);
if (field->options().GetExtension(os::statsd::state_field_option).option() ==
- os::statsd::StateField::PRIMARY) {
+ os::statsd::StateField::PRIMARY_FIELD) {
if (javaType == JAVA_TYPE_UNKNOWN ||
javaType == JAVA_TYPE_ATTRIBUTION_CHAIN ||
javaType == JAVA_TYPE_OBJECT || javaType == JAVA_TYPE_BYTE_ARRAY) {
@@ -300,7 +303,7 @@ int collate_atom(const Descriptor *atom, AtomDecl *atomDecl,
}
if (field->options().GetExtension(os::statsd::state_field_option).option() ==
- os::statsd::StateField::EXCLUSIVE) {
+ os::statsd::StateField::EXCLUSIVE_STATE) {
if (javaType == JAVA_TYPE_UNKNOWN ||
javaType == JAVA_TYPE_ATTRIBUTION_CHAIN ||
javaType == JAVA_TYPE_OBJECT || javaType == JAVA_TYPE_BYTE_ARRAY) {
@@ -312,6 +315,21 @@ int collate_atom(const Descriptor *atom, AtomDecl *atomDecl,
} else {
errorCount++;
}
+
+ if (field->options()
+ .GetExtension(os::statsd::state_field_option)
+ .has_default_state_value()) {
+ atomDecl->defaultState = field->options()
+ .GetExtension(os::statsd::state_field_option)
+ .default_state_value();
+ }
+
+ if (field->options().GetExtension(os::statsd::state_field_option).has_reset_state_value()) {
+ atomDecl->resetState = field->options()
+ .GetExtension(os::statsd::state_field_option)
+ .reset_state_value();
+ }
+ atomDecl->nested = field->options().GetExtension(os::statsd::state_field_option).nested();
}
if (field->options().GetExtension(os::statsd::is_uid) == true) {
diff --git a/tools/stats_log_api_gen/Collation.h b/tools/stats_log_api_gen/Collation.h
index 87d4d5db0cee..65d8e3efee5b 100644
--- a/tools/stats_log_api_gen/Collation.h
+++ b/tools/stats_log_api_gen/Collation.h
@@ -88,6 +88,9 @@ struct AtomDecl {
vector<int> primaryFields;
int exclusiveField = 0;
+ int defaultState = INT_MAX;
+ int resetState = INT_MAX;
+ bool nested;
int uidField = 0;
diff --git a/tools/stats_log_api_gen/atoms_info_writer.cpp b/tools/stats_log_api_gen/atoms_info_writer.cpp
index 66ae96401974..984c929f63bd 100644
--- a/tools/stats_log_api_gen/atoms_info_writer.cpp
+++ b/tools/stats_log_api_gen/atoms_info_writer.cpp
@@ -25,11 +25,15 @@ namespace android {
namespace stats_log_api_gen {
static void write_atoms_info_header_body(FILE* out, const Atoms& atoms) {
+ fprintf(out, "static int UNSET_VALUE = INT_MAX;\n");
fprintf(out, "static int FIRST_UID_IN_CHAIN = 0;\n");
fprintf(out, "struct StateAtomFieldOptions {\n");
fprintf(out, " std::vector<int> primaryFields;\n");
fprintf(out, " int exclusiveField;\n");
+ fprintf(out, " int defaultState = UNSET_VALUE;\n");
+ fprintf(out, " int resetState = UNSET_VALUE;\n");
+ fprintf(out, " bool nested;\n");
fprintf(out, "};\n");
fprintf(out, "\n");
@@ -126,7 +130,7 @@ static void write_atoms_info_cpp_body(FILE* out, const Atoms& atoms) {
"static std::map<int, StateAtomFieldOptions> "
"getStateAtomFieldOptions() {\n");
fprintf(out, " std::map<int, StateAtomFieldOptions> options;\n");
- fprintf(out, " StateAtomFieldOptions opt;\n");
+ fprintf(out, " StateAtomFieldOptions* opt;\n");
for (set<AtomDecl>::const_iterator atom = atoms.decls.begin();
atom != atoms.decls.end(); atom++) {
if (atom->primaryFields.size() == 0 && atom->exclusiveField == 0) {
@@ -136,14 +140,26 @@ static void write_atoms_info_cpp_body(FILE* out, const Atoms& atoms) {
"\n // Adding primary and exclusive fields for atom "
"(%d)%s\n",
atom->code, atom->name.c_str());
- fprintf(out, " opt.primaryFields.clear();\n");
+ fprintf(out, " opt = &(options[static_cast<int>(%s)]);\n",
+ make_constant_name(atom->name).c_str());
+ fprintf(out, " opt->primaryFields.reserve(%lu);\n", atom->primaryFields.size());
for (const auto& field : atom->primaryFields) {
- fprintf(out, " opt.primaryFields.push_back(%d);\n", field);
+ fprintf(out, " opt->primaryFields.push_back(%d);\n", field);
}
- fprintf(out, " opt.exclusiveField = %d;\n", atom->exclusiveField);
- fprintf(out, " options[static_cast<int>(%s)] = opt;\n",
- make_constant_name(atom->name).c_str());
+ fprintf(out, " opt->exclusiveField = %d;\n", atom->exclusiveField);
+ if (atom->defaultState != INT_MAX) {
+ fprintf(out, " opt->defaultState = %d;\n", atom->defaultState);
+ } else {
+ fprintf(out, " opt->defaultState = UNSET_VALUE;\n");
+ }
+
+ if (atom->resetState != INT_MAX) {
+ fprintf(out, " opt->resetState = %d;\n", atom->resetState);
+ } else {
+ fprintf(out, " opt->resetState = UNSET_VALUE;\n");
+ }
+ fprintf(out, " opt->nested = %d;\n", atom->nested);
}
fprintf(out, " return options;\n");
diff --git a/tools/stats_log_api_gen/test.proto b/tools/stats_log_api_gen/test.proto
index c9a47632b4bb..b892194410ae 100644
--- a/tools/stats_log_api_gen/test.proto
+++ b/tools/stats_log_api_gen/test.proto
@@ -148,53 +148,42 @@ message GoodStateAtoms {
// The atom has only primary field but no exclusive state field.
message BadStateAtom1 {
- optional int32 uid = 1
- [(android.os.statsd.state_field_option).option = PRIMARY];
+ optional int32 uid = 1 [(android.os.statsd.state_field_option).option = PRIMARY_FIELD];
}
// Only primative types can be annotated.
message BadStateAtom2 {
repeated android.os.statsd.AttributionNode attribution = 1
- [(android.os.statsd.state_field_option).option = PRIMARY];
- optional int32 state = 2
- [(android.os.statsd.state_field_option).option = EXCLUSIVE];
+ [(android.os.statsd.state_field_option).option = PRIMARY_FIELD];
+ optional int32 state = 2 [(android.os.statsd.state_field_option).option = EXCLUSIVE_STATE];
}
// Having 2 exclusive state field in the atom means the atom is badly designed.
// E.g., putting bluetooth state and wifi state in the same atom.
message BadStateAtom3 {
- optional int32 uid = 1
- [(android.os.statsd.state_field_option).option = PRIMARY];
- optional int32 state = 2
- [(android.os.statsd.state_field_option).option = EXCLUSIVE];
- optional int32 state2 = 3
- [(android.os.statsd.state_field_option).option = EXCLUSIVE];
+ optional int32 uid = 1 [(android.os.statsd.state_field_option).option = PRIMARY_FIELD];
+ optional int32 state = 2 [(android.os.statsd.state_field_option).option = EXCLUSIVE_STATE];
+ optional int32 state2 = 3 [(android.os.statsd.state_field_option).option = EXCLUSIVE_STATE];
}
message GoodStateAtom1 {
- optional int32 uid = 1
- [(android.os.statsd.state_field_option).option = PRIMARY];
- optional int32 state = 2
- [(android.os.statsd.state_field_option).option = EXCLUSIVE];
+ optional int32 uid = 1 [(android.os.statsd.state_field_option).option = PRIMARY_FIELD];
+ optional int32 state = 2 [(android.os.statsd.state_field_option).option = EXCLUSIVE_STATE];
}
// Atoms can have exclusive state field, but no primary field. That means
// the state is globally exclusive (e.g., DisplayState).
message GoodStateAtom2 {
optional int32 uid = 1;
- optional int32 state = 2
- [(android.os.statsd.state_field_option).option = EXCLUSIVE];
+ optional int32 state = 2 [(android.os.statsd.state_field_option).option = EXCLUSIVE_STATE];
}
// We can have more than one primary fields. That means their combination is a
// primary key.
message GoodStateAtom3 {
- optional int32 uid = 1
- [(android.os.statsd.state_field_option).option = PRIMARY];
- optional int32 tid = 2
- [(android.os.statsd.state_field_option).option = PRIMARY];
- optional int32 state = 3
- [(android.os.statsd.state_field_option).option = EXCLUSIVE];
+ optional int32 uid = 1 [(android.os.statsd.state_field_option).option = PRIMARY_FIELD];
+ optional int32 tid = 2 [(android.os.statsd.state_field_option).option = PRIMARY_FIELD];
+ optional int32 state = 3 [(android.os.statsd.state_field_option).option = EXCLUSIVE_STATE];
}
message WhitelistedAtom {
diff --git a/wifi/java/android/net/wifi/IScoreChangeCallback.aidl b/wifi/java/android/net/wifi/IScoreChangeCallback.aidl
index fd236107bc6e..462a97844d76 100644
--- a/wifi/java/android/net/wifi/IScoreChangeCallback.aidl
+++ b/wifi/java/android/net/wifi/IScoreChangeCallback.aidl
@@ -16,6 +16,8 @@
package android.net.wifi;
+import android.net.NetworkScore;
+
/**
* Interface for Wi-Fi network score callback.
*
@@ -23,7 +25,7 @@ package android.net.wifi;
*/
oneway interface IScoreChangeCallback
{
- void onStatusChange(int sessionId, boolean exiting);
+ void onScoreChange(int sessionId, in NetworkScore score);
void onTriggerUpdateOfWifiUsabilityStats(int sessionId);
}
diff --git a/wifi/java/android/net/wifi/WifiConfiguration.java b/wifi/java/android/net/wifi/WifiConfiguration.java
index 8d95cb010856..91b7df372e01 100644
--- a/wifi/java/android/net/wifi/WifiConfiguration.java
+++ b/wifi/java/android/net/wifi/WifiConfiguration.java
@@ -1252,8 +1252,8 @@ public class WifiConfiguration implements Parcelable {
/** @hide */
@Retention(RetentionPolicy.SOURCE)
- @IntDef(value = {
- NETWORK_SELECTION_ENABLE,
+ @IntDef(prefix = "DISABLED_", value = {
+ DISABLED_NONE,
DISABLED_ASSOCIATION_REJECTION,
DISABLED_AUTHENTICATION_FAILURE,
DISABLED_DHCP_FAILURE,
@@ -1267,7 +1267,7 @@ public class WifiConfiguration implements Parcelable {
// Quality Network disabled reasons
/** Default value. Means not disabled. */
- public static final int NETWORK_SELECTION_ENABLE = 0;
+ public static final int DISABLED_NONE = 0;
/**
* The starting index for network selection disabled reasons.
* @hide
@@ -1356,7 +1356,7 @@ public class WifiConfiguration implements Parcelable {
private static SparseArray<DisableReasonInfo> buildDisableReasonInfos() {
SparseArray<DisableReasonInfo> reasons = new SparseArray<>();
- reasons.append(NETWORK_SELECTION_ENABLE,
+ reasons.append(DISABLED_NONE,
new DisableReasonInfo(
// Note that these strings are persisted in
// XmlUtil.NetworkSelectionStatusXmlUtil#writeToXml,
@@ -1610,7 +1610,7 @@ public class WifiConfiguration implements Parcelable {
}
/** True if the device has ever connected to this network, false otherwise. */
- public boolean getHasEverConnected() {
+ public boolean hasEverConnected() {
return mHasEverConnected;
}
@@ -1650,7 +1650,7 @@ public class WifiConfiguration implements Parcelable {
/**
*
* Set the current network's disable reason.
- * One of the {@link #NETWORK_SELECTION_ENABLE} or DISABLED_* constants.
+ * One of the {@link #DISABLED_NONE} or DISABLED_* constants.
* e.g. {@link #DISABLED_ASSOCIATION_REJECTION}.
* @see NetworkSelectionStatus#getNetworkSelectionDisableReason()
*/
@@ -1674,7 +1674,7 @@ public class WifiConfiguration implements Parcelable {
/**
* Get the network disable reason string for a reason code (for debugging).
- * @param reason specific error reason. One of the {@link #NETWORK_SELECTION_ENABLE} or
+ * @param reason specific error reason. One of the {@link #DISABLED_NONE} or
* DISABLED_* constants e.g. {@link #DISABLED_ASSOCIATION_REJECTION}.
* @return network disable reason string, or null if the reason is invalid.
*/
@@ -1740,7 +1740,7 @@ public class WifiConfiguration implements Parcelable {
/**
* Returns the current network's disable reason.
- * One of the {@link #NETWORK_SELECTION_ENABLE} or DISABLED_* constants
+ * One of the {@link #DISABLED_NONE} or DISABLED_* constants
* e.g. {@link #DISABLED_ASSOCIATION_REJECTION}.
*/
@NetworkSelectionDisableReason
@@ -1780,13 +1780,13 @@ public class WifiConfiguration implements Parcelable {
/**
* Get the disable counter of a specific reason.
- * @param reason specific failure reason. One of the {@link #NETWORK_SELECTION_ENABLE} or
+ * @param reason specific failure reason. One of the {@link #DISABLED_NONE} or
* DISABLED_* constants e.g. {@link #DISABLED_ASSOCIATION_REJECTION}.
* @exception IllegalArgumentException for invalid reason
* @return counter number for specific error reason.
*/
public int getDisableReasonCounter(@NetworkSelectionDisableReason int reason) {
- if (reason >= NETWORK_SELECTION_ENABLE && reason < NETWORK_SELECTION_DISABLED_MAX) {
+ if (reason >= DISABLED_NONE && reason < NETWORK_SELECTION_DISABLED_MAX) {
return mNetworkSeclectionDisableCounter[reason];
} else {
throw new IllegalArgumentException("Illegal reason value: " + reason);
@@ -1801,7 +1801,7 @@ public class WifiConfiguration implements Parcelable {
* @hide
*/
public void setDisableReasonCounter(int reason, int value) {
- if (reason >= NETWORK_SELECTION_ENABLE && reason < NETWORK_SELECTION_DISABLED_MAX) {
+ if (reason >= DISABLED_NONE && reason < NETWORK_SELECTION_DISABLED_MAX) {
mNetworkSeclectionDisableCounter[reason] = value;
} else {
throw new IllegalArgumentException("Illegal reason value: " + reason);
@@ -1815,7 +1815,7 @@ public class WifiConfiguration implements Parcelable {
* @hide
*/
public void incrementDisableReasonCounter(int reason) {
- if (reason >= NETWORK_SELECTION_ENABLE && reason < NETWORK_SELECTION_DISABLED_MAX) {
+ if (reason >= DISABLED_NONE && reason < NETWORK_SELECTION_DISABLED_MAX) {
mNetworkSeclectionDisableCounter[reason]++;
} else {
throw new IllegalArgumentException("Illegal reason value: " + reason);
@@ -1829,8 +1829,8 @@ public class WifiConfiguration implements Parcelable {
* @hide
*/
public void clearDisableReasonCounter(int reason) {
- if (reason >= NETWORK_SELECTION_ENABLE && reason < NETWORK_SELECTION_DISABLED_MAX) {
- mNetworkSeclectionDisableCounter[reason] = NETWORK_SELECTION_ENABLE;
+ if (reason >= DISABLED_NONE && reason < NETWORK_SELECTION_DISABLED_MAX) {
+ mNetworkSeclectionDisableCounter[reason] = DISABLED_NONE;
} else {
throw new IllegalArgumentException("Illegal reason value: " + reason);
}
@@ -1841,7 +1841,7 @@ public class WifiConfiguration implements Parcelable {
* @hide
*/
public void clearDisableReasonCounter() {
- Arrays.fill(mNetworkSeclectionDisableCounter, NETWORK_SELECTION_ENABLE);
+ Arrays.fill(mNetworkSeclectionDisableCounter, DISABLED_NONE);
}
/**
@@ -1871,7 +1871,7 @@ public class WifiConfiguration implements Parcelable {
public void copy(NetworkSelectionStatus source) {
mStatus = source.mStatus;
mNetworkSelectionDisableReason = source.mNetworkSelectionDisableReason;
- for (int index = NETWORK_SELECTION_ENABLE; index < NETWORK_SELECTION_DISABLED_MAX;
+ for (int index = DISABLED_NONE; index < NETWORK_SELECTION_DISABLED_MAX;
index++) {
mNetworkSeclectionDisableCounter[index] =
source.mNetworkSeclectionDisableCounter[index];
@@ -1882,14 +1882,14 @@ public class WifiConfiguration implements Parcelable {
setCandidate(source.getCandidate());
setCandidateScore(source.getCandidateScore());
setConnectChoice(source.getConnectChoice());
- setHasEverConnected(source.getHasEverConnected());
+ setHasEverConnected(source.hasEverConnected());
}
/** @hide */
public void writeToParcel(Parcel dest) {
dest.writeInt(getNetworkSelectionStatus());
dest.writeInt(getNetworkSelectionDisableReason());
- for (int index = NETWORK_SELECTION_ENABLE; index < NETWORK_SELECTION_DISABLED_MAX;
+ for (int index = DISABLED_NONE; index < NETWORK_SELECTION_DISABLED_MAX;
index++) {
dest.writeInt(getDisableReasonCounter(index));
}
@@ -1901,14 +1901,14 @@ public class WifiConfiguration implements Parcelable {
} else {
dest.writeInt(CONNECT_CHOICE_NOT_EXISTS);
}
- dest.writeInt(getHasEverConnected() ? 1 : 0);
+ dest.writeInt(hasEverConnected() ? 1 : 0);
}
/** @hide */
public void readFromParcel(Parcel in) {
setNetworkSelectionStatus(in.readInt());
setNetworkSelectionDisableReason(in.readInt());
- for (int index = NETWORK_SELECTION_ENABLE; index < NETWORK_SELECTION_DISABLED_MAX;
+ for (int index = DISABLED_NONE; index < NETWORK_SELECTION_DISABLED_MAX;
index++) {
setDisableReasonCounter(index, in.readInt());
}
@@ -2151,8 +2151,8 @@ public class WifiConfiguration implements Parcelable {
sbuf.append(" mNetworkSelectionDisableReason ")
.append(mNetworkSelectionStatus.getNetworkDisableReasonString() + "\n");
- for (int index = mNetworkSelectionStatus.NETWORK_SELECTION_ENABLE;
- index < mNetworkSelectionStatus.NETWORK_SELECTION_DISABLED_MAX; index++) {
+ for (int index = NetworkSelectionStatus.DISABLED_NONE;
+ index < NetworkSelectionStatus.NETWORK_SELECTION_DISABLED_MAX; index++) {
if (mNetworkSelectionStatus.getDisableReasonCounter(index) != 0) {
sbuf.append(NetworkSelectionStatus.getNetworkDisableReasonString(index)
+ " counter:" + mNetworkSelectionStatus.getDisableReasonCounter(index)
@@ -2164,7 +2164,7 @@ public class WifiConfiguration implements Parcelable {
sbuf.append(" connect choice: ").append(mNetworkSelectionStatus.getConnectChoice());
}
sbuf.append(" hasEverConnected: ")
- .append(mNetworkSelectionStatus.getHasEverConnected()).append("\n");
+ .append(mNetworkSelectionStatus.hasEverConnected()).append("\n");
if (this.numAssociation > 0) {
sbuf.append(" numAssociation ").append(this.numAssociation).append("\n");
@@ -2533,7 +2533,7 @@ public class WifiConfiguration implements Parcelable {
* @hide
*/
@Nullable
- @SystemApi
+ @UnsupportedAppUsage
public StaticIpConfiguration getStaticIpConfiguration() {
return mIpConfiguration.getStaticIpConfiguration();
}
@@ -2549,7 +2549,7 @@ public class WifiConfiguration implements Parcelable {
* @hide
*/
@NonNull
- @SystemApi
+ @UnsupportedAppUsage
public IpConfiguration.IpAssignment getIpAssignment() {
return mIpConfiguration.getIpAssignment();
}
@@ -2565,7 +2565,7 @@ public class WifiConfiguration implements Parcelable {
* @hide
*/
@NonNull
- @SystemApi
+ @UnsupportedAppUsage
public IpConfiguration.ProxySettings getProxySettings() {
return mIpConfiguration.getProxySettings();
}
@@ -2628,7 +2628,7 @@ public class WifiConfiguration implements Parcelable {
* Set the {@link ProxySettings} and {@link ProxyInfo} for this network.
* @hide
*/
- @SystemApi
+ @UnsupportedAppUsage
public void setProxy(@NonNull ProxySettings settings, @NonNull ProxyInfo proxy) {
mIpConfiguration.setProxySettings(settings);
mIpConfiguration.setHttpProxy(proxy);
diff --git a/wifi/java/android/net/wifi/WifiEnterpriseConfig.java b/wifi/java/android/net/wifi/WifiEnterpriseConfig.java
index 04016b606b96..db0d36e5c086 100644
--- a/wifi/java/android/net/wifi/WifiEnterpriseConfig.java
+++ b/wifi/java/android/net/wifi/WifiEnterpriseConfig.java
@@ -60,13 +60,13 @@ public class WifiEnterpriseConfig implements Parcelable {
"android.net.wifi.extra.WAPI_AS_CERTIFICATE_DATA";
/**
- * Intent extra: name for WAPI AS certificates
+ * Intent extra: name for WAPI USER certificates
*/
public static final String EXTRA_WAPI_USER_CERTIFICATE_NAME =
"android.net.wifi.extra.WAPI_USER_CERTIFICATE_NAME";
/**
- * Intent extra: data for WAPI AS certificates
+ * Intent extra: data for WAPI USER certificates
*/
public static final String EXTRA_WAPI_USER_CERTIFICATE_DATA =
"android.net.wifi.extra.WAPI_USER_CERTIFICATE_DATA";
diff --git a/wifi/java/android/net/wifi/WifiManager.java b/wifi/java/android/net/wifi/WifiManager.java
index 09dc17140044..1dc4a069fe2f 100644
--- a/wifi/java/android/net/wifi/WifiManager.java
+++ b/wifi/java/android/net/wifi/WifiManager.java
@@ -38,6 +38,7 @@ import android.net.ConnectivityManager;
import android.net.DhcpInfo;
import android.net.MacAddress;
import android.net.Network;
+import android.net.NetworkScore;
import android.net.NetworkStack;
import android.net.wifi.hotspot2.IProvisioningCallback;
import android.net.wifi.hotspot2.OsuProvider;
@@ -256,12 +257,12 @@ public class WifiManager {
* - {@link #EXTRA_SCAN_AVAILABLE}
*/
@SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
- public static final String ACTION_WIFI_SCAN_AVAILABLE =
- "android.net.wifi.action.WIFI_SCAN_AVAILABLE";
+ public static final String ACTION_WIFI_SCAN_AVAILABILITY_CHANGED =
+ "android.net.wifi.action.WIFI_SCAN_AVAILABILITY_CHANGED";
/**
* A boolean extra indicating whether scanning is currently available.
- * Sent in the broadcast {@link #ACTION_WIFI_SCAN_AVAILABLE}.
+ * Sent in the broadcast {@link #ACTION_WIFI_SCAN_AVAILABILITY_CHANGED}.
* Its value is true if scanning is currently available, false otherwise.
*/
public static final String EXTRA_SCAN_AVAILABLE = "android.net.wifi.extra.SCAN_AVAILABLE";
@@ -5948,10 +5949,11 @@ public class WifiManager {
*
* @param sessionId The ID to indicate current Wi-Fi network connection obtained from
* {@link WifiConnectedNetworkScorer#start(int)}.
- * @param isUsable The bit to indicate whether current Wi-Fi network is usable or not.
- * Populated by connected network scorer in applications.
+ * @param score The {@link android.net.NetworkScore} object representing the
+ * characteristics of current Wi-Fi network. Populated by connected network
+ * scorer in applications.
*/
- void onStatusChange(int sessionId, boolean isUsable);
+ void onScoreChange(int sessionId, @NonNull NetworkScore score);
/**
* Called by applications to trigger an update of {@link WifiUsabilityStatsEntry}.
@@ -5977,9 +5979,9 @@ public class WifiManager {
}
@Override
- public void onStatusChange(int sessionId, boolean isUsable) {
+ public void onScoreChange(int sessionId, @NonNull NetworkScore score) {
try {
- mScoreChangeCallback.onStatusChange(sessionId, isUsable);
+ mScoreChangeCallback.onScoreChange(sessionId, score);
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
diff --git a/wifi/java/android/net/wifi/WifiScanner.java b/wifi/java/android/net/wifi/WifiScanner.java
index 8250a95c97a9..a85f40b3c1b8 100644
--- a/wifi/java/android/net/wifi/WifiScanner.java
+++ b/wifi/java/android/net/wifi/WifiScanner.java
@@ -837,7 +837,7 @@ public class WifiScanner {
*
* @param enable set to true to enable scanning, set to false to disable all types of scanning.
*
- * @see WifiManager#ACTION_WIFI_SCAN_AVAILABLE
+ * @see WifiManager#ACTION_WIFI_SCAN_AVAILABILITY_CHANGED
* {@hide}
*/
@SystemApi
diff --git a/wifi/java/android/net/wifi/wificond/NativeScanResult.java b/wifi/java/android/net/wifi/wificond/NativeScanResult.java
index 6ed17081bdb5..85251e8b1d42 100644
--- a/wifi/java/android/net/wifi/wificond/NativeScanResult.java
+++ b/wifi/java/android/net/wifi/wificond/NativeScanResult.java
@@ -169,7 +169,7 @@ public final class NativeScanResult implements Parcelable {
}
/**
- * @hide
+ * Construct an empty native scan result.
*/
public NativeScanResult() { }
diff --git a/wifi/java/android/net/wifi/wificond/NativeWifiClient.java b/wifi/java/android/net/wifi/wificond/NativeWifiClient.java
index 554f9295ef0d..916c11579075 100644
--- a/wifi/java/android/net/wifi/wificond/NativeWifiClient.java
+++ b/wifi/java/android/net/wifi/wificond/NativeWifiClient.java
@@ -36,8 +36,7 @@ public final class NativeWifiClient implements Parcelable {
@NonNull public final byte[] macAddress;
/**
- * public constructor
- * @hide
+ * Construct a native Wi-Fi client.
*/
public NativeWifiClient(@NonNull byte[] macAddress) {
this.macAddress = macAddress;
diff --git a/wifi/java/android/net/wifi/wificond/RadioChainInfo.java b/wifi/java/android/net/wifi/wificond/RadioChainInfo.java
index 64102dde94c0..97c0ee9d1c2c 100644
--- a/wifi/java/android/net/wifi/wificond/RadioChainInfo.java
+++ b/wifi/java/android/net/wifi/wificond/RadioChainInfo.java
@@ -61,7 +61,9 @@ public final class RadioChainInfo implements Parcelable {
return level;
}
- /** @hide */
+ /**
+ * Construct a RadioChainInfo.
+ */
public RadioChainInfo(int chainId, int level) {
this.chainId = chainId;
this.level = level;
diff --git a/wifi/java/android/net/wifi/wificond/WifiCondManager.java b/wifi/java/android/net/wifi/wificond/WifiCondManager.java
index 4847640b1418..43aa1b64efbc 100644
--- a/wifi/java/android/net/wifi/wificond/WifiCondManager.java
+++ b/wifi/java/android/net/wifi/wificond/WifiCondManager.java
@@ -41,16 +41,19 @@ import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.util.ArrayList;
import java.util.Arrays;
+import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.Executor;
import java.util.concurrent.atomic.AtomicBoolean;
+import java.util.stream.Collectors;
/**
- * This class encapsulates the interface the wificond daemon presents to the Wi-Fi framework. The
- * interface is only for use by the Wi-Fi framework and access is protected by SELinux permissions.
+ * This class encapsulates the interface the wificond (Wi-Fi Conductor) daemon presents to the
+ * Wi-Fi framework. The interface is only for use by the Wi-Fi framework and access is protected
+ * by SELinux permissions: only the system server and wpa_supplicant can use WifiCondManager.
*
* @hide
*/
@@ -1011,13 +1014,13 @@ public class WifiCondManager {
* WifiScanner.WIFI_BAND_5_GHZ
* WifiScanner.WIFI_BAND_5_GHZ_DFS_ONLY
* WifiScanner.WIFI_BAND_6_GHZ
- * @return frequencies vector of valid frequencies (MHz), or an empty array for error.
+ * @return frequencies List of valid frequencies (MHz), or an empty list for error.
* @throws IllegalArgumentException if band is not recognized.
*/
- public @NonNull int[] getChannelsMhzForBand(@WifiAnnotations.WifiBandBasic int band) {
+ public @NonNull List<Integer> getChannelsMhzForBand(@WifiAnnotations.WifiBandBasic int band) {
if (mWificond == null) {
Log.e(TAG, "No valid wificond scanner interface handler");
- return new int[0];
+ return Collections.emptyList();
}
int[] result = null;
try {
@@ -1041,9 +1044,9 @@ public class WifiCondManager {
Log.e(TAG, "Failed to request getChannelsForBand due to remote exception");
}
if (result == null) {
- result = new int[0];
+ return Collections.emptyList();
}
- return result;
+ return Arrays.stream(result).boxed().collect(Collectors.toList());
}
/** Helper function to look up the interface handle using name */
diff --git a/wifi/tests/src/android/net/wifi/wificond/WifiCondManagerTest.java b/wifi/tests/src/android/net/wifi/wificond/WifiCondManagerTest.java
index f262268d7179..5ba02a779663 100644
--- a/wifi/tests/src/android/net/wifi/wificond/WifiCondManagerTest.java
+++ b/wifi/tests/src/android/net/wifi/wificond/WifiCondManagerTest.java
@@ -65,6 +65,8 @@ import java.nio.charset.CharsetDecoder;
import java.nio.charset.CharsetEncoder;
import java.nio.charset.StandardCharsets;
import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collections;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
@@ -741,11 +743,43 @@ public class WifiCondManagerTest {
verify(deathHandler).run();
// The handles should be cleared after death.
- assertEquals(0, mWificondControl.getChannelsMhzForBand(WifiScanner.WIFI_BAND_5_GHZ).length);
+ assertEquals(0, mWificondControl.getChannelsMhzForBand(WifiScanner.WIFI_BAND_5_GHZ).size());
verify(mWificond, never()).getAvailable5gNonDFSChannels();
}
/**
+ * Verify primitive array to list translation of channel API.
+ */
+ @Test
+ public void testGetChannels() throws Exception {
+ int[] resultsEmpty = new int[0];
+ int[] resultsSingle = new int[]{100};
+ int[] resultsMore = new int[]{100, 200};
+
+ List<Integer> emptyList = Collections.emptyList();
+ List<Integer> singleList = Arrays.asList(100);
+ List<Integer> moreList = Arrays.asList(100, 200);
+
+ when(mWificond.getAvailable2gChannels()).thenReturn(null);
+ assertEquals(mWificondControl.getChannelsMhzForBand(WifiScanner.WIFI_BAND_24_GHZ),
+ emptyList);
+ assertEquals(mWificondControl.getChannelsMhzForBand(WifiScanner.WIFI_BAND_5_GHZ),
+ emptyList);
+
+ when(mWificond.getAvailable2gChannels()).thenReturn(resultsEmpty);
+ assertEquals(mWificondControl.getChannelsMhzForBand(WifiScanner.WIFI_BAND_24_GHZ),
+ emptyList);
+
+ when(mWificond.getAvailable2gChannels()).thenReturn(resultsSingle);
+ assertEquals(mWificondControl.getChannelsMhzForBand(WifiScanner.WIFI_BAND_24_GHZ),
+ singleList);
+
+ when(mWificond.getAvailable2gChannels()).thenReturn(resultsMore);
+ assertEquals(mWificondControl.getChannelsMhzForBand(WifiScanner.WIFI_BAND_24_GHZ),
+ moreList);
+ }
+
+ /**
* sendMgmtFrame() should fail if a null callback is passed in.
*/
@Test