summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--Android.bp2
-rw-r--r--ApiDocs.bp8
-rw-r--r--PREUPLOAD.cfg1
-rw-r--r--StubLibraries.bp33
-rw-r--r--apct-tests/perftests/autofill/Android.bp1
-rw-r--r--apct-tests/perftests/autofill/src/android/view/autofill/AbstractAutofillPerfTestCase.java76
-rw-r--r--apct-tests/perftests/autofill/src/android/view/autofill/AutofillTestWatcher.java162
-rw-r--r--apct-tests/perftests/autofill/src/android/view/autofill/LoginTest.java52
-rw-r--r--apct-tests/perftests/autofill/src/android/view/autofill/MyAutofillService.java21
-rw-r--r--apex/jobscheduler/framework/java/android/app/job/JobScheduler.java15
-rw-r--r--apex/jobscheduler/service/java/com/android/server/alarm/AlarmManagerService.java215
-rw-r--r--apex/jobscheduler/service/java/com/android/server/alarm/BatchingAlarmStore.java2
-rw-r--r--apex/jobscheduler/service/java/com/android/server/job/JobSchedulerService.java31
-rw-r--r--apex/jobscheduler/service/java/com/android/server/job/controllers/ComponentController.java203
-rw-r--r--apex/jobscheduler/service/java/com/android/server/job/controllers/JobStatus.java9
-rw-r--r--apex/jobscheduler/service/java/com/android/server/usage/AppStandbyController.java16
-rw-r--r--api/Android.bp90
-rw-r--r--api/current.txt100
-rw-r--r--api/module-lib-current.txt6
-rw-r--r--api/system-current.txt98
-rw-r--r--api/system-lint-baseline.txt88
-rw-r--r--api/test-current.txt15
-rw-r--r--api/test-lint-baseline.txt230
-rw-r--r--cmds/bootanimation/BootAnimation.cpp110
-rw-r--r--cmds/bootanimation/BootAnimation.h12
-rw-r--r--cmds/bootanimation/FORMAT.md8
-rw-r--r--cmds/statsd/src/atoms.proto66
-rw-r--r--cmds/statsd/src/condition/ConditionTimer.h48
-rw-r--r--cmds/statsd/src/condition/ConditionTracker.h6
-rw-r--r--cmds/statsd/src/metrics/EventMetricProducer.cpp47
-rw-r--r--cmds/statsd/src/metrics/EventMetricProducer.h16
-rw-r--r--cmds/statsd/src/metrics/MetricProducer.cpp32
-rw-r--r--cmds/statsd/src/metrics/MetricProducer.h50
-rw-r--r--cmds/statsd/src/metrics/MetricsManager.cpp21
-rw-r--r--cmds/statsd/src/metrics/ValueMetricProducer.cpp115
-rw-r--r--cmds/statsd/src/metrics/ValueMetricProducer.h20
-rw-r--r--cmds/statsd/src/metrics/parsing_utils/config_update_utils.cpp287
-rw-r--r--cmds/statsd/src/metrics/parsing_utils/config_update_utils.h81
-rw-r--r--cmds/statsd/src/metrics/parsing_utils/metrics_manager_util.cpp301
-rw-r--r--cmds/statsd/src/metrics/parsing_utils/metrics_manager_util.h63
-rw-r--r--cmds/statsd/tests/condition/ConditionTimer_test.cpp6
-rw-r--r--cmds/statsd/tests/metrics/ValueMetricProducer_test.cpp2053
-rw-r--r--cmds/statsd/tests/metrics/parsing_utils/config_update_utils_test.cpp593
-rw-r--r--core/java/android/app/ActivityThread.java450
-rw-r--r--core/java/android/app/ApplicationPackageManager.java13
-rw-r--r--core/java/android/app/ClientTransactionHandler.java54
-rw-r--r--core/java/android/app/ContextImpl.java7
-rw-r--r--core/java/android/app/IActivityManager.aidl7
-rw-r--r--core/java/android/app/IActivityTaskManager.aidl6
-rw-r--r--core/java/android/app/LocalActivityManager.java43
-rw-r--r--core/java/android/app/StatusBarManager.java1
-rw-r--r--core/java/android/app/TEST_MAPPING4
-rw-r--r--core/java/android/app/servertransaction/ActivityConfigurationChangeItem.java12
-rw-r--r--core/java/android/app/servertransaction/ActivityLifecycleItem.java2
-rw-r--r--core/java/android/app/servertransaction/ActivityRelaunchItem.java30
-rw-r--r--core/java/android/app/servertransaction/ActivityResultItem.java11
-rw-r--r--core/java/android/app/servertransaction/ActivityTransactionItem.java69
-rw-r--r--core/java/android/app/servertransaction/DestroyActivityItem.java8
-rw-r--r--core/java/android/app/servertransaction/EnterPipRequestedItem.java8
-rw-r--r--core/java/android/app/servertransaction/LaunchActivityItem.java3
-rw-r--r--core/java/android/app/servertransaction/MoveToDisplayItem.java13
-rw-r--r--core/java/android/app/servertransaction/NewIntentItem.java11
-rw-r--r--core/java/android/app/servertransaction/PauseActivityItem.java9
-rw-r--r--core/java/android/app/servertransaction/ResumeActivityItem.java8
-rw-r--r--core/java/android/app/servertransaction/StartActivityItem.java9
-rw-r--r--core/java/android/app/servertransaction/StopActivityItem.java8
-rw-r--r--core/java/android/app/servertransaction/TopResumedActivityChangeItem.java26
-rw-r--r--core/java/android/app/servertransaction/TransactionExecutor.java12
-rw-r--r--core/java/android/bluetooth/BluetoothCodecConfig.java32
-rw-r--r--core/java/android/bluetooth/BluetoothCodecStatus.java5
-rw-r--r--core/java/android/bluetooth/BluetoothPan.java35
-rw-r--r--core/java/android/content/Context.java22
-rw-r--r--core/java/android/content/om/TEST_MAPPING14
-rw-r--r--core/java/android/content/pm/ActivityInfo.java10
-rw-r--r--core/java/android/content/pm/ApkChecksum.java40
-rw-r--r--core/java/android/content/pm/ApplicationInfo.java11
-rw-r--r--core/java/android/content/pm/Checksum.java27
-rw-r--r--core/java/android/content/pm/IPackageManager.aidl7
-rw-r--r--core/java/android/content/pm/PackageInstaller.java4
-rw-r--r--core/java/android/content/pm/PackageManager.java26
-rw-r--r--core/java/android/content/pm/TEST_MAPPING14
-rw-r--r--core/java/android/content/res/Configuration.java63
-rw-r--r--core/java/android/content/res/TEST_MAPPING14
-rw-r--r--core/java/android/hardware/biometrics/BiometricManager.java1
-rw-r--r--core/java/android/hardware/biometrics/SensorProperties.java82
-rw-r--r--core/java/android/hardware/display/DisplayManager.java25
-rw-r--r--core/java/android/hardware/display/DisplayManagerGlobal.java26
-rw-r--r--core/java/android/hardware/display/IDisplayManager.aidl6
-rw-r--r--core/java/android/hardware/face/IFaceService.aidl2
-rw-r--r--core/java/android/hardware/fingerprint/FingerprintSensorProperties.java45
-rw-r--r--core/java/android/hardware/fingerprint/IFingerprintService.aidl2
-rw-r--r--core/java/android/hardware/soundtrigger/ConversionUtil.java9
-rw-r--r--core/java/android/hardware/soundtrigger/SoundTrigger.java184
-rw-r--r--core/java/android/hardware/soundtrigger/SoundTriggerModule.java34
-rw-r--r--core/java/android/inputmethodservice/InputMethodService.java49
-rw-r--r--core/java/android/os/Debug.java6
-rw-r--r--core/java/android/os/incremental/IIncrementalService.aidl2
-rw-r--r--core/java/android/os/incremental/IncrementalFileStorages.java10
-rw-r--r--core/java/android/os/incremental/IncrementalStorage.java5
-rw-r--r--core/java/android/preference/SeekBarVolumizer.java51
-rw-r--r--core/java/android/preference/VolumePreference.java5
-rw-r--r--core/java/android/provider/DeviceConfig.java9
-rw-r--r--core/java/android/provider/Settings.java45
-rw-r--r--core/java/android/provider/Telephony.java14
-rw-r--r--core/java/android/service/notification/ZenModeConfig.java3
-rw-r--r--core/java/android/service/voice/AlwaysOnHotwordDetector.java44
-rw-r--r--core/java/android/service/voice/VoiceInteractionService.java14
-rw-r--r--core/java/android/telephony/PhoneStateListener.java32
-rw-r--r--core/java/android/telephony/TelephonyRegistryManager.java11
-rw-r--r--core/java/android/util/FeatureFlagUtils.java3
-rw-r--r--core/java/android/util/IntArray.java11
-rw-r--r--core/java/android/view/Choreographer.java14
-rw-r--r--core/java/android/view/DisplayEventReceiver.java12
-rw-r--r--core/java/android/view/FrameMetrics.java30
-rw-r--r--core/java/android/view/IWindowSession.aidl42
-rw-r--r--core/java/android/view/InputChannel.java7
-rw-r--r--core/java/android/view/InsetsState.java22
-rw-r--r--core/java/android/view/SurfaceControl.java43
-rw-r--r--core/java/android/view/SurfaceView.java100
-rw-r--r--core/java/android/view/View.java12
-rw-r--r--core/java/android/view/ViewRootImpl.java172
-rw-r--r--core/java/android/view/WindowManagerImpl.java19
-rw-r--r--core/java/android/view/inputmethod/EditorInfo.java47
-rw-r--r--core/java/android/view/inputmethod/InputMethodManager.java4
-rw-r--r--core/java/android/window/WindowContainerTransaction.java35
-rw-r--r--core/java/android/window/WindowMetricsHelper.java17
-rw-r--r--core/java/com/android/internal/BrightnessSynchronizer.java25
-rw-r--r--core/java/com/android/internal/app/ISoundTriggerService.aidl78
-rw-r--r--core/java/com/android/internal/app/ISoundTriggerSession.aidl68
-rw-r--r--core/java/com/android/internal/app/IVoiceInteractionManagerService.aidl78
-rw-r--r--core/java/com/android/internal/app/IVoiceInteractionSoundTriggerSession.aidl95
-rw-r--r--core/java/com/android/internal/config/sysui/SystemUiDeviceConfigFlags.java11
-rw-r--r--core/java/com/android/internal/infra/AbstractRemoteService.java1
-rw-r--r--core/java/com/android/internal/jank/FrameTracker.java70
-rw-r--r--core/java/com/android/internal/jank/InteractionJankMonitor.java272
-rw-r--r--core/java/com/android/internal/listeners/ListenerExecutor.java86
-rw-r--r--core/java/com/android/internal/logging/UiEventLoggerImpl.java13
-rw-r--r--core/java/com/android/internal/os/BatteryStatsImpl.java21
-rw-r--r--core/java/com/android/internal/os/BinderCallsStats.java19
-rw-r--r--core/java/com/android/internal/os/BinderInternal.java8
-rw-r--r--core/java/com/android/internal/os/SystemServerCpuThreadReader.java18
-rw-r--r--core/java/com/android/internal/policy/DecorView.java109
-rw-r--r--core/java/com/android/internal/protolog/ProtoLogGroup.java4
-rw-r--r--core/java/com/android/internal/telephony/IPhoneStateListener.aidl2
-rw-r--r--core/jni/android_os_Debug.cpp4
-rw-r--r--core/jni/android_view_DisplayEventReceiver.cpp11
-rw-r--r--core/jni/android_view_InputChannel.cpp47
-rw-r--r--core/jni/android_view_InputChannel.h2
-rw-r--r--core/proto/android/content/configuration.proto1
-rw-r--r--core/proto/android/providers/settings/config.proto5
-rw-r--r--core/proto/android/providers/settings/global.proto2
-rw-r--r--core/proto/android/providers/settings/secure.proto12
-rw-r--r--core/proto/android/stats/hdmi/enums.proto122
-rw-r--r--core/res/AndroidManifest.xml24
-rw-r--r--core/res/res/values-af/strings.xml1
-rw-r--r--core/res/res/values-am/strings.xml5
-rw-r--r--core/res/res/values-ar/strings.xml3
-rw-r--r--core/res/res/values-as/strings.xml1
-rw-r--r--core/res/res/values-az/strings.xml1
-rw-r--r--core/res/res/values-b+sr+Latn/strings.xml7
-rw-r--r--core/res/res/values-be/strings.xml27
-rw-r--r--core/res/res/values-bg/strings.xml1
-rw-r--r--core/res/res/values-bn/strings.xml2
-rw-r--r--core/res/res/values-bs/strings.xml3
-rw-r--r--core/res/res/values-ca/strings.xml3
-rw-r--r--core/res/res/values-cs/strings.xml1
-rw-r--r--core/res/res/values-da/strings.xml1
-rw-r--r--core/res/res/values-de/strings.xml2
-rw-r--r--core/res/res/values-el/strings.xml1
-rw-r--r--core/res/res/values-en-rAU/strings.xml1
-rw-r--r--core/res/res/values-en-rCA/strings.xml1
-rw-r--r--core/res/res/values-en-rGB/strings.xml1
-rw-r--r--core/res/res/values-en-rIN/strings.xml1
-rw-r--r--core/res/res/values-en-rXC/strings.xml1
-rw-r--r--core/res/res/values-es-rUS/strings.xml1
-rw-r--r--core/res/res/values-es/strings.xml1
-rw-r--r--core/res/res/values-et/strings.xml1
-rw-r--r--core/res/res/values-eu/strings.xml1
-rw-r--r--core/res/res/values-fa/strings.xml7
-rw-r--r--core/res/res/values-fi/strings.xml1
-rw-r--r--core/res/res/values-fr-rCA/strings.xml1
-rw-r--r--core/res/res/values-fr/strings.xml1
-rw-r--r--core/res/res/values-gl/strings.xml1
-rw-r--r--core/res/res/values-gu/strings.xml8
-rw-r--r--core/res/res/values-hi/strings.xml1
-rw-r--r--core/res/res/values-hr/strings.xml1
-rw-r--r--core/res/res/values-hu/strings.xml1
-rw-r--r--core/res/res/values-hy/strings.xml19
-rw-r--r--core/res/res/values-in/strings.xml3
-rw-r--r--core/res/res/values-is/strings.xml1
-rw-r--r--core/res/res/values-it/strings.xml1
-rw-r--r--core/res/res/values-iw/strings.xml1
-rw-r--r--core/res/res/values-ja/strings.xml1
-rw-r--r--core/res/res/values-ka/strings.xml1
-rw-r--r--core/res/res/values-kk/strings.xml7
-rw-r--r--core/res/res/values-km/strings.xml3
-rw-r--r--core/res/res/values-kn/strings.xml2
-rw-r--r--core/res/res/values-ko/strings.xml1
-rw-r--r--core/res/res/values-ky/strings.xml1
-rw-r--r--core/res/res/values-lo/strings.xml1
-rw-r--r--core/res/res/values-lt/strings.xml1
-rw-r--r--core/res/res/values-lv/strings.xml1
-rw-r--r--core/res/res/values-mcc310-mnc150-as/strings.xml24
-rw-r--r--core/res/res/values-mk/strings.xml1
-rw-r--r--core/res/res/values-ml/strings.xml1
-rw-r--r--core/res/res/values-mn/strings.xml1
-rw-r--r--core/res/res/values-mr/strings.xml2
-rw-r--r--core/res/res/values-ms/strings.xml1
-rw-r--r--core/res/res/values-my/strings.xml1
-rw-r--r--core/res/res/values-nb/strings.xml1
-rw-r--r--core/res/res/values-ne/strings.xml2
-rw-r--r--core/res/res/values-nl/strings.xml19
-rw-r--r--core/res/res/values-or/strings.xml1
-rw-r--r--core/res/res/values-pa/strings.xml4
-rw-r--r--core/res/res/values-pl/strings.xml1
-rw-r--r--core/res/res/values-pt-rBR/strings.xml1
-rw-r--r--core/res/res/values-pt-rPT/strings.xml1
-rw-r--r--core/res/res/values-pt/strings.xml1
-rw-r--r--core/res/res/values-ro/strings.xml1
-rw-r--r--core/res/res/values-ru/strings.xml1
-rw-r--r--core/res/res/values-si/strings.xml1
-rw-r--r--core/res/res/values-sk/strings.xml1
-rw-r--r--core/res/res/values-sl/strings.xml1
-rw-r--r--core/res/res/values-sq/strings.xml1
-rw-r--r--core/res/res/values-sr/strings.xml7
-rw-r--r--core/res/res/values-sv/strings.xml1
-rw-r--r--core/res/res/values-sw/strings.xml1
-rw-r--r--core/res/res/values-ta/strings.xml1
-rw-r--r--core/res/res/values-te/strings.xml1
-rw-r--r--core/res/res/values-th/strings.xml1
-rw-r--r--core/res/res/values-tl/strings.xml1
-rw-r--r--core/res/res/values-tr/strings.xml1
-rw-r--r--core/res/res/values-uk/strings.xml1
-rw-r--r--core/res/res/values-ur/strings.xml2
-rw-r--r--core/res/res/values-uz/strings.xml3
-rw-r--r--core/res/res/values-vi/strings.xml1
-rw-r--r--core/res/res/values-zh-rCN/strings.xml1
-rw-r--r--core/res/res/values-zh-rHK/strings.xml1
-rw-r--r--core/res/res/values-zh-rTW/strings.xml1
-rw-r--r--core/res/res/values-zu/strings.xml1
-rw-r--r--core/res/res/values/attrs_manifest.xml10
-rw-r--r--core/res/res/values/config.xml5
-rw-r--r--core/res/res/values/strings.xml7
-rw-r--r--core/res/res/values/symbols.xml2
-rw-r--r--core/tests/PackageInstallerSessions/Android.bp5
-rw-r--r--core/tests/PackageInstallerSessions/src/android/content/pm/PackageSessionTests.kt78
-rw-r--r--core/tests/coretests/AndroidManifest.xml9
-rw-r--r--core/tests/coretests/src/android/app/activity/ActivityThreadTest.java62
-rw-r--r--core/tests/coretests/src/android/app/servertransaction/TransactionExecutorTests.java64
-rw-r--r--core/tests/coretests/src/android/content/ContextTest.java38
-rw-r--r--core/tests/coretests/src/android/view/InsetsAnimationControlImplTest.java15
-rw-r--r--core/tests/coretests/src/android/view/InsetsControllerTest.java15
-rw-r--r--core/tests/coretests/src/android/view/InsetsStateTest.java338
-rw-r--r--core/tests/coretests/src/android/view/inputmethod/EditorInfoTest.java9
-rw-r--r--core/tests/coretests/src/android/widget/focus/ListOfEditTexts.java130
-rw-r--r--core/tests/coretests/src/com/android/internal/jank/FrameTrackerTest.java38
-rw-r--r--core/tests/coretests/src/com/android/internal/jank/InteractionJankMonitorTest.java99
-rw-r--r--core/tests/coretests/src/com/android/internal/os/BatteryStatsBinderCallStatsTest.java10
-rw-r--r--core/tests/coretests/src/com/android/internal/os/BinderCallsStatsTest.java27
-rw-r--r--core/tests/coretests/src/com/android/internal/os/SystemServicePowerCalculatorTest.java4
-rw-r--r--core/tests/mockingcoretests/src/android/app/activity/ActivityThreadClientTest.java12
-rw-r--r--core/tests/powertests/PowerStatsLoadTests/AndroidManifest.xml1
-rw-r--r--core/tests/powertests/PowerStatsLoadTests/src/com/android/frameworks/core/powerstatsloadtests/PowerMetricsCollector.java47
-rw-r--r--core/tests/utiltests/src/com/android/internal/util/CharSequencesTest.java15
-rw-r--r--data/etc/platform.xml3
-rw-r--r--data/etc/privapp-permissions-platform.xml1
-rw-r--r--data/etc/services.core.protolog.json69
-rw-r--r--data/fonts/fonts.xml29
-rw-r--r--data/keyboards/Vendor_27f8_Product_0bbe.kl54
-rw-r--r--graphics/java/android/graphics/BlurShader.java28
-rw-r--r--graphics/java/android/graphics/FrameInfo.java26
-rw-r--r--graphics/java/android/graphics/HardwareRenderer.java3
-rw-r--r--graphics/java/android/graphics/ParcelableColorSpace.java2
-rw-r--r--graphics/java/android/graphics/Shader.java6
-rw-r--r--graphics/java/android/graphics/fonts/Font.java73
-rw-r--r--libs/WindowManager/Jetpack/src/androidx/window/sidecar/SettingsSidecarImpl.java11
-rw-r--r--libs/WindowManager/Shell/res/values/strings.xml4
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/common/DisplayImeController.java5
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/onehanded/OneHandedController.java57
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/onehanded/OneHandedTutorialHandler.java31
-rw-r--r--libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/onehanded/OneHandedControllerTest.java7
-rw-r--r--libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/onehanded/OneHandedTutorialHandlerTest.java8
-rw-r--r--libs/hwui/FrameInfo.cpp3
-rw-r--r--libs/hwui/FrameInfo.h9
-rw-r--r--libs/hwui/OWNERS3
-rw-r--r--libs/hwui/SkiaCanvas.cpp3
-rw-r--r--libs/hwui/SkiaCanvas.h3
-rw-r--r--libs/hwui/hwui/Canvas.cpp18
-rw-r--r--libs/hwui/hwui/Canvas.h3
-rw-r--r--libs/hwui/hwui/MinikinSkia.cpp3
-rw-r--r--libs/hwui/jni/Graphics.cpp59
-rw-r--r--libs/hwui/jni/GraphicsJNI.h12
-rw-r--r--libs/hwui/jni/Paint.cpp57
-rw-r--r--libs/hwui/jni/Shader.cpp5
-rw-r--r--libs/hwui/jni/android_graphics_HardwareRenderer.cpp2
-rw-r--r--libs/hwui/jni/fonts/Font.cpp83
-rw-r--r--libs/hwui/pipeline/skia/GLFunctorDrawable.cpp9
-rw-r--r--libs/hwui/pipeline/skia/SkiaPipeline.cpp7
-rw-r--r--libs/hwui/pipeline/skia/VkFunctorDrawable.cpp2
-rw-r--r--libs/hwui/pipeline/skia/VkInteropFunctorDrawable.cpp2
-rw-r--r--libs/hwui/renderthread/CanvasContext.cpp22
-rw-r--r--libs/hwui/renderthread/CanvasContext.h2
-rw-r--r--libs/hwui/renderthread/DrawFrameTask.cpp4
-rw-r--r--libs/hwui/renderthread/RenderThread.cpp4
-rw-r--r--libs/hwui/renderthread/RenderThread.h2
-rw-r--r--libs/hwui/renderthread/TimeLord.cpp14
-rw-r--r--libs/hwui/renderthread/TimeLord.h5
-rw-r--r--libs/hwui/shader/BlurShader.cpp5
-rw-r--r--libs/hwui/shader/BlurShader.h6
-rw-r--r--libs/hwui/tests/macrobench/TestSceneRunner.cpp6
-rw-r--r--location/java/android/location/ILocationManager.aidl2
-rw-r--r--location/java/android/location/LocationManager.java67
-rw-r--r--location/java/android/location/LocationRequest.java14
-rw-r--r--media/Android.bp93
-rw-r--r--media/OWNERS4
-rw-r--r--media/TEST_MAPPING12
-rw-r--r--media/aidl/android/media/audio/common/AudioChannelMask.aidl (renamed from media/java/android/media/audio/common/AudioChannelMask.aidl)0
-rw-r--r--media/aidl/android/media/audio/common/AudioConfig.aidl (renamed from media/java/android/media/audio/common/AudioConfig.aidl)0
-rw-r--r--media/aidl/android/media/audio/common/AudioFormat.aidl (renamed from media/java/android/media/audio/common/AudioFormat.aidl)0
-rw-r--r--media/aidl/android/media/audio/common/AudioOffloadInfo.aidl (renamed from media/java/android/media/audio/common/AudioOffloadInfo.aidl)0
-rw-r--r--media/aidl/android/media/audio/common/AudioStreamType.aidl (renamed from media/java/android/media/audio/common/AudioStreamType.aidl)0
-rw-r--r--media/aidl/android/media/audio/common/AudioUsage.aidl (renamed from media/java/android/media/audio/common/AudioUsage.aidl)0
-rw-r--r--media/aidl/android/media/permission/Identity.aidl32
-rw-r--r--media/aidl/android/media/soundtrigger_middleware/AudioCapabilities.aidl (renamed from media/java/android/media/soundtrigger_middleware/AudioCapabilities.aidl)0
-rw-r--r--media/aidl/android/media/soundtrigger_middleware/ConfidenceLevel.aidl (renamed from media/java/android/media/soundtrigger_middleware/ConfidenceLevel.aidl)0
-rw-r--r--media/aidl/android/media/soundtrigger_middleware/ISoundTriggerCallback.aidl (renamed from media/java/android/media/soundtrigger_middleware/ISoundTriggerCallback.aidl)0
-rw-r--r--media/aidl/android/media/soundtrigger_middleware/ISoundTriggerMiddlewareService.aidl89
-rw-r--r--media/aidl/android/media/soundtrigger_middleware/ISoundTriggerModule.aidl (renamed from media/java/android/media/soundtrigger_middleware/ISoundTriggerModule.aidl)0
-rw-r--r--media/aidl/android/media/soundtrigger_middleware/ModelParameter.aidl (renamed from media/java/android/media/soundtrigger_middleware/ModelParameter.aidl)0
-rw-r--r--media/aidl/android/media/soundtrigger_middleware/ModelParameterRange.aidl (renamed from media/java/android/media/soundtrigger_middleware/ModelParameterRange.aidl)0
-rw-r--r--media/aidl/android/media/soundtrigger_middleware/OWNERS (renamed from media/java/android/media/soundtrigger_middleware/OWNERS)0
-rw-r--r--media/aidl/android/media/soundtrigger_middleware/Phrase.aidl (renamed from media/java/android/media/soundtrigger_middleware/Phrase.aidl)0
-rw-r--r--media/aidl/android/media/soundtrigger_middleware/PhraseRecognitionEvent.aidl (renamed from media/java/android/media/soundtrigger_middleware/PhraseRecognitionEvent.aidl)0
-rw-r--r--media/aidl/android/media/soundtrigger_middleware/PhraseRecognitionExtra.aidl (renamed from media/java/android/media/soundtrigger_middleware/PhraseRecognitionExtra.aidl)0
-rw-r--r--media/aidl/android/media/soundtrigger_middleware/PhraseSoundModel.aidl (renamed from media/java/android/media/soundtrigger_middleware/PhraseSoundModel.aidl)0
-rw-r--r--media/aidl/android/media/soundtrigger_middleware/RecognitionConfig.aidl (renamed from media/java/android/media/soundtrigger_middleware/RecognitionConfig.aidl)0
-rw-r--r--media/aidl/android/media/soundtrigger_middleware/RecognitionEvent.aidl (renamed from media/java/android/media/soundtrigger_middleware/RecognitionEvent.aidl)0
-rw-r--r--media/aidl/android/media/soundtrigger_middleware/RecognitionMode.aidl (renamed from media/java/android/media/soundtrigger_middleware/RecognitionMode.aidl)0
-rw-r--r--media/aidl/android/media/soundtrigger_middleware/RecognitionStatus.aidl (renamed from media/java/android/media/soundtrigger_middleware/RecognitionStatus.aidl)0
-rw-r--r--media/aidl/android/media/soundtrigger_middleware/SoundModel.aidl (renamed from media/java/android/media/soundtrigger_middleware/SoundModel.aidl)3
-rw-r--r--media/aidl/android/media/soundtrigger_middleware/SoundModelType.aidl (renamed from media/java/android/media/soundtrigger_middleware/SoundModelType.aidl)0
-rw-r--r--media/aidl/android/media/soundtrigger_middleware/SoundTriggerModuleDescriptor.aidl (renamed from media/java/android/media/soundtrigger_middleware/SoundTriggerModuleDescriptor.aidl)0
-rw-r--r--media/aidl/android/media/soundtrigger_middleware/SoundTriggerModuleProperties.aidl (renamed from media/java/android/media/soundtrigger_middleware/SoundTriggerModuleProperties.aidl)0
-rw-r--r--media/aidl/android/media/soundtrigger_middleware/Status.aidl (renamed from media/java/android/media/soundtrigger_middleware/Status.aidl)0
-rw-r--r--media/java/android/media/AudioAttributes.aidl2
-rw-r--r--media/java/android/media/AudioManager.java22
-rw-r--r--media/java/android/media/CamcorderProfile.java14
-rw-r--r--media/java/android/media/DrmInitData.java6
-rw-r--r--media/java/android/media/ExifInterface.java49
-rw-r--r--media/java/android/media/ExifInterfaceUtils.java18
-rw-r--r--media/java/android/media/MediaExtractor.java4
-rw-r--r--media/java/android/media/MediaFormat.java48
-rw-r--r--media/java/android/media/MediaMetadata.aidl2
-rw-r--r--media/java/android/media/MediaTranscodeManager.java239
-rw-r--r--media/java/android/media/MediaTranscodingException.java38
-rw-r--r--media/java/android/media/RemoteController.java2
-rw-r--r--media/java/android/media/browse/MediaBrowser.java2
-rw-r--r--media/java/android/media/permission/ClearCallingIdentityContext.java58
-rw-r--r--media/java/android/media/permission/CompositeSafeCloseable.java40
-rw-r--r--media/java/android/media/permission/IdentityContext.java100
-rw-r--r--media/java/android/media/permission/PermissionUtil.java250
-rw-r--r--media/java/android/media/permission/SafeCloseable.java (renamed from tests/utils/testutils/java/android/view/test/InsetsModeSession.java)24
-rw-r--r--media/java/android/media/session/MediaSessionManager.java55
-rw-r--r--media/java/android/media/soundtrigger/SoundTriggerDetector.java12
-rw-r--r--media/java/android/media/soundtrigger/SoundTriggerManager.java53
-rw-r--r--media/java/android/media/soundtrigger_middleware/ISoundTriggerMiddlewareService.aidl42
-rw-r--r--media/jni/android_media_tv_Tuner.cpp15
-rw-r--r--media/jni/android_media_tv_Tuner.h1
-rw-r--r--media/tests/MediaTranscodingTest/src/com/android/mediatranscodingtest/MediaTranscodeManagerDiedTest.java22
-rw-r--r--media/tests/MediaTranscodingTest/src/com/android/mediatranscodingtest/MediaTranscodeManagerTest.java44
-rw-r--r--media/tests/MediaTranscodingTest/src/com/android/mediatranscodingtest/MediaTranscodingBenchmark.java4
-rw-r--r--non-updatable-api/Android.bp35
-rw-r--r--non-updatable-api/current.txt94
-rw-r--r--non-updatable-api/module-lib-current.txt6
-rw-r--r--non-updatable-api/system-current.txt94
-rw-r--r--packages/CarSystemUI/res-keyguard/layout/keyguard_password_view.xml2
-rw-r--r--packages/CarSystemUI/res/layout/sysui_overlay_window.xml3
-rw-r--r--packages/CarSystemUI/res/values/dimens.xml6
-rw-r--r--packages/CarSystemUI/res/values/strings.xml2
-rw-r--r--packages/CarSystemUI/src/com/android/systemui/car/hvac/AdjustableTemperatureView.java23
-rw-r--r--packages/CarSystemUI/src/com/android/systemui/car/keyguard/CarKeyguardViewController.java5
-rw-r--r--packages/CarSystemUI/src/com/android/systemui/car/navigationbar/SystemBarConfigs.java4
-rw-r--r--packages/CarSystemUI/src/com/android/systemui/car/notification/NotificationPanelViewController.java35
-rw-r--r--packages/CarSystemUI/src/com/android/systemui/car/userswitcher/FullScreenUserSwitcherViewController.java5
-rw-r--r--packages/CarSystemUI/src/com/android/systemui/car/window/OverlayPanelViewController.java2
-rw-r--r--packages/CarSystemUI/src/com/android/systemui/car/window/OverlayViewController.java19
-rw-r--r--packages/CarSystemUI/src/com/android/systemui/car/window/OverlayViewGlobalStateController.java29
-rw-r--r--packages/CarSystemUI/src/com/android/systemui/car/window/SystemUIOverlayWindowController.java3
-rw-r--r--packages/CarSystemUI/src/com/android/systemui/wm/DisplaySystemBarsController.java27
-rw-r--r--packages/CarSystemUI/tests/src/com/android/systemui/car/hvac/AdjustableTemperatureViewTest.java42
-rw-r--r--packages/CarSystemUI/tests/src/com/android/systemui/car/window/OverlayPanelViewControllerTest.java29
-rw-r--r--packages/CarSystemUI/tests/src/com/android/systemui/car/window/OverlayViewGlobalStateControllerTest.java156
-rw-r--r--packages/InputDevices/res/values-af/strings.xml1
-rw-r--r--packages/InputDevices/res/values-am/strings.xml1
-rw-r--r--packages/InputDevices/res/values-ar/strings.xml5
-rw-r--r--packages/InputDevices/res/values-as/strings.xml4
-rw-r--r--packages/InputDevices/res/values-az/strings.xml4
-rw-r--r--packages/InputDevices/res/values-b+sr+Latn/strings.xml4
-rw-r--r--packages/InputDevices/res/values-be/strings.xml4
-rw-r--r--packages/InputDevices/res/values-bg/strings.xml1
-rw-r--r--packages/InputDevices/res/values-bn/strings.xml5
-rw-r--r--packages/InputDevices/res/values-bs/strings.xml1
-rw-r--r--packages/InputDevices/res/values-ca/strings.xml1
-rw-r--r--packages/InputDevices/res/values-cs/strings.xml2
-rw-r--r--packages/InputDevices/res/values-da/strings.xml1
-rw-r--r--packages/InputDevices/res/values-de/strings.xml1
-rw-r--r--packages/InputDevices/res/values-el/strings.xml1
-rw-r--r--packages/InputDevices/res/values-en-rAU/strings.xml1
-rw-r--r--packages/InputDevices/res/values-en-rCA/strings.xml1
-rw-r--r--packages/InputDevices/res/values-en-rGB/strings.xml1
-rw-r--r--packages/InputDevices/res/values-en-rIN/strings.xml1
-rw-r--r--packages/InputDevices/res/values-en-rXC/strings.xml1
-rw-r--r--packages/InputDevices/res/values-es-rUS/strings.xml1
-rw-r--r--packages/InputDevices/res/values-es/strings.xml1
-rw-r--r--packages/InputDevices/res/values-et/strings.xml1
-rw-r--r--packages/InputDevices/res/values-eu/strings.xml1
-rw-r--r--packages/InputDevices/res/values-fa/strings.xml1
-rw-r--r--packages/InputDevices/res/values-fi/strings.xml4
-rw-r--r--packages/InputDevices/res/values-fr-rCA/strings.xml2
-rw-r--r--packages/InputDevices/res/values-fr/strings.xml2
-rw-r--r--packages/InputDevices/res/values-gl/strings.xml1
-rw-r--r--packages/InputDevices/res/values-gu/strings.xml1
-rw-r--r--packages/InputDevices/res/values-hi/strings.xml2
-rw-r--r--packages/InputDevices/res/values-hr/strings.xml1
-rw-r--r--packages/InputDevices/res/values-hu/strings.xml1
-rw-r--r--packages/InputDevices/res/values-hy/strings.xml1
-rw-r--r--packages/InputDevices/res/values-in/strings.xml1
-rw-r--r--packages/InputDevices/res/values-is/strings.xml1
-rw-r--r--packages/InputDevices/res/values-it/strings.xml1
-rw-r--r--packages/InputDevices/res/values-iw/strings.xml1
-rw-r--r--packages/InputDevices/res/values-ja/strings.xml1
-rw-r--r--packages/InputDevices/res/values-ka/strings.xml1
-rw-r--r--packages/InputDevices/res/values-kk/strings.xml5
-rw-r--r--packages/InputDevices/res/values-km/strings.xml2
-rw-r--r--packages/InputDevices/res/values-kn/strings.xml4
-rw-r--r--packages/InputDevices/res/values-ko/strings.xml4
-rw-r--r--packages/InputDevices/res/values-ky/strings.xml4
-rw-r--r--packages/InputDevices/res/values-lo/strings.xml1
-rw-r--r--packages/InputDevices/res/values-lt/strings.xml1
-rw-r--r--packages/InputDevices/res/values-lv/strings.xml4
-rw-r--r--packages/InputDevices/res/values-mk/strings.xml2
-rw-r--r--packages/InputDevices/res/values-ml/strings.xml4
-rw-r--r--packages/InputDevices/res/values-mn/strings.xml4
-rw-r--r--packages/InputDevices/res/values-mr/strings.xml4
-rw-r--r--packages/InputDevices/res/values-ms/strings.xml4
-rw-r--r--packages/InputDevices/res/values-my/strings.xml1
-rw-r--r--packages/InputDevices/res/values-nb/strings.xml1
-rw-r--r--packages/InputDevices/res/values-ne/strings.xml5
-rw-r--r--packages/InputDevices/res/values-nl/strings.xml3
-rw-r--r--packages/InputDevices/res/values-or/strings.xml4
-rw-r--r--packages/InputDevices/res/values-pa/strings.xml4
-rw-r--r--packages/InputDevices/res/values-pl/strings.xml1
-rw-r--r--packages/InputDevices/res/values-pt-rBR/strings.xml1
-rw-r--r--packages/InputDevices/res/values-pt-rPT/strings.xml1
-rw-r--r--packages/InputDevices/res/values-pt/strings.xml1
-rw-r--r--packages/InputDevices/res/values-ro/strings.xml1
-rw-r--r--packages/InputDevices/res/values-ru/strings.xml1
-rw-r--r--packages/InputDevices/res/values-si/strings.xml1
-rw-r--r--packages/InputDevices/res/values-sk/strings.xml4
-rw-r--r--packages/InputDevices/res/values-sl/strings.xml1
-rw-r--r--packages/InputDevices/res/values-sq/strings.xml2
-rw-r--r--packages/InputDevices/res/values-sr/strings.xml4
-rw-r--r--packages/InputDevices/res/values-sv/strings.xml1
-rw-r--r--packages/InputDevices/res/values-sw/strings.xml1
-rw-r--r--packages/InputDevices/res/values-ta/strings.xml5
-rw-r--r--packages/InputDevices/res/values-te/strings.xml1
-rw-r--r--packages/InputDevices/res/values-th/strings.xml1
-rw-r--r--packages/InputDevices/res/values-tl/strings.xml2
-rw-r--r--packages/InputDevices/res/values-tr/strings.xml1
-rw-r--r--packages/InputDevices/res/values-uk/strings.xml1
-rw-r--r--packages/InputDevices/res/values-ur/strings.xml1
-rw-r--r--packages/InputDevices/res/values-uz/strings.xml4
-rw-r--r--packages/InputDevices/res/values-vi/strings.xml1
-rw-r--r--packages/InputDevices/res/values-zh-rCN/strings.xml1
-rw-r--r--packages/InputDevices/res/values-zh-rHK/strings.xml1
-rw-r--r--packages/InputDevices/res/values-zh-rTW/strings.xml1
-rw-r--r--packages/InputDevices/res/values-zu/strings.xml1
-rw-r--r--packages/SettingsLib/HelpUtils/res/values-te/strings.xml2
-rw-r--r--packages/SettingsLib/res/values-ar/strings.xml10
-rw-r--r--packages/SettingsLib/res/values-bn/strings.xml4
-rw-r--r--packages/SettingsLib/res/values-fa/strings.xml12
-rw-r--r--packages/SettingsLib/res/values-gu/strings.xml4
-rw-r--r--packages/SettingsLib/res/values-hi/strings.xml2
-rw-r--r--packages/SettingsLib/res/values-hu/strings.xml2
-rw-r--r--packages/SettingsLib/res/values-hy/arrays.xml2
-rw-r--r--packages/SettingsLib/res/values-hy/strings.xml6
-rw-r--r--packages/SettingsLib/res/values-ne/strings.xml4
-rw-r--r--packages/SettingsLib/res/values-sw/strings.xml8
-rw-r--r--packages/SettingsLib/res/values-te/strings.xml2
-rw-r--r--packages/SettingsLib/res/values-th/strings.xml2
-rw-r--r--packages/SettingsLib/src/com/android/settingslib/bluetooth/LocalBluetoothProfileManager.java5
-rw-r--r--packages/SettingsLib/src/com/android/settingslib/bluetooth/MapClientProfile.java2
-rw-r--r--packages/SettingsLib/src/com/android/settingslib/media/MediaOutputSliceConstants.java6
-rw-r--r--packages/SettingsLib/src/com/android/settingslib/wifi/OWNERS8
-rw-r--r--packages/SettingsProvider/src/android/provider/settings/backup/SecureSettings.java4
-rw-r--r--packages/SettingsProvider/src/android/provider/settings/validators/SecureSettingsValidators.java3
-rw-r--r--packages/SettingsProvider/src/com/android/providers/settings/SettingsProtoDumpUtil.java45
-rw-r--r--packages/SettingsProvider/test/src/android/provider/SettingsBackupTest.java1
-rw-r--r--packages/Shell/AndroidManifest.xml3
-rw-r--r--packages/SoundPicker/src/com/android/soundpicker/RingtonePickerActivity.java22
-rw-r--r--packages/SystemUI/AndroidManifest.xml3
-rw-r--r--packages/SystemUI/plugin/src/com/android/systemui/plugins/qs/QSTile.java4
-rw-r--r--packages/SystemUI/res-keyguard/values-km/strings.xml2
-rw-r--r--packages/SystemUI/res-keyguard/values-te/strings.xml2
-rw-r--r--packages/SystemUI/res/drawable/ic_move_magnification.xml (renamed from packages/SystemUI/res/drawable/ic_move.xml)2
-rw-r--r--packages/SystemUI/res/drawable/tv_rect_dark_left_rounded.xml26
-rw-r--r--packages/SystemUI/res/drawable/tv_rect_dark_right_rounded.xml26
-rw-r--r--packages/SystemUI/res/layout/media_output_dialog.xml24
-rw-r--r--packages/SystemUI/res/layout/notification_conversation_info.xml2
-rw-r--r--packages/SystemUI/res/layout/notification_info.xml5
-rw-r--r--packages/SystemUI/res/layout/partial_conversation_info.xml5
-rw-r--r--packages/SystemUI/res/layout/tv_audio_recording_indicator.xml81
-rw-r--r--packages/SystemUI/res/layout/window_magnifier_view.xml26
-rw-r--r--packages/SystemUI/res/values-af/strings.xml20
-rw-r--r--packages/SystemUI/res/values-am/strings.xml20
-rw-r--r--packages/SystemUI/res/values-ar/strings.xml24
-rw-r--r--packages/SystemUI/res/values-as/strings.xml20
-rw-r--r--packages/SystemUI/res/values-az/strings.xml20
-rw-r--r--packages/SystemUI/res/values-b+sr+Latn/strings.xml20
-rw-r--r--packages/SystemUI/res/values-be/strings.xml22
-rw-r--r--packages/SystemUI/res/values-bg/strings.xml22
-rw-r--r--packages/SystemUI/res/values-bn/strings.xml27
-rw-r--r--packages/SystemUI/res/values-bs/strings.xml20
-rw-r--r--packages/SystemUI/res/values-ca/strings.xml26
-rw-r--r--packages/SystemUI/res/values-cs/strings.xml31
-rw-r--r--packages/SystemUI/res/values-da/strings.xml22
-rw-r--r--packages/SystemUI/res/values-de/strings.xml20
-rw-r--r--packages/SystemUI/res/values-el/strings.xml22
-rw-r--r--packages/SystemUI/res/values-en-rAU/strings.xml22
-rw-r--r--packages/SystemUI/res/values-en-rCA/strings.xml22
-rw-r--r--packages/SystemUI/res/values-en-rGB/strings.xml22
-rw-r--r--packages/SystemUI/res/values-en-rIN/strings.xml22
-rw-r--r--packages/SystemUI/res/values-en-rXC/strings.xml20
-rw-r--r--packages/SystemUI/res/values-es-rUS/strings.xml30
-rw-r--r--packages/SystemUI/res/values-es/strings.xml24
-rw-r--r--packages/SystemUI/res/values-et/strings.xml20
-rw-r--r--packages/SystemUI/res/values-eu/strings.xml20
-rw-r--r--packages/SystemUI/res/values-fa/strings.xml22
-rw-r--r--packages/SystemUI/res/values-fi/strings.xml22
-rw-r--r--packages/SystemUI/res/values-fr-rCA/strings.xml27
-rw-r--r--packages/SystemUI/res/values-fr/strings.xml27
-rw-r--r--packages/SystemUI/res/values-gl/strings.xml26
-rw-r--r--packages/SystemUI/res/values-gu/strings.xml22
-rw-r--r--packages/SystemUI/res/values-hi/strings.xml33
-rw-r--r--packages/SystemUI/res/values-hr/strings.xml20
-rw-r--r--packages/SystemUI/res/values-hu/strings.xml20
-rw-r--r--packages/SystemUI/res/values-hy/strings.xml22
-rw-r--r--packages/SystemUI/res/values-in/strings.xml22
-rw-r--r--packages/SystemUI/res/values-is/strings.xml24
-rw-r--r--packages/SystemUI/res/values-it/strings.xml24
-rw-r--r--packages/SystemUI/res/values-iw/strings.xml20
-rw-r--r--packages/SystemUI/res/values-ja/strings.xml20
-rw-r--r--packages/SystemUI/res/values-ka/strings.xml22
-rw-r--r--packages/SystemUI/res/values-kk/strings.xml32
-rw-r--r--packages/SystemUI/res/values-km/strings.xml24
-rw-r--r--packages/SystemUI/res/values-kn/strings.xml22
-rw-r--r--packages/SystemUI/res/values-ko/strings.xml26
-rw-r--r--packages/SystemUI/res/values-ky/strings.xml24
-rw-r--r--packages/SystemUI/res/values-lo/strings.xml20
-rw-r--r--packages/SystemUI/res/values-lt/strings.xml22
-rw-r--r--packages/SystemUI/res/values-lv/strings.xml22
-rw-r--r--packages/SystemUI/res/values-mk/strings.xml24
-rw-r--r--packages/SystemUI/res/values-ml/strings.xml22
-rw-r--r--packages/SystemUI/res/values-mn/strings.xml22
-rw-r--r--packages/SystemUI/res/values-mr/strings.xml20
-rw-r--r--packages/SystemUI/res/values-ms/strings.xml22
-rw-r--r--packages/SystemUI/res/values-my/strings.xml20
-rw-r--r--packages/SystemUI/res/values-nb/strings.xml20
-rw-r--r--packages/SystemUI/res/values-ne/strings.xml22
-rw-r--r--packages/SystemUI/res/values-nl/strings.xml24
-rw-r--r--packages/SystemUI/res/values-or/strings.xml24
-rw-r--r--packages/SystemUI/res/values-pa/strings.xml20
-rw-r--r--packages/SystemUI/res/values-pl/strings.xml20
-rw-r--r--packages/SystemUI/res/values-pt-rBR/strings.xml26
-rw-r--r--packages/SystemUI/res/values-pt-rPT/strings.xml24
-rw-r--r--packages/SystemUI/res/values-pt/strings.xml26
-rw-r--r--packages/SystemUI/res/values-ro/strings.xml20
-rw-r--r--packages/SystemUI/res/values-ru/strings.xml22
-rw-r--r--packages/SystemUI/res/values-si/strings.xml20
-rw-r--r--packages/SystemUI/res/values-sk/strings.xml20
-rw-r--r--packages/SystemUI/res/values-sl/strings.xml22
-rw-r--r--packages/SystemUI/res/values-sq/strings.xml27
-rw-r--r--packages/SystemUI/res/values-sr/strings.xml20
-rw-r--r--packages/SystemUI/res/values-sv/strings.xml20
-rw-r--r--packages/SystemUI/res/values-sw/strings.xml20
-rw-r--r--packages/SystemUI/res/values-ta/strings.xml27
-rw-r--r--packages/SystemUI/res/values-te/strings.xml22
-rw-r--r--packages/SystemUI/res/values-th/strings.xml30
-rw-r--r--packages/SystemUI/res/values-tl/strings.xml20
-rw-r--r--packages/SystemUI/res/values-tr/strings.xml20
-rw-r--r--packages/SystemUI/res/values-uk/strings.xml20
-rw-r--r--packages/SystemUI/res/values-ur/strings.xml20
-rw-r--r--packages/SystemUI/res/values-uz/strings.xml20
-rw-r--r--packages/SystemUI/res/values-vi/strings.xml20
-rw-r--r--packages/SystemUI/res/values-zh-rCN/strings.xml20
-rw-r--r--packages/SystemUI/res/values-zh-rHK/strings.xml24
-rw-r--r--packages/SystemUI/res/values-zh-rTW/strings.xml24
-rw-r--r--packages/SystemUI/res/values-zu/strings.xml20
-rw-r--r--packages/SystemUI/res/values/colors_tv.xml2
-rw-r--r--packages/SystemUI/res/values/dimens.xml5
-rw-r--r--packages/SystemUI/shared/src/com/android/systemui/shared/system/WindowManagerWrapper.java27
-rw-r--r--packages/SystemUI/src/com/android/keyguard/KeyguardSecurityContainer.java13
-rw-r--r--packages/SystemUI/src/com/android/systemui/Dependency.java4
-rw-r--r--packages/SystemUI/src/com/android/systemui/SystemUIFactory.java26
-rw-r--r--packages/SystemUI/src/com/android/systemui/accessibility/WindowMagnificationController.java33
-rw-r--r--packages/SystemUI/src/com/android/systemui/appops/AppOpsControllerImpl.java27
-rw-r--r--packages/SystemUI/src/com/android/systemui/assist/AssistHandleReminderExpBehavior.java86
-rw-r--r--packages/SystemUI/src/com/android/systemui/biometrics/AuthController.java34
-rw-r--r--packages/SystemUI/src/com/android/systemui/biometrics/UdfpsController.java110
-rw-r--r--packages/SystemUI/src/com/android/systemui/bubbles/BubbleController.java6
-rw-r--r--packages/SystemUI/src/com/android/systemui/bubbles/BubbleExpandedView.java18
-rw-r--r--packages/SystemUI/src/com/android/systemui/bubbles/BubbleLoggerImpl.java2
-rw-r--r--packages/SystemUI/src/com/android/systemui/bubbles/dagger/BubbleModule.java3
-rw-r--r--packages/SystemUI/src/com/android/systemui/dagger/DefaultBroadcastReceiverBinder.java8
-rw-r--r--packages/SystemUI/src/com/android/systemui/dagger/DependencyProvider.java8
-rw-r--r--packages/SystemUI/src/com/android/systemui/doze/DozeLog.java16
-rw-r--r--packages/SystemUI/src/com/android/systemui/doze/DozeLogger.kt16
-rw-r--r--packages/SystemUI/src/com/android/systemui/doze/DozeMachine.java10
-rw-r--r--packages/SystemUI/src/com/android/systemui/doze/DozeScreenBrightness.java13
-rw-r--r--packages/SystemUI/src/com/android/systemui/keyguard/Lifecycle.java7
-rw-r--r--packages/SystemUI/src/com/android/systemui/media/MediaCarouselController.kt9
-rw-r--r--packages/SystemUI/src/com/android/systemui/media/MediaDeviceManager.kt34
-rw-r--r--packages/SystemUI/src/com/android/systemui/media/MediaTimeoutListener.kt3
-rw-r--r--packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputAdapter.java55
-rw-r--r--packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputBaseAdapter.java36
-rw-r--r--packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputBaseDialog.java23
-rw-r--r--packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputController.java11
-rw-r--r--packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputDialog.java8
-rw-r--r--packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputDialogFactory.kt16
-rw-r--r--packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputDialogReceiver.kt (renamed from packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutDialogReceiver.kt)4
-rw-r--r--packages/SystemUI/src/com/android/systemui/navigationbar/buttons/KeyButtonDrawable.java18
-rw-r--r--packages/SystemUI/src/com/android/systemui/navigationbar/buttons/KeyButtonRipple.java10
-rw-r--r--packages/SystemUI/src/com/android/systemui/navigationbar/gestural/BackGestureTfClassifierProvider.java66
-rw-r--r--packages/SystemUI/src/com/android/systemui/navigationbar/gestural/EdgeBackGestureHandler.java138
-rw-r--r--packages/SystemUI/src/com/android/systemui/pip/Pip.java104
-rw-r--r--packages/SystemUI/src/com/android/systemui/pip/PipAnimationController.java75
-rw-r--r--packages/SystemUI/src/com/android/systemui/pip/PipSurfaceTransactionHelper.java22
-rw-r--r--packages/SystemUI/src/com/android/systemui/pip/PipTaskOrganizer.java25
-rw-r--r--packages/SystemUI/src/com/android/systemui/pip/phone/PipController.java247
-rw-r--r--packages/SystemUI/src/com/android/systemui/pip/phone/PipMediaController.java25
-rw-r--r--packages/SystemUI/src/com/android/systemui/pip/phone/PipMenuActivityController.java13
-rw-r--r--packages/SystemUI/src/com/android/systemui/pip/phone/PipMenuView.java8
-rw-r--r--packages/SystemUI/src/com/android/systemui/pip/phone/PipResizeGestureHandler.java39
-rw-r--r--packages/SystemUI/src/com/android/systemui/pip/phone/PipTouchHandler.java53
-rw-r--r--packages/SystemUI/src/com/android/systemui/pip/phone/PipUtils.java11
-rw-r--r--packages/SystemUI/src/com/android/systemui/pip/tv/PipController.java227
-rw-r--r--packages/SystemUI/src/com/android/systemui/pip/tv/PipControlsViewController.java9
-rw-r--r--packages/SystemUI/src/com/android/systemui/pip/tv/PipNotification.java7
-rw-r--r--packages/SystemUI/src/com/android/systemui/pip/tv/dagger/TvPipModule.java90
-rw-r--r--packages/SystemUI/src/com/android/systemui/qs/customize/TileQueryHelper.java87
-rw-r--r--packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSTileImpl.java23
-rw-r--r--packages/SystemUI/src/com/android/systemui/recents/OverviewProxyService.java29
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/dagger/NotificationsModule.java16
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRow.java31
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationGuts.java9
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayout.java46
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutController.java52
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardBouncer.java56
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManager.java18
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/tv/micdisclosure/AudioRecordingDisclosureBar.java207
-rw-r--r--packages/SystemUI/src/com/android/systemui/tv/TvSystemUIBinder.java3
-rw-r--r--packages/SystemUI/src/com/android/systemui/util/sensors/ThresholdSensorImpl.java2
-rw-r--r--packages/SystemUI/src/com/android/systemui/wmshell/TvWMShellModule.java53
-rw-r--r--packages/SystemUI/src/com/android/systemui/wmshell/WMShell.java129
-rw-r--r--packages/SystemUI/src/com/android/systemui/wmshell/WMShellBaseModule.java38
-rw-r--r--packages/SystemUI/src/com/android/systemui/wmshell/WMShellModule.java46
-rw-r--r--packages/SystemUI/src/com/android/systemui/wmshell/WindowManagerShellWrapper.java63
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/SysuiTestCase.java2
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/accessibility/WindowMagnificationControllerTest.java81
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/appops/AppOpsControllerTest.java8
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/biometrics/AuthControllerTest.java33
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/biometrics/UdfpsControllerTest.java246
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/bubbles/BubbleControllerTest.java4
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/bubbles/NewNotifPipelineBubbleControllerTest.java4
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/bubbles/TestableBubbleController.java4
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/doze/DozeScreenBrightnessTest.java22
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/keyguard/WakefulnessLifecycleTest.java6
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/media/MediaDeviceManagerTest.kt74
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/media/MediaTimeoutListenerTest.kt42
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/media/dialog/MediaOutputBaseDialogTest.java36
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/media/dialog/MediaOutputDialogTest.java105
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/pip/PipAnimationControllerTest.java14
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/pip/phone/PipControllerTest.java109
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/pip/phone/PipTaskOrganizerTest.java93
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/pip/phone/PipTouchHandlerTest.java21
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/qs/customize/TileQueryHelperTest.java141
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/recents/OverviewProxyServiceTest.java117
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRowTest.java29
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutTest.java32
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollerControllerTest.java10
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/KeyguardBouncerTest.java43
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/wmshell/WMShellTest.java51
-rw-r--r--packages/Tethering/apex/Android.bp1
-rw-r--r--packages/Tethering/bpf_progs/Android.bp33
-rw-r--r--packages/Tethering/bpf_progs/offload.c207
-rw-r--r--packages/Tethering/jni/android_net_util_TetheringUtils.cpp52
-rw-r--r--packages/Tethering/src/android/net/ip/DadProxy.java54
-rw-r--r--packages/Tethering/src/android/net/ip/IpServer.java45
-rw-r--r--packages/Tethering/src/android/net/ip/NeighborPacketForwarder.java180
-rw-r--r--packages/Tethering/src/android/net/ip/RouterAdvertisementDaemon.java16
-rw-r--r--packages/Tethering/src/android/net/util/TetheringUtils.java33
-rw-r--r--packages/Tethering/tests/integration/Android.bp1
-rw-r--r--packages/Tethering/tests/privileged/Android.bp21
-rw-r--r--packages/Tethering/tests/privileged/src/android/net/ip/DadProxyTest.java338
-rw-r--r--packages/Tethering/tests/unit/jarjar-rules.txt5
-rw-r--r--packages/Tethering/tests/unit/src/android/net/ip/IpServerTest.java91
-rw-r--r--packages/Tethering/tests/unit/src/com/android/networkstack/tethering/TetheringTest.java13
-rw-r--r--services/accessibility/java/com/android/server/accessibility/AccessibilityWindowManager.java9
-rw-r--r--services/autofill/java/com/android/server/autofill/RemoteAugmentedAutofillService.java23
-rw-r--r--services/autofill/java/com/android/server/autofill/Session.java36
-rw-r--r--services/autofill/java/com/android/server/autofill/ui/InlineFillUi.java114
-rw-r--r--services/autofill/java/com/android/server/autofill/ui/InlineSuggestionFactory.java189
-rw-r--r--services/autofill/java/com/android/server/autofill/ui/RemoteInlineSuggestionViewConnector.java22
-rw-r--r--services/companion/java/com/android/server/companion/CompanionDeviceManagerService.java5
-rw-r--r--services/core/java/android/os/BatteryStatsInternal.java7
-rw-r--r--services/core/java/com/android/server/BinderCallsStatsService.java15
-rw-r--r--services/core/java/com/android/server/BluetoothManagerService.java3
-rw-r--r--services/core/java/com/android/server/StorageManagerService.java5
-rw-r--r--services/core/java/com/android/server/TelephonyRegistry.java6
-rw-r--r--services/core/java/com/android/server/UiModeManagerService.java47
-rw-r--r--services/core/java/com/android/server/am/ActiveServices.java7
-rw-r--r--services/core/java/com/android/server/am/ActivityManagerService.java16
-rw-r--r--services/core/java/com/android/server/am/ActivityManagerShellCommand.java14
-rw-r--r--services/core/java/com/android/server/am/BatteryStatsService.java28
-rw-r--r--services/core/java/com/android/server/am/BroadcastQueue.java2
-rw-r--r--services/core/java/com/android/server/am/CachedAppOptimizer.java55
-rw-r--r--services/core/java/com/android/server/am/ProcessRecord.java11
-rw-r--r--services/core/java/com/android/server/am/ServiceRecord.java99
-rw-r--r--services/core/java/com/android/server/appop/AppOpsService.java6
-rw-r--r--services/core/java/com/android/server/audio/AudioService.java11
-rw-r--r--services/core/java/com/android/server/audio/BtHelper.java1
-rw-r--r--services/core/java/com/android/server/biometrics/SensorConfig.java4
-rw-r--r--services/core/java/com/android/server/biometrics/Utils.java19
-rw-r--r--services/core/java/com/android/server/biometrics/sensors/face/Face10.java2
-rw-r--r--services/core/java/com/android/server/biometrics/sensors/face/FaceAuthenticator.java2
-rw-r--r--services/core/java/com/android/server/biometrics/sensors/face/FaceService.java6
-rw-r--r--services/core/java/com/android/server/biometrics/sensors/fingerprint/Fingerprint21.java18
-rw-r--r--services/core/java/com/android/server/biometrics/sensors/fingerprint/Fingerprint21UdfpsMock.java13
-rw-r--r--services/core/java/com/android/server/biometrics/sensors/fingerprint/FingerprintAuthenticator.java2
-rw-r--r--services/core/java/com/android/server/biometrics/sensors/fingerprint/FingerprintService.java6
-rw-r--r--services/core/java/com/android/server/connectivity/KeepaliveTracker.java10
-rw-r--r--services/core/java/com/android/server/connectivity/Vpn.java114
-rw-r--r--services/core/java/com/android/server/display/DisplayDeviceConfig.java74
-rw-r--r--services/core/java/com/android/server/display/DisplayManagerService.java35
-rw-r--r--services/core/java/com/android/server/display/DisplayModeDirector.java60
-rw-r--r--services/core/java/com/android/server/display/LocalDisplayAdapter.java9
-rw-r--r--services/core/java/com/android/server/hdmi/HdmiCecAtomWriter.java192
-rw-r--r--services/core/java/com/android/server/hdmi/HdmiCecController.java71
-rw-r--r--services/core/java/com/android/server/hdmi/HdmiCecMessageValidator.java24
-rw-r--r--services/core/java/com/android/server/hdmi/HdmiControlService.java15
-rw-r--r--services/core/java/com/android/server/input/InputManagerService.java55
-rw-r--r--services/core/java/com/android/server/location/AbstractLocationProvider.java13
-rw-r--r--services/core/java/com/android/server/location/LocationManagerService.java10
-rw-r--r--services/core/java/com/android/server/location/LocationProviderManager.java256
-rw-r--r--services/core/java/com/android/server/location/PassiveLocationProviderManager.java2
-rw-r--r--services/core/java/com/android/server/location/geofence/GeofenceManager.java15
-rw-r--r--services/core/java/com/android/server/location/gnss/GnssAntennaInfoProvider.java4
-rw-r--r--services/core/java/com/android/server/location/gnss/GnssListenerMultiplexer.java40
-rw-r--r--services/core/java/com/android/server/location/gnss/GnssMeasurementsProvider.java5
-rw-r--r--services/core/java/com/android/server/location/gnss/GnssNavigationMessageProvider.java5
-rw-r--r--services/core/java/com/android/server/location/gnss/GnssStatusProvider.java5
-rw-r--r--services/core/java/com/android/server/location/listeners/BinderListenerRegistration.java25
-rw-r--r--services/core/java/com/android/server/location/listeners/ListenerMultiplexer.java156
-rw-r--r--services/core/java/com/android/server/location/listeners/ListenerRegistration.java60
-rw-r--r--services/core/java/com/android/server/location/listeners/PendingIntentListenerRegistration.java24
-rw-r--r--services/core/java/com/android/server/location/listeners/RemoteListenerRegistration.java17
-rw-r--r--services/core/java/com/android/server/location/listeners/RemovableListenerRegistration.java29
-rw-r--r--services/core/java/com/android/server/location/listeners/RequestListenerRegistration.java58
-rw-r--r--services/core/java/com/android/server/location/timezone/BinderLocationTimeZoneProvider.java214
-rw-r--r--services/core/java/com/android/server/location/timezone/ControllerCallbackImpl.java43
-rw-r--r--services/core/java/com/android/server/location/timezone/ControllerEnvironmentImpl.java51
-rw-r--r--services/core/java/com/android/server/location/timezone/ControllerImpl.java420
-rw-r--r--services/core/java/com/android/server/location/timezone/HandlerThreadingDomain.java73
-rw-r--r--services/core/java/com/android/server/location/timezone/LocationTimeZoneManagerService.java269
-rw-r--r--services/core/java/com/android/server/location/timezone/LocationTimeZoneManagerShellCommand.java80
-rw-r--r--services/core/java/com/android/server/location/timezone/LocationTimeZoneProvider.java511
-rw-r--r--services/core/java/com/android/server/location/timezone/LocationTimeZoneProviderController.java124
-rw-r--r--services/core/java/com/android/server/location/timezone/LocationTimeZoneProviderProxy.java119
-rw-r--r--services/core/java/com/android/server/location/timezone/NullLocationTimeZoneProvider.java97
-rw-r--r--services/core/java/com/android/server/location/timezone/SimulatedBinderProviderEvent.java162
-rw-r--r--services/core/java/com/android/server/location/timezone/SimulatedLocationTimeZoneProviderProxy.java118
-rw-r--r--services/core/java/com/android/server/location/timezone/ThreadingDomain.java120
-rw-r--r--services/core/java/com/android/server/locksettings/LockSettingsService.java12
-rw-r--r--services/core/java/com/android/server/media/MediaButtonReceiverHolder.java2
-rwxr-xr-xservices/core/java/com/android/server/notification/NotificationManagerService.java169
-rw-r--r--services/core/java/com/android/server/notification/NotificationShellCmd.java21
-rw-r--r--services/core/java/com/android/server/pm/ApkChecksums.java28
-rw-r--r--services/core/java/com/android/server/pm/PackageInstallerService.java7
-rw-r--r--services/core/java/com/android/server/pm/PackageInstallerSession.java102
-rw-r--r--services/core/java/com/android/server/pm/PackageManagerService.java252
-rw-r--r--services/core/java/com/android/server/pm/PackageManagerShellCommand.java9
-rw-r--r--services/core/java/com/android/server/pm/PackageManagerShellCommandDataLoader.java14
-rw-r--r--services/core/java/com/android/server/pm/PackageSetting.java4
-rw-r--r--services/core/java/com/android/server/pm/SELinuxMMAC.java27
-rw-r--r--services/core/java/com/android/server/pm/SettingBase.java10
-rw-r--r--services/core/java/com/android/server/pm/Settings.java230
-rw-r--r--services/core/java/com/android/server/pm/StagingManager.java58
-rw-r--r--services/core/java/com/android/server/pm/TEST_MAPPING9
-rw-r--r--services/core/java/com/android/server/pm/UserManagerService.java6
-rw-r--r--services/core/java/com/android/server/pm/permission/AppIdPermissionState.java320
-rw-r--r--services/core/java/com/android/server/pm/permission/BasePermission.java9
-rw-r--r--services/core/java/com/android/server/pm/permission/DevicePermissionState.java77
-rw-r--r--services/core/java/com/android/server/pm/permission/LegacyPermissionDataProvider.java44
-rw-r--r--services/core/java/com/android/server/pm/permission/PermissionManagerService.java1149
-rw-r--r--services/core/java/com/android/server/pm/permission/PermissionManagerServiceInternal.java22
-rw-r--r--services/core/java/com/android/server/pm/permission/PermissionState.java128
-rw-r--r--services/core/java/com/android/server/pm/permission/PermissionsState.java962
-rw-r--r--services/core/java/com/android/server/pm/permission/UidPermissionState.java551
-rw-r--r--services/core/java/com/android/server/pm/permission/UserPermissionState.java103
-rw-r--r--services/core/java/com/android/server/soundtrigger_middleware/ConversionUtil.java17
-rw-r--r--services/core/java/com/android/server/soundtrigger_middleware/ISoundTriggerMiddlewareInternal.java25
-rw-r--r--services/core/java/com/android/server/soundtrigger_middleware/SoundTriggerHw2Enforcer.java7
-rw-r--r--services/core/java/com/android/server/soundtrigger_middleware/SoundTriggerMiddlewareLogging.java216
-rw-r--r--services/core/java/com/android/server/soundtrigger_middleware/SoundTriggerMiddlewarePermission.java348
-rw-r--r--services/core/java/com/android/server/soundtrigger_middleware/SoundTriggerMiddlewareService.java113
-rw-r--r--services/core/java/com/android/server/soundtrigger_middleware/SoundTriggerMiddlewareValidation.java309
-rw-r--r--services/core/java/com/android/server/soundtrigger_middleware/SoundTriggerModule.java5
-rw-r--r--services/core/java/com/android/server/timezonedetector/TimeZoneDetectorService.java8
-rw-r--r--services/core/java/com/android/server/vr/Vr2dDisplay.java1
-rw-r--r--services/core/java/com/android/server/wm/ActivityRecord.java4
-rw-r--r--services/core/java/com/android/server/wm/ActivityStarter.java11
-rw-r--r--services/core/java/com/android/server/wm/ActivityTaskManagerService.java47
-rw-r--r--services/core/java/com/android/server/wm/BLASTSyncEngine.java20
-rw-r--r--services/core/java/com/android/server/wm/BarController.java340
-rw-r--r--services/core/java/com/android/server/wm/DisplayAreaOrganizerController.java20
-rw-r--r--services/core/java/com/android/server/wm/DisplayContent.java68
-rw-r--r--services/core/java/com/android/server/wm/DisplayPolicy.java620
-rw-r--r--services/core/java/com/android/server/wm/DragState.java15
-rw-r--r--services/core/java/com/android/server/wm/EmbeddedWindowController.java16
-rw-r--r--services/core/java/com/android/server/wm/EventLogTags.logtags2
-rw-r--r--services/core/java/com/android/server/wm/HighRefreshRateDenylist.java (renamed from services/core/java/com/android/server/wm/HighRefreshRateBlacklist.java)38
-rw-r--r--services/core/java/com/android/server/wm/InputConsumerImpl.java17
-rw-r--r--services/core/java/com/android/server/wm/InsetsPolicy.java38
-rw-r--r--services/core/java/com/android/server/wm/InsetsSourceProvider.java21
-rw-r--r--services/core/java/com/android/server/wm/InsetsStateController.java5
-rw-r--r--services/core/java/com/android/server/wm/Letterbox.java11
-rw-r--r--services/core/java/com/android/server/wm/RefreshRatePolicy.java8
-rw-r--r--services/core/java/com/android/server/wm/SafeActivityOptions.java8
-rw-r--r--services/core/java/com/android/server/wm/StatusBarController.java109
-rw-r--r--services/core/java/com/android/server/wm/Task.java18
-rw-r--r--services/core/java/com/android/server/wm/TaskDisplayArea.java57
-rw-r--r--services/core/java/com/android/server/wm/TaskPositioner.java12
-rw-r--r--services/core/java/com/android/server/wm/TaskPositioningController.java2
-rw-r--r--services/core/java/com/android/server/wm/TaskSnapshotController.java6
-rw-r--r--services/core/java/com/android/server/wm/TaskSnapshotSurface.java54
-rw-r--r--services/core/java/com/android/server/wm/WindowContainer.java3
-rw-r--r--services/core/java/com/android/server/wm/WindowManagerService.java12
-rw-r--r--services/core/java/com/android/server/wm/WindowOrganizerController.java17
-rw-r--r--services/core/java/com/android/server/wm/WindowProcessController.java19
-rw-r--r--services/core/java/com/android/server/wm/WindowState.java22
-rw-r--r--services/core/java/com/android/server/wm/WindowStateAnimator.java8
-rw-r--r--services/core/jni/com_android_server_am_CachedAppOptimizer.cpp30
-rw-r--r--services/core/jni/com_android_server_input_InputManagerService.cpp124
-rw-r--r--services/core/jni/com_android_server_pm_PackageManagerShellCommandDataLoader.cpp31
-rw-r--r--services/devicepolicy/java/com/android/server/devicepolicy/CallerIdentity.java8
-rw-r--r--services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java646
-rw-r--r--services/incremental/BinderIncrementalService.cpp12
-rw-r--r--services/incremental/BinderIncrementalService.h4
-rw-r--r--services/incremental/IncrementalService.cpp109
-rw-r--r--services/incremental/IncrementalService.h7
-rw-r--r--services/java/com/android/server/SystemServer.java14
-rw-r--r--services/tests/mockingservicestests/src/com/android/server/alarm/AlarmManagerServiceTest.java151
-rw-r--r--services/tests/mockingservicestests/src/com/android/server/job/controllers/JobStatusTest.java5
-rw-r--r--services/tests/mockingservicestests/src/com/android/server/job/controllers/QuotaControllerTest.java2
-rw-r--r--services/tests/mockingservicestests/src/com/android/server/location/LocationProviderManagerTest.java26
-rw-r--r--services/tests/mockingservicestests/src/com/android/server/location/listeners/ListenerMultiplexerTest.java21
-rw-r--r--services/tests/servicestests/src/com/android/server/am/BatteryStatsServiceTest.java122
-rw-r--r--services/tests/servicestests/src/com/android/server/biometrics/AuthServiceTest.java15
-rw-r--r--services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerTest.java2
-rw-r--r--services/tests/servicestests/src/com/android/server/display/DisplayModeDirectorTest.java38
-rw-r--r--services/tests/servicestests/src/com/android/server/display/color/GlobalSaturationTintControllerTest.java11
-rw-r--r--services/tests/servicestests/src/com/android/server/hdmi/ActiveSourceActionTest.java2
-rw-r--r--services/tests/servicestests/src/com/android/server/hdmi/ArcInitiationActionFromAvrTest.java191
-rw-r--r--services/tests/servicestests/src/com/android/server/hdmi/ArcTerminationActionFromAvrTest.java160
-rw-r--r--services/tests/servicestests/src/com/android/server/hdmi/FakeNativeWrapper.java11
-rw-r--r--services/tests/servicestests/src/com/android/server/hdmi/HdmiCecControllerTest.java28
-rw-r--r--services/tests/servicestests/src/com/android/server/hdmi/HdmiCecLocalDeviceAudioSystemTest.java4
-rw-r--r--services/tests/servicestests/src/com/android/server/hdmi/HdmiCecLocalDevicePlaybackTest.java4
-rw-r--r--services/tests/servicestests/src/com/android/server/hdmi/HdmiCecLocalDeviceTest.java5
-rw-r--r--services/tests/servicestests/src/com/android/server/hdmi/HdmiCecLocalDeviceTvTest.java4
-rw-r--r--services/tests/servicestests/src/com/android/server/hdmi/HdmiControlServiceBinderAPITest.java12
-rw-r--r--services/tests/servicestests/src/com/android/server/hdmi/HdmiControlServiceTest.java4
-rw-r--r--services/tests/servicestests/src/com/android/server/location/timezone/ControllerImplTest.java528
-rw-r--r--services/tests/servicestests/src/com/android/server/location/timezone/HandlerThreadingDomainTest.java208
-rw-r--r--services/tests/servicestests/src/com/android/server/location/timezone/NullLocationTimeZoneProviderTest.java134
-rw-r--r--services/tests/servicestests/src/com/android/server/location/timezone/TestSupport.java53
-rw-r--r--services/tests/servicestests/src/com/android/server/location/timezone/TestThreadingDomain.java128
-rw-r--r--services/tests/servicestests/src/com/android/server/locksettings/SyntheticPasswordTests.java18
-rw-r--r--services/tests/servicestests/src/com/android/server/pm/PackageManagerSettingsTests.java31
-rw-r--r--services/tests/servicestests/src/com/android/server/pm/SELinuxMMACTest.java41
-rw-r--r--services/tests/servicestests/src/com/android/server/soundtrigger_middleware/SoundTriggerMiddlewareImplTest.java3
-rw-r--r--services/tests/servicestests/src/com/android/server/timezonedetector/TestState.java23
-rw-r--r--services/tests/servicestests/src/com/android/server/usage/AppStandbyControllerTests.java32
-rwxr-xr-xservices/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java117
-rw-r--r--services/tests/wmtests/src/com/android/server/wm/ActivityStackTests.java3
-rw-r--r--services/tests/wmtests/src/com/android/server/wm/ActivityStarterTests.java7
-rw-r--r--services/tests/wmtests/src/com/android/server/wm/DisplayAreaOrganizerTest.java30
-rw-r--r--services/tests/wmtests/src/com/android/server/wm/DisplayPolicyLayoutTests.java2
-rw-r--r--services/tests/wmtests/src/com/android/server/wm/DisplayPolicyTests.java67
-rw-r--r--services/tests/wmtests/src/com/android/server/wm/DisplayPolicyTestsBase.java3
-rw-r--r--services/tests/wmtests/src/com/android/server/wm/HighRefreshRateDenylistTest.java (renamed from services/tests/wmtests/src/com/android/server/wm/HighRefreshRateBlacklistTest.java)94
-rw-r--r--services/tests/wmtests/src/com/android/server/wm/InsetsPolicyTest.java48
-rw-r--r--services/tests/wmtests/src/com/android/server/wm/InsetsStateControllerTest.java18
-rw-r--r--services/tests/wmtests/src/com/android/server/wm/RefreshRatePolicyTest.java8
-rw-r--r--services/tests/wmtests/src/com/android/server/wm/ScreenDecorWindowTests.java4
-rw-r--r--services/tests/wmtests/src/com/android/server/wm/SizeCompatTests.java2
-rw-r--r--services/tests/wmtests/src/com/android/server/wm/SystemServicesTestRule.java6
-rw-r--r--services/tests/wmtests/src/com/android/server/wm/TaskDisplayAreaTests.java55
-rw-r--r--services/tests/wmtests/src/com/android/server/wm/TaskRecordTests.java39
-rw-r--r--services/tests/wmtests/src/com/android/server/wm/TestDisplayContent.java4
-rw-r--r--services/tests/wmtests/src/com/android/server/wm/WindowManagerServiceTests.java12
-rw-r--r--services/tests/wmtests/src/com/android/server/wm/WindowOrganizerTests.java44
-rw-r--r--services/voiceinteraction/java/com/android/server/soundtrigger/SoundTriggerHelper.java18
-rw-r--r--services/voiceinteraction/java/com/android/server/soundtrigger/SoundTriggerInternal.java179
-rw-r--r--services/voiceinteraction/java/com/android/server/soundtrigger/SoundTriggerService.java1274
-rw-r--r--services/voiceinteraction/java/com/android/server/voiceinteraction/VoiceInteractionManagerService.java267
-rw-r--r--telephony/api/system-current.txt33
-rw-r--r--telephony/java/android/telephony/CallForwardingInfo.java105
-rwxr-xr-xtelephony/java/android/telephony/CarrierConfigManager.java46
-rw-r--r--telephony/java/android/telephony/CellIdentityNr.java6
-rw-r--r--telephony/java/android/telephony/TelephonyManager.java317
-rw-r--r--telephony/java/android/telephony/gsm/GsmCellLocation.java2
-rw-r--r--telephony/java/android/telephony/ims/ImsService.java76
-rw-r--r--telephony/java/android/telephony/ims/aidl/IImsServiceController.aidl8
-rw-r--r--telephony/java/android/telephony/ims/compat/ImsService.java87
-rw-r--r--telephony/java/com/android/ims/internal/IImsServiceController.aidl10
-rw-r--r--telephony/java/com/android/internal/telephony/ICallForwardingInfoCallback.aidl25
-rw-r--r--telephony/java/com/android/internal/telephony/ITelephony.aidl76
-rw-r--r--tests/FlickerTests/Android.bp6
-rw-r--r--tests/FlickerTests/src/com/android/server/wm/flicker/ime/CloseImeAutoOpenWindowToHomeTest.kt2
-rw-r--r--tests/FlickerTests/src/com/android/server/wm/flicker/ime/CloseImeWindowToAppTest.kt2
-rw-r--r--tests/FlickerTests/src/com/android/server/wm/flicker/ime/CloseImeWindowToHomeTest.kt2
-rw-r--r--tests/FlickerTests/src/com/android/server/wm/flicker/ime/OpenImeWindowTest.kt2
-rw-r--r--tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppColdTest.kt2
-rw-r--r--tests/FlickerTests/src/com/android/server/wm/flicker/splitscreen/SplitScreenToLauncherTest.kt2
-rw-r--r--tests/ManagedProfileLifecycleStressTest/src/com/android/test/stress/ManagedProfileLifecycleStressTest.java52
-rw-r--r--tests/SoundTriggerTestApp/src/com/android/test/soundtrigger/SoundTriggerTestService.java22
-rw-r--r--tests/SoundTriggerTestApp/src/com/android/test/soundtrigger/SoundTriggerUtil.java35
-rw-r--r--tests/net/integration/util/com/android/server/NetworkAgentWrapper.java16
-rw-r--r--tests/net/java/com/android/server/ConnectivityServiceTest.java26
-rw-r--r--tools/validatekeymaps/Main.cpp10
-rw-r--r--wifi/api/current.txt6
-rw-r--r--wifi/api/system-current.txt4
-rw-r--r--wifi/java/android/net/wifi/ScanResult.java21
-rw-r--r--wifi/java/android/net/wifi/SoftApInfo.java39
-rw-r--r--wifi/java/android/net/wifi/WifiConfiguration.java26
-rw-r--r--wifi/java/android/net/wifi/WifiManager.java7
-rw-r--r--wifi/java/android/net/wifi/hotspot2/pps/HomeSp.java59
-rw-r--r--wifi/tests/src/android/net/wifi/ScanResultTest.java7
-rw-r--r--wifi/tests/src/android/net/wifi/SoftApInfoTest.java18
950 files changed, 27530 insertions, 11388 deletions
diff --git a/Android.bp b/Android.bp
index c6f9362b4919..59530079d4ad 100644
--- a/Android.bp
+++ b/Android.bp
@@ -547,6 +547,7 @@ java_library {
exclude_srcs: ["core/java/android/content/pm/AndroidTestBaseUpdater.java"],
aidl: {
generate_get_transaction_name: true,
+ local_include_dirs: ["media/aidl"],
},
dxflags: [
"--core-library",
@@ -583,6 +584,7 @@ java_library {
// in favor of an API stubs dependency in java_library "framework" below.
"mimemap",
"mediatranscoding_aidl_interface-java",
+ "soundtrigger_middleware-aidl-java",
],
// For backwards compatibility.
stem: "framework",
diff --git a/ApiDocs.bp b/ApiDocs.bp
index ca921ff97c35..c82fee0fe8ac 100644
--- a/ApiDocs.bp
+++ b/ApiDocs.bp
@@ -83,6 +83,10 @@ stubs_defaults {
merge_annotations_dirs: [
"metalava-manual",
],
+ // TODO(b/169090544): remove below aidl includes.
+ aidl: {
+ local_include_dirs: ["media/aidl"],
+ },
}
droidstubs {
@@ -150,6 +154,10 @@ doc_defaults {
":current-support-api",
":current-androidx-api",
],
+ // TODO(b/169090544): remove below aidl includes.
+ aidl: {
+ local_include_dirs: ["media/aidl"],
+ },
}
doc_defaults {
diff --git a/PREUPLOAD.cfg b/PREUPLOAD.cfg
index fc5efc6e03ac..7a8d1a1caf25 100644
--- a/PREUPLOAD.cfg
+++ b/PREUPLOAD.cfg
@@ -11,6 +11,7 @@ clang_format = --commit ${PREUPLOAD_COMMIT} --style file --extensions c,h,cc,cpp
libs/input/
services/core/jni/
services/incremental/
+ tools/
[Hook Scripts]
checkstyle_hook = ${REPO_ROOT}/prebuilts/checkstyle/checkstyle.py --sha ${PREUPLOAD_COMMIT}
diff --git a/StubLibraries.bp b/StubLibraries.bp
index bb6538739c49..852fcc6128c4 100644
--- a/StubLibraries.bp
+++ b/StubLibraries.bp
@@ -51,9 +51,12 @@ stubs_defaults {
":android_icu4j_public_api_files",
"**/package.html",
],
- // TODO(b/147699819): remove below aidl includes.
+ // TODO(b/147699819, b/169090544): remove below aidl includes.
aidl: {
- local_include_dirs: ["telephony/java"],
+ local_include_dirs: [
+ "telephony/java",
+ "media/aidl",
+ ],
},
libs: ["framework-internal-utils"],
installable: false,
@@ -488,29 +491,3 @@ java_library_static {
":hwbinder-stubs-docs",
],
}
-
-/////////////////////////////////////////////////////////////////////
-// api/*-current.txt files for use by modules in other directories
-// like the CTS test
-/////////////////////////////////////////////////////////////////////
-
-filegroup {
- name: "frameworks-base-api-current.txt",
- srcs: [
- "api/current.txt",
- ],
-}
-
-filegroup {
- name: "frameworks-base-api-system-current.txt",
- srcs: [
- "api/system-current.txt",
- ],
-}
-
-filegroup {
- name: "frameworks-base-api-system-removed.txt",
- srcs: [
- "api/system-removed.txt",
- ],
-}
diff --git a/apct-tests/perftests/autofill/Android.bp b/apct-tests/perftests/autofill/Android.bp
index 4f6c21af9281..79176ff993c4 100644
--- a/apct-tests/perftests/autofill/Android.bp
+++ b/apct-tests/perftests/autofill/Android.bp
@@ -20,6 +20,7 @@ android_test {
"androidx.test.rules",
"androidx.annotation_annotation",
"apct-perftests-utils",
+ "compatibility-device-util-axt",
"collector-device-lib",
],
platform_apis: true,
diff --git a/apct-tests/perftests/autofill/src/android/view/autofill/AbstractAutofillPerfTestCase.java b/apct-tests/perftests/autofill/src/android/view/autofill/AbstractAutofillPerfTestCase.java
index 8f8fc29ee3c4..54e1860d3390 100644
--- a/apct-tests/perftests/autofill/src/android/view/autofill/AbstractAutofillPerfTestCase.java
+++ b/apct-tests/perftests/autofill/src/android/view/autofill/AbstractAutofillPerfTestCase.java
@@ -11,44 +11,54 @@
* 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
+ * limitations under the License.
*/
package android.view.autofill;
+import static com.android.compatibility.common.util.ShellUtils.runShellCommand;
+
import static org.junit.Assert.assertTrue;
import android.os.Looper;
import android.perftests.utils.PerfStatusReporter;
import android.perftests.utils.PerfTestActivity;
-import android.perftests.utils.SettingsHelper;
import android.perftests.utils.SettingsStateKeeperRule;
import android.provider.Settings;
+import android.util.Log;
import androidx.test.InstrumentationRegistry;
import androidx.test.rule.ActivityTestRule;
-import org.junit.After;
+import org.junit.AfterClass;
import org.junit.Before;
+import org.junit.BeforeClass;
import org.junit.ClassRule;
import org.junit.Rule;
+import org.junit.rules.RuleChain;
/**
* Base class for all autofill tests.
*/
public abstract class AbstractAutofillPerfTestCase {
+ private static final String TAG = "AbstractAutofillPerfTestCase";
+
@ClassRule
public static final SettingsStateKeeperRule mServiceSettingsKeeper =
new SettingsStateKeeperRule(InstrumentationRegistry.getTargetContext(),
Settings.Secure.AUTOFILL_SERVICE);
- @Rule
- public ActivityTestRule<PerfTestActivity> mActivityRule =
+ protected final AutofillTestWatcher mTestWatcher = MyAutofillService.getTestWatcher();
+ protected ActivityTestRule<PerfTestActivity> mActivityRule =
new ActivityTestRule<>(PerfTestActivity.class);
+ protected PerfStatusReporter mPerfStatusReporter = new PerfStatusReporter();
@Rule
- public PerfStatusReporter mPerfStatusReporter = new PerfStatusReporter();
+ public final RuleChain mAllRules = RuleChain
+ .outerRule(mTestWatcher)
+ .around(mPerfStatusReporter)
+ .around(mActivityRule);
private final int mLayoutId;
@@ -56,6 +66,27 @@ public abstract class AbstractAutofillPerfTestCase {
mLayoutId = layoutId;
}
+ @BeforeClass
+ public static void disableDefaultAugmentedService() {
+ Log.v(TAG, "@BeforeClass: disableDefaultAugmentedService()");
+ setDefaultAugmentedAutofillServiceEnabled(false);
+ }
+
+ @AfterClass
+ public static void enableDefaultAugmentedService() {
+ Log.v(TAG, "@AfterClass: enableDefaultAugmentedService()");
+ setDefaultAugmentedAutofillServiceEnabled(true);
+ }
+
+ /**
+ * Enables / disables the default augmented autofill service.
+ */
+ private static void setDefaultAugmentedAutofillServiceEnabled(boolean enabled) {
+ Log.d(TAG, "setDefaultAugmentedAutofillServiceEnabled(): " + enabled);
+ runShellCommand("cmd autofill set default-augmented-service-enabled 0 %s",
+ Boolean.toString(enabled));
+ }
+
/**
* Prepares the activity so that by the time the test is run it has reference to its fields.
*/
@@ -72,41 +103,8 @@ public abstract class AbstractAutofillPerfTestCase {
});
}
- @Before
- public void enableService() {
- MyAutofillService.resetStaticState();
- MyAutofillService.setEnabled(true);
- }
-
- @After
- public void disableService() {
- // Must disable service so calls are ignored in case of errors during the test case;
- // otherwise, other tests will fail because these calls are made in the UI thread (as both
- // the service, the tests, and the app run in the same process).
- MyAutofillService.setEnabled(false);
- }
-
/**
* Initializes the {@link PerfTestActivity} after it was launched.
*/
protected abstract void onCreate(PerfTestActivity activity);
-
- /**
- * Uses the {@code settings} binary to set the autofill service.
- */
- protected void setService() {
- SettingsHelper.syncSet(InstrumentationRegistry.getTargetContext(),
- SettingsHelper.NAMESPACE_SECURE,
- Settings.Secure.AUTOFILL_SERVICE,
- MyAutofillService.COMPONENT_NAME);
- }
-
- /**
- * Uses the {@code settings} binary to reset the autofill service.
- */
- protected void resetService() {
- SettingsHelper.syncDelete(InstrumentationRegistry.getTargetContext(),
- SettingsHelper.NAMESPACE_SECURE,
- Settings.Secure.AUTOFILL_SERVICE);
- }
}
diff --git a/apct-tests/perftests/autofill/src/android/view/autofill/AutofillTestWatcher.java b/apct-tests/perftests/autofill/src/android/view/autofill/AutofillTestWatcher.java
new file mode 100644
index 000000000000..2475d988f107
--- /dev/null
+++ b/apct-tests/perftests/autofill/src/android/view/autofill/AutofillTestWatcher.java
@@ -0,0 +1,162 @@
+/*
+ * 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.view.autofill;
+
+import static com.android.compatibility.common.util.ShellUtils.runShellCommand;
+
+import android.perftests.utils.SettingsHelper;
+import android.provider.Settings;
+import android.util.Log;
+
+import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
+import androidx.test.InstrumentationRegistry;
+
+import org.junit.rules.TestWatcher;
+import org.junit.runner.Description;
+
+import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.TimeUnit;
+
+/**
+ * Custom {@link TestWatcher} that does the setup and reset tasks for the tests.
+ */
+final class AutofillTestWatcher extends TestWatcher {
+
+ private static final String TAG = "AutofillTestWatcher";
+ private static final long GENERIC_TIMEOUT_MS = 10_000;
+
+ private static ServiceWatcher sServiceWatcher;
+
+ private String mOriginalLogLevel;
+
+ @Override
+ protected void starting(Description description) {
+ super.starting(description);
+ final String testName = description.getDisplayName();
+ Log.i(TAG, "Starting " + testName);
+
+ enableVerboseLog();
+ // Prepare the service before each test.
+ // Disable the current AutofillService.
+ resetAutofillService();
+ // Set MyAutofillService status enable, it can start to accept the calls.
+ enableMyAutofillService();
+ setServiceWatcher();
+ }
+
+ @Override
+ protected void finished(Description description) {
+ super.finished(description);
+ final String testName = description.getDisplayName();
+ Log.i(TAG, "Finished " + testName);
+ restoreLogLevel();
+ // Set MyAutofillService status disable, so the calls are ignored.
+ disableMyAutofillService();
+ clearServiceWatcher();
+ }
+
+ void waitServiceConnect() throws InterruptedException {
+ if (sServiceWatcher != null) {
+ Log.d(TAG, "waitServiceConnect()");
+ sServiceWatcher.waitOnConnected();
+ }
+ }
+
+ /**
+ * Uses the {@code settings} binary to set the autofill service.
+ */
+ void setAutofillService() {
+ SettingsHelper.syncSet(InstrumentationRegistry.getTargetContext(),
+ SettingsHelper.NAMESPACE_SECURE,
+ Settings.Secure.AUTOFILL_SERVICE,
+ MyAutofillService.COMPONENT_NAME);
+ }
+
+ /**
+ * Uses the {@code settings} binary to reset the autofill service.
+ */
+ void resetAutofillService() {
+ SettingsHelper.syncDelete(InstrumentationRegistry.getTargetContext(),
+ SettingsHelper.NAMESPACE_SECURE,
+ Settings.Secure.AUTOFILL_SERVICE);
+ }
+
+ private void enableMyAutofillService() {
+ MyAutofillService.resetStaticState();
+ MyAutofillService.setEnabled(true);
+ }
+
+ private void disableMyAutofillService() {
+ // Must disable service so calls are ignored in case of errors during the test case;
+ // otherwise, other tests will fail because these calls are made in the UI thread (as both
+ // the service, the tests, and the app run in the same process).
+ MyAutofillService.setEnabled(false);
+ }
+
+ private void enableVerboseLog() {
+ mOriginalLogLevel = runShellCommand("cmd autofill get log_level");
+ Log.d(TAG, "enableVerboseLog(), mOriginalLogLevel=" + mOriginalLogLevel);
+ if (!mOriginalLogLevel.equals("verbose")) {
+ runShellCommand("cmd autofill set log_level verbose");
+ }
+ }
+
+ private void restoreLogLevel() {
+ Log.d(TAG, "restoreLogLevel to " + mOriginalLogLevel);
+ if (!mOriginalLogLevel.equals("verbose")) {
+ runShellCommand("cmd autofill set log_level %s", mOriginalLogLevel);
+ }
+ }
+
+ private static void setServiceWatcher() {
+ if (sServiceWatcher == null) {
+ sServiceWatcher = new ServiceWatcher();
+ }
+ }
+
+ private static void clearServiceWatcher() {
+ if (sServiceWatcher != null) {
+ sServiceWatcher = null;
+ }
+ }
+
+ public static final class ServiceWatcher {
+ private final CountDownLatch mConnected = new CountDownLatch(1);
+
+ public static void onConnected() {
+ Log.i(TAG, "onConnected: sServiceWatcher=" + sServiceWatcher);
+
+ sServiceWatcher.mConnected.countDown();
+ }
+
+ @NonNull
+ public void waitOnConnected() throws InterruptedException {
+ await(mConnected, "not connected");
+ }
+
+ private void await(@NonNull CountDownLatch latch, @NonNull String fmt,
+ @Nullable Object... args)
+ throws InterruptedException {
+ final boolean called = latch.await(GENERIC_TIMEOUT_MS, TimeUnit.MILLISECONDS);
+ if (!called) {
+ throw new IllegalStateException(String.format(fmt, args)
+ + " in " + GENERIC_TIMEOUT_MS + "ms");
+ }
+ }
+ }
+}
diff --git a/apct-tests/perftests/autofill/src/android/view/autofill/LoginTest.java b/apct-tests/perftests/autofill/src/android/view/autofill/LoginTest.java
index 5f52dc782422..37b4bde034df 100644
--- a/apct-tests/perftests/autofill/src/android/view/autofill/LoginTest.java
+++ b/apct-tests/perftests/autofill/src/android/view/autofill/LoginTest.java
@@ -11,7 +11,7 @@
* 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
+ * limitations under the License.
*/
package android.view.autofill;
@@ -55,15 +55,15 @@ public class LoginTest extends AbstractAutofillPerfTestCase {
*/
@Test
public void testFocus_noService() throws Throwable {
- resetService();
+ mTestWatcher.resetAutofillService();
- mActivityRule.runOnUiThread(() -> {
- BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
- while (state.keepRunning()) {
+ BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+ while (state.keepRunning()) {
+ mActivityRule.runOnUiThread(() -> {
mUsername.requestFocus();
mPassword.requestFocus();
- }
- });
+ });
+ }
}
/**
@@ -73,7 +73,7 @@ public class LoginTest extends AbstractAutofillPerfTestCase {
@Test
public void testFocus_serviceDoesNotAutofill() throws Throwable {
MyAutofillService.newCannedResponse().reply();
- setService();
+ mTestWatcher.setAutofillService();
// Must first focus in a field to trigger autofill and wait for service response
// outside the loop
@@ -82,13 +82,13 @@ public class LoginTest extends AbstractAutofillPerfTestCase {
// Then focus on password so loop start with focus away from username
mActivityRule.runOnUiThread(() -> mPassword.requestFocus());
- mActivityRule.runOnUiThread(() -> {
- BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
- while (state.keepRunning()) {
+ BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+ while (state.keepRunning()) {
+ mActivityRule.runOnUiThread(() -> {
mUsername.requestFocus();
mPassword.requestFocus();
- }
- });
+ });
+ }
}
/**
@@ -102,7 +102,7 @@ public class LoginTest extends AbstractAutofillPerfTestCase {
.setUsername(mUsername.getAutofillId(), "user")
.setPassword(mPassword.getAutofillId(), "pass")
.reply();
- setService();
+ mTestWatcher.setAutofillService();
// Callback is used to slow down the calls made to the autofill server so the
// app is not crashed due to binder exhaustion. But the time spent waiting for the callbacks
@@ -157,7 +157,7 @@ public class LoginTest extends AbstractAutofillPerfTestCase {
.setUsername(mUsername.getAutofillId(), "user")
.setIgnored(mPassword.getAutofillId())
.reply();
- setService();
+ mTestWatcher.setAutofillService();
// Callback is used to slow down the calls made to the autofill server so the
// app is not crashed due to binder exhaustion. But the time spent waiting for the callbacks
@@ -201,7 +201,7 @@ public class LoginTest extends AbstractAutofillPerfTestCase {
*/
@Test
public void testChange_noService() throws Throwable {
- resetService();
+ mTestWatcher.resetAutofillService();
changeTest(false);
}
@@ -213,7 +213,7 @@ public class LoginTest extends AbstractAutofillPerfTestCase {
@Test
public void testChange_serviceDoesNotAutofill() throws Throwable {
MyAutofillService.newCannedResponse().reply();
- setService();
+ mTestWatcher.setAutofillService();
changeTest(true);
}
@@ -227,7 +227,7 @@ public class LoginTest extends AbstractAutofillPerfTestCase {
.setUsername(mUsername.getAutofillId(), "user")
.setPassword(mPassword.getAutofillId(), "pass")
.reply();
- setService();
+ mTestWatcher.setAutofillService();
changeTest(true);
}
@@ -242,7 +242,7 @@ public class LoginTest extends AbstractAutofillPerfTestCase {
.setUsername(mUsername.getAutofillId(), "user")
.setIgnored(mPassword.getAutofillId())
.reply();
- setService();
+ mTestWatcher.setAutofillService();
changeTest(true);
}
@@ -252,18 +252,18 @@ public class LoginTest extends AbstractAutofillPerfTestCase {
// outside the loop
mActivityRule.runOnUiThread(() -> mUsername.requestFocus());
if (waitForService) {
+ mTestWatcher.waitServiceConnect();
MyAutofillService.getLastFillRequest();
}
- mActivityRule.runOnUiThread(() -> {
-
- BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
- while (state.keepRunning()) {
+ BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+ while (state.keepRunning()) {
+ mActivityRule.runOnUiThread(() -> {
mUsername.setText("");
mUsername.setText("a");
mPassword.setText("");
mPassword.setText("x");
- }
- });
+ });
+ }
}
// TODO(b/162216576): fix fail test and re-enable it
@@ -274,7 +274,7 @@ public class LoginTest extends AbstractAutofillPerfTestCase {
.setUsername(mUsername.getAutofillId(), "user")
.setPassword(mPassword.getAutofillId(), "pass")
.reply();
- setService();
+ mTestWatcher.setAutofillService();
MyAutofillCallback callback = new MyAutofillCallback();
mAfm.registerCallback(callback);
diff --git a/apct-tests/perftests/autofill/src/android/view/autofill/MyAutofillService.java b/apct-tests/perftests/autofill/src/android/view/autofill/MyAutofillService.java
index 7060233fc80f..ddac68b93ebd 100644
--- a/apct-tests/perftests/autofill/src/android/view/autofill/MyAutofillService.java
+++ b/apct-tests/perftests/autofill/src/android/view/autofill/MyAutofillService.java
@@ -11,7 +11,7 @@
* 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
+ * limitations under the License.
*/
package android.view.autofill;
@@ -28,12 +28,9 @@ import android.util.Pair;
import android.widget.RemoteViews;
import androidx.annotation.NonNull;
-import androidx.annotation.Nullable;
import com.android.perftests.autofill.R;
-import java.util.ArrayList;
-import java.util.List;
import java.util.concurrent.BlockingQueue;
import java.util.concurrent.LinkedBlockingQueue;
import java.util.concurrent.TimeUnit;
@@ -44,7 +41,7 @@ import java.util.concurrent.TimeUnit;
public class MyAutofillService extends AutofillService {
private static final String TAG = "MyAutofillService";
- private static final int TIMEOUT_MS = 5000;
+ private static final int TIMEOUT_MS = 5_000;
private static final String PACKAGE_NAME = "com.android.perftests.autofill";
static final String COMPONENT_NAME = PACKAGE_NAME + "/android.view.autofill.MyAutofillService";
@@ -56,6 +53,14 @@ public class MyAutofillService extends AutofillService {
private static boolean sEnabled;
/**
+ * Returns the TestWatcher that was used for the testing.
+ */
+ @NonNull
+ public static AutofillTestWatcher getTestWatcher() {
+ return new AutofillTestWatcher();
+ }
+
+ /**
* Resets the static state associated with the service.
*/
static void resetStaticState() {
@@ -93,6 +98,11 @@ public class MyAutofillService extends AutofillService {
}
@Override
+ public void onConnected() {
+ AutofillTestWatcher.ServiceWatcher.onConnected();
+ }
+
+ @Override
public void onFillRequest(FillRequest request, CancellationSignal cancellationSignal,
FillCallback callback) {
try {
@@ -116,6 +126,7 @@ public class MyAutofillService extends AutofillService {
onError("ignoring onFillRequest(): response not set", callback);
return;
}
+ // TODO(b/162216576): fix error FillResponse
Dataset.Builder dataset = new Dataset.Builder(newDatasetPresentation("dataset"));
boolean hasData = false;
if (response.mUsername != null) {
diff --git a/apex/jobscheduler/framework/java/android/app/job/JobScheduler.java b/apex/jobscheduler/framework/java/android/app/job/JobScheduler.java
index 42725c51fd87..bf5781beb52b 100644
--- a/apex/jobscheduler/framework/java/android/app/job/JobScheduler.java
+++ b/apex/jobscheduler/framework/java/android/app/job/JobScheduler.java
@@ -101,10 +101,15 @@ public abstract class JobScheduler {
* version {@link android.os.Build.VERSION_CODES#Q}. As such, the system may throttle calls to
* this API if calls are made too frequently in a short amount of time.
*
+ * <p>Note: The JobService component needs to be enabled in order to successfully schedule a
+ * job.
+ *
* @param job The job you wish scheduled. See
* {@link android.app.job.JobInfo.Builder JobInfo.Builder} for more detail on the sorts of jobs
* you can schedule.
* @return the result of the schedule request.
+ * @throws IllegalArgumentException if the specified {@link JobService} doesn't exist or is
+ * disabled.
*/
public abstract @Result int schedule(@NonNull JobInfo job);
@@ -137,11 +142,21 @@ public abstract class JobScheduler {
* work you are enqueue, since currently this will always be treated as a different JobInfo,
* even if the ClipData contents are exactly the same.</p>
*
+ * <p class="caution"><strong>Note:</strong> Scheduling a job can have a high cost, even if it's
+ * rescheduling the same job and the job didn't execute, especially on platform versions before
+ * version {@link android.os.Build.VERSION_CODES#Q}. As such, the system may throttle calls to
+ * this API if calls are made too frequently in a short amount of time.
+ *
+ * <p>Note: The JobService component needs to be enabled in order to successfully schedule a
+ * job.
+ *
* @param job The job you wish to enqueue work for. See
* {@link android.app.job.JobInfo.Builder JobInfo.Builder} for more detail on the sorts of jobs
* you can schedule.
* @param work New work to enqueue. This will be available later when the job starts running.
* @return the result of the enqueue request.
+ * @throws IllegalArgumentException if the specified {@link JobService} doesn't exist or is
+ * disabled.
*/
public abstract @Result int enqueue(@NonNull JobInfo job, @NonNull JobWorkItem work);
diff --git a/apex/jobscheduler/service/java/com/android/server/alarm/AlarmManagerService.java b/apex/jobscheduler/service/java/com/android/server/alarm/AlarmManagerService.java
index ec7e99e78cb3..05910a5b171e 100644
--- a/apex/jobscheduler/service/java/com/android/server/alarm/AlarmManagerService.java
+++ b/apex/jobscheduler/service/java/com/android/server/alarm/AlarmManagerService.java
@@ -26,6 +26,7 @@ import static android.app.AlarmManager.RTC_WAKEUP;
import static android.content.pm.PackageManager.MATCH_SYSTEM_ONLY;
import static android.os.UserHandle.USER_SYSTEM;
+import android.annotation.NonNull;
import android.annotation.UserIdInt;
import android.app.Activity;
import android.app.ActivityManagerInternal;
@@ -39,12 +40,10 @@ import android.app.PendingIntent;
import android.app.usage.UsageStatsManager;
import android.app.usage.UsageStatsManagerInternal;
import android.content.BroadcastReceiver;
-import android.content.ContentResolver;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
import android.content.pm.PackageManagerInternal;
-import android.database.ContentObserver;
import android.net.Uri;
import android.os.BatteryManager;
import android.os.Binder;
@@ -68,6 +67,7 @@ import android.os.ThreadLocalWorkSource;
import android.os.Trace;
import android.os.UserHandle;
import android.os.WorkSource;
+import android.provider.DeviceConfig;
import android.provider.Settings;
import android.system.Os;
import android.text.TextUtils;
@@ -76,7 +76,6 @@ import android.text.format.DateUtils;
import android.util.ArrayMap;
import android.util.ArraySet;
import android.util.IndentingPrintWriter;
-import android.util.KeyValueListParser;
import android.util.Log;
import android.util.LongArrayQueue;
import android.util.MutableBoolean;
@@ -102,6 +101,7 @@ import com.android.server.AppStateTrackerImpl;
import com.android.server.AppStateTrackerImpl.Listener;
import com.android.server.DeviceIdleInternal;
import com.android.server.EventLogTags;
+import com.android.server.JobSchedulerBackgroundThread;
import com.android.server.LocalServices;
import com.android.server.SystemService;
import com.android.server.SystemServiceManager;
@@ -252,7 +252,7 @@ public class AlarmManagerService extends SystemService {
final ArrayList<IdleDispatchEntry> mAllowWhileIdleDispatches = new ArrayList();
interface Stats {
- int REORDER_ALARMS_FOR_STANDBY = 1;
+ int REORDER_ALARMS_FOR_STANDBY = 0;
}
private final StatLogger mStatLogger = new StatLogger("Alarm manager stats", new String[]{
@@ -374,7 +374,7 @@ public class AlarmManagerService extends SystemService {
* holding the AlarmManagerService.mLock lock.
*/
@VisibleForTesting
- final class Constants extends ContentObserver {
+ final class Constants implements DeviceConfig.OnPropertiesChangedListener {
// Key names stored in the settings value.
@VisibleForTesting
static final String KEY_MIN_FUTURITY = "min_futurity";
@@ -394,17 +394,19 @@ public class AlarmManagerService extends SystemService {
@VisibleForTesting
static final String KEY_MAX_ALARMS_PER_UID = "max_alarms_per_uid";
private static final String KEY_APP_STANDBY_WINDOW = "app_standby_window";
+ private static final String KEY_PREFIX_STANDBY_QUOTA = "standby_quota_";
@VisibleForTesting
final String[] KEYS_APP_STANDBY_QUOTAS = {
- "standby_active_quota",
- "standby_working_quota",
- "standby_frequent_quota",
- "standby_rare_quota",
- "standby_never_quota",
+ KEY_PREFIX_STANDBY_QUOTA + "active",
+ KEY_PREFIX_STANDBY_QUOTA + "working",
+ KEY_PREFIX_STANDBY_QUOTA + "frequent",
+ KEY_PREFIX_STANDBY_QUOTA + "rare",
+ KEY_PREFIX_STANDBY_QUOTA + "never",
};
// 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_QUOTA =
+ KEY_PREFIX_STANDBY_QUOTA + "restricted";
private static final String KEY_APP_STANDBY_RESTRICTED_WINDOW =
"app_standby_restricted_window";
@@ -458,20 +460,18 @@ public class AlarmManagerService extends SystemService {
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(',');
private long mLastAllowWhileIdleWhitelistDuration = -1;
- public Constants(Handler handler) {
- super(handler);
+ Constants() {
updateAllowWhileIdleWhitelistDurationLocked();
+ for (int i = 0; i < APP_STANDBY_QUOTAS.length; i++) {
+ APP_STANDBY_QUOTAS[i] = DEFAULT_APP_STANDBY_QUOTAS[i];
+ }
}
- public void start(ContentResolver resolver) {
- mResolver = resolver;
- mResolver.registerContentObserver(Settings.Global.getUriFor(
- Settings.Global.ALARM_MANAGER_CONSTANTS), false, this);
- updateConstants();
+ public void start() {
+ mInjector.registerDeviceConfigListener(this);
+ onPropertiesChanged(DeviceConfig.getProperties(DeviceConfig.NAMESPACE_ALARM_MANAGER));
}
public void updateAllowWhileIdleWhitelistDurationLocked() {
@@ -484,71 +484,115 @@ public class AlarmManagerService extends SystemService {
}
@Override
- public void onChange(boolean selfChange, Uri uri) {
- updateConstants();
- }
-
- private void updateConstants() {
+ public void onPropertiesChanged(@NonNull DeviceConfig.Properties properties) {
+ boolean standbyQuotaUpdated = false;
synchronized (mLock) {
- try {
- mParser.setString(Settings.Global.getString(mResolver,
- Settings.Global.ALARM_MANAGER_CONSTANTS));
- } catch (IllegalArgumentException e) {
- // Failed to parse the settings string, log this and move on
- // with defaults.
- Slog.e(TAG, "Bad alarm manager settings", e);
- }
-
- MIN_FUTURITY = mParser.getLong(KEY_MIN_FUTURITY, DEFAULT_MIN_FUTURITY);
- MIN_INTERVAL = mParser.getLong(KEY_MIN_INTERVAL, DEFAULT_MIN_INTERVAL);
- MAX_INTERVAL = mParser.getLong(KEY_MAX_INTERVAL, DEFAULT_MAX_INTERVAL);
- ALLOW_WHILE_IDLE_SHORT_TIME = mParser.getLong(KEY_ALLOW_WHILE_IDLE_SHORT_TIME,
- DEFAULT_ALLOW_WHILE_IDLE_SHORT_TIME);
- ALLOW_WHILE_IDLE_LONG_TIME = mParser.getLong(KEY_ALLOW_WHILE_IDLE_LONG_TIME,
- DEFAULT_ALLOW_WHILE_IDLE_LONG_TIME);
- ALLOW_WHILE_IDLE_WHITELIST_DURATION = mParser.getLong(
- KEY_ALLOW_WHILE_IDLE_WHITELIST_DURATION,
- DEFAULT_ALLOW_WHILE_IDLE_WHITELIST_DURATION);
- LISTENER_TIMEOUT = mParser.getLong(KEY_LISTENER_TIMEOUT,
- DEFAULT_LISTENER_TIMEOUT);
-
- APP_STANDBY_WINDOW = mParser.getLong(KEY_APP_STANDBY_WINDOW,
- DEFAULT_APP_STANDBY_WINDOW);
- if (APP_STANDBY_WINDOW > DEFAULT_APP_STANDBY_WINDOW) {
- Slog.w(TAG, "Cannot exceed the app_standby_window size of "
- + DEFAULT_APP_STANDBY_WINDOW);
- APP_STANDBY_WINDOW = DEFAULT_APP_STANDBY_WINDOW;
- } else if (APP_STANDBY_WINDOW < DEFAULT_APP_STANDBY_WINDOW) {
- // Not recommended outside of testing.
- Slog.w(TAG, "Using a non-default app_standby_window of " + APP_STANDBY_WINDOW);
- }
+ for (String name : properties.getKeyset()) {
+ if (name == null) {
+ continue;
+ }
- APP_STANDBY_QUOTAS[ACTIVE_INDEX] = mParser.getInt(
- KEYS_APP_STANDBY_QUOTAS[ACTIVE_INDEX],
- DEFAULT_APP_STANDBY_QUOTAS[ACTIVE_INDEX]);
- for (int i = WORKING_INDEX; i < KEYS_APP_STANDBY_QUOTAS.length; i++) {
- APP_STANDBY_QUOTAS[i] = mParser.getInt(KEYS_APP_STANDBY_QUOTAS[i],
- Math.min(APP_STANDBY_QUOTAS[i - 1], DEFAULT_APP_STANDBY_QUOTAS[i]));
+ switch (name) {
+ case KEY_MIN_FUTURITY:
+ MIN_FUTURITY = properties.getLong(
+ KEY_MIN_FUTURITY, DEFAULT_MIN_FUTURITY);
+ break;
+ case KEY_MIN_INTERVAL:
+ MIN_INTERVAL = properties.getLong(
+ KEY_MIN_INTERVAL, DEFAULT_MIN_INTERVAL);
+ break;
+ case KEY_MAX_INTERVAL:
+ MAX_INTERVAL = properties.getLong(
+ KEY_MAX_INTERVAL, DEFAULT_MAX_INTERVAL);
+ break;
+ case KEY_ALLOW_WHILE_IDLE_SHORT_TIME:
+ ALLOW_WHILE_IDLE_SHORT_TIME = properties.getLong(
+ KEY_ALLOW_WHILE_IDLE_SHORT_TIME,
+ DEFAULT_ALLOW_WHILE_IDLE_SHORT_TIME);
+ break;
+ case KEY_ALLOW_WHILE_IDLE_LONG_TIME:
+ ALLOW_WHILE_IDLE_LONG_TIME = properties.getLong(
+ KEY_ALLOW_WHILE_IDLE_LONG_TIME,
+ DEFAULT_ALLOW_WHILE_IDLE_LONG_TIME);
+ break;
+ case KEY_ALLOW_WHILE_IDLE_WHITELIST_DURATION:
+ ALLOW_WHILE_IDLE_WHITELIST_DURATION = properties.getLong(
+ KEY_ALLOW_WHILE_IDLE_WHITELIST_DURATION,
+ DEFAULT_ALLOW_WHILE_IDLE_WHITELIST_DURATION);
+ updateAllowWhileIdleWhitelistDurationLocked();
+ break;
+ case KEY_LISTENER_TIMEOUT:
+ LISTENER_TIMEOUT = properties.getLong(
+ KEY_LISTENER_TIMEOUT, DEFAULT_LISTENER_TIMEOUT);
+ break;
+ case KEY_MAX_ALARMS_PER_UID:
+ MAX_ALARMS_PER_UID = properties.getInt(
+ KEY_MAX_ALARMS_PER_UID, DEFAULT_MAX_ALARMS_PER_UID);
+ if (MAX_ALARMS_PER_UID < DEFAULT_MAX_ALARMS_PER_UID) {
+ Slog.w(TAG, "Cannot set " + KEY_MAX_ALARMS_PER_UID + " lower than "
+ + DEFAULT_MAX_ALARMS_PER_UID);
+ MAX_ALARMS_PER_UID = DEFAULT_MAX_ALARMS_PER_UID;
+ }
+ break;
+ case KEY_APP_STANDBY_WINDOW:
+ case KEY_APP_STANDBY_RESTRICTED_WINDOW:
+ updateStandbyWindowsLocked();
+ break;
+ default:
+ if (name.startsWith(KEY_PREFIX_STANDBY_QUOTA) && !standbyQuotaUpdated) {
+ // The quotas need to be updated in order, so we can't just rely
+ // on the property iteration order.
+ updateStandbyQuotasLocked();
+ standbyQuotaUpdated = true;
+ }
+ break;
+ }
}
+ }
+ }
- APP_STANDBY_RESTRICTED_QUOTA = Math.max(1,
- mParser.getInt(KEY_APP_STANDBY_RESTRICTED_QUOTA,
- DEFAULT_APP_STANDBY_RESTRICTED_QUOTA));
+ private void updateStandbyQuotasLocked() {
+ // The bucket quotas need to be read as an atomic unit but the properties passed to
+ // onPropertiesChanged may only have one key populated at a time.
+ final DeviceConfig.Properties properties = DeviceConfig.getProperties(
+ DeviceConfig.NAMESPACE_ALARM_MANAGER, KEYS_APP_STANDBY_QUOTAS);
- APP_STANDBY_RESTRICTED_WINDOW = Math.max(APP_STANDBY_WINDOW,
- mParser.getLong(KEY_APP_STANDBY_RESTRICTED_WINDOW,
- DEFAULT_APP_STANDBY_RESTRICTED_WINDOW));
+ APP_STANDBY_QUOTAS[ACTIVE_INDEX] = properties.getInt(
+ KEYS_APP_STANDBY_QUOTAS[ACTIVE_INDEX],
+ DEFAULT_APP_STANDBY_QUOTAS[ACTIVE_INDEX]);
+ for (int i = WORKING_INDEX; i < KEYS_APP_STANDBY_QUOTAS.length; i++) {
+ APP_STANDBY_QUOTAS[i] = properties.getInt(
+ KEYS_APP_STANDBY_QUOTAS[i],
+ Math.min(APP_STANDBY_QUOTAS[i - 1], DEFAULT_APP_STANDBY_QUOTAS[i]));
+ }
- 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) {
- Slog.w(TAG, "Cannot set " + KEY_MAX_ALARMS_PER_UID + " lower than "
- + DEFAULT_MAX_ALARMS_PER_UID);
- MAX_ALARMS_PER_UID = DEFAULT_MAX_ALARMS_PER_UID;
- }
+ APP_STANDBY_RESTRICTED_QUOTA = Math.max(1,
+ DeviceConfig.getInt(DeviceConfig.NAMESPACE_ALARM_MANAGER,
+ KEY_APP_STANDBY_RESTRICTED_QUOTA,
+ DEFAULT_APP_STANDBY_RESTRICTED_QUOTA));
+ }
- updateAllowWhileIdleWhitelistDurationLocked();
+ private void updateStandbyWindowsLocked() {
+ // The bucket windows need to be read as an atomic unit but the properties passed to
+ // onPropertiesChanged may only have one key populated at a time.
+ final DeviceConfig.Properties properties = DeviceConfig.getProperties(
+ DeviceConfig.NAMESPACE_ALARM_MANAGER,
+ KEY_APP_STANDBY_WINDOW, KEY_APP_STANDBY_RESTRICTED_WINDOW);
+ APP_STANDBY_WINDOW = properties.getLong(
+ KEY_APP_STANDBY_WINDOW, DEFAULT_APP_STANDBY_WINDOW);
+ if (APP_STANDBY_WINDOW > DEFAULT_APP_STANDBY_WINDOW) {
+ Slog.w(TAG, "Cannot exceed the app_standby_window size of "
+ + DEFAULT_APP_STANDBY_WINDOW);
+ APP_STANDBY_WINDOW = DEFAULT_APP_STANDBY_WINDOW;
+ } else if (APP_STANDBY_WINDOW < DEFAULT_APP_STANDBY_WINDOW) {
+ // Not recommended outside of testing.
+ Slog.w(TAG, "Using a non-default app_standby_window of " + APP_STANDBY_WINDOW);
}
+
+ APP_STANDBY_RESTRICTED_WINDOW = Math.max(APP_STANDBY_WINDOW,
+ properties.getLong(
+ KEY_APP_STANDBY_RESTRICTED_WINDOW,
+ DEFAULT_APP_STANDBY_RESTRICTED_WINDOW));
}
void dump(PrintWriter pw, String prefix) {
@@ -1199,7 +1243,7 @@ public class AlarmManagerService extends SystemService {
synchronized (mLock) {
mHandler = new AlarmHandler();
- mConstants = new Constants(mHandler);
+ mConstants = new Constants();
mAppWakeupHistory = new AppWakeupHistory(Constants.DEFAULT_APP_STANDBY_WINDOW);
mNextWakeup = mNextNonWakeup = 0;
@@ -1284,7 +1328,7 @@ public class AlarmManagerService extends SystemService {
public void onBootPhase(int phase) {
if (phase == PHASE_SYSTEM_SERVICES_READY) {
synchronized (mLock) {
- mConstants.start(getContext().getContentResolver());
+ mConstants.start();
mAppOps = (AppOpsManager) getContext().getSystemService(Context.APP_OPS_SERVICE);
mLocalDeviceIdleController =
LocalServices.getService(DeviceIdleInternal.class);
@@ -2722,6 +2766,14 @@ public class AlarmManagerService extends SystemService {
mPendingBackgroundAlarms.removeAt(i);
}
}
+ for (int i = mPendingNonWakeupAlarms.size() - 1; i >= 0; i--) {
+ final Alarm a = mPendingNonWakeupAlarms.get(i);
+ if (a.matches(operation, directReceiver)) {
+ // Don't set didRemove, since this doesn't impact the scheduled alarms.
+ mPendingNonWakeupAlarms.remove(i);
+ decrementAlarmCount(a.uid, 1);
+ }
+ }
if (didRemove) {
if (DEBUG_BATCH) {
Slog.v(TAG, "remove(operation) changed bounds; rebatching");
@@ -3374,6 +3426,11 @@ public class AlarmManagerService extends SystemService {
ClockReceiver getClockReceiver(AlarmManagerService service) {
return service.new ClockReceiver();
}
+
+ void registerDeviceConfigListener(DeviceConfig.OnPropertiesChangedListener listener) {
+ DeviceConfig.addOnPropertiesChangedListener(DeviceConfig.NAMESPACE_ALARM_MANAGER,
+ JobSchedulerBackgroundThread.getExecutor(), listener);
+ }
}
private class AlarmThread extends Thread {
diff --git a/apex/jobscheduler/service/java/com/android/server/alarm/BatchingAlarmStore.java b/apex/jobscheduler/service/java/com/android/server/alarm/BatchingAlarmStore.java
index a08c22213c1f..91c0c0579c69 100644
--- a/apex/jobscheduler/service/java/com/android/server/alarm/BatchingAlarmStore.java
+++ b/apex/jobscheduler/service/java/com/android/server/alarm/BatchingAlarmStore.java
@@ -46,7 +46,7 @@ public class BatchingAlarmStore implements AlarmStore {
private AlarmClockRemovalListener mAlarmClockRemovalListener;
interface Stats {
- int REBATCH_ALL_ALARMS = 1;
+ int REBATCH_ALL_ALARMS = 0;
}
final StatLogger mStatLogger = new StatLogger("Alarm store stats", new String[]{
diff --git a/apex/jobscheduler/service/java/com/android/server/job/JobSchedulerService.java b/apex/jobscheduler/service/java/com/android/server/job/JobSchedulerService.java
index cf4caea9c487..4512d77e650e 100644
--- a/apex/jobscheduler/service/java/com/android/server/job/JobSchedulerService.java
+++ b/apex/jobscheduler/service/java/com/android/server/job/JobSchedulerService.java
@@ -87,11 +87,11 @@ import com.android.server.AppStateTrackerImpl;
import com.android.server.DeviceIdleInternal;
import com.android.server.JobSchedulerBackgroundThread;
import com.android.server.LocalServices;
-import com.android.server.SystemService.TargetUser;
import com.android.server.job.JobSchedulerServiceDumpProto.ActiveJob;
import com.android.server.job.JobSchedulerServiceDumpProto.PendingJob;
import com.android.server.job.controllers.BackgroundJobsController;
import com.android.server.job.controllers.BatteryController;
+import com.android.server.job.controllers.ComponentController;
import com.android.server.job.controllers.ConnectivityController;
import com.android.server.job.controllers.ContentObserverController;
import com.android.server.job.controllers.DeviceIdleJobsController;
@@ -1484,6 +1484,7 @@ public class JobSchedulerService extends com.android.server.SystemService
mControllers.add(mDeviceIdleJobsController);
mQuotaController = new QuotaController(this);
mControllers.add(mQuotaController);
+ mControllers.add(new ComponentController(this));
mRestrictiveControllers = new ArrayList<>();
mRestrictiveControllers.add(mBatteryController);
@@ -2300,21 +2301,12 @@ public class JobSchedulerService extends com.android.server.SystemService
return false;
}
- // The expensive check: validate that the defined package+service is
- // still present & viable.
+ // Validate that the defined package+service is still present & viable.
return isComponentUsable(job);
}
private boolean isComponentUsable(@NonNull JobStatus job) {
- final ServiceInfo service;
- try {
- // TODO: cache result until we're notified that something in the package changed.
- service = AppGlobals.getPackageManager().getServiceInfo(
- job.getServiceComponent(), PackageManager.MATCH_DEBUG_TRIAGED_MISSING,
- job.getUserId());
- } catch (RemoteException e) {
- throw new RuntimeException(e);
- }
+ final ServiceInfo service = job.serviceInfo;
if (service == null) {
if (DEBUG) {
@@ -2598,13 +2590,14 @@ public class JobSchedulerService extends com.android.server.SystemService
// job that runs one of the app's services, as well as verifying that the
// named service properly requires the BIND_JOB_SERVICE permission
private void enforceValidJobRequest(int uid, JobInfo job) {
- final IPackageManager pm = AppGlobals.getPackageManager();
+ final PackageManager pm = getContext()
+ .createContextAsUser(UserHandle.getUserHandleForUid(uid), 0)
+ .getPackageManager();
final ComponentName service = job.getService();
try {
ServiceInfo si = pm.getServiceInfo(service,
PackageManager.MATCH_DIRECT_BOOT_AWARE
- | PackageManager.MATCH_DIRECT_BOOT_UNAWARE,
- UserHandle.getUserId(uid));
+ | PackageManager.MATCH_DIRECT_BOOT_UNAWARE);
if (si == null) {
throw new IllegalArgumentException("No such service " + service);
}
@@ -2616,8 +2609,10 @@ public class JobSchedulerService extends com.android.server.SystemService
throw new IllegalArgumentException("Scheduled service " + service
+ " does not require android.permission.BIND_JOB_SERVICE permission");
}
- } catch (RemoteException e) {
- // Can't happen; the Package Manager is in this same process
+ } catch (NameNotFoundException e) {
+ throw new IllegalArgumentException(
+ "Tried to schedule job for non-existent package: "
+ + service.getPackageName());
}
}
@@ -3104,7 +3099,7 @@ public class JobSchedulerService extends com.android.server.SystemService
try {
componentPresent = (AppGlobals.getPackageManager().getServiceInfo(
js.getServiceComponent(),
- PackageManager.MATCH_DEBUG_TRIAGED_MISSING,
+ PackageManager.MATCH_DIRECT_BOOT_AUTO,
js.getUserId()) != null);
} catch (RemoteException e) {
}
diff --git a/apex/jobscheduler/service/java/com/android/server/job/controllers/ComponentController.java b/apex/jobscheduler/service/java/com/android/server/job/controllers/ComponentController.java
new file mode 100644
index 000000000000..999c53fb7daf
--- /dev/null
+++ b/apex/jobscheduler/service/java/com/android/server/job/controllers/ComponentController.java
@@ -0,0 +1,203 @@
+/*
+ * 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.job.controllers;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.content.BroadcastReceiver;
+import android.content.ComponentName;
+import android.content.Context;
+import android.content.Intent;
+import android.content.IntentFilter;
+import android.content.pm.PackageManager;
+import android.content.pm.PackageManager.NameNotFoundException;
+import android.content.pm.ServiceInfo;
+import android.net.Uri;
+import android.os.UserHandle;
+import android.util.IndentingPrintWriter;
+import android.util.Log;
+import android.util.Slog;
+import android.util.SparseArrayMap;
+import android.util.proto.ProtoOutputStream;
+
+import com.android.server.job.JobSchedulerService;
+
+import java.util.Objects;
+import java.util.function.Consumer;
+import java.util.function.Predicate;
+
+/**
+ * Controller that tracks changes in the service component's enabled state.
+ */
+public class ComponentController extends StateController {
+ private static final String TAG = "JobScheduler.Component";
+ private static final boolean DEBUG = JobSchedulerService.DEBUG
+ || Log.isLoggable(TAG, Log.DEBUG);
+
+ private final BroadcastReceiver mBroadcastReceiver = new BroadcastReceiver() {
+
+ @Override
+ public void onReceive(Context context, Intent intent) {
+ final String action = intent.getAction();
+ if (action == null) {
+ Slog.wtf(TAG, "Intent action was null");
+ return;
+ }
+ switch (action) {
+ case Intent.ACTION_PACKAGE_CHANGED:
+ final Uri uri = intent.getData();
+ final String pkg = uri != null ? uri.getSchemeSpecificPart() : null;
+ final String[] changedComponents = intent.getStringArrayExtra(
+ Intent.EXTRA_CHANGED_COMPONENT_NAME_LIST);
+ if (pkg != null && changedComponents != null && changedComponents.length > 0) {
+ updateComponentStateForPackage(pkg);
+ }
+ break;
+ case Intent.ACTION_USER_UNLOCKED:
+ case Intent.ACTION_USER_STOPPED:
+ final int userId = intent.getIntExtra(Intent.EXTRA_USER_HANDLE, 0);
+ updateComponentStateForUser(userId);
+ break;
+ }
+ }
+ };
+
+ private final SparseArrayMap<ComponentName, ServiceInfo> mServiceInfoCache =
+ new SparseArrayMap<>();
+
+ private final ComponentStateUpdateFunctor mComponentStateUpdateFunctor =
+ new ComponentStateUpdateFunctor();
+
+ public ComponentController(JobSchedulerService service) {
+ super(service);
+
+ final IntentFilter filter = new IntentFilter();
+ filter.addAction(Intent.ACTION_PACKAGE_CHANGED);
+ filter.addDataScheme("package");
+ mContext.registerReceiverAsUser(
+ mBroadcastReceiver, UserHandle.ALL, filter, null, null);
+ final IntentFilter userFilter = new IntentFilter();
+ userFilter.addAction(Intent.ACTION_USER_UNLOCKED);
+ userFilter.addAction(Intent.ACTION_USER_STOPPED);
+ mContext.registerReceiverAsUser(
+ mBroadcastReceiver, UserHandle.ALL, userFilter, null, null);
+
+ }
+
+ @Override
+ public void maybeStartTrackingJobLocked(JobStatus jobStatus, JobStatus lastJob) {
+ updateComponentEnabledStateLocked(jobStatus);
+ }
+
+ @Override
+ public void maybeStopTrackingJobLocked(JobStatus jobStatus, JobStatus incomingJob,
+ boolean forUpdate) {
+ }
+
+ @Nullable
+ private ServiceInfo getServiceInfo(JobStatus jobStatus) {
+ final ComponentName service = jobStatus.getServiceComponent();
+ final int userId = jobStatus.getUserId();
+ ServiceInfo si = mServiceInfoCache.get(userId, service);
+ if (si == null) {
+ try {
+ // createContextAsUser may potentially be expensive
+ // TODO: cache user context or improve ContextImpl implementation if this becomes
+ // a problem
+ si = mContext.createContextAsUser(UserHandle.of(userId), 0)
+ .getPackageManager()
+ .getServiceInfo(service, PackageManager.MATCH_DIRECT_BOOT_AUTO);
+ } catch (NameNotFoundException e) {
+ Slog.e(TAG, "Job exists for non-existent package: " + service.getPackageName());
+ return null;
+ }
+ mServiceInfoCache.add(userId, service, si);
+ }
+ return si;
+ }
+
+ private boolean updateComponentEnabledStateLocked(JobStatus jobStatus) {
+ final ServiceInfo service = getServiceInfo(jobStatus);
+
+ if (DEBUG && service == null) {
+ Slog.v(TAG, jobStatus.toShortString() + " component not present");
+ }
+ final ServiceInfo ogService = jobStatus.serviceInfo;
+ jobStatus.serviceInfo = service;
+ return !Objects.equals(ogService, service);
+ }
+
+ private void updateComponentStateForPackage(final String pkg) {
+ synchronized (mLock) {
+ for (int u = mServiceInfoCache.numMaps() - 1; u >= 0; --u) {
+ final int userId = mServiceInfoCache.keyAt(u);
+
+ for (int c = mServiceInfoCache.numElementsForKey(userId) - 1; c >= 0; --c) {
+ final ComponentName cn = mServiceInfoCache.keyAt(u, c);
+ if (cn.getPackageName().equals(pkg)) {
+ mServiceInfoCache.delete(userId, cn);
+ }
+ }
+ }
+ updateComponentStatesLocked(
+ jobStatus -> jobStatus.getServiceComponent().getPackageName().equals(pkg));
+ }
+ }
+
+ private void updateComponentStateForUser(final int userId) {
+ synchronized (mLock) {
+ mServiceInfoCache.delete(userId);
+ updateComponentStatesLocked(jobStatus -> {
+ // Using user ID instead of source user ID because the service will run under the
+ // user ID, not source user ID.
+ return jobStatus.getUserId() == userId;
+ });
+ }
+ }
+
+ private void updateComponentStatesLocked(@NonNull Predicate<JobStatus> filter) {
+ mComponentStateUpdateFunctor.reset();
+ mService.getJobStore().forEachJob(filter, mComponentStateUpdateFunctor);
+ if (mComponentStateUpdateFunctor.mChanged) {
+ mStateChangedListener.onControllerStateChanged();
+ }
+ }
+
+ final class ComponentStateUpdateFunctor implements Consumer<JobStatus> {
+ boolean mChanged;
+
+ @Override
+ public void accept(JobStatus jobStatus) {
+ mChanged |= updateComponentEnabledStateLocked(jobStatus);
+ }
+
+ private void reset() {
+ mChanged = false;
+ }
+ }
+
+ @Override
+ public void dumpControllerStateLocked(IndentingPrintWriter pw, Predicate<JobStatus> predicate) {
+
+ }
+
+ @Override
+ public void dumpControllerStateLocked(ProtoOutputStream proto, long fieldId,
+ Predicate<JobStatus> predicate) {
+
+ }
+}
diff --git a/apex/jobscheduler/service/java/com/android/server/job/controllers/JobStatus.java b/apex/jobscheduler/service/java/com/android/server/job/controllers/JobStatus.java
index d7be2595e88b..c7cc2f03b062 100644
--- a/apex/jobscheduler/service/java/com/android/server/job/controllers/JobStatus.java
+++ b/apex/jobscheduler/service/java/com/android/server/job/controllers/JobStatus.java
@@ -27,6 +27,7 @@ import android.app.job.JobInfo;
import android.app.job.JobWorkItem;
import android.content.ClipData;
import android.content.ComponentName;
+import android.content.pm.ServiceInfo;
import android.net.Network;
import android.net.Uri;
import android.os.RemoteException;
@@ -296,6 +297,7 @@ public final class JobStatus {
public ArraySet<Uri> changedUris;
public ArraySet<String> changedAuthorities;
public Network network;
+ public ServiceInfo serviceInfo;
public int lastEvaluatedPriority;
@@ -1284,8 +1286,8 @@ public final class JobStatus {
// run if its constraints are satisfied).
// DeviceNotDozing implicit constraint must be satisfied
// NotRestrictedInBackground implicit constraint must be satisfied
- return mReadyNotDozing && mReadyNotRestrictedInBg && (mReadyDeadlineSatisfied
- || isConstraintsSatisfied(satisfiedConstraints));
+ return mReadyNotDozing && mReadyNotRestrictedInBg && (serviceInfo != null)
+ && (mReadyDeadlineSatisfied || isConstraintsSatisfied(satisfiedConstraints));
}
/** All constraints besides implicit and deadline. */
@@ -1767,6 +1769,9 @@ public final class JobStatus {
pw.print(prefix);
pw.print(" readyDynamicSatisfied: ");
pw.println(mReadyDynamicSatisfied);
+ pw.print(prefix);
+ pw.print(" readyComponentEnabled: ");
+ pw.println(serviceInfo != null);
if (changedAuthorities != null) {
pw.print(prefix); pw.println("Changed authorities:");
diff --git a/apex/jobscheduler/service/java/com/android/server/usage/AppStandbyController.java b/apex/jobscheduler/service/java/com/android/server/usage/AppStandbyController.java
index c033138d5f20..22e1eec8883d 100644
--- a/apex/jobscheduler/service/java/com/android/server/usage/AppStandbyController.java
+++ b/apex/jobscheduler/service/java/com/android/server/usage/AppStandbyController.java
@@ -1159,6 +1159,10 @@ public class AppStandbyController implements AppStandbyInternal {
if (isDeviceProvisioningPackage(packageName)) {
return STANDBY_BUCKET_EXEMPTED;
}
+
+ if (mInjector.isWellbeingPackage(packageName)) {
+ return STANDBY_BUCKET_WORKING_SET;
+ }
}
// Check this last, as it can be the most expensive check
@@ -1966,6 +1970,7 @@ public class AppStandbyController implements AppStandbyInternal {
*/
@GuardedBy("mPowerWhitelistedApps")
private final ArraySet<String> mPowerWhitelistedApps = new ArraySet<>();
+ private String mWellbeingApp = null;
Injector(Context context, Looper looper) {
mContext = context;
@@ -1999,6 +2004,9 @@ public class AppStandbyController implements AppStandbyInternal {
if (activityManager.isLowRamDevice() || ActivityManager.isSmallBatteryDevice()) {
mAutoRestrictedBucketDelayMs = 12 * ONE_HOUR;
}
+
+ final PackageManager packageManager = mContext.getPackageManager();
+ mWellbeingApp = packageManager.getWellbeingPackageName();
}
mBootPhase = phase;
}
@@ -2043,6 +2051,14 @@ public class AppStandbyController implements AppStandbyInternal {
}
}
+ /**
+ * Returns {@code true} if the supplied package is the wellbeing app. Otherwise,
+ * returns {@code false}.
+ */
+ boolean isWellbeingPackage(String packageName) {
+ return mWellbeingApp != null && mWellbeingApp.equals(packageName);
+ }
+
void updatePowerWhitelistCache() {
try {
// Don't call out to DeviceIdleController with the lock held.
diff --git a/api/Android.bp b/api/Android.bp
index 54ff82c97e17..cb6d448caf63 100644
--- a/api/Android.bp
+++ b/api/Android.bp
@@ -1,7 +1,97 @@
+// 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 {
+ default_visibility: ["//visibility:private"],
+}
+
+// *-current.txt files for use by modules in other directories like cts
+filegroup {
+ name: "frameworks-base-api-current.txt",
+ srcs: ["current.txt"],
+ visibility: ["//visibility:public"],
+}
+
+filegroup {
+ name: "frameworks-base-api-system-current.txt",
+ srcs: ["system-current.txt"],
+ visibility: ["//visibility:public"],
+}
+
+filegroup {
+ name: "frameworks-base-api-system-removed.txt",
+ srcs: ["system-removed.txt"],
+ visibility: ["//visibility:public"],
+}
+
genrule {
name: "current-api-xml",
tools: ["metalava"],
srcs: ["current.txt"],
out: ["current.api"],
cmd: "$(location metalava) --no-banner -convert2xmlnostrip $(in) $(out)",
+ visibility: ["//visibility:public"],
+}
+
+genrule {
+ name: "frameworks-base-api-current-merged.txt",
+ srcs: [
+ ":conscrypt.module.public.api{.public.api.txt}",
+ ":framework-media{.public.api.txt}",
+ ":framework-mediaprovider{.public.api.txt}",
+ ":framework-permission{.public.api.txt}",
+ ":framework-sdkextensions{.public.api.txt}",
+ ":framework-statsd{.public.api.txt}",
+ ":framework-tethering{.public.api.txt}",
+ ":framework-wifi{.public.api.txt}",
+ ":non-updatable-current.txt",
+ ],
+ out: ["current.txt"],
+ tools: ["metalava"],
+ cmd: "$(location metalava) --no-banner --format=v2 $(in) --api $(out)",
+}
+
+genrule {
+ name: "frameworks-base-api-system-current-merged.txt",
+ srcs: [
+ ":framework-media{.system.api.txt}",
+ ":framework-mediaprovider{.system.api.txt}",
+ ":framework-permission{.system.api.txt}",
+ ":framework-sdkextensions{.system.api.txt}",
+ ":framework-statsd{.system.api.txt}",
+ ":framework-tethering{.system.api.txt}",
+ ":framework-wifi{.system.api.txt}",
+ ":non-updatable-system-current.txt",
+ ],
+ out: ["system-current.txt"],
+ tools: ["metalava"],
+ cmd: "$(location metalava) --no-banner --format=v2 $(in) --api $(out)",
+}
+
+genrule {
+ name: "frameworks-base-api-module-lib-current-merged.txt",
+ srcs: [
+ ":framework-media{.module-lib.api.txt}",
+ ":framework-mediaprovider{.module-lib.api.txt}",
+ ":framework-permission{.module-lib.api.txt}",
+ ":framework-sdkextensions{.module-lib.api.txt}",
+ ":framework-statsd{.module-lib.api.txt}",
+ ":framework-tethering{.module-lib.api.txt}",
+ ":framework-wifi{.module-lib.api.txt}",
+ ":non-updatable-module-lib-current.txt",
+ ],
+ out: ["module-lib-current.txt"],
+ tools: ["metalava"],
+ cmd: "$(location metalava) --no-banner --format=v2 $(in) --api $(out)",
}
diff --git a/api/current.txt b/api/current.txt
index 922e89c3efd9..4b0901a122e7 100644
--- a/api/current.txt
+++ b/api/current.txt
@@ -11359,6 +11359,7 @@ package android.content.pm {
field public static final int CONFIG_COLOR_MODE = 16384; // 0x4000
field public static final int CONFIG_DENSITY = 4096; // 0x1000
field public static final int CONFIG_FONT_SCALE = 1073741824; // 0x40000000
+ field public static final int CONFIG_FORCE_BOLD_TEXT = 268435456; // 0x10000000
field public static final int CONFIG_KEYBOARD = 16; // 0x10
field public static final int CONFIG_KEYBOARD_HIDDEN = 32; // 0x20
field public static final int CONFIG_LAYOUT_DIRECTION = 8192; // 0x2000
@@ -11451,6 +11452,7 @@ package android.content.pm {
method public int describeContents();
method public int getKind();
method @Nullable public java.security.cert.Certificate getSourceCertificate() throws java.security.cert.CertificateException;
+ method @Nullable public String getSourcePackageName();
method @Nullable public String getSplitName();
method @NonNull public byte[] getValue();
method public void writeToParcel(@NonNull android.os.Parcel, int);
@@ -11559,6 +11561,7 @@ package android.content.pm {
}
public final class Checksum implements android.os.Parcelable {
+ ctor public Checksum(int, @NonNull byte[]);
method public int describeContents();
method public int getKind();
method @NonNull public byte[] getValue();
@@ -11566,9 +11569,9 @@ package android.content.pm {
field @NonNull public static final android.os.Parcelable.Creator<android.content.pm.Checksum> CREATOR;
field public static final int PARTIAL_MERKLE_ROOT_1M_SHA256 = 32; // 0x20
field public static final int PARTIAL_MERKLE_ROOT_1M_SHA512 = 64; // 0x40
- field public static final int WHOLE_MD5 = 2; // 0x2
+ field @Deprecated public static final int WHOLE_MD5 = 2; // 0x2
field public static final int WHOLE_MERKLE_ROOT_4K_SHA256 = 1; // 0x1
- field public static final int WHOLE_SHA1 = 4; // 0x4
+ field @Deprecated public static final int WHOLE_SHA1 = 4; // 0x4
field public static final int WHOLE_SHA256 = 8; // 0x8
field public static final int WHOLE_SHA512 = 16; // 0x10
}
@@ -11864,7 +11867,7 @@ package android.content.pm {
public static class PackageInstaller.Session implements java.io.Closeable {
method public void abandon();
- method public void addChecksums(@NonNull String, @NonNull java.util.List<android.content.pm.Checksum>) throws java.io.IOException;
+ method @Deprecated public void addChecksums(@NonNull String, @NonNull java.util.List<android.content.pm.Checksum>) throws java.io.IOException;
method public void addChildSessionId(int);
method public void close();
method public void commit(@NonNull android.content.IntentSender);
@@ -12061,6 +12064,7 @@ package android.content.pm {
method public boolean getSyntheticAppDetailsActivityEnabled(@NonNull String);
method @NonNull public abstract android.content.pm.FeatureInfo[] getSystemAvailableFeatures();
method @Nullable public abstract String[] getSystemSharedLibraryNames();
+ method @IntRange(from=0) public int getTargetSdkVersion(@NonNull String) throws android.content.pm.PackageManager.NameNotFoundException;
method @Nullable public abstract CharSequence getText(@NonNull String, @StringRes int, @Nullable android.content.pm.ApplicationInfo);
method @NonNull public abstract android.graphics.drawable.Drawable getUserBadgedDrawableForDensity(@NonNull android.graphics.drawable.Drawable, @NonNull android.os.UserHandle, @Nullable android.graphics.Rect, int);
method @NonNull public abstract android.graphics.drawable.Drawable getUserBadgedIcon(@NonNull android.graphics.drawable.Drawable, @NonNull android.os.UserHandle);
@@ -12679,6 +12683,9 @@ package android.content.res {
field public static final int COLOR_MODE_WIDE_COLOR_GAMUT_YES = 2; // 0x2
field @NonNull public static final android.os.Parcelable.Creator<android.content.res.Configuration> CREATOR;
field public static final int DENSITY_DPI_UNDEFINED = 0; // 0x0
+ field public static final int FORCE_BOLD_TEXT_NO = 1; // 0x1
+ field public static final int FORCE_BOLD_TEXT_UNDEFINED = 0; // 0x0
+ field public static final int FORCE_BOLD_TEXT_YES = 2; // 0x2
field public static final int HARDKEYBOARDHIDDEN_NO = 1; // 0x1
field public static final int HARDKEYBOARDHIDDEN_UNDEFINED = 0; // 0x0
field public static final int HARDKEYBOARDHIDDEN_YES = 2; // 0x2
@@ -12745,6 +12752,7 @@ package android.content.res {
field public int colorMode;
field public int densityDpi;
field public float fontScale;
+ field public int forceBoldText;
field public int hardKeyboardHidden;
field public int keyboard;
field public int keyboardHidden;
@@ -14284,6 +14292,7 @@ package android.graphics {
public final class BlurShader extends android.graphics.Shader {
ctor public BlurShader(float, float, @Nullable android.graphics.Shader);
+ ctor public BlurShader(float, float, @Nullable android.graphics.Shader, @NonNull android.graphics.Shader.TileMode);
}
public class Camera {
@@ -15187,6 +15196,20 @@ package android.graphics {
ctor public PaintFlagsDrawFilter(int, int);
}
+ public final class ParcelableColorSpace extends android.graphics.ColorSpace implements android.os.Parcelable {
+ ctor public ParcelableColorSpace(@NonNull android.graphics.ColorSpace);
+ method public int describeContents();
+ method @NonNull public float[] fromXyz(@NonNull float[]);
+ method @NonNull public android.graphics.ColorSpace getColorSpace();
+ method public float getMaxValue(int);
+ method public float getMinValue(int);
+ method public static boolean isParcelable(@NonNull android.graphics.ColorSpace);
+ method public boolean isWideGamut();
+ method @NonNull public float[] toXyz(@NonNull float[]);
+ method public void writeToParcel(@NonNull android.os.Parcel, int);
+ field @NonNull public static final android.os.Parcelable.Creator<android.graphics.ParcelableColorSpace> CREATOR;
+ }
+
public class Path {
ctor public Path();
ctor public Path(@Nullable android.graphics.Path);
@@ -15622,6 +15645,7 @@ package android.graphics {
public enum Shader.TileMode {
enum_constant public static final android.graphics.Shader.TileMode CLAMP;
+ enum_constant public static final android.graphics.Shader.TileMode DECAL;
enum_constant public static final android.graphics.Shader.TileMode MIRROR;
enum_constant public static final android.graphics.Shader.TileMode REPEAT;
}
@@ -16365,7 +16389,9 @@ package android.graphics.fonts {
method @Nullable public android.graphics.fonts.FontVariationAxis[] getAxes();
method @NonNull public java.nio.ByteBuffer getBuffer();
method @Nullable public java.io.File getFile();
+ method public float getGlyphBounds(@IntRange(from=0) int, @NonNull android.graphics.Paint, @Nullable android.graphics.RectF);
method @NonNull public android.os.LocaleList getLocaleList();
+ method public void getMetrics(@NonNull android.graphics.Paint, @Nullable android.graphics.Paint.FontMetrics);
method @NonNull public android.graphics.fonts.FontStyle getStyle();
method @IntRange(from=0) public int getTtcIndex();
}
@@ -16377,6 +16403,7 @@ package android.graphics.fonts {
ctor public Font.Builder(@NonNull android.os.ParcelFileDescriptor, @IntRange(from=0) long, @IntRange(from=0xffffffff) long);
ctor public Font.Builder(@NonNull android.content.res.AssetManager, @NonNull String);
ctor public Font.Builder(@NonNull android.content.res.Resources, int);
+ ctor public Font.Builder(@NonNull android.graphics.fonts.Font);
method @NonNull public android.graphics.fonts.Font build() throws java.io.IOException;
method @NonNull public android.graphics.fonts.Font.Builder setFontVariationSettings(@Nullable String);
method @NonNull public android.graphics.fonts.Font.Builder setFontVariationSettings(@Nullable android.graphics.fonts.FontVariationAxis[]);
@@ -24886,6 +24913,7 @@ package android.media {
}
public static final class DrmInitData.SchemeInitData {
+ ctor public DrmInitData.SchemeInitData(@NonNull java.util.UUID, @NonNull String, @NonNull byte[]);
field @NonNull public static final java.util.UUID UUID_NIL;
field public final byte[] data;
field public final String mimeType;
@@ -24904,6 +24932,10 @@ package android.media {
method public double getAttributeDouble(@NonNull String, double);
method public int getAttributeInt(@NonNull String, int);
method @Nullable public long[] getAttributeRange(@NonNull String);
+ method public long getDateTime();
+ method public long getDateTimeDigitized();
+ method public long getDateTimeOriginal();
+ method public long getGpsDateTime();
method public boolean getLatLong(float[]);
method public byte[] getThumbnail();
method public android.graphics.Bitmap getThumbnailBitmap();
@@ -26274,6 +26306,7 @@ package android.media {
field public static final String KEY_ROTATION = "rotation-degrees";
field public static final String KEY_SAMPLE_RATE = "sample-rate";
field public static final String KEY_SLICE_HEIGHT = "slice-height";
+ field public static final String KEY_SLOW_MOTION_MARKERS = "slow-motion-markers";
field public static final String KEY_STRIDE = "stride";
field public static final String KEY_TEMPORAL_LAYERING = "ts-schema";
field public static final String KEY_TILE_HEIGHT = "tile-height";
@@ -27325,6 +27358,12 @@ package android.media {
field public static final android.media.MediaTimestamp TIMESTAMP_UNKNOWN;
}
+ public class MediaTranscodingException extends java.lang.Exception {
+ }
+
+ public static final class MediaTranscodingException.ServiceNotAvailableException extends android.media.MediaTranscodingException {
+ }
+
public interface MicrophoneDirection {
method public boolean setPreferredMicrophoneDirection(int);
method public boolean setPreferredMicrophoneFieldDimension(@FloatRange(from=-1.0, to=1.0) float);
@@ -31994,9 +32033,15 @@ package android.net.wifi.hotspot2.pps {
method public int describeContents();
method public String getFqdn();
method public String getFriendlyName();
+ method @Nullable public long[] getMatchAllOis();
+ method @Nullable public long[] getMatchAnyOis();
+ method @Nullable public String[] getOtherHomePartners();
method public long[] getRoamingConsortiumOis();
method public void setFqdn(String);
method public void setFriendlyName(String);
+ method public void setMatchAllOis(@Nullable long[]);
+ method public void setMatchAnyOis(@Nullable long[]);
+ method public void setOtherHomePartners(@Nullable String[]);
method public void setRoamingConsortiumOis(long[]);
method public void writeToParcel(android.os.Parcel, int);
field @NonNull public static final android.os.Parcelable.Creator<android.net.wifi.hotspot2.pps.HomeSp> CREATOR;
@@ -44279,54 +44324,8 @@ package android.service.textservice {
package android.service.voice {
- public class AlwaysOnHotwordDetector {
- method public android.content.Intent createEnrollIntent();
- method public android.content.Intent createReEnrollIntent();
- method public android.content.Intent createUnEnrollIntent();
- method public int getParameter(int);
- method public int getSupportedAudioCapabilities();
- method public int getSupportedRecognitionModes();
- method @Nullable public android.service.voice.AlwaysOnHotwordDetector.ModelParamRange queryParameter(int);
- method public int setParameter(int, int);
- method public boolean startRecognition(int);
- method public boolean stopRecognition();
- field public static final int AUDIO_CAPABILITY_ECHO_CANCELLATION = 1; // 0x1
- field public static final int AUDIO_CAPABILITY_NOISE_SUPPRESSION = 2; // 0x2
- field public static final int MODEL_PARAM_THRESHOLD_FACTOR = 0; // 0x0
- field public static final int RECOGNITION_FLAG_ALLOW_MULTIPLE_TRIGGERS = 2; // 0x2
- field public static final int RECOGNITION_FLAG_CAPTURE_TRIGGER_AUDIO = 1; // 0x1
- field public static final int RECOGNITION_FLAG_ENABLE_AUDIO_ECHO_CANCELLATION = 4; // 0x4
- field public static final int RECOGNITION_FLAG_ENABLE_AUDIO_NOISE_SUPPRESSION = 8; // 0x8
- field public static final int RECOGNITION_MODE_USER_IDENTIFICATION = 2; // 0x2
- field public static final int RECOGNITION_MODE_VOICE_TRIGGER = 1; // 0x1
- field public static final int STATE_HARDWARE_UNAVAILABLE = -2; // 0xfffffffe
- field public static final int STATE_KEYPHRASE_ENROLLED = 2; // 0x2
- field public static final int STATE_KEYPHRASE_UNENROLLED = 1; // 0x1
- field @Deprecated public static final int STATE_KEYPHRASE_UNSUPPORTED = -1; // 0xffffffff
- }
-
- public abstract static class AlwaysOnHotwordDetector.Callback {
- ctor public AlwaysOnHotwordDetector.Callback();
- method public abstract void onAvailabilityChanged(int);
- method public abstract void onDetected(@NonNull android.service.voice.AlwaysOnHotwordDetector.EventPayload);
- method public abstract void onError();
- method public abstract void onRecognitionPaused();
- method public abstract void onRecognitionResumed();
- }
-
- public static class AlwaysOnHotwordDetector.EventPayload {
- method @Nullable public android.media.AudioFormat getCaptureAudioFormat();
- method @Nullable public byte[] getTriggerAudio();
- }
-
- public static final class AlwaysOnHotwordDetector.ModelParamRange {
- method public int getEnd();
- method public int getStart();
- }
-
public class VoiceInteractionService extends android.app.Service {
ctor public VoiceInteractionService();
- method public final android.service.voice.AlwaysOnHotwordDetector createAlwaysOnHotwordDetector(String, java.util.Locale, android.service.voice.AlwaysOnHotwordDetector.Callback);
method public int getDisabledShowContext();
method public static boolean isActiveService(android.content.Context, android.content.ComponentName);
method public android.os.IBinder onBind(android.content.Intent);
@@ -46760,6 +46759,7 @@ package android.telephony {
field public static final String KEY_APN_EXPAND_BOOL = "apn_expand_bool";
field public static final String KEY_APN_SETTINGS_DEFAULT_APN_TYPES_STRING_ARRAY = "apn_settings_default_apn_types_string_array";
field public static final String KEY_AUTO_RETRY_ENABLED_BOOL = "auto_retry_enabled_bool";
+ field public static final String KEY_CALL_BARRING_DEFAULT_SERVICE_CLASS_INT = "call_barring_default_service_class_int";
field public static final String KEY_CALL_BARRING_SUPPORTS_DEACTIVATE_ALL_BOOL = "call_barring_supports_deactivate_all_bool";
field public static final String KEY_CALL_BARRING_SUPPORTS_PASSWORD_CHANGE_BOOL = "call_barring_supports_password_change_bool";
field public static final String KEY_CALL_BARRING_VISIBILITY_BOOL = "call_barring_visibility_bool";
@@ -46987,6 +46987,8 @@ package android.telephony {
field public static final String KEY_WFC_EMERGENCY_ADDRESS_CARRIER_APP_STRING = "wfc_emergency_address_carrier_app_string";
field public static final String KEY_WORLD_MODE_ENABLED_BOOL = "world_mode_enabled_bool";
field public static final String KEY_WORLD_PHONE_BOOL = "world_phone_bool";
+ field public static final int SERVICE_CLASS_NONE = 0; // 0x0
+ field public static final int SERVICE_CLASS_VOICE = 1; // 0x1
}
public static final class CarrierConfigManager.Apn {
diff --git a/api/module-lib-current.txt b/api/module-lib-current.txt
index 4f0e2cd3c768..d27f5a5c006e 100644
--- a/api/module-lib-current.txt
+++ b/api/module-lib-current.txt
@@ -10,6 +10,10 @@ package android.app {
field public static final String ACTION_NOTIFICATION_LISTENER_ENABLED_CHANGED = "android.app.action.NOTIFICATION_LISTENER_ENABLED_CHANGED";
}
+ public class StatusBarManager {
+ method @RequiresPermission(android.Manifest.permission.STATUS_BAR) public void setDisabledForSimNetworkLock(boolean);
+ }
+
}
package android.content.rollback {
@@ -62,6 +66,7 @@ package android.media.session {
}
public final class MediaSessionManager {
+ method public void addOnActiveSessionsChangedListener(@NonNull android.media.session.MediaSessionManager.OnActiveSessionsChangedListener, @Nullable android.content.ComponentName, int, @Nullable android.os.Handler);
method public void dispatchMediaKeyEventAsSystemService(@NonNull android.view.KeyEvent);
method public boolean dispatchMediaKeyEventAsSystemService(@NonNull android.media.session.MediaSession.Token, @NonNull android.view.KeyEvent);
method public void dispatchVolumeKeyEventAsSystemService(@NonNull android.view.KeyEvent, int);
@@ -149,6 +154,7 @@ package android.os {
package android.provider {
public final class DeviceConfig {
+ field public static final String NAMESPACE_ALARM_MANAGER = "alarm_manager";
field public static final String NAMESPACE_DEVICE_IDLE = "device_idle";
}
diff --git a/api/system-current.txt b/api/system-current.txt
index b8fa299b01eb..118184df9d00 100644
--- a/api/system-current.txt
+++ b/api/system-current.txt
@@ -1592,12 +1592,16 @@ package android.bluetooth {
method @RequiresPermission(android.Manifest.permission.BLUETOOTH_PRIVILEGED) public void setBluetoothTethering(boolean);
method @RequiresPermission(android.Manifest.permission.BLUETOOTH_PRIVILEGED) public boolean setConnectionPolicy(@NonNull android.bluetooth.BluetoothDevice, int);
field public static final String ACTION_CONNECTION_STATE_CHANGED = "android.bluetooth.pan.profile.action.CONNECTION_STATE_CHANGED";
+ field public static final String ACTION_TETHERING_STATE_CHANGED = "android.bluetooth.action.TETHERING_STATE_CHANGED";
field public static final String EXTRA_LOCAL_ROLE = "android.bluetooth.pan.extra.LOCAL_ROLE";
+ field public static final String EXTRA_TETHERING_STATE = "android.bluetooth.extra.TETHERING_STATE";
field public static final int LOCAL_NAP_ROLE = 1; // 0x1
field public static final int LOCAL_PANU_ROLE = 2; // 0x2
field public static final int PAN_ROLE_NONE = 0; // 0x0
field public static final int REMOTE_NAP_ROLE = 1; // 0x1
field public static final int REMOTE_PANU_ROLE = 2; // 0x2
+ field public static final int TETHERING_STATE_OFF = 1; // 0x1
+ field public static final int TETHERING_STATE_ON = 2; // 0x2
}
public class BluetoothPbap implements android.bluetooth.BluetoothProfile {
@@ -4363,7 +4367,7 @@ package android.media {
}
public final class MediaTranscodeManager {
- method @NonNull public android.media.MediaTranscodeManager.TranscodingJob enqueueRequest(@NonNull android.media.MediaTranscodeManager.TranscodingRequest, @NonNull java.util.concurrent.Executor, @NonNull android.media.MediaTranscodeManager.OnTranscodingFinishedListener) throws java.io.FileNotFoundException;
+ method @NonNull public android.media.MediaTranscodeManager.TranscodingJob enqueueRequest(@NonNull android.media.MediaTranscodeManager.TranscodingRequest, @NonNull java.util.concurrent.Executor, @NonNull android.media.MediaTranscodeManager.OnTranscodingFinishedListener) throws java.io.FileNotFoundException, android.media.MediaTranscodingException.ServiceNotAvailableException;
field public static final int PRIORITY_REALTIME = 1; // 0x1
field public static final int TRANSCODING_TYPE_VIDEO = 1; // 0x1
}
@@ -4378,7 +4382,6 @@ package android.media {
method @IntRange(from=0, to=100) public int getProgress();
method public int getResult();
method public int getStatus();
- method public boolean retry();
method public void setOnProgressUpdateListener(@NonNull java.util.concurrent.Executor, @Nullable android.media.MediaTranscodeManager.TranscodingJob.OnProgressUpdateListener);
method public void setOnProgressUpdateListener(int, @NonNull java.util.concurrent.Executor, @Nullable android.media.MediaTranscodeManager.TranscodingJob.OnProgressUpdateListener);
field public static final int RESULT_CANCELED = 4; // 0x4
@@ -4396,6 +4399,8 @@ package android.media {
}
public static final class MediaTranscodeManager.TranscodingRequest {
+ method public int getClientPid();
+ method public int getClientUid();
method @NonNull public android.net.Uri getDestinationUri();
method public int getPriority();
method @NonNull public android.net.Uri getSourceUri();
@@ -4406,6 +4411,8 @@ package android.media {
public static final class MediaTranscodeManager.TranscodingRequest.Builder {
ctor public MediaTranscodeManager.TranscodingRequest.Builder();
method @NonNull public android.media.MediaTranscodeManager.TranscodingRequest build();
+ method @NonNull public android.media.MediaTranscodeManager.TranscodingRequest.Builder setClientPid(int);
+ method @NonNull public android.media.MediaTranscodeManager.TranscodingRequest.Builder setClientUid(int);
method @NonNull public android.media.MediaTranscodeManager.TranscodingRequest.Builder setDestinationUri(@NonNull android.net.Uri);
method @NonNull public android.media.MediaTranscodeManager.TranscodingRequest.Builder setPriority(int);
method @NonNull public android.media.MediaTranscodeManager.TranscodingRequest.Builder setSourceUri(@NonNull android.net.Uri);
@@ -7205,6 +7212,7 @@ package android.net.wifi {
method public int getBandwidth();
method @Nullable public android.net.MacAddress getBssid();
method public int getFrequency();
+ method public int getWifiStandard();
method public void writeToParcel(@NonNull android.os.Parcel, int);
field public static final int CHANNEL_WIDTH_160MHZ = 6; // 0x6
field public static final int CHANNEL_WIDTH_20MHZ = 2; // 0x2
@@ -7244,6 +7252,7 @@ package android.net.wifi {
field @Deprecated public static final int RANDOMIZATION_NONE = 0; // 0x0
field @Deprecated public static final int RANDOMIZATION_PERSISTENT = 1; // 0x1
field @Deprecated public static final int RECENT_FAILURE_AP_UNABLE_TO_HANDLE_NEW_STA = 17; // 0x11
+ field @Deprecated public static final int RECENT_FAILURE_MBO_OCE_DISCONNECT = 1001; // 0x3e9
field @Deprecated public static final int RECENT_FAILURE_NONE = 0; // 0x0
field @Deprecated public boolean allowAutojoin;
field @Deprecated public int carrierId;
@@ -7419,7 +7428,7 @@ package android.net.wifi {
field public static final int EASY_CONNECT_NETWORK_ROLE_AP = 1; // 0x1
field public static final int EASY_CONNECT_NETWORK_ROLE_STA = 0; // 0x0
field public static final String EXTRA_CHANGE_REASON = "changeReason";
- field public static final String EXTRA_LINK_PROPERTIES = "android.net.wifi.extra.LINK_PROPERTIES";
+ field @Deprecated public static final String EXTRA_LINK_PROPERTIES = "android.net.wifi.extra.LINK_PROPERTIES";
field public static final String EXTRA_MULTIPLE_NETWORKS_CHANGED = "multipleChanges";
field public static final String EXTRA_OSU_NETWORK = "android.net.wifi.extra.OSU_NETWORK";
field public static final String EXTRA_PREVIOUS_WIFI_AP_STATE = "previous_wifi_state";
@@ -9374,6 +9383,7 @@ package android.provider {
field public static final String APN_SET_ID = "apn_set_id";
field public static final int CARRIER_EDITED = 4; // 0x4
field public static final String EDITED_STATUS = "edited";
+ field public static final int MATCH_ALL_APN_SET_ID = -1; // 0xffffffff
field public static final String MAX_CONNECTIONS = "max_conns";
field public static final String MODEM_PERSIST = "modem_cognitive";
field public static final String MTU = "mtu";
@@ -10267,7 +10277,53 @@ package android.service.trust {
package android.service.voice {
+ public class AlwaysOnHotwordDetector {
+ method @Nullable public android.content.Intent createEnrollIntent();
+ method @Nullable public android.content.Intent createReEnrollIntent();
+ method @Nullable public android.content.Intent createUnEnrollIntent();
+ method @RequiresPermission(allOf={android.Manifest.permission.RECORD_AUDIO, android.Manifest.permission.CAPTURE_AUDIO_HOTWORD}) public int getParameter(int);
+ method public int getSupportedAudioCapabilities();
+ method public int getSupportedRecognitionModes();
+ method @Nullable @RequiresPermission(allOf={android.Manifest.permission.RECORD_AUDIO, android.Manifest.permission.CAPTURE_AUDIO_HOTWORD}) public android.service.voice.AlwaysOnHotwordDetector.ModelParamRange queryParameter(int);
+ method @RequiresPermission(allOf={android.Manifest.permission.RECORD_AUDIO, android.Manifest.permission.CAPTURE_AUDIO_HOTWORD}) public int setParameter(int, int);
+ method @RequiresPermission(allOf={android.Manifest.permission.RECORD_AUDIO, android.Manifest.permission.CAPTURE_AUDIO_HOTWORD}) public boolean startRecognition(int);
+ method @RequiresPermission(allOf={android.Manifest.permission.RECORD_AUDIO, android.Manifest.permission.CAPTURE_AUDIO_HOTWORD}) public boolean stopRecognition();
+ field public static final int AUDIO_CAPABILITY_ECHO_CANCELLATION = 1; // 0x1
+ field public static final int AUDIO_CAPABILITY_NOISE_SUPPRESSION = 2; // 0x2
+ field public static final int MODEL_PARAM_THRESHOLD_FACTOR = 0; // 0x0
+ field public static final int RECOGNITION_FLAG_ALLOW_MULTIPLE_TRIGGERS = 2; // 0x2
+ field public static final int RECOGNITION_FLAG_CAPTURE_TRIGGER_AUDIO = 1; // 0x1
+ field public static final int RECOGNITION_FLAG_ENABLE_AUDIO_ECHO_CANCELLATION = 4; // 0x4
+ field public static final int RECOGNITION_FLAG_ENABLE_AUDIO_NOISE_SUPPRESSION = 8; // 0x8
+ field public static final int RECOGNITION_MODE_USER_IDENTIFICATION = 2; // 0x2
+ field public static final int RECOGNITION_MODE_VOICE_TRIGGER = 1; // 0x1
+ field public static final int STATE_HARDWARE_UNAVAILABLE = -2; // 0xfffffffe
+ field public static final int STATE_KEYPHRASE_ENROLLED = 2; // 0x2
+ field public static final int STATE_KEYPHRASE_UNENROLLED = 1; // 0x1
+ field @Deprecated public static final int STATE_KEYPHRASE_UNSUPPORTED = -1; // 0xffffffff
+ }
+
+ public abstract static class AlwaysOnHotwordDetector.Callback {
+ ctor public AlwaysOnHotwordDetector.Callback();
+ method public abstract void onAvailabilityChanged(int);
+ method public abstract void onDetected(@NonNull android.service.voice.AlwaysOnHotwordDetector.EventPayload);
+ method public abstract void onError();
+ method public abstract void onRecognitionPaused();
+ method public abstract void onRecognitionResumed();
+ }
+
+ public static class AlwaysOnHotwordDetector.EventPayload {
+ method @Nullable public android.media.AudioFormat getCaptureAudioFormat();
+ method @Nullable public byte[] getTriggerAudio();
+ }
+
+ public static final class AlwaysOnHotwordDetector.ModelParamRange {
+ method public int getEnd();
+ method public int getStart();
+ }
+
public class VoiceInteractionService extends android.app.Service {
+ method @NonNull public final android.service.voice.AlwaysOnHotwordDetector createAlwaysOnHotwordDetector(String, java.util.Locale, android.service.voice.AlwaysOnHotwordDetector.Callback);
method @NonNull @RequiresPermission("android.permission.MANAGE_VOICE_KEYPHRASES") public final android.media.voice.KeyphraseModelManager createKeyphraseModelManager();
}
@@ -10658,6 +10714,26 @@ package android.telephony {
field @NonNull public static final android.os.Parcelable.Creator<android.telephony.CallAttributes> CREATOR;
}
+ public final class CallForwardingInfo implements android.os.Parcelable {
+ ctor public CallForwardingInfo(boolean, int, @Nullable String, int);
+ method public int describeContents();
+ method @Nullable public String getNumber();
+ method public int getReason();
+ method public int getTimeoutSeconds();
+ method public boolean isEnabled();
+ field @NonNull public static final android.os.Parcelable.Creator<android.telephony.CallForwardingInfo> CREATOR;
+ field public static final int ERROR_FDN_CHECK_FAILURE = 2; // 0x2
+ field public static final int ERROR_NOT_SUPPORTED = 3; // 0x3
+ field public static final int ERROR_UNKNOWN = 1; // 0x1
+ field public static final int REASON_ALL = 4; // 0x4
+ field public static final int REASON_ALL_CONDITIONAL = 5; // 0x5
+ field public static final int REASON_BUSY = 1; // 0x1
+ field public static final int REASON_NOT_REACHABLE = 3; // 0x3
+ field public static final int REASON_NO_REPLY = 2; // 0x2
+ field public static final int REASON_UNCONDITIONAL = 0; // 0x0
+ field public static final int SUCCESS = 0; // 0x0
+ }
+
public final class CallQuality implements android.os.Parcelable {
ctor public CallQuality(int, int, int, int, int, int, int, int, int, int, int);
ctor public CallQuality(int, int, int, int, int, int, int, int, int, int, int, boolean, boolean, boolean);
@@ -10931,7 +11007,8 @@ package android.telephony {
method public void onCallAttributesChanged(@NonNull android.telephony.CallAttributes);
method @Deprecated public void onOutgoingEmergencyCall(@NonNull android.telephony.emergency.EmergencyNumber);
method public void onOutgoingEmergencyCall(@NonNull android.telephony.emergency.EmergencyNumber, int);
- method public void onOutgoingEmergencySms(@NonNull android.telephony.emergency.EmergencyNumber);
+ method @Deprecated public void onOutgoingEmergencySms(@NonNull android.telephony.emergency.EmergencyNumber);
+ method public void onOutgoingEmergencySms(@NonNull android.telephony.emergency.EmergencyNumber, int);
method public void onPhysicalChannelConfigurationChanged(@NonNull java.util.List<android.telephony.PhysicalChannelConfig>);
method @RequiresPermission("android.permission.READ_PRECISE_PHONE_STATE") public void onPreciseCallStateChanged(@NonNull android.telephony.PreciseCallState);
method public void onRadioPowerStateChanged(int);
@@ -11318,6 +11395,8 @@ package android.telephony {
method @Deprecated @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public java.util.List<android.service.carrier.CarrierIdentifier> getAllowedCarriers(int);
method @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public long getAllowedNetworkTypes();
method @Nullable @RequiresPermission(android.Manifest.permission.INTERACT_ACROSS_USERS) public android.content.ComponentName getAndUpdateDefaultRespondViaMessageApplication();
+ method @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public void getCallForwarding(int, @NonNull java.util.concurrent.Executor, @NonNull android.telephony.TelephonyManager.CallForwardingInfoCallback);
+ method @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public void getCallWaitingStatus(@NonNull java.util.concurrent.Executor, @NonNull java.util.function.Consumer<java.lang.Integer>);
method @Nullable @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public android.telephony.ImsiEncryptionInfo getCarrierInfoForImsiEncryption(int);
method public java.util.List<java.lang.String> getCarrierPackageNamesForIntent(android.content.Intent);
method public java.util.List<java.lang.String> getCarrierPackageNamesForIntentAndPhone(android.content.Intent, int);
@@ -11394,6 +11473,8 @@ package android.telephony {
method @RequiresPermission(android.Manifest.permission.CONNECTIVITY_INTERNAL) public void resetSettings();
method @Deprecated @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public int setAllowedCarriers(int, java.util.List<android.service.carrier.CarrierIdentifier>);
method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public boolean setAllowedNetworkTypes(long);
+ method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public void setCallForwarding(@NonNull android.telephony.CallForwardingInfo, @Nullable java.util.concurrent.Executor, @Nullable java.util.function.Consumer<java.lang.Integer>);
+ method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public void setCallWaitingStatus(boolean, @Nullable java.util.concurrent.Executor, @Nullable java.util.function.Consumer<java.lang.Integer>);
method @Deprecated @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public void setCarrierDataEnabled(boolean);
method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public int setCarrierRestrictionRules(@NonNull android.telephony.CarrierRestrictionRules);
method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public void setDataActivationState(int);
@@ -11431,6 +11512,10 @@ package android.telephony {
field public static final String ACTION_SIM_APPLICATION_STATE_CHANGED = "android.telephony.action.SIM_APPLICATION_STATE_CHANGED";
field public static final String ACTION_SIM_CARD_STATE_CHANGED = "android.telephony.action.SIM_CARD_STATE_CHANGED";
field public static final String ACTION_SIM_SLOT_STATUS_CHANGED = "android.telephony.action.SIM_SLOT_STATUS_CHANGED";
+ field public static final int CALL_WAITING_STATUS_DISABLED = 2; // 0x2
+ field public static final int CALL_WAITING_STATUS_ENABLED = 1; // 0x1
+ field public static final int CALL_WAITING_STATUS_NOT_SUPPORTED = 4; // 0x4
+ field public static final int CALL_WAITING_STATUS_UNKNOWN_ERROR = 3; // 0x3
field public static final int CARRIER_PRIVILEGE_STATUS_ERROR_LOADING_RULES = -2; // 0xfffffffe
field public static final int CARRIER_PRIVILEGE_STATUS_HAS_ACCESS = 1; // 0x1
field public static final int CARRIER_PRIVILEGE_STATUS_NO_ACCESS = 0; // 0x0
@@ -11485,6 +11570,11 @@ package android.telephony {
field public static final int SRVCC_STATE_HANDOVER_STARTED = 0; // 0x0
}
+ public static interface TelephonyManager.CallForwardingInfoCallback {
+ method public void onCallForwardingInfoAvailable(@NonNull android.telephony.CallForwardingInfo);
+ method public void onError(int);
+ }
+
public final class UiccAccessRule implements android.os.Parcelable {
ctor public UiccAccessRule(byte[], @Nullable String, long);
method public int describeContents();
diff --git a/api/system-lint-baseline.txt b/api/system-lint-baseline.txt
index 9c40b6cf63ce..3a9f49c316ea 100644
--- a/api/system-lint-baseline.txt
+++ b/api/system-lint-baseline.txt
@@ -1,43 +1,48 @@
// Baseline format: 1.0
AcronymName: android.net.NetworkCapabilities#setSSID(String):
- Acronyms should not be capitalized in method names: was `setSSID`, should this be `setSsid`?
+
ActionValue: android.location.Location#EXTRA_NO_GPS_LOCATION:
-ActionValue: android.net.wifi.WifiManager#ACTION_LINK_CONFIGURATION_CHANGED:
-
-
-// Tethering broadcast action / extras cannot change name for backwards compatibility
ActionValue: android.net.TetheringManager#ACTION_TETHER_STATE_CHANGED:
- Inconsistent action value; expected `android.net.action.TETHER_STATE_CHANGED`, was `android.net.conn.TETHER_STATE_CHANGED`
+
ActionValue: android.net.TetheringManager#EXTRA_ACTIVE_TETHER:
- Inconsistent extra value; expected `android.net.extra.ACTIVE_TETHER`, was `tetherArray`
+
ActionValue: android.net.TetheringManager#EXTRA_AVAILABLE_TETHER:
- Inconsistent extra value; expected `android.net.extra.AVAILABLE_TETHER`, was `availableArray`
+
ActionValue: android.net.TetheringManager#EXTRA_ERRORED_TETHER:
- Inconsistent extra value; expected `android.net.extra.ERRORED_TETHER`, was `erroredArray`
+
+ActionValue: android.net.wifi.WifiManager#ACTION_LINK_CONFIGURATION_CHANGED:
+
+
ArrayReturn: android.bluetooth.BluetoothCodecStatus#BluetoothCodecStatus(android.bluetooth.BluetoothCodecConfig, android.bluetooth.BluetoothCodecConfig[], android.bluetooth.BluetoothCodecConfig[]) parameter #1:
- Method parameter should be Collection<BluetoothCodecConfig> (or subclass) instead of raw array; was `android.bluetooth.BluetoothCodecConfig[]`
+
ArrayReturn: android.bluetooth.BluetoothCodecStatus#BluetoothCodecStatus(android.bluetooth.BluetoothCodecConfig, android.bluetooth.BluetoothCodecConfig[], android.bluetooth.BluetoothCodecConfig[]) parameter #2:
- Method parameter should be Collection<BluetoothCodecConfig> (or subclass) instead of raw array; was `android.bluetooth.BluetoothCodecConfig[]`
+
ArrayReturn: android.bluetooth.BluetoothCodecStatus#getCodecsLocalCapabilities():
- Method should return Collection<BluetoothCodecConfig> (or subclass) instead of raw array; was `android.bluetooth.BluetoothCodecConfig[]`
+
ArrayReturn: android.bluetooth.BluetoothCodecStatus#getCodecsSelectableCapabilities():
- Method should return Collection<BluetoothCodecConfig> (or subclass) instead of raw array; was `android.bluetooth.BluetoothCodecConfig[]`
+
ArrayReturn: android.bluetooth.BluetoothUuid#containsAnyUuid(android.os.ParcelUuid[], android.os.ParcelUuid[]) parameter #0:
- Method parameter should be Collection<ParcelUuid> (or subclass) instead of raw array; was `android.os.ParcelUuid[]`
+
ArrayReturn: android.bluetooth.BluetoothUuid#containsAnyUuid(android.os.ParcelUuid[], android.os.ParcelUuid[]) parameter #1:
- Method parameter should be Collection<ParcelUuid> (or subclass) instead of raw array; was `android.os.ParcelUuid[]`
+
ArrayReturn: android.media.tv.tuner.Tuner.FilterCallback#onFilterEvent(android.media.tv.tuner.Tuner.Filter, android.media.tv.tuner.filter.FilterEvent[]) parameter #1:
- Method parameter should be Collection<FilterEvent> (or subclass) instead of raw array; was `android.media.tv.tuner.filter.FilterEvent[]`
+
ArrayReturn: android.net.NetworkScoreManager#requestScores(android.net.NetworkKey[]) parameter #0:
- Method parameter should be Collection<NetworkKey> (or subclass) instead of raw array; was `android.net.NetworkKey[]`
+
ArrayReturn: android.view.contentcapture.ViewNode#getAutofillOptions():
+BuilderSetStyle: android.net.IpSecTransform.Builder#buildTunnelModeTransform(java.net.InetAddress, android.net.IpSecManager.SecurityParameterIndex):
+ Builder methods names should use setFoo() / addFoo() / clearFoo() style: method android.net.IpSecTransform.Builder.buildTunnelModeTransform(java.net.InetAddress,android.net.IpSecManager.SecurityParameterIndex)
+
+
+ExecutorRegistration: android.media.MediaPlayer#setOnImsRxNoticeListener(android.media.MediaPlayer.OnImsRxNoticeListener, android.os.Handler):
+ Registration methods should have overload that accepts delivery Executor: `setOnImsRxNoticeListener`
ExecutorRegistration: android.net.wifi.p2p.WifiP2pManager#deletePersistentGroup(android.net.wifi.p2p.WifiP2pManager.Channel, int, android.net.wifi.p2p.WifiP2pManager.ActionListener):
ExecutorRegistration: android.net.wifi.p2p.WifiP2pManager#factoryReset(android.net.wifi.p2p.WifiP2pManager.Channel, android.net.wifi.p2p.WifiP2pManager.ActionListener):
@@ -65,7 +70,7 @@ GenericException: android.service.autofill.augmented.FillWindow#finalize():
IntentBuilderName: android.content.Context#registerReceiverForAllUsers(android.content.BroadcastReceiver, android.content.IntentFilter, String, android.os.Handler):
- Methods creating an Intent should be named `create<Foo>Intent()`, was `registerReceiverForAllUsers`
+
KotlinKeyword: android.app.Notification#when:
@@ -73,7 +78,19 @@ KotlinKeyword: android.app.Notification#when:
KotlinOperator: android.telephony.CbGeoUtils.Geometry#contains(android.telephony.CbGeoUtils.LatLng):
- Method can be invoked as a "in" operator from Kotlin: `contains` (this is usually desirable; just make sure it makes sense for this type of object)
+
+
+
+MissingGetterMatchingBuilder: android.net.wifi.rtt.RangingRequest.Builder#addResponder(android.net.wifi.rtt.ResponderConfig):
+ android.net.wifi.rtt.RangingRequest does not declare a `getResponders()` method matching method android.net.wifi.rtt.RangingRequest.Builder.addResponder(android.net.wifi.rtt.ResponderConfig)
+MissingGetterMatchingBuilder: android.security.keystore.KeyGenParameterSpec.Builder#setUid(int):
+ android.security.keystore.KeyGenParameterSpec does not declare a `getUid()` method matching method android.security.keystore.KeyGenParameterSpec.Builder.setUid(int)
+MissingGetterMatchingBuilder: android.service.autofill.Dataset.Builder#setFieldInlinePresentation(android.view.autofill.AutofillId, android.view.autofill.AutofillValue, java.util.regex.Pattern, android.service.autofill.InlinePresentation):
+ android.service.autofill.Dataset does not declare a `getFieldInlinePresentation()` method matching method android.service.autofill.Dataset.Builder.setFieldInlinePresentation(android.view.autofill.AutofillId,android.view.autofill.AutofillValue,java.util.regex.Pattern,android.service.autofill.InlinePresentation)
+MissingGetterMatchingBuilder: android.telecom.CallScreeningService.CallResponse.Builder#setShouldScreenCallViaAudioProcessing(boolean):
+ android.telecom.CallScreeningService.CallResponse does not declare a `shouldScreenCallViaAudioProcessing()` method matching method android.telecom.CallScreeningService.CallResponse.Builder.setShouldScreenCallViaAudioProcessing(boolean)
+MissingGetterMatchingBuilder: android.telephony.mbms.DownloadRequest.Builder#setServiceId(String):
+ android.telephony.mbms.DownloadRequest does not declare a `getServiceId()` method matching method android.telephony.mbms.DownloadRequest.Builder.setServiceId(String)
MissingNullability: android.hardware.soundtrigger.SoundTrigger.ModuleProperties#toString():
@@ -217,28 +234,27 @@ MutableBareField: android.net.wifi.WifiScanner.ScanSettings#type:
NoClone: android.service.contentcapture.ContentCaptureService#dump(java.io.FileDescriptor, java.io.PrintWriter, String[]) parameter #0:
-
+
NoSettingsProvider: android.provider.Settings.Global#TETHER_OFFLOAD_DISABLED:
- New setting keys are not allowed. (Field: TETHER_OFFLOAD_DISABLED)
+
NoSettingsProvider: android.provider.Settings.Global#TETHER_SUPPORTED:
- New setting keys are not allowed. (Field: TETHER_SUPPORTED)
NotCloseable: android.bluetooth.BluetoothA2dpSink:
- Classes that release resources (finalize()) should implement AutoClosable and CloseGuard: class android.bluetooth.BluetoothA2dpSink
+
NotCloseable: android.bluetooth.BluetoothMap:
- Classes that release resources (finalize()) should implement AutoClosable and CloseGuard: class android.bluetooth.BluetoothMap
+
NotCloseable: android.bluetooth.BluetoothPan:
- Classes that release resources (finalize()) should implement AutoClosable and CloseGuard: class android.bluetooth.BluetoothPan
+
NotCloseable: android.bluetooth.BluetoothPbap:
- Classes that release resources (finalize()) should implement AutoClosable and CloseGuard: class android.bluetooth.BluetoothPbap
+
OnNameExpected: android.content.ContentProvider#checkUriPermission(android.net.Uri, int, int):
- If implemented by developer, should follow the on<Something> style; otherwise consider marking final
+
PairedRegistration: android.net.wifi.nl80211.WifiNl80211Manager#registerApCallback(String, java.util.concurrent.Executor, android.net.wifi.nl80211.WifiNl80211Manager.SoftApCallback):
@@ -300,7 +316,7 @@ SamShouldBeLast: android.app.AlarmManager#setWindow(int, long, long, String, and
SamShouldBeLast: android.app.WallpaperInfo#dump(android.util.Printer, String):
SamShouldBeLast: android.app.WallpaperManager#addOnColorsChangedListener(android.app.WallpaperManager.OnColorsChangedListener, android.os.Handler):
- SAM-compatible parameters (such as parameter 1, "listener", in android.app.WallpaperManager.addOnColorsChangedListener) should be last to improve Kotlin interoperability; see https://kotlinlang.org/docs/reference/java-interop.html#sam-conversions
+
SamShouldBeLast: android.app.admin.DevicePolicyManager#installSystemUpdate(android.content.ComponentName, android.net.Uri, java.util.concurrent.Executor, android.app.admin.DevicePolicyManager.InstallSystemUpdateCallback):
SamShouldBeLast: android.content.Context#bindIsolatedService(android.content.Intent, int, String, java.util.concurrent.Executor, android.content.ServiceConnection):
@@ -334,19 +350,19 @@ SamShouldBeLast: android.location.LocationManager#registerGnssNavigationMessageC
SamShouldBeLast: android.location.LocationManager#registerGnssStatusCallback(java.util.concurrent.Executor, android.location.GnssStatus.Callback):
SamShouldBeLast: android.location.LocationManager#requestLocationUpdates(String, long, float, android.location.LocationListener, android.os.Looper):
- SAM-compatible parameters (such as parameter 4, "listener", in android.location.LocationManager.requestLocationUpdates) should be last to improve Kotlin interoperability; see https://kotlinlang.org/docs/reference/java-interop.html#sam-conversions
+
SamShouldBeLast: android.location.LocationManager#requestLocationUpdates(String, long, float, java.util.concurrent.Executor, android.location.LocationListener):
SamShouldBeLast: android.location.LocationManager#requestLocationUpdates(android.location.LocationRequest, java.util.concurrent.Executor, android.location.LocationListener):
SamShouldBeLast: android.location.LocationManager#requestLocationUpdates(long, float, android.location.Criteria, android.location.LocationListener, android.os.Looper):
- SAM-compatible parameters (such as parameter 4, "listener", in android.location.LocationManager.requestLocationUpdates) should be last to improve Kotlin interoperability; see https://kotlinlang.org/docs/reference/java-interop.html#sam-conversions
+
SamShouldBeLast: android.location.LocationManager#requestLocationUpdates(long, float, android.location.Criteria, java.util.concurrent.Executor, android.location.LocationListener):
SamShouldBeLast: android.location.LocationManager#requestSingleUpdate(String, android.location.LocationListener, android.os.Looper):
- SAM-compatible parameters (such as parameter 2, "listener", in android.location.LocationManager.requestSingleUpdate) should be last to improve Kotlin interoperability; see https://kotlinlang.org/docs/reference/java-interop.html#sam-conversions
+
SamShouldBeLast: android.location.LocationManager#requestSingleUpdate(android.location.Criteria, android.location.LocationListener, android.os.Looper):
- SAM-compatible parameters (such as parameter 2, "listener", in android.location.LocationManager.requestSingleUpdate) should be last to improve Kotlin interoperability; see https://kotlinlang.org/docs/reference/java-interop.html#sam-conversions
+
SamShouldBeLast: android.media.AudioFocusRequest.Builder#setOnAudioFocusChangeListener(android.media.AudioManager.OnAudioFocusChangeListener, android.os.Handler):
SamShouldBeLast: android.media.AudioManager#requestAudioFocus(android.media.AudioManager.OnAudioFocusChangeListener, int, int):
@@ -359,6 +375,10 @@ SamShouldBeLast: android.media.AudioRecordingMonitor#registerAudioRecordingCallb
SamShouldBeLast: android.media.AudioRouting#addOnRoutingChangedListener(android.media.AudioRouting.OnRoutingChangedListener, android.os.Handler):
+SamShouldBeLast: android.media.AudioTrack#addOnRoutingChangedListener(android.media.AudioRouting.OnRoutingChangedListener, android.os.Handler):
+ SAM-compatible parameters (such as parameter 1, "listener", in android.media.AudioTrack.addOnRoutingChangedListener) should be last to improve Kotlin interoperability; see https://kotlinlang.org/docs/reference/java-interop.html#sam-conversions
+SamShouldBeLast: android.media.MediaPlayer#setOnImsRxNoticeListener(android.media.MediaPlayer.OnImsRxNoticeListener, android.os.Handler):
+ SAM-compatible parameters (such as parameter 1, "listener", in android.media.MediaPlayer.setOnImsRxNoticeListener) should be last to improve Kotlin interoperability; see https://kotlinlang.org/docs/reference/java-interop.html#sam-conversions
SamShouldBeLast: android.media.MediaRecorder#addOnRoutingChangedListener(android.media.AudioRouting.OnRoutingChangedListener, android.os.Handler):
SamShouldBeLast: android.media.MediaRecorder#registerAudioRecordingCallback(java.util.concurrent.Executor, android.media.AudioManager.AudioRecordingCallback):
@@ -510,8 +530,8 @@ ServiceName: android.provider.DeviceConfig#NAMESPACE_PACKAGE_MANAGER_SERVICE:
UserHandle: android.companion.CompanionDeviceManager#isDeviceAssociated(String, android.net.MacAddress, android.os.UserHandle):
- When a method overload is needed to target a specific UserHandle, callers should be directed to use Context.createPackageContextAsUser() and re-obtain the relevant Manager, and no new API should be added
+
UserHandleName: android.telephony.CellBroadcastIntents#sendOrderedBroadcastForBackgroundReceivers(android.content.Context, android.os.UserHandle, android.content.Intent, String, String, android.content.BroadcastReceiver, android.os.Handler, int, String, android.os.Bundle):
- Method taking UserHandle should be named `doFooAsUser` or `queryFooForUser`, was `sendOrderedBroadcastForBackgroundReceivers`
+
diff --git a/api/test-current.txt b/api/test-current.txt
index d57269e48e64..128e84f59048 100644
--- a/api/test-current.txt
+++ b/api/test-current.txt
@@ -17,6 +17,7 @@ package android {
field public static final String MANAGE_ROLLBACKS = "android.permission.MANAGE_ROLLBACKS";
field public static final String NETWORK_SETTINGS = "android.permission.NETWORK_SETTINGS";
field public static final String NETWORK_STACK = "android.permission.NETWORK_STACK";
+ field public static final String OVERRIDE_DISPLAY_MODE_REQUESTS = "android.permission.OVERRIDE_DISPLAY_MODE_REQUESTS";
field public static final String READ_CELL_BROADCASTS = "android.permission.READ_CELL_BROADCASTS";
field public static final String READ_PRIVILEGED_PHONE_STATE = "android.permission.READ_PRIVILEGED_PHONE_STATE";
field public static final String REMOVE_TASKS = "android.permission.REMOVE_TASKS";
@@ -996,6 +997,7 @@ package android.content.pm {
method public void setEnableRollback(boolean, int);
method @RequiresPermission("android.permission.INSTALL_GRANT_RUNTIME_PERMISSIONS") public void setGrantedRuntimePermissions(String[]);
method @RequiresPermission(android.Manifest.permission.INSTALL_PACKAGES) public void setInstallAsApex();
+ method public void setInstallAsInstantApp(boolean);
method public void setInstallerPackageName(@Nullable String);
method public void setRequestDowngrade(boolean);
method @RequiresPermission(android.Manifest.permission.INSTALL_PACKAGES) public void setStaged();
@@ -1313,6 +1315,8 @@ package android.hardware.display {
method public android.graphics.Point getStableDisplaySize();
method public boolean isMinimalPostProcessingRequested(int);
method @RequiresPermission(android.Manifest.permission.CONFIGURE_DISPLAY_BRIGHTNESS) public void setBrightnessConfiguration(android.hardware.display.BrightnessConfiguration);
+ method @RequiresPermission(android.Manifest.permission.OVERRIDE_DISPLAY_MODE_REQUESTS) public void setShouldAlwaysRespectAppRequestedMode(boolean);
+ method @RequiresPermission(android.Manifest.permission.OVERRIDE_DISPLAY_MODE_REQUESTS) public boolean shouldAlwaysRespectAppRequestedMode();
field public static final int VIRTUAL_DISPLAY_FLAG_SHOULD_SHOW_SYSTEM_DECORATIONS = 512; // 0x200
field public static final int VIRTUAL_DISPLAY_FLAG_TRUSTED = 1024; // 0x400
}
@@ -1817,7 +1821,7 @@ package android.media {
}
public final class MediaTranscodeManager {
- method @NonNull public android.media.MediaTranscodeManager.TranscodingJob enqueueRequest(@NonNull android.media.MediaTranscodeManager.TranscodingRequest, @NonNull java.util.concurrent.Executor, @NonNull android.media.MediaTranscodeManager.OnTranscodingFinishedListener) throws java.io.FileNotFoundException;
+ method @NonNull public android.media.MediaTranscodeManager.TranscodingJob enqueueRequest(@NonNull android.media.MediaTranscodeManager.TranscodingRequest, @NonNull java.util.concurrent.Executor, @NonNull android.media.MediaTranscodeManager.OnTranscodingFinishedListener) throws java.io.FileNotFoundException, android.media.MediaTranscodingException.ServiceNotAvailableException;
field public static final int PRIORITY_REALTIME = 1; // 0x1
field public static final int TRANSCODING_TYPE_VIDEO = 1; // 0x1
}
@@ -1832,7 +1836,6 @@ package android.media {
method @IntRange(from=0, to=100) public int getProgress();
method public int getResult();
method public int getStatus();
- method public boolean retry();
method public void setOnProgressUpdateListener(@NonNull java.util.concurrent.Executor, @Nullable android.media.MediaTranscodeManager.TranscodingJob.OnProgressUpdateListener);
method public void setOnProgressUpdateListener(int, @NonNull java.util.concurrent.Executor, @Nullable android.media.MediaTranscodeManager.TranscodingJob.OnProgressUpdateListener);
field public static final int RESULT_CANCELED = 4; // 0x4
@@ -1850,6 +1853,8 @@ package android.media {
}
public static final class MediaTranscodeManager.TranscodingRequest {
+ method public int getClientPid();
+ method public int getClientUid();
method @NonNull public android.net.Uri getDestinationUri();
method public int getPriority();
method @NonNull public android.net.Uri getSourceUri();
@@ -1860,6 +1865,8 @@ package android.media {
public static final class MediaTranscodeManager.TranscodingRequest.Builder {
ctor public MediaTranscodeManager.TranscodingRequest.Builder();
method @NonNull public android.media.MediaTranscodeManager.TranscodingRequest build();
+ method @NonNull public android.media.MediaTranscodeManager.TranscodingRequest.Builder setClientPid(int);
+ method @NonNull public android.media.MediaTranscodeManager.TranscodingRequest.Builder setClientUid(int);
method @NonNull public android.media.MediaTranscodeManager.TranscodingRequest.Builder setDestinationUri(@NonNull android.net.Uri);
method @NonNull public android.media.MediaTranscodeManager.TranscodingRequest.Builder setPriority(int);
method @NonNull public android.media.MediaTranscodeManager.TranscodingRequest.Builder setSourceUri(@NonNull android.net.Uri);
@@ -3292,6 +3299,7 @@ package android.provider {
method @RequiresPermission(android.Manifest.permission.WRITE_DEVICE_CONFIG) public static void resetToDefaults(int, @Nullable String);
method @RequiresPermission(android.Manifest.permission.WRITE_DEVICE_CONFIG) public static boolean setProperties(@NonNull android.provider.DeviceConfig.Properties) throws android.provider.DeviceConfig.BadConfigException;
method @RequiresPermission(android.Manifest.permission.WRITE_DEVICE_CONFIG) public static boolean setProperty(@NonNull String, @NonNull String, @Nullable String, boolean);
+ field public static final String NAMESPACE_ALARM_MANAGER = "alarm_manager";
field public static final String NAMESPACE_ANDROID = "android";
field public static final String NAMESPACE_AUTOFILL = "autofill";
field public static final String NAMESPACE_BIOMETRICS = "biometrics";
@@ -4139,7 +4147,8 @@ package android.telephony {
public class PhoneStateListener {
method @Deprecated public void onOutgoingEmergencyCall(@NonNull android.telephony.emergency.EmergencyNumber);
method public void onOutgoingEmergencyCall(@NonNull android.telephony.emergency.EmergencyNumber, int);
- method public void onOutgoingEmergencySms(@NonNull android.telephony.emergency.EmergencyNumber);
+ method @Deprecated public void onOutgoingEmergencySms(@NonNull android.telephony.emergency.EmergencyNumber);
+ method public void onOutgoingEmergencySms(@NonNull android.telephony.emergency.EmergencyNumber, int);
field @RequiresPermission("android.permission.READ_ACTIVE_EMERGENCY_SESSION") public static final int LISTEN_OUTGOING_EMERGENCY_CALL = 268435456; // 0x10000000
field @RequiresPermission("android.permission.READ_ACTIVE_EMERGENCY_SESSION") public static final int LISTEN_OUTGOING_EMERGENCY_SMS = 536870912; // 0x20000000
}
diff --git a/api/test-lint-baseline.txt b/api/test-lint-baseline.txt
index 63ba4aa16662..91a09e3d8890 100644
--- a/api/test-lint-baseline.txt
+++ b/api/test-lint-baseline.txt
@@ -74,11 +74,11 @@ ActionValue: android.telephony.mbms.vendor.VendorUtils#EXTRA_TEMP_LIST:
ArrayReturn: android.app.UiAutomation#executeShellCommandRw(String):
ArrayReturn: android.hardware.soundtrigger.SoundTrigger.KeyphraseSoundModel#KeyphraseSoundModel(java.util.UUID, java.util.UUID, byte[], android.hardware.soundtrigger.SoundTrigger.Keyphrase[]) parameter #3:
- Method parameter should be Collection<Keyphrase> (or subclass) instead of raw array; was `android.hardware.soundtrigger.SoundTrigger.Keyphrase[]`
+
ArrayReturn: android.hardware.soundtrigger.SoundTrigger.KeyphraseSoundModel#KeyphraseSoundModel(java.util.UUID, java.util.UUID, byte[], android.hardware.soundtrigger.SoundTrigger.Keyphrase[], int) parameter #3:
- Method parameter should be Collection<Keyphrase> (or subclass) instead of raw array; was `android.hardware.soundtrigger.SoundTrigger.Keyphrase[]`
+
ArrayReturn: android.hardware.soundtrigger.SoundTrigger.KeyphraseSoundModel#getKeyphrases():
- Method should return Collection<Keyphrase> (or subclass) instead of raw array; was `android.hardware.soundtrigger.SoundTrigger.Keyphrase[]`
+
ArrayReturn: android.location.GnssMeasurementsEvent#GnssMeasurementsEvent(android.location.GnssClock, android.location.GnssMeasurement[]) parameter #1:
ArrayReturn: android.media.AudioRecordingConfiguration#AudioRecordingConfiguration(int, int, int, android.media.AudioFormat, android.media.AudioFormat, int, String, int, boolean, int, android.media.audiofx.AudioEffect.Descriptor[], android.media.audiofx.AudioEffect.Descriptor[]) parameter #10:
@@ -218,33 +218,33 @@ BannedThrow: android.os.Process#getThreadScheduler(int):
BuilderSetStyle: android.media.audiopolicy.AudioMixingRule.Builder#allowPrivilegedPlaybackCapture(boolean):
- Builder methods names should use setFoo() / addFoo() / clearFoo() style: method android.media.audiopolicy.AudioMixingRule.Builder.allowPrivilegedPlaybackCapture(boolean)
+
BuilderSetStyle: android.media.audiopolicy.AudioMixingRule.Builder#excludeMixRule(int, Object):
- Builder methods names should use setFoo() / addFoo() / clearFoo() style: method android.media.audiopolicy.AudioMixingRule.Builder.excludeMixRule(int,Object)
+
BuilderSetStyle: android.media.audiopolicy.AudioMixingRule.Builder#excludeRule(android.media.AudioAttributes, int):
- Builder methods names should use setFoo() / addFoo() / clearFoo() style: method android.media.audiopolicy.AudioMixingRule.Builder.excludeRule(android.media.AudioAttributes,int)
+
BuilderSetStyle: android.net.NetworkCapabilities.Builder#removeCapability(int):
- Builder methods names should use setFoo() / addFoo() / clearFoo() style: method android.net.NetworkCapabilities.Builder.removeCapability(int)
+
BuilderSetStyle: android.net.NetworkCapabilities.Builder#removeTransportType(int):
- Builder methods names should use setFoo() / addFoo() / clearFoo() style: method android.net.NetworkCapabilities.Builder.removeTransportType(int)
+
BuilderSetStyle: android.net.metrics.RaEvent.Builder#updateDnsslLifetime(long):
- Builder methods names should use setFoo() / addFoo() / clearFoo() style: method android.net.metrics.RaEvent.Builder.updateDnsslLifetime(long)
+
BuilderSetStyle: android.net.metrics.RaEvent.Builder#updatePrefixPreferredLifetime(long):
- Builder methods names should use setFoo() / addFoo() / clearFoo() style: method android.net.metrics.RaEvent.Builder.updatePrefixPreferredLifetime(long)
+
BuilderSetStyle: android.net.metrics.RaEvent.Builder#updatePrefixValidLifetime(long):
- Builder methods names should use setFoo() / addFoo() / clearFoo() style: method android.net.metrics.RaEvent.Builder.updatePrefixValidLifetime(long)
+
BuilderSetStyle: android.net.metrics.RaEvent.Builder#updateRdnssLifetime(long):
- Builder methods names should use setFoo() / addFoo() / clearFoo() style: method android.net.metrics.RaEvent.Builder.updateRdnssLifetime(long)
+
BuilderSetStyle: android.net.metrics.RaEvent.Builder#updateRouteInfoLifetime(long):
- Builder methods names should use setFoo() / addFoo() / clearFoo() style: method android.net.metrics.RaEvent.Builder.updateRouteInfoLifetime(long)
+
BuilderSetStyle: android.net.metrics.RaEvent.Builder#updateRouterLifetime(long):
- Builder methods names should use setFoo() / addFoo() / clearFoo() style: method android.net.metrics.RaEvent.Builder.updateRouterLifetime(long)
+
BuilderSetStyle: android.os.StrictMode.ThreadPolicy.Builder#detectExplicitGc():
- Builder methods names should use setFoo() / addFoo() / clearFoo() style: method android.os.StrictMode.ThreadPolicy.Builder.detectExplicitGc()
+
BuilderSetStyle: android.os.StrictMode.VmPolicy.Builder#detectIncorrectContextUse():
- Builder methods names should use setFoo() / addFoo() / clearFoo() style: method android.os.StrictMode.VmPolicy.Builder.detectIncorrectContextUse()
+
BuilderSetStyle: android.os.StrictMode.VmPolicy.Builder#permitIncorrectContextUse():
- Builder methods names should use setFoo() / addFoo() / clearFoo() style: method android.os.StrictMode.VmPolicy.Builder.permitIncorrectContextUse()
+
CallbackInterface: android.app.prediction.AppPredictor.Callback:
@@ -382,11 +382,11 @@ ExecutorRegistration: android.os.IncidentManager#requestAuthorization(int, Strin
ExecutorRegistration: android.os.RemoteCallback#RemoteCallback(android.os.RemoteCallback.OnResultListener, android.os.Handler):
ExecutorRegistration: android.permission.PermissionControllerManager#countPermissionApps(java.util.List<java.lang.String>, int, android.permission.PermissionControllerManager.OnCountPermissionAppsResultCallback, android.os.Handler):
- Registration methods should have overload that accepts delivery Executor: `countPermissionApps`
+
ExecutorRegistration: android.permission.PermissionControllerManager#getAppPermissions(String, android.permission.PermissionControllerManager.OnGetAppPermissionResultCallback, android.os.Handler):
ExecutorRegistration: android.service.watchdog.ExplicitHealthCheckService#setCallback(android.os.RemoteCallback):
- Registration methods should have overload that accepts delivery Executor: `setCallback`
+
ExecutorRegistration: android.telephony.ims.stub.ImsCallSessionImplBase#setListener(android.telephony.ims.ImsCallSessionListener):
ExecutorRegistration: android.telephony.ims.stub.ImsUtImplBase#setListener(android.telephony.ims.ImsUtListener):
@@ -406,7 +406,7 @@ ExecutorRegistration: android.telephony.mbms.vendor.MbmsStreamingServiceBase#ini
ExecutorRegistration: android.telephony.mbms.vendor.MbmsStreamingServiceBase#startStreaming(int, String, android.telephony.mbms.StreamingServiceCallback):
ExecutorRegistration: android.window.WindowOrganizer#applySyncTransaction(android.window.WindowContainerTransaction, android.window.WindowContainerTransactionCallback):
- Registration methods should have overload that accepts delivery Executor: `applySyncTransaction`
+
ForbiddenSuperClass: android.app.AppDetailsActivity:
@@ -428,9 +428,9 @@ GenericException: android.service.autofill.augmented.FillWindow#finalize():
GetterOnBuilder: android.hardware.display.BrightnessConfiguration.Builder#getMaxCorrectionsByCategory():
- Getter should be on the built object, not the builder: method android.hardware.display.BrightnessConfiguration.Builder.getMaxCorrectionsByCategory()
+
GetterOnBuilder: android.hardware.display.BrightnessConfiguration.Builder#getMaxCorrectionsByPackageName():
- Getter should be on the built object, not the builder: method android.hardware.display.BrightnessConfiguration.Builder.getMaxCorrectionsByPackageName()
+
GetterSetterNames: android.app.NotificationChannel#isBlockableSystem():
@@ -510,7 +510,7 @@ IntentBuilderName: android.app.backup.BackupManager#getConfigurationIntent(Strin
IntentBuilderName: android.app.backup.BackupManager#getDataManagementIntent(String):
IntentBuilderName: android.hardware.soundtrigger.KeyphraseEnrollmentInfo#getManageKeyphraseIntent(int, String, java.util.Locale):
- Methods creating an Intent should be named `create<Foo>Intent()`, was `getManageKeyphraseIntent`
+
IntentName: android.provider.Settings.Secure#VOICE_INTERACTION_SERVICE:
@@ -560,7 +560,7 @@ ListenerLast: android.hardware.camera2.CameraDevice#createCustomCaptureSession(a
ListenerLast: android.location.LocationManager#requestLocationUpdates(android.location.LocationRequest, android.location.LocationListener, android.os.Looper) parameter #2:
ListenerLast: android.permission.PermissionControllerManager#countPermissionApps(java.util.List<java.lang.String>, int, android.permission.PermissionControllerManager.OnCountPermissionAppsResultCallback, android.os.Handler) parameter #3:
- Listeners should always be at end of argument list (method `countPermissionApps`)
+
ListenerLast: android.permission.PermissionControllerManager#getAppPermissions(String, android.permission.PermissionControllerManager.OnGetAppPermissionResultCallback, android.os.Handler) parameter #2:
ListenerLast: android.telephony.mbms.vendor.MbmsGroupCallServiceBase#initialize(android.telephony.mbms.MbmsGroupCallSessionCallback, int) parameter #1:
@@ -593,140 +593,152 @@ MinMaxConstant: android.view.autofill.AutofillManager#MAX_TEMP_AUGMENTED_SERVICE
+MissingGetterMatchingBuilder: android.app.ActivityView.Builder#setAttributeSet(android.util.AttributeSet):
+ android.app.ActivityView does not declare a `getAttributeSet()` method matching method android.app.ActivityView.Builder.setAttributeSet(android.util.AttributeSet)
+MissingGetterMatchingBuilder: android.app.ActivityView.Builder#setDefaultStyle(int):
+ android.app.ActivityView does not declare a `getDefaultStyle()` method matching method android.app.ActivityView.Builder.setDefaultStyle(int)
+MissingGetterMatchingBuilder: android.app.ActivityView.Builder#setDisableSurfaceViewBackgroundLayer(boolean):
+ android.app.ActivityView does not declare a `isDisableSurfaceViewBackgroundLayer()` method matching method android.app.ActivityView.Builder.setDisableSurfaceViewBackgroundLayer(boolean)
+MissingGetterMatchingBuilder: android.app.ActivityView.Builder#setSingleInstance(boolean):
+ android.app.ActivityView does not declare a `isSingleInstance()` method matching method android.app.ActivityView.Builder.setSingleInstance(boolean)
+MissingGetterMatchingBuilder: android.app.ActivityView.Builder#setUsePublicVirtualDisplay(boolean):
+ android.app.ActivityView does not declare a `isUsePublicVirtualDisplay()` method matching method android.app.ActivityView.Builder.setUsePublicVirtualDisplay(boolean)
+MissingGetterMatchingBuilder: android.app.ActivityView.Builder#setUseTrustedDisplay(boolean):
+ android.app.ActivityView does not declare a `isUseTrustedDisplay()` method matching method android.app.ActivityView.Builder.setUseTrustedDisplay(boolean)
MissingGetterMatchingBuilder: android.app.AppOpsManager.HistoricalOpsRequest.Builder#setAttributionTag(String):
- android.app.AppOpsManager.HistoricalOpsRequest does not declare a `getAttributionTag()` method matching method android.app.AppOpsManager.HistoricalOpsRequest.Builder.setAttributionTag(String)
+
MissingGetterMatchingBuilder: android.app.AppOpsManager.HistoricalOpsRequest.Builder#setFlags(int):
- android.app.AppOpsManager.HistoricalOpsRequest does not declare a `getFlags()` method matching method android.app.AppOpsManager.HistoricalOpsRequest.Builder.setFlags(int)
+
MissingGetterMatchingBuilder: android.app.AppOpsManager.HistoricalOpsRequest.Builder#setOpNames(java.util.List<java.lang.String>):
- android.app.AppOpsManager.HistoricalOpsRequest does not declare a `getOpNames()` method matching method android.app.AppOpsManager.HistoricalOpsRequest.Builder.setOpNames(java.util.List<java.lang.String>)
+
MissingGetterMatchingBuilder: android.app.AppOpsManager.HistoricalOpsRequest.Builder#setPackageName(String):
- android.app.AppOpsManager.HistoricalOpsRequest does not declare a `getPackageName()` method matching method android.app.AppOpsManager.HistoricalOpsRequest.Builder.setPackageName(String)
+
MissingGetterMatchingBuilder: android.app.AppOpsManager.HistoricalOpsRequest.Builder#setUid(int):
- android.app.AppOpsManager.HistoricalOpsRequest does not declare a `getUid()` method matching method android.app.AppOpsManager.HistoricalOpsRequest.Builder.setUid(int)
+
MissingGetterMatchingBuilder: android.content.integrity.RuleSet.Builder#addRules(java.util.List<android.content.integrity.Rule>):
- android.content.integrity.RuleSet does not declare a `getRuless()` method matching method android.content.integrity.RuleSet.Builder.addRules(java.util.List<android.content.integrity.Rule>)
+
MissingGetterMatchingBuilder: android.hardware.display.BrightnessConfiguration.Builder#addCorrectionByCategory(int, android.hardware.display.BrightnessCorrection):
- android.hardware.display.BrightnessConfiguration does not declare a `getCorrectionByCategorys()` method matching method android.hardware.display.BrightnessConfiguration.Builder.addCorrectionByCategory(int,android.hardware.display.BrightnessCorrection)
+
MissingGetterMatchingBuilder: android.hardware.display.BrightnessConfiguration.Builder#addCorrectionByPackageName(String, android.hardware.display.BrightnessCorrection):
- android.hardware.display.BrightnessConfiguration does not declare a `getCorrectionByPackageNames()` method matching method android.hardware.display.BrightnessConfiguration.Builder.addCorrectionByPackageName(String,android.hardware.display.BrightnessCorrection)
+
MissingGetterMatchingBuilder: android.hardware.display.BrightnessConfiguration.Builder#setDescription(String):
- android.hardware.display.BrightnessConfiguration does not declare a `getDescription()` method matching method android.hardware.display.BrightnessConfiguration.Builder.setDescription(String)
+
MissingGetterMatchingBuilder: android.hardware.lights.LightsRequest.Builder#setLight(android.hardware.lights.Light, android.hardware.lights.LightState):
- android.hardware.lights.LightsRequest does not declare a `getLight()` method matching method android.hardware.lights.LightsRequest.Builder.setLight(android.hardware.lights.Light,android.hardware.lights.LightState)
+
MissingGetterMatchingBuilder: android.media.VolumeShaper.Configuration.Builder#setOptionFlags(int):
- android.media.VolumeShaper.Configuration does not declare a `getOptionFlags()` method matching method android.media.VolumeShaper.Configuration.Builder.setOptionFlags(int)
+
MissingGetterMatchingBuilder: android.media.audiopolicy.AudioMix.Builder#setDevice(android.media.AudioDeviceInfo):
- android.media.audiopolicy.AudioMix does not declare a `getDevice()` method matching method android.media.audiopolicy.AudioMix.Builder.setDevice(android.media.AudioDeviceInfo)
+
MissingGetterMatchingBuilder: android.media.audiopolicy.AudioMix.Builder#setFormat(android.media.AudioFormat):
- android.media.audiopolicy.AudioMix does not declare a `getFormat()` method matching method android.media.audiopolicy.AudioMix.Builder.setFormat(android.media.AudioFormat)
+
MissingGetterMatchingBuilder: android.media.audiopolicy.AudioMix.Builder#setRouteFlags(int):
- android.media.audiopolicy.AudioMix does not declare a `getRouteFlags()` method matching method android.media.audiopolicy.AudioMix.Builder.setRouteFlags(int)
+
MissingGetterMatchingBuilder: android.media.audiopolicy.AudioMixingRule.Builder#addMixRule(int, Object):
- android.media.audiopolicy.AudioMixingRule does not declare a `getMixRules()` method matching method android.media.audiopolicy.AudioMixingRule.Builder.addMixRule(int,Object)
+
MissingGetterMatchingBuilder: android.media.audiopolicy.AudioMixingRule.Builder#addRule(android.media.AudioAttributes, int):
- android.media.audiopolicy.AudioMixingRule does not declare a `getRules()` method matching method android.media.audiopolicy.AudioMixingRule.Builder.addRule(android.media.AudioAttributes,int)
+
MissingGetterMatchingBuilder: android.media.audiopolicy.AudioPolicy.Builder#addMix(android.media.audiopolicy.AudioMix):
- android.media.audiopolicy.AudioPolicy does not declare a `getMixs()` method matching method android.media.audiopolicy.AudioPolicy.Builder.addMix(android.media.audiopolicy.AudioMix)
+
MissingGetterMatchingBuilder: android.media.audiopolicy.AudioPolicy.Builder#setAudioPolicyFocusListener(android.media.audiopolicy.AudioPolicy.AudioPolicyFocusListener):
- android.media.audiopolicy.AudioPolicy does not declare a `getAudioPolicyFocusListener()` method matching method android.media.audiopolicy.AudioPolicy.Builder.setAudioPolicyFocusListener(android.media.audiopolicy.AudioPolicy.AudioPolicyFocusListener)
+
MissingGetterMatchingBuilder: android.media.audiopolicy.AudioPolicy.Builder#setAudioPolicyStatusListener(android.media.audiopolicy.AudioPolicy.AudioPolicyStatusListener):
- android.media.audiopolicy.AudioPolicy does not declare a `getAudioPolicyStatusListener()` method matching method android.media.audiopolicy.AudioPolicy.Builder.setAudioPolicyStatusListener(android.media.audiopolicy.AudioPolicy.AudioPolicyStatusListener)
+
MissingGetterMatchingBuilder: android.media.audiopolicy.AudioPolicy.Builder#setAudioPolicyVolumeCallback(android.media.audiopolicy.AudioPolicy.AudioPolicyVolumeCallback):
- android.media.audiopolicy.AudioPolicy does not declare a `getAudioPolicyVolumeCallback()` method matching method android.media.audiopolicy.AudioPolicy.Builder.setAudioPolicyVolumeCallback(android.media.audiopolicy.AudioPolicy.AudioPolicyVolumeCallback)
+
MissingGetterMatchingBuilder: android.media.audiopolicy.AudioPolicy.Builder#setIsAudioFocusPolicy(boolean):
- android.media.audiopolicy.AudioPolicy does not declare a `isIsAudioFocusPolicy()` method matching method android.media.audiopolicy.AudioPolicy.Builder.setIsAudioFocusPolicy(boolean)
+
MissingGetterMatchingBuilder: android.media.audiopolicy.AudioPolicy.Builder#setIsTestFocusPolicy(boolean):
- android.media.audiopolicy.AudioPolicy does not declare a `isIsTestFocusPolicy()` method matching method android.media.audiopolicy.AudioPolicy.Builder.setIsTestFocusPolicy(boolean)
+
MissingGetterMatchingBuilder: android.media.audiopolicy.AudioPolicy.Builder#setLooper(android.os.Looper):
- android.media.audiopolicy.AudioPolicy does not declare a `getLooper()` method matching method android.media.audiopolicy.AudioPolicy.Builder.setLooper(android.os.Looper)
+
MissingGetterMatchingBuilder: android.net.CaptivePortalData.Builder#setBytesRemaining(long):
- android.net.CaptivePortalData does not declare a `getBytesRemaining()` method matching method android.net.CaptivePortalData.Builder.setBytesRemaining(long)
+
MissingGetterMatchingBuilder: android.net.CaptivePortalData.Builder#setExpiryTime(long):
- android.net.CaptivePortalData does not declare a `getExpiryTime()` method matching method android.net.CaptivePortalData.Builder.setExpiryTime(long)
+
MissingGetterMatchingBuilder: android.net.CaptivePortalData.Builder#setRefreshTime(long):
- android.net.CaptivePortalData does not declare a `getRefreshTime()` method matching method android.net.CaptivePortalData.Builder.setRefreshTime(long)
+
MissingGetterMatchingBuilder: android.net.NetworkCapabilities.Builder#addCapability(int):
- android.net.NetworkCapabilities does not declare a `getCapabilitys()` method matching method android.net.NetworkCapabilities.Builder.addCapability(int)
+
MissingGetterMatchingBuilder: android.net.NetworkCapabilities.Builder#setRequestorPackageName(String):
- android.net.NetworkCapabilities does not declare a `getRequestorPackageName()` method matching method android.net.NetworkCapabilities.Builder.setRequestorPackageName(String)
+
MissingGetterMatchingBuilder: android.net.NetworkCapabilities.Builder#setRequestorUid(int):
- android.net.NetworkCapabilities does not declare a `getRequestorUid()` method matching method android.net.NetworkCapabilities.Builder.setRequestorUid(int)
+
MissingGetterMatchingBuilder: android.net.TetheringManager.TetheringRequest.Builder#setShouldShowEntitlementUi(boolean):
- android.net.TetheringManager.TetheringRequest does not declare a `shouldShowEntitlementUi()` method matching method android.net.TetheringManager.TetheringRequest.Builder.setShouldShowEntitlementUi(boolean)
+
MissingGetterMatchingBuilder: android.net.TetheringManager.TetheringRequest.Builder#setStaticIpv4Addresses(android.net.LinkAddress, android.net.LinkAddress):
- android.net.TetheringManager.TetheringRequest does not declare a `getStaticIpv4Addresses()` method matching method android.net.TetheringManager.TetheringRequest.Builder.setStaticIpv4Addresses(android.net.LinkAddress,android.net.LinkAddress)
+
MissingGetterMatchingBuilder: android.net.metrics.ApfProgramEvent.Builder#setActualLifetime(long):
- android.net.metrics.ApfProgramEvent does not declare a `getActualLifetime()` method matching method android.net.metrics.ApfProgramEvent.Builder.setActualLifetime(long)
+
MissingGetterMatchingBuilder: android.net.metrics.ApfProgramEvent.Builder#setCurrentRas(int):
- android.net.metrics.ApfProgramEvent does not declare a `getCurrentRas()` method matching method android.net.metrics.ApfProgramEvent.Builder.setCurrentRas(int)
+
MissingGetterMatchingBuilder: android.net.metrics.ApfProgramEvent.Builder#setFilteredRas(int):
- android.net.metrics.ApfProgramEvent does not declare a `getFilteredRas()` method matching method android.net.metrics.ApfProgramEvent.Builder.setFilteredRas(int)
+
MissingGetterMatchingBuilder: android.net.metrics.ApfProgramEvent.Builder#setFlags(boolean, boolean):
- android.net.metrics.ApfProgramEvent does not declare a `isFlags()` method matching method android.net.metrics.ApfProgramEvent.Builder.setFlags(boolean,boolean)
+
MissingGetterMatchingBuilder: android.net.metrics.ApfProgramEvent.Builder#setLifetime(long):
- android.net.metrics.ApfProgramEvent does not declare a `getLifetime()` method matching method android.net.metrics.ApfProgramEvent.Builder.setLifetime(long)
+
MissingGetterMatchingBuilder: android.net.metrics.ApfProgramEvent.Builder#setProgramLength(int):
- android.net.metrics.ApfProgramEvent does not declare a `getProgramLength()` method matching method android.net.metrics.ApfProgramEvent.Builder.setProgramLength(int)
+
MissingGetterMatchingBuilder: android.net.metrics.ApfStats.Builder#setDroppedRas(int):
- android.net.metrics.ApfStats does not declare a `getDroppedRas()` method matching method android.net.metrics.ApfStats.Builder.setDroppedRas(int)
+
MissingGetterMatchingBuilder: android.net.metrics.ApfStats.Builder#setDurationMs(long):
- android.net.metrics.ApfStats does not declare a `getDurationMs()` method matching method android.net.metrics.ApfStats.Builder.setDurationMs(long)
+
MissingGetterMatchingBuilder: android.net.metrics.ApfStats.Builder#setMatchingRas(int):
- android.net.metrics.ApfStats does not declare a `getMatchingRas()` method matching method android.net.metrics.ApfStats.Builder.setMatchingRas(int)
+
MissingGetterMatchingBuilder: android.net.metrics.ApfStats.Builder#setMaxProgramSize(int):
- android.net.metrics.ApfStats does not declare a `getMaxProgramSize()` method matching method android.net.metrics.ApfStats.Builder.setMaxProgramSize(int)
+
MissingGetterMatchingBuilder: android.net.metrics.ApfStats.Builder#setParseErrors(int):
- android.net.metrics.ApfStats does not declare a `getParseErrors()` method matching method android.net.metrics.ApfStats.Builder.setParseErrors(int)
+
MissingGetterMatchingBuilder: android.net.metrics.ApfStats.Builder#setProgramUpdates(int):
- android.net.metrics.ApfStats does not declare a `getProgramUpdates()` method matching method android.net.metrics.ApfStats.Builder.setProgramUpdates(int)
+
MissingGetterMatchingBuilder: android.net.metrics.ApfStats.Builder#setProgramUpdatesAll(int):
- android.net.metrics.ApfStats does not declare a `getProgramUpdatesAll()` method matching method android.net.metrics.ApfStats.Builder.setProgramUpdatesAll(int)
+
MissingGetterMatchingBuilder: android.net.metrics.ApfStats.Builder#setProgramUpdatesAllowingMulticast(int):
- android.net.metrics.ApfStats does not declare a `getProgramUpdatesAllowingMulticast()` method matching method android.net.metrics.ApfStats.Builder.setProgramUpdatesAllowingMulticast(int)
+
MissingGetterMatchingBuilder: android.net.metrics.ApfStats.Builder#setReceivedRas(int):
- android.net.metrics.ApfStats does not declare a `getReceivedRas()` method matching method android.net.metrics.ApfStats.Builder.setReceivedRas(int)
+
MissingGetterMatchingBuilder: android.net.metrics.ApfStats.Builder#setZeroLifetimeRas(int):
- android.net.metrics.ApfStats does not declare a `getZeroLifetimeRas()` method matching method android.net.metrics.ApfStats.Builder.setZeroLifetimeRas(int)
+
MissingGetterMatchingBuilder: android.net.metrics.DhcpClientEvent.Builder#setDurationMs(int):
- android.net.metrics.DhcpClientEvent does not declare a `getDurationMs()` method matching method android.net.metrics.DhcpClientEvent.Builder.setDurationMs(int)
+
MissingGetterMatchingBuilder: android.net.metrics.DhcpClientEvent.Builder#setMsg(String):
- android.net.metrics.DhcpClientEvent does not declare a `getMsg()` method matching method android.net.metrics.DhcpClientEvent.Builder.setMsg(String)
+
MissingGetterMatchingBuilder: android.net.metrics.ValidationProbeEvent.Builder#setDurationMs(long):
- android.net.metrics.ValidationProbeEvent does not declare a `getDurationMs()` method matching method android.net.metrics.ValidationProbeEvent.Builder.setDurationMs(long)
+
MissingGetterMatchingBuilder: android.net.metrics.ValidationProbeEvent.Builder#setProbeType(int, boolean):
- android.net.metrics.ValidationProbeEvent does not declare a `getProbeType()` method matching method android.net.metrics.ValidationProbeEvent.Builder.setProbeType(int,boolean)
+
MissingGetterMatchingBuilder: android.net.metrics.ValidationProbeEvent.Builder#setReturnCode(int):
- android.net.metrics.ValidationProbeEvent does not declare a `getReturnCode()` method matching method android.net.metrics.ValidationProbeEvent.Builder.setReturnCode(int)
+
MissingGetterMatchingBuilder: android.security.keystore.KeyGenParameterSpec.Builder#setUniqueIdIncluded(boolean):
- android.security.keystore.KeyGenParameterSpec does not declare a `isUniqueIdIncluded()` method matching method android.security.keystore.KeyGenParameterSpec.Builder.setUniqueIdIncluded(boolean)
+
MissingGetterMatchingBuilder: android.service.autofill.Dataset.Builder#setFieldInlinePresentation(android.view.autofill.AutofillId, android.view.autofill.AutofillValue, java.util.regex.Pattern, android.service.autofill.InlinePresentation):
- android.service.autofill.Dataset does not declare a `getFieldInlinePresentation()` method matching method android.service.autofill.Dataset.Builder.setFieldInlinePresentation(android.view.autofill.AutofillId,android.view.autofill.AutofillValue,java.util.regex.Pattern,android.service.autofill.InlinePresentation)
+
MissingGetterMatchingBuilder: android.service.autofill.augmented.FillResponse.Builder#setClientState(android.os.Bundle):
- android.service.autofill.augmented.FillResponse does not declare a `getClientState()` method matching method android.service.autofill.augmented.FillResponse.Builder.setClientState(android.os.Bundle)
+
MissingGetterMatchingBuilder: android.service.autofill.augmented.FillResponse.Builder#setFillWindow(android.service.autofill.augmented.FillWindow):
- android.service.autofill.augmented.FillResponse does not declare a `getFillWindow()` method matching method android.service.autofill.augmented.FillResponse.Builder.setFillWindow(android.service.autofill.augmented.FillWindow)
+
MissingGetterMatchingBuilder: android.service.autofill.augmented.FillResponse.Builder#setInlineSuggestions(java.util.List<android.service.autofill.Dataset>):
- android.service.autofill.augmented.FillResponse does not declare a `getInlineSuggestions()` method matching method android.service.autofill.augmented.FillResponse.Builder.setInlineSuggestions(java.util.List<android.service.autofill.Dataset>)
+
MissingGetterMatchingBuilder: android.telecom.CallScreeningService.CallResponse.Builder#setShouldScreenCallViaAudioProcessing(boolean):
- android.telecom.CallScreeningService.CallResponse does not declare a `shouldScreenCallViaAudioProcessing()` method matching method android.telecom.CallScreeningService.CallResponse.Builder.setShouldScreenCallViaAudioProcessing(boolean)
+
MissingGetterMatchingBuilder: android.telecom.ConnectionRequest.Builder#setIsAdhocConferenceCall(boolean):
- android.telecom.ConnectionRequest does not declare a `isIsAdhocConferenceCall()` method matching method android.telecom.ConnectionRequest.Builder.setIsAdhocConferenceCall(boolean)
+
MissingGetterMatchingBuilder: android.telecom.ConnectionRequest.Builder#setRttPipeFromInCall(android.os.ParcelFileDescriptor):
- android.telecom.ConnectionRequest does not declare a `getRttPipeFromInCall()` method matching method android.telecom.ConnectionRequest.Builder.setRttPipeFromInCall(android.os.ParcelFileDescriptor)
+
MissingGetterMatchingBuilder: android.telecom.ConnectionRequest.Builder#setRttPipeToInCall(android.os.ParcelFileDescriptor):
- android.telecom.ConnectionRequest does not declare a `getRttPipeToInCall()` method matching method android.telecom.ConnectionRequest.Builder.setRttPipeToInCall(android.os.ParcelFileDescriptor)
+
MissingGetterMatchingBuilder: android.telecom.ConnectionRequest.Builder#setShouldShowIncomingCallUi(boolean):
- android.telecom.ConnectionRequest does not declare a `shouldShowIncomingCallUi()` method matching method android.telecom.ConnectionRequest.Builder.setShouldShowIncomingCallUi(boolean)
+
MissingGetterMatchingBuilder: android.telecom.PhoneAccount.Builder#setGroupId(String):
- android.telecom.PhoneAccount does not declare a `getGroupId()` method matching method android.telecom.PhoneAccount.Builder.setGroupId(String)
+
MissingGetterMatchingBuilder: android.telephony.NetworkRegistrationInfo.Builder#setEmergencyOnly(boolean):
- android.telephony.NetworkRegistrationInfo does not declare a `isEmergencyOnly()` method matching method android.telephony.NetworkRegistrationInfo.Builder.setEmergencyOnly(boolean)
+
MissingGetterMatchingBuilder: android.telephony.ims.ImsSsData.Builder#setCallForwardingInfo(java.util.List<android.telephony.ims.ImsCallForwardInfo>):
- android.telephony.ims.ImsSsData does not declare a `getCallForwardingInfo()` method matching method android.telephony.ims.ImsSsData.Builder.setCallForwardingInfo(java.util.List<android.telephony.ims.ImsCallForwardInfo>)
+
MissingGetterMatchingBuilder: android.telephony.ims.stub.ImsFeatureConfiguration.Builder#addFeature(int, int):
- android.telephony.ims.stub.ImsFeatureConfiguration does not declare a `getFeatures()` method matching method android.telephony.ims.stub.ImsFeatureConfiguration.Builder.addFeature(int,int)
+
MissingGetterMatchingBuilder: android.telephony.mbms.DownloadRequest.Builder#setServiceId(String):
- android.telephony.mbms.DownloadRequest does not declare a `getServiceId()` method matching method android.telephony.mbms.DownloadRequest.Builder.setServiceId(String)
+
MissingNullability: android.app.Activity#onMovedToDisplay(int, android.content.res.Configuration) parameter #1:
@@ -2590,7 +2602,7 @@ OnNameExpected: android.service.notification.NotificationAssistantService#attach
OnNameExpected: android.service.quicksettings.TileService#isQuickSettingsSupported():
OnNameExpected: android.service.watchdog.ExplicitHealthCheckService#setCallback(android.os.RemoteCallback):
- If implemented by developer, should follow the on<Something> style; otherwise consider marking final
+
OnNameExpected: android.telephony.ims.ImsService#createMmTelFeature(int):
OnNameExpected: android.telephony.ims.ImsService#createRcsFeature(int):
@@ -2620,11 +2632,11 @@ OnNameExpected: android.telephony.mbms.vendor.MbmsGroupCallServiceBase#updateGro
OptionalBuilderConstructorArgument: android.app.prediction.AppTargetEvent.Builder#Builder(android.app.prediction.AppTarget, int) parameter #0:
- Builder constructor arguments must be mandatory (i.e. not @Nullable): parameter target in android.app.prediction.AppTargetEvent.Builder(android.app.prediction.AppTarget target, int actionType)
+
OptionalBuilderConstructorArgument: android.net.CaptivePortalData.Builder#Builder(android.net.CaptivePortalData) parameter #0:
- Builder constructor arguments must be mandatory (i.e. not @Nullable): parameter data in android.net.CaptivePortalData.Builder(android.net.CaptivePortalData data)
+
OptionalBuilderConstructorArgument: android.os.VibrationAttributes.Builder#Builder(android.media.AudioAttributes, android.os.VibrationEffect) parameter #1:
- Builder constructor arguments must be mandatory (i.e. not @Nullable): parameter effect in android.os.VibrationAttributes.Builder(android.media.AudioAttributes audio, android.os.VibrationEffect effect)
+
PackageLayering: android.util.FeatureFlagUtils:
@@ -2824,7 +2836,7 @@ SamShouldBeLast: android.os.IHwBinder#linkToDeath(android.os.IHwBinder.DeathReci
SamShouldBeLast: android.os.StrictMode.ViolationInfo#dump(android.util.Printer, String):
SamShouldBeLast: android.permission.PermissionControllerManager#countPermissionApps(java.util.List<java.lang.String>, int, android.permission.PermissionControllerManager.OnCountPermissionAppsResultCallback, android.os.Handler):
- SAM-compatible parameters (such as parameter 3, "callback", in android.permission.PermissionControllerManager.countPermissionApps) should be last to improve Kotlin interoperability; see https://kotlinlang.org/docs/reference/java-interop.html#sam-conversions
+
SamShouldBeLast: android.permission.PermissionControllerManager#getAppPermissions(String, android.permission.PermissionControllerManager.OnGetAppPermissionResultCallback, android.os.Handler):
SamShouldBeLast: android.permission.PermissionControllerManager#revokeRuntimePermissions(java.util.Map<java.lang.String,java.util.List<java.lang.String>>, boolean, int, java.util.concurrent.Executor, android.permission.PermissionControllerManager.OnRevokeRuntimePermissionsCallback):
@@ -2870,21 +2882,21 @@ SetterReturnsThis: android.media.audiopolicy.AudioPolicy.Builder#setAudioPolicyS
StaticFinalBuilder: android.content.integrity.RuleSet.Builder:
- Builder must be final: android.content.integrity.RuleSet.Builder
+
StaticFinalBuilder: android.hardware.display.BrightnessConfiguration.Builder:
- Builder must be final: android.hardware.display.BrightnessConfiguration.Builder
+
StaticFinalBuilder: android.media.audiopolicy.AudioMix.Builder:
- Builder must be final: android.media.audiopolicy.AudioMix.Builder
+
StaticFinalBuilder: android.media.audiopolicy.AudioMixingRule.Builder:
- Builder must be final: android.media.audiopolicy.AudioMixingRule.Builder
+
StaticFinalBuilder: android.media.audiopolicy.AudioPolicy.Builder:
- Builder must be final: android.media.audiopolicy.AudioPolicy.Builder
+
StaticFinalBuilder: android.net.CaptivePortalData.Builder:
- Builder must be final: android.net.CaptivePortalData.Builder
+
StaticFinalBuilder: android.net.TetheringManager.TetheringRequest.Builder:
- Builder must be final: android.net.TetheringManager.TetheringRequest.Builder
+
StaticFinalBuilder: android.telephony.ims.stub.ImsFeatureConfiguration.Builder:
- Builder must be final: android.telephony.ims.stub.ImsFeatureConfiguration.Builder
+
StaticUtils: android.os.health.HealthKeys:
@@ -2912,15 +2924,15 @@ StreamFiles: android.provider.MediaStore#scanVolume(android.content.Context, jav
UseIcu: android.hardware.soundtrigger.KeyphraseEnrollmentInfo#getKeyphraseMetadata(String, java.util.Locale) parameter #1:
- Type `java.util.Locale` should be replaced with richer ICU type `android.icu.util.ULocale`
+
UseIcu: android.hardware.soundtrigger.KeyphraseEnrollmentInfo#getManageKeyphraseIntent(int, String, java.util.Locale) parameter #2:
- Type `java.util.Locale` should be replaced with richer ICU type `android.icu.util.ULocale`
+
UseIcu: android.hardware.soundtrigger.KeyphraseMetadata#supportsLocale(java.util.Locale) parameter #0:
- Type `java.util.Locale` should be replaced with richer ICU type `android.icu.util.ULocale`
+
UseIcu: android.hardware.soundtrigger.SoundTrigger.Keyphrase#Keyphrase(int, int, java.util.Locale, String, int[]) parameter #2:
- Type `java.util.Locale` should be replaced with richer ICU type `android.icu.util.ULocale`
+
UseIcu: android.hardware.soundtrigger.SoundTrigger.Keyphrase#getLocale():
- Type `java.util.Locale` should be replaced with richer ICU type `android.icu.util.ULocale`
+
UseParcelFileDescriptor: android.util.proto.ProtoOutputStream#ProtoOutputStream(java.io.FileDescriptor) parameter #0:
diff --git a/cmds/bootanimation/BootAnimation.cpp b/cmds/bootanimation/BootAnimation.cpp
index 9c796128df84..046145f90808 100644
--- a/cmds/bootanimation/BootAnimation.cpp
+++ b/cmds/bootanimation/BootAnimation.cpp
@@ -482,6 +482,8 @@ status_t BootAnimation::readyToRun() {
mFlingerSurface = s;
mTargetInset = -1;
+ projectSceneToWindow();
+
// Register a display event receiver
mDisplayEventReceiver = std::make_unique<DisplayEventReceiver>();
status_t status = mDisplayEventReceiver->initCheck();
@@ -493,6 +495,16 @@ status_t BootAnimation::readyToRun() {
return NO_ERROR;
}
+void BootAnimation::projectSceneToWindow() {
+ glViewport(0, 0, mWidth, mHeight);
+ glScissor(0, 0, mWidth, mHeight);
+ glMatrixMode(GL_PROJECTION);
+ glLoadIdentity();
+ glOrthof(0, static_cast<float>(mWidth), 0, static_cast<float>(mHeight), -1, 1);
+ glMatrixMode(GL_MODELVIEW);
+ glLoadIdentity();
+}
+
void BootAnimation::resizeSurface(int newWidth, int newHeight) {
// We assume this function is called on the animation thread.
if (newWidth == mWidth && newHeight == mHeight) {
@@ -517,8 +529,8 @@ void BootAnimation::resizeSurface(int newWidth, int newHeight) {
SLOGE("Can't make the new surface current. Error %d", eglGetError());
return;
}
- glViewport(0, 0, mWidth, mHeight);
- glScissor(0, 0, mWidth, mHeight);
+
+ projectSceneToWindow();
mSurface = surface;
}
@@ -799,6 +811,37 @@ status_t BootAnimation::initFont(Font* font, const char* fallback) {
return status;
}
+void BootAnimation::fadeFrame(const int frameLeft, const int frameBottom, const int frameWidth,
+ const int frameHeight, const Animation::Part& part,
+ const int fadedFramesCount) {
+ glEnable(GL_BLEND);
+ glEnableClientState(GL_VERTEX_ARRAY);
+ glDisable(GL_TEXTURE_2D);
+ // avoid creating a hole due to mixing result alpha with GL_REPLACE texture
+ glBlendFuncSeparateOES(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA, GL_ZERO, GL_ONE);
+
+ const float alpha = static_cast<float>(fadedFramesCount) / part.framesToFadeCount;
+ glColor4f(part.backgroundColor[0], part.backgroundColor[1], part.backgroundColor[2], alpha);
+
+ const float frameStartX = static_cast<float>(frameLeft);
+ const float frameStartY = static_cast<float>(frameBottom);
+ const float frameEndX = frameStartX + frameWidth;
+ const float frameEndY = frameStartY + frameHeight;
+ const GLfloat frameRect[] = {
+ frameStartX, frameStartY,
+ frameEndX, frameStartY,
+ frameEndX, frameEndY,
+ frameStartX, frameEndY
+ };
+ glVertexPointer(2, GL_FLOAT, 0, frameRect);
+ glDrawArrays(GL_TRIANGLE_FAN, 0, 4);
+
+ glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
+ glEnable(GL_TEXTURE_2D);
+ glDisableClientState(GL_VERTEX_ARRAY);
+ glDisable(GL_BLEND);
+}
+
void BootAnimation::drawText(const char* str, const Font& font, bool bold, int* x, int* y) {
glEnable(GL_BLEND); // Allow us to draw on top of the animation
glBindTexture(GL_TEXTURE_2D, font.texture.name);
@@ -890,23 +933,34 @@ bool BootAnimation::parseAnimationDesc(Animation& animation) {
int height = 0;
int count = 0;
int pause = 0;
+ int framesToFadeCount = 0;
char path[ANIM_ENTRY_NAME_MAX];
char color[7] = "000000"; // default to black if unspecified
char clockPos1[TEXT_POS_LEN_MAX + 1] = "";
char clockPos2[TEXT_POS_LEN_MAX + 1] = "";
-
char pathType;
+
+ int nextReadPos;
+
if (sscanf(l, "%d %d %d", &width, &height, &fps) == 3) {
// SLOGD("> w=%d, h=%d, fps=%d", width, height, fps);
animation.width = width;
animation.height = height;
animation.fps = fps;
- } else if (sscanf(l, " %c %d %d %" STRTO(ANIM_PATH_MAX) "s #%6s %16s %16s",
- &pathType, &count, &pause, path, color, clockPos1, clockPos2) >= 4) {
- //SLOGD("> type=%c, count=%d, pause=%d, path=%s, color=%s, clockPos1=%s, clockPos2=%s",
- // pathType, count, pause, path, color, clockPos1, clockPos2);
+ } else if (sscanf(l, "%c %d %d %" STRTO(ANIM_PATH_MAX) "s%n",
+ &pathType, &count, &pause, path, &nextReadPos) >= 4) {
+ if (pathType == 'f') {
+ sscanf(l + nextReadPos, " %d #%6s %16s %16s", &framesToFadeCount, color, clockPos1,
+ clockPos2);
+ } else {
+ sscanf(l + nextReadPos, " #%6s %16s %16s", color, clockPos1, clockPos2);
+ }
+ // SLOGD("> type=%c, count=%d, pause=%d, path=%s, framesToFadeCount=%d, color=%s, "
+ // "clockPos1=%s, clockPos2=%s",
+ // pathType, count, pause, path, framesToFadeCount, color, clockPos1, clockPos2);
Animation::Part part;
part.playUntilComplete = pathType == 'c';
+ part.framesToFadeCount = framesToFadeCount;
part.count = count;
part.pause = pause;
part.path = path;
@@ -925,6 +979,7 @@ bool BootAnimation::parseAnimationDesc(Animation& animation) {
// SLOGD("> SYSTEM");
Animation::Part part;
part.playUntilComplete = false;
+ part.framesToFadeCount = 0;
part.count = 1;
part.pause = 0;
part.audioData = nullptr;
@@ -1121,12 +1176,19 @@ bool BootAnimation::movie() {
return false;
}
+bool BootAnimation::shouldStopPlayingPart(const Animation::Part& part, const int fadedFramesCount) {
+ // stop playing only if it is time to exit and it's a partial part which has been faded out
+ return exitPending() && !part.playUntilComplete && fadedFramesCount >= part.framesToFadeCount;
+}
+
bool BootAnimation::playAnimation(const Animation& animation) {
const size_t pcount = animation.parts.size();
nsecs_t frameDuration = s2ns(1) / animation.fps;
SLOGD("%sAnimationShownTiming start time: %" PRId64 "ms", mShuttingDown ? "Shutdown" : "Boot",
elapsedRealtime());
+
+ int fadedFramesCount = 0;
for (size_t i=0 ; i<pcount ; i++) {
const Animation::Part& part(animation.parts[i]);
const size_t fcount = part.frames.size();
@@ -1140,10 +1202,9 @@ bool BootAnimation::playAnimation(const Animation& animation) {
continue; //to next part
}
- for (int r=0 ; !part.count || r<part.count ; r++) {
- // Exit any non playuntil complete parts immediately
- if(exitPending() && !part.playUntilComplete)
- break;
+ // process the part not only while the count allows but also if already fading
+ for (int r=0 ; !part.count || r<part.count || fadedFramesCount > 0 ; r++) {
+ if (shouldStopPlayingPart(part, fadedFramesCount)) break;
mCallbacks->playPart(i, part, r);
@@ -1153,7 +1214,9 @@ bool BootAnimation::playAnimation(const Animation& animation) {
part.backgroundColor[2],
1.0f);
- for (size_t j=0 ; j<fcount && (!exitPending() || part.playUntilComplete) ; j++) {
+ for (size_t j=0 ; j<fcount ; j++) {
+ if (shouldStopPlayingPart(part, fadedFramesCount)) break;
+
processDisplayEvents();
const int animationX = (mWidth - animation.width) / 2;
@@ -1192,11 +1255,22 @@ bool BootAnimation::playAnimation(const Animation& animation) {
}
// specify the y center as ceiling((mHeight - frame.trimHeight) / 2)
// which is equivalent to mHeight - (yc + frame.trimHeight)
- glDrawTexiOES(xc, mHeight - (yc + frame.trimHeight),
- 0, frame.trimWidth, frame.trimHeight);
+ const int frameDrawY = mHeight - (yc + frame.trimHeight);
+ glDrawTexiOES(xc, frameDrawY, 0, frame.trimWidth, frame.trimHeight);
+
+ // if the part hasn't been stopped yet then continue fading if necessary
+ if (exitPending() && part.hasFadingPhase()) {
+ fadeFrame(xc, frameDrawY, frame.trimWidth, frame.trimHeight, part,
+ ++fadedFramesCount);
+ if (fadedFramesCount >= part.framesToFadeCount) {
+ fadedFramesCount = MAX_FADED_FRAMES_COUNT; // no more fading
+ }
+ }
+
if (mClockEnabled && mTimeIsAccurate && validClock(part)) {
drawClock(animation.clockFont, part.clockPosX, part.clockPosY);
}
+
handleViewport(frameDuration);
eglSwapBuffers(mDisplay, mSurface);
@@ -1221,11 +1295,11 @@ bool BootAnimation::playAnimation(const Animation& animation) {
usleep(part.pause * ns2us(frameDuration));
- // For infinite parts, we've now played them at least once, so perhaps exit
- if(exitPending() && !part.count && mCurrentInset >= mTargetInset)
- break;
+ if (exitPending() && !part.count && mCurrentInset >= mTargetInset &&
+ !part.hasFadingPhase()) {
+ break; // exit the infinite non-fading part when it has been played at least once
+ }
}
-
}
// Free textures created for looping parts now that the animation is done.
diff --git a/cmds/bootanimation/BootAnimation.h b/cmds/bootanimation/BootAnimation.h
index 9e6e4aa42f1c..4699cfe2ac2d 100644
--- a/cmds/bootanimation/BootAnimation.h
+++ b/cmds/bootanimation/BootAnimation.h
@@ -19,6 +19,7 @@
#include <vector>
#include <queue>
+#include <climits>
#include <stdint.h>
#include <sys/types.h>
@@ -43,6 +44,8 @@ class SurfaceControl;
class BootAnimation : public Thread, public IBinder::DeathRecipient
{
public:
+ static constexpr int MAX_FADED_FRAMES_COUNT = std::numeric_limits<int>::max();
+
struct Texture {
GLint w;
GLint h;
@@ -82,10 +85,15 @@ public:
String8 trimData;
SortedVector<Frame> frames;
bool playUntilComplete;
+ int framesToFadeCount;
float backgroundColor[3];
uint8_t* audioData;
int audioLength;
Animation* animation;
+
+ bool hasFadingPhase() const {
+ return !playUntilComplete && framesToFadeCount > 0;
+ }
};
int fps;
int width;
@@ -160,6 +168,8 @@ private:
bool movie();
void drawText(const char* str, const Font& font, bool bold, int* x, int* y);
void drawClock(const Font& font, const int xPos, const int yPos);
+ void fadeFrame(int frameLeft, int frameBottom, int frameWidth, int frameHeight,
+ const Animation::Part& part, int fadedFramesCount);
bool validClock(const Animation::Part& part);
Animation* loadAnimation(const String8&);
bool playAnimation(const Animation&);
@@ -172,7 +182,9 @@ private:
EGLConfig getEglConfig(const EGLDisplay&);
ui::Size limitSurfaceSize(int width, int height) const;
void resizeSurface(int newWidth, int newHeight);
+ void projectSceneToWindow();
+ bool shouldStopPlayingPart(const Animation::Part& part, int fadedFramesCount);
void checkExit();
void handleViewport(nsecs_t timestep);
diff --git a/cmds/bootanimation/FORMAT.md b/cmds/bootanimation/FORMAT.md
index 5946515aa263..f9b83c957d5b 100644
--- a/cmds/bootanimation/FORMAT.md
+++ b/cmds/bootanimation/FORMAT.md
@@ -30,14 +30,20 @@ The first line defines the general parameters of the animation:
It is followed by a number of rows of the form:
- TYPE COUNT PAUSE PATH [#RGBHEX [CLOCK1 [CLOCK2]]]
+ TYPE COUNT PAUSE PATH [FADE [#RGBHEX [CLOCK1 [CLOCK2]]]]
* **TYPE:** a single char indicating what type of animation segment this is:
+ `p` -- this part will play unless interrupted by the end of the boot
+ `c` -- this part will play to completion, no matter what
+ + `f` -- same as `p` but in addition the specified number of frames is being faded out while
+ continue playing. Only the first interrupted `f` part is faded out, other subsequent `f`
+ parts are skipped
* **COUNT:** how many times to play the animation, or 0 to loop forever until boot is complete
* **PAUSE:** number of FRAMES to delay after this part ends
* **PATH:** directory in which to find the frames for this part (e.g. `part0`)
+ * **FADE:** _(ONLY FOR `f` TYPE)_ number of frames to fade out when interrupted where `0` means
+ _immediately_ which makes `f ... 0` behave like `p` and doesn't count it as a fading
+ part
* **RGBHEX:** _(OPTIONAL)_ a background color, specified as `#RRGGBB`
* **CLOCK1, CLOCK2:** _(OPTIONAL)_ the coordinates at which to draw the current time (for watches):
+ If only `CLOCK1` is provided it is the y-coordinate of the clock and the x-coordinate
diff --git a/cmds/statsd/src/atoms.proto b/cmds/statsd/src/atoms.proto
index fc1455be29b1..0a09801708a5 100644
--- a/cmds/statsd/src/atoms.proto
+++ b/cmds/statsd/src/atoms.proto
@@ -38,6 +38,7 @@ import "frameworks/base/core/proto/android/net/networkcapabilities.proto";
import "frameworks/base/core/proto/android/os/enums.proto";
import "frameworks/base/core/proto/android/server/connectivity/data_stall_event.proto";
import "frameworks/base/core/proto/android/server/enums.proto";
+import "frameworks/base/core/proto/android/stats/hdmi/enums.proto";
import "frameworks/base/core/proto/android/server/job/enums.proto";
import "frameworks/base/core/proto/android/server/location/enums.proto";
import "frameworks/base/core/proto/android/service/procstats_enum.proto";
@@ -490,6 +491,8 @@ message Atom {
UIActionLatencyReported ui_action_latency_reported = 306 [(module) = "framework"];
WifiDisconnectReported wifi_disconnect_reported = 307 [(module) = "wifi"];
WifiConnectionStateChanged wifi_connection_state_changed = 308 [(module) = "wifi"];
+ HdmiCecActiveSourceChanged hdmi_cec_active_source_changed = 309 [(module) = "framework"];
+ HdmiCecMessageReported hdmi_cec_message_reported = 310 [(module) = "framework"];
// StatsdStats tracks platform atoms with ids upto 500.
// Update StatsdStats::kMaxPushedAtomId when atom ids here approach that value.
@@ -11433,4 +11436,65 @@ message BlobInfo {
// List of leasees of this Blob
optional BlobLeaseeListProto leasees = 5 [(log_mode) = MODE_BYTES];
-} \ No newline at end of file
+}
+
+/**
+ * Logs when the HDMI CEC active source changes.
+ *
+ * Logged from:
+ * frameworks/base/services/core/java/com/android/server/hdmi/HdmiCecAtomWriter.java
+ */
+message HdmiCecActiveSourceChanged {
+ // The logical address of the active source.
+ optional android.stats.hdmi.LogicalAddress active_source_logical_address = 1;
+
+ // The physical address of the active source. Consists of four hexadecimal nibbles.
+ // Examples: 0x1234, 0x0000 (root device). 0xFFFF represents an unknown or invalid address.
+ // See section 8.7 in the HDMI 1.4b spec for details.
+ optional int32 active_source_physical_address = 2;
+
+ // The relationship between this device and the active source.
+ optional android.stats.hdmi.PathRelationship local_relationship = 3;
+}
+
+/**
+ * Logs when an HDMI CEC message is sent or received.
+ *
+ * Logged from:
+ * frameworks/base/services/core/java/com/android/server/hdmi/HdmiCecAtomWriter.java
+ */
+message HdmiCecMessageReported {
+ // The calling uid of the application that caused this atom to be written.
+ optional int32 uid = 1 [(is_uid) = true];
+
+ // Whether a HDMI CEC message is sent from this device, to this device, or neither.
+ optional android.stats.hdmi.MessageDirection direction = 2;
+
+ // The HDMI CEC logical address of the initiator.
+ optional android.stats.hdmi.LogicalAddress initiator_logical_address = 3;
+
+ // The HDMI CEC logical address of the destination.
+ optional android.stats.hdmi.LogicalAddress destination_logical_address = 4;
+
+ // The opcode of the message. Ranges from 0x00 to 0xFF.
+ // For all values, see section "CEC 15 Message Descriptions" in the HDMI CEC 1.4b spec.
+ optional int32 opcode = 5;
+
+ // The result of attempting to send the message on its final retransmission attempt.
+ // Only applicable to outgoing messages; set to SEND_MESSAGE_RESULT_UNKNOWN otherwise.
+ optional android.stats.hdmi.SendMessageResult send_message_result = 6;
+
+ // Fields specific to <User Control Pressed> messages
+
+ // The user control command that was received.
+ optional android.stats.hdmi.UserControlPressedCommand user_control_pressed_command = 7;
+
+ // Fields specific to <Feature Abort> messages
+
+ // The opcode of the message that was feature aborted.
+ // Set to 0x100 when unknown or not applicable.
+ optional int32 feature_abort_opcode = 8;
+
+ // The reason for the feature abort.
+ optional android.stats.hdmi.FeatureAbortReason feature_abort_reason = 9;
+}
diff --git a/cmds/statsd/src/condition/ConditionTimer.h b/cmds/statsd/src/condition/ConditionTimer.h
index 442bc11934fe..1fbe25279736 100644
--- a/cmds/statsd/src/condition/ConditionTimer.h
+++ b/cmds/statsd/src/condition/ConditionTimer.h
@@ -36,7 +36,7 @@ class ConditionTimer {
public:
explicit ConditionTimer(bool initCondition, int64_t bucketStartNs) : mCondition(initCondition) {
if (initCondition) {
- mLastConditionTrueTimestampNs = bucketStartNs;
+ mLastConditionChangeTimestampNs = bucketStartNs;
}
};
@@ -44,21 +44,46 @@ public:
// When a new bucket is created, this value will be reset to 0.
int64_t mTimerNs = 0;
- // Last elapsed real timestamp when condition turned to true
- // When a new bucket is created and the condition is true, then the timestamp is set
- // to be the bucket start timestamp.
- int64_t mLastConditionTrueTimestampNs = 0;
+ // Last elapsed real timestamp when condition changed.
+ int64_t mLastConditionChangeTimestampNs = 0;
bool mCondition = false;
int64_t newBucketStart(int64_t nextBucketStartNs) {
if (mCondition) {
- mTimerNs += (nextBucketStartNs - mLastConditionTrueTimestampNs);
- mLastConditionTrueTimestampNs = nextBucketStartNs;
+ // Normally, the next bucket happens after the last condition
+ // change. In this case, add the time between the condition becoming
+ // true to the next bucket start time.
+ // Otherwise, the next bucket start time is before the last
+ // condition change time, this means that the condition was false at
+ // the bucket boundary before the condition became true, so the
+ // timer should not get updated and the last condition change time
+ // remains as is.
+ if (nextBucketStartNs >= mLastConditionChangeTimestampNs) {
+ mTimerNs += (nextBucketStartNs - mLastConditionChangeTimestampNs);
+ mLastConditionChangeTimestampNs = nextBucketStartNs;
+ }
+ } else if (mLastConditionChangeTimestampNs > nextBucketStartNs) {
+ // The next bucket start time is before the last condition change
+ // time, this means that the condition was true at the bucket
+ // boundary before the condition became false, so adjust the timer
+ // to match how long the condition was true to the bucket boundary.
+ // This means remove the amount the condition stayed true in the
+ // next bucket from the current bucket.
+ mTimerNs -= (mLastConditionChangeTimestampNs - nextBucketStartNs);
}
int64_t temp = mTimerNs;
mTimerNs = 0;
+
+ if (!mCondition && (mLastConditionChangeTimestampNs > nextBucketStartNs)) {
+ // The next bucket start time is before the last condition change
+ // time, this means that the condition was true at the bucket
+ // boundary and remained true in the next bucket up to the condition
+ // change to false, so adjust the timer to match how long the
+ // condition stayed true in the next bucket (now the current bucket).
+ mTimerNs = mLastConditionChangeTimestampNs - nextBucketStartNs;
+ }
return temp;
}
@@ -67,11 +92,10 @@ public:
return;
}
mCondition = newCondition;
- if (newCondition) {
- mLastConditionTrueTimestampNs = timestampNs;
- } else {
- mTimerNs += (timestampNs - mLastConditionTrueTimestampNs);
+ if (newCondition == false) {
+ mTimerNs += (timestampNs - mLastConditionChangeTimestampNs);
}
+ mLastConditionChangeTimestampNs = timestampNs;
}
FRIEND_TEST(ConditionTimerTest, TestTimer_Inital_False);
@@ -80,4 +104,4 @@ public:
} // namespace statsd
} // namespace os
-} // namespace android \ No newline at end of file
+} // namespace android
diff --git a/cmds/statsd/src/condition/ConditionTracker.h b/cmds/statsd/src/condition/ConditionTracker.h
index 3bf4e636be89..5a6b8cf334eb 100644
--- a/cmds/statsd/src/condition/ConditionTracker.h
+++ b/cmds/statsd/src/condition/ConditionTracker.h
@@ -68,9 +68,9 @@ public:
// index: the new index of this tracker in allConditionProtos and allConditionTrackers.
// allConditionTrackers: the list of all ConditionTrackers (this is needed because we may also
// need to call onConfigUpdated() on child conditions)
- // [atomMatchingTrackerMap]: map of atom matcher id to index after the config update
- // [conditionTrackerMap]: map of condition tracker id to index after the config update.
- // returns whether or not the update is successful
+ // atomMatchingTrackerMap: map of atom matcher id to index after the config update.
+ // conditionTrackerMap: map of condition tracker id to index after the config update.
+ // returns whether or not the update is successful.
virtual bool onConfigUpdated(const std::vector<Predicate>& allConditionProtos, const int index,
const std::vector<sp<ConditionTracker>>& allConditionTrackers,
const std::unordered_map<int64_t, int>& atomMatchingTrackerMap,
diff --git a/cmds/statsd/src/metrics/EventMetricProducer.cpp b/cmds/statsd/src/metrics/EventMetricProducer.cpp
index dfe4559b05fb..ca302c0e71fb 100644
--- a/cmds/statsd/src/metrics/EventMetricProducer.cpp
+++ b/cmds/statsd/src/metrics/EventMetricProducer.cpp
@@ -18,12 +18,14 @@
#include "Log.h"
#include "EventMetricProducer.h"
-#include "stats_util.h"
-#include "stats_log_util.h"
#include <limits.h>
#include <stdlib.h>
+#include "metrics/parsing_utils/metrics_manager_util.h"
+#include "stats_log_util.h"
+#include "stats_util.h"
+
using android::util::FIELD_COUNT_REPEATED;
using android::util::FIELD_TYPE_BOOL;
using android::util::FIELD_TYPE_FLOAT;
@@ -82,6 +84,47 @@ EventMetricProducer::~EventMetricProducer() {
VLOG("~EventMetricProducer() called");
}
+bool EventMetricProducer::onConfigUpdatedLocked(
+ const StatsdConfig& config, const int configIndex, const int metricIndex,
+ const vector<sp<AtomMatchingTracker>>& allAtomMatchingTrackers,
+ const unordered_map<int64_t, int>& oldAtomMatchingTrackerMap,
+ const unordered_map<int64_t, int>& newAtomMatchingTrackerMap,
+ const sp<EventMatcherWizard>& matcherWizard,
+ const vector<sp<ConditionTracker>>& allConditionTrackers,
+ const unordered_map<int64_t, int>& conditionTrackerMap, const sp<ConditionWizard>& wizard,
+ const unordered_map<int64_t, int>& metricToActivationMap,
+ unordered_map<int, vector<int>>& trackerToMetricMap,
+ unordered_map<int, vector<int>>& conditionToMetricMap,
+ unordered_map<int, vector<int>>& activationAtomTrackerToMetricMap,
+ unordered_map<int, vector<int>>& deactivationAtomTrackerToMetricMap,
+ vector<int>& metricsWithActivation) {
+ if (!MetricProducer::onConfigUpdatedLocked(
+ config, configIndex, metricIndex, allAtomMatchingTrackers,
+ oldAtomMatchingTrackerMap, newAtomMatchingTrackerMap, matcherWizard,
+ allConditionTrackers, conditionTrackerMap, wizard, metricToActivationMap,
+ trackerToMetricMap, conditionToMetricMap, activationAtomTrackerToMetricMap,
+ deactivationAtomTrackerToMetricMap, metricsWithActivation)) {
+ return false;
+ }
+
+ const EventMetric& metric = config.event_metric(configIndex);
+ int trackerIndex;
+ // Update appropriate indices, specifically mConditionIndex and MetricsManager maps.
+ if (!handleMetricWithAtomMatchingTrackers(metric.what(), metricIndex, false,
+ allAtomMatchingTrackers, newAtomMatchingTrackerMap,
+ trackerToMetricMap, trackerIndex)) {
+ return false;
+ }
+
+ if (metric.has_condition() &&
+ !handleMetricWithConditions(metric.condition(), metricIndex, conditionTrackerMap,
+ metric.links(), allConditionTrackers, mConditionTrackerIndex,
+ conditionToMetricMap)) {
+ return false;
+ }
+ return true;
+}
+
void EventMetricProducer::dropDataLocked(const int64_t dropTimeNs) {
mProto->clear();
StatsdStats::getInstance().noteBucketDropped(mMetricId);
diff --git a/cmds/statsd/src/metrics/EventMetricProducer.h b/cmds/statsd/src/metrics/EventMetricProducer.h
index e828dddcbb18..3347d7b6aab5 100644
--- a/cmds/statsd/src/metrics/EventMetricProducer.h
+++ b/cmds/statsd/src/metrics/EventMetricProducer.h
@@ -69,6 +69,22 @@ private:
// Internal interface to handle sliced condition change.
void onSlicedConditionMayChangeLocked(bool overallCondition, const int64_t eventTime) override;
+ bool onConfigUpdatedLocked(
+ const StatsdConfig& config, const int configIndex, const int metricIndex,
+ const std::vector<sp<AtomMatchingTracker>>& allAtomMatchingTrackers,
+ const std::unordered_map<int64_t, int>& oldAtomMatchingTrackerMap,
+ const std::unordered_map<int64_t, int>& newAtomMatchingTrackerMap,
+ const sp<EventMatcherWizard>& matcherWizard,
+ const std::vector<sp<ConditionTracker>>& allConditionTrackers,
+ const std::unordered_map<int64_t, int>& conditionTrackerMap,
+ const sp<ConditionWizard>& wizard,
+ const std::unordered_map<int64_t, int>& metricToActivationMap,
+ std::unordered_map<int, std::vector<int>>& trackerToMetricMap,
+ std::unordered_map<int, std::vector<int>>& conditionToMetricMap,
+ std::unordered_map<int, std::vector<int>>& activationAtomTrackerToMetricMap,
+ std::unordered_map<int, std::vector<int>>& deactivationAtomTrackerToMetricMap,
+ std::vector<int>& metricsWithActivation) override;
+
void dropDataLocked(const int64_t dropTimeNs) override;
// Internal function to calculate the current used bytes.
diff --git a/cmds/statsd/src/metrics/MetricProducer.cpp b/cmds/statsd/src/metrics/MetricProducer.cpp
index c1c1d20f00e2..95a7d40ea9a9 100644
--- a/cmds/statsd/src/metrics/MetricProducer.cpp
+++ b/cmds/statsd/src/metrics/MetricProducer.cpp
@@ -20,6 +20,7 @@
#include "MetricProducer.h"
#include "../guardrail/StatsdStats.h"
+#include "metrics/parsing_utils/metrics_manager_util.h"
#include "state/StateTracker.h"
using android::util::FIELD_COUNT_REPEATED;
@@ -72,6 +73,37 @@ MetricProducer::MetricProducer(
mStateGroupMap(stateGroupMap) {
}
+bool MetricProducer::onConfigUpdatedLocked(
+ const StatsdConfig& config, const int configIndex, const int metricIndex,
+ const vector<sp<AtomMatchingTracker>>& allAtomMatchingTrackers,
+ const unordered_map<int64_t, int>& oldAtomMatchingTrackerMap,
+ const unordered_map<int64_t, int>& newAtomMatchingTrackerMap,
+ const sp<EventMatcherWizard>& matcherWizard,
+ const vector<sp<ConditionTracker>>& allConditionTrackers,
+ const unordered_map<int64_t, int>& conditionTrackerMap, const sp<ConditionWizard>& wizard,
+ const unordered_map<int64_t, int>& metricToActivationMap,
+ unordered_map<int, vector<int>>& trackerToMetricMap,
+ unordered_map<int, vector<int>>& conditionToMetricMap,
+ unordered_map<int, vector<int>>& activationAtomTrackerToMetricMap,
+ unordered_map<int, vector<int>>& deactivationAtomTrackerToMetricMap,
+ vector<int>& metricsWithActivation) {
+ sp<ConditionWizard> tmpWizard = mWizard;
+ mWizard = wizard;
+
+ unordered_map<int, shared_ptr<Activation>> newEventActivationMap;
+ unordered_map<int, vector<shared_ptr<Activation>>> newEventDeactivationMap;
+ if (!handleMetricActivationOnConfigUpdate(
+ config, mMetricId, metricIndex, metricToActivationMap, oldAtomMatchingTrackerMap,
+ newAtomMatchingTrackerMap, mEventActivationMap, activationAtomTrackerToMetricMap,
+ deactivationAtomTrackerToMetricMap, metricsWithActivation, newEventActivationMap,
+ newEventDeactivationMap)) {
+ return false;
+ }
+ mEventActivationMap = newEventActivationMap;
+ mEventDeactivationMap = newEventDeactivationMap;
+ return true;
+}
+
void MetricProducer::onMatchedLogEventLocked(const size_t matcherIndex, const LogEvent& event) {
if (!mIsActive) {
return;
diff --git a/cmds/statsd/src/metrics/MetricProducer.h b/cmds/statsd/src/metrics/MetricProducer.h
index bb590aac54d6..320b28581a38 100644
--- a/cmds/statsd/src/metrics/MetricProducer.h
+++ b/cmds/statsd/src/metrics/MetricProducer.h
@@ -26,6 +26,7 @@
#include "anomaly/AnomalyTracker.h"
#include "condition/ConditionWizard.h"
#include "config/ConfigKey.h"
+#include "matchers/EventMatcherWizard.h"
#include "matchers/matcher_util.h"
#include "packages/PackageInfoListener.h"
#include "state/StateListener.h"
@@ -151,6 +152,33 @@ public:
return conditionIndex >= 0 ? initialConditionCache[conditionIndex] : ConditionState::kTrue;
}
+ // Update appropriate state on config updates. Primarily, all indices need to be updated.
+ // This metric and all of its dependencies are guaranteed to be preserved across the update.
+ // This function also updates several maps used by metricsManager.
+ bool onConfigUpdated(
+ const StatsdConfig& config, const int configIndex, const int metricIndex,
+ const std::vector<sp<AtomMatchingTracker>>& allAtomMatchingTrackers,
+ const std::unordered_map<int64_t, int>& oldAtomMatchingTrackerMap,
+ const std::unordered_map<int64_t, int>& newAtomMatchingTrackerMap,
+ const sp<EventMatcherWizard>& matcherWizard,
+ const std::vector<sp<ConditionTracker>>& allConditionTrackers,
+ const std::unordered_map<int64_t, int>& conditionTrackerMap,
+ const sp<ConditionWizard>& wizard,
+ const std::unordered_map<int64_t, int>& metricToActivationMap,
+ std::unordered_map<int, std::vector<int>>& trackerToMetricMap,
+ std::unordered_map<int, std::vector<int>>& conditionToMetricMap,
+ std::unordered_map<int, std::vector<int>>& activationAtomTrackerToMetricMap,
+ std::unordered_map<int, std::vector<int>>& deactivationAtomTrackerToMetricMap,
+ std::vector<int>& metricsWithActivation) {
+ std::lock_guard<std::mutex> lock(mMutex);
+ return onConfigUpdatedLocked(config, configIndex, metricIndex, allAtomMatchingTrackers,
+ oldAtomMatchingTrackerMap, newAtomMatchingTrackerMap,
+ matcherWizard, allConditionTrackers, conditionTrackerMap,
+ wizard, metricToActivationMap, trackerToMetricMap,
+ conditionToMetricMap, activationAtomTrackerToMetricMap,
+ deactivationAtomTrackerToMetricMap, metricsWithActivation);
+ };
+
/**
* Force a partial bucket split on app upgrade
*/
@@ -209,6 +237,25 @@ public:
dumpLatency, str_set, protoOutput);
}
+ // Update appropriate state on config updates. Primarily, all indices need to be updated.
+ // This metric and all of its dependencies are guaranteed to be preserved across the update.
+ // This function also updates several maps used by metricsManager.
+ virtual bool onConfigUpdatedLocked(
+ const StatsdConfig& config, const int configIndex, const int metricIndex,
+ const std::vector<sp<AtomMatchingTracker>>& allAtomMatchingTrackers,
+ const std::unordered_map<int64_t, int>& oldAtomMatchingTrackerMap,
+ const std::unordered_map<int64_t, int>& newAtomMatchingTrackerMap,
+ const sp<EventMatcherWizard>& matcherWizard,
+ const std::vector<sp<ConditionTracker>>& allConditionTrackers,
+ const std::unordered_map<int64_t, int>& conditionTrackerMap,
+ const sp<ConditionWizard>& wizard,
+ const std::unordered_map<int64_t, int>& metricToActivationMap,
+ std::unordered_map<int, std::vector<int>>& trackerToMetricMap,
+ std::unordered_map<int, std::vector<int>>& conditionToMetricMap,
+ std::unordered_map<int, std::vector<int>>& activationAtomTrackerToMetricMap,
+ std::unordered_map<int, std::vector<int>>& deactivationAtomTrackerToMetricMap,
+ std::vector<int>& metricsWithActivation);
+
void clearPastBuckets(const int64_t dumpTimeNs) {
std::lock_guard<std::mutex> lock(mMutex);
return clearPastBucketsLocked(dumpTimeNs);
@@ -517,6 +564,9 @@ protected:
FRIEND_TEST(ValueMetricE2eTest, TestInitialConditionChanges);
FRIEND_TEST(MetricsManagerTest, TestInitialConditions);
+
+ FRIEND_TEST(ConfigUpdateTest, TestUpdateMetricActivations);
+ FRIEND_TEST(ConfigUpdateTest, TestUpdateEventMetrics);
};
} // namespace statsd
diff --git a/cmds/statsd/src/metrics/MetricsManager.cpp b/cmds/statsd/src/metrics/MetricsManager.cpp
index 39806890c42d..ab0d286d6b29 100644
--- a/cmds/statsd/src/metrics/MetricsManager.cpp
+++ b/cmds/statsd/src/metrics/MetricsManager.cpp
@@ -206,18 +206,33 @@ bool MetricsManager::updateConfig(const StatsdConfig& config, const int64_t time
unordered_map<int64_t, int> newAtomMatchingTrackerMap;
vector<sp<ConditionTracker>> newConditionTrackers;
unordered_map<int64_t, int> newConditionTrackerMap;
+ map<int64_t, uint64_t> newStateProtoHashes;
+ vector<sp<MetricProducer>> newMetricProducers;
+ unordered_map<int64_t, int> newMetricProducerMap;
mTagIds.clear();
+ mConditionToMetricMap.clear();
+ mTrackerToMetricMap.clear();
mTrackerToConditionMap.clear();
+ mActivationAtomTrackerToMetricMap.clear();
+ mDeactivationAtomTrackerToMetricMap.clear();
+ mMetricIndexesWithActivation.clear();
+ mNoReportMetricIds.clear();
mConfigValid = updateStatsdConfig(
mConfigKey, config, mUidMap, mPullerManager, anomalyAlarmMonitor, periodicAlarmMonitor,
timeBaseNs, currentTimeNs, mAllAtomMatchingTrackers, mAtomMatchingTrackerMap,
- mAllConditionTrackers, mConditionTrackerMap, mTagIds, newAtomMatchingTrackers,
- newAtomMatchingTrackerMap, newConditionTrackers, newConditionTrackerMap,
- mTrackerToConditionMap);
+ mAllConditionTrackers, mConditionTrackerMap, mAllMetricProducers, mMetricProducerMap,
+ mStateProtoHashes, mTagIds, newAtomMatchingTrackers, newAtomMatchingTrackerMap,
+ newConditionTrackers, newConditionTrackerMap, newMetricProducers, newMetricProducerMap,
+ mConditionToMetricMap, mTrackerToMetricMap, mTrackerToConditionMap,
+ mActivationAtomTrackerToMetricMap, mDeactivationAtomTrackerToMetricMap,
+ mMetricIndexesWithActivation, newStateProtoHashes, mNoReportMetricIds);
mAllAtomMatchingTrackers = newAtomMatchingTrackers;
mAtomMatchingTrackerMap = newAtomMatchingTrackerMap;
mAllConditionTrackers = newConditionTrackers;
mConditionTrackerMap = newConditionTrackerMap;
+ mAllMetricProducers = newMetricProducers;
+ mMetricProducerMap = newMetricProducerMap;
+ mStateProtoHashes = newStateProtoHashes;
return mConfigValid;
}
diff --git a/cmds/statsd/src/metrics/ValueMetricProducer.cpp b/cmds/statsd/src/metrics/ValueMetricProducer.cpp
index 39ae9a47f2bf..3d57cfe318c5 100644
--- a/cmds/statsd/src/metrics/ValueMetricProducer.cpp
+++ b/cmds/statsd/src/metrics/ValueMetricProducer.cpp
@@ -301,7 +301,6 @@ void ValueMetricProducer::onDumpReportLocked(const int64_t dumpTimeNs,
protoOutput->write(FIELD_TYPE_INT32 | FIELD_ID_BUCKET_DROP_REASON, dropEvent.reason);
protoOutput->write(FIELD_TYPE_INT64 | FIELD_ID_DROP_TIME,
(long long)(NanoToMillis(dropEvent.dropTimeNs)));
- ;
protoOutput->end(dropEventToken);
}
protoOutput->end(wrapperToken);
@@ -346,8 +345,11 @@ void ValueMetricProducer::onDumpReportLocked(const int64_t dumpTimeNs,
protoOutput->write(FIELD_TYPE_INT64 | FIELD_ID_BUCKET_NUM,
(long long)(getBucketNumFromEndTimeNs(bucket.mBucketEndNs)));
}
- // only write the condition timer value if the metric has a condition.
- if (mConditionTrackerIndex >= 0) {
+ // We only write the condition timer value if the metric has a
+ // condition and/or is sliced by state.
+ // If the metric is sliced by state, the condition timer value is
+ // also sliced by state to reflect time spent in that state.
+ if (mConditionTrackerIndex >= 0 || !mSlicedStateAtoms.empty()) {
protoOutput->write(FIELD_TYPE_INT64 | FIELD_ID_CONDITION_TRUE_NS,
(long long)bucket.mConditionTrueNs);
}
@@ -454,6 +456,8 @@ void ValueMetricProducer::onActiveStateChangedLocked(const int64_t& eventTimeNs)
// Let condition timer know of new active state.
mConditionTimer.onConditionChanged(mIsActive, eventTimeNs);
+
+ updateCurrentSlicedBucketConditionTimers(mIsActive, eventTimeNs);
}
void ValueMetricProducer::onConditionChangedLocked(const bool condition,
@@ -476,6 +480,8 @@ void ValueMetricProducer::onConditionChangedLocked(const bool condition,
invalidateCurrentBucket(eventTimeNs, BucketDropReason::EVENT_IN_WRONG_BUCKET);
mCondition = ConditionState::kUnknown;
mConditionTimer.onConditionChanged(mCondition, eventTimeNs);
+
+ updateCurrentSlicedBucketConditionTimers(mCondition, eventTimeNs);
return;
}
@@ -517,6 +523,29 @@ void ValueMetricProducer::onConditionChangedLocked(const bool condition,
flushIfNeededLocked(eventTimeNs);
mConditionTimer.onConditionChanged(mCondition, eventTimeNs);
+
+ updateCurrentSlicedBucketConditionTimers(mCondition, eventTimeNs);
+}
+
+void ValueMetricProducer::updateCurrentSlicedBucketConditionTimers(bool newCondition,
+ int64_t eventTimeNs) {
+ if (mSlicedStateAtoms.empty()) {
+ return;
+ }
+
+ // Utilize the current state key of each DimensionsInWhat key to determine
+ // which condition timers to update.
+ //
+ // Assumes that the MetricDimensionKey exists in `mCurrentSlicedBucket`.
+ bool inPulledData;
+ for (const auto& [dimensionInWhatKey, dimensionInWhatInfo] : mCurrentBaseInfo) {
+ // If the new condition is true, turn ON the condition timer only if
+ // the DimensionInWhat key was present in the pulled data.
+ inPulledData = dimensionInWhatInfo.hasCurrentState;
+ mCurrentSlicedBucket[MetricDimensionKey(dimensionInWhatKey,
+ dimensionInWhatInfo.currentState)]
+ .conditionTimer.onConditionChanged(newCondition && inPulledData, eventTimeNs);
+ }
}
void ValueMetricProducer::prepareFirstBucketLocked() {
@@ -618,8 +647,8 @@ void ValueMetricProducer::accumulateEvents(const std::vector<std::shared_ptr<Log
// 2. A superset of the current mStateChangePrimaryKey
// was not found in the new pulled data (i.e. not in mMatchedDimensionInWhatKeys)
// then we need to reset the base.
- for (auto& slice : mCurrentSlicedBucket) {
- const auto& whatKey = slice.first.getDimensionKeyInWhat();
+ for (auto& [metricDimensionKey, currentValueBucket] : mCurrentSlicedBucket) {
+ const auto& whatKey = metricDimensionKey.getDimensionKeyInWhat();
bool presentInPulledData =
mMatchedMetricDimensionKeys.find(whatKey) != mMatchedMetricDimensionKeys.end();
if (!presentInPulledData && whatKey.contains(mStateChangePrimaryKey.second)) {
@@ -627,6 +656,12 @@ void ValueMetricProducer::accumulateEvents(const std::vector<std::shared_ptr<Log
for (auto& baseInfo : it->second.baseInfos) {
baseInfo.hasBase = false;
}
+ // Set to false when DimensionInWhat key is not present in a pull.
+ // Used in onMatchedLogEventInternalLocked() to ensure the condition
+ // timer is turned on the next pull when data is present.
+ it->second.hasCurrentState = false;
+ // Turn OFF condition timer for keys not present in pulled data.
+ currentValueBucket.conditionTimer.onConditionChanged(false, eventElapsedTimeNs);
}
}
mMatchedMetricDimensionKeys.clear();
@@ -789,21 +824,26 @@ void ValueMetricProducer::onMatchedLogEventInternalLocked(
return;
}
- DimensionsInWhatInfo& dimensionsInWhatInfo = mCurrentBaseInfo[whatKey];
+ const auto& returnVal =
+ mCurrentBaseInfo.emplace(whatKey, DimensionsInWhatInfo(getUnknownStateKey()));
+ DimensionsInWhatInfo& dimensionsInWhatInfo = returnVal.first->second;
+ const HashableDimensionKey oldStateKey = dimensionsInWhatInfo.currentState;
vector<BaseInfo>& baseInfos = dimensionsInWhatInfo.baseInfos;
if (baseInfos.size() < mFieldMatchers.size()) {
VLOG("Resizing number of intervals to %d", (int)mFieldMatchers.size());
baseInfos.resize(mFieldMatchers.size());
}
+ // Ensure we turn on the condition timer in the case where dimensions
+ // were missing on a previous pull due to a state change.
+ bool stateChange = oldStateKey != stateKey;
if (!dimensionsInWhatInfo.hasCurrentState) {
- dimensionsInWhatInfo.currentState = getUnknownStateKey();
+ stateChange = true;
dimensionsInWhatInfo.hasCurrentState = true;
}
// We need to get the intervals stored with the previous state key so we can
// close these value intervals.
- const auto oldStateKey = dimensionsInWhatInfo.currentState;
vector<Interval>& intervals =
mCurrentSlicedBucket[MetricDimensionKey(whatKey, oldStateKey)].intervals;
if (intervals.size() < mFieldMatchers.size()) {
@@ -916,6 +956,17 @@ void ValueMetricProducer::onMatchedLogEventInternalLocked(
interval.sampleSize += 1;
}
+ // State change.
+ if (!mSlicedStateAtoms.empty() && stateChange) {
+ // Turn OFF the condition timer for the previous state key.
+ mCurrentSlicedBucket[MetricDimensionKey(whatKey, oldStateKey)]
+ .conditionTimer.onConditionChanged(false, eventTimeNs);
+
+ // Turn ON the condition timer for the new state key.
+ mCurrentSlicedBucket[MetricDimensionKey(whatKey, stateKey)]
+ .conditionTimer.onConditionChanged(true, eventTimeNs);
+ }
+
// Only trigger the tracker if all intervals are correct and we have not skipped the bucket due
// to MULTIPLE_BUCKETS_SKIPPED.
if (useAnomalyDetection && !multipleBucketsSkipped(calcBucketsForwardCount(eventTimeNs))) {
@@ -990,12 +1041,18 @@ void ValueMetricProducer::flushCurrentBucketLocked(const int64_t& eventTimeNs,
if (!mCurrentBucketIsSkipped) {
bool bucketHasData = false;
// The current bucket is large enough to keep.
- for (const auto& slice : mCurrentSlicedBucket) {
- PastValueBucket bucket = buildPartialBucket(bucketEndTime, slice.second.intervals);
- bucket.mConditionTrueNs = conditionTrueDuration;
+ for (auto& [metricDimensionKey, currentValueBucket] : mCurrentSlicedBucket) {
+ PastValueBucket bucket =
+ buildPartialBucket(bucketEndTime, currentValueBucket.intervals);
+ if (!mSlicedStateAtoms.empty()) {
+ bucket.mConditionTrueNs =
+ currentValueBucket.conditionTimer.newBucketStart(bucketEndTime);
+ } else {
+ bucket.mConditionTrueNs = conditionTrueDuration;
+ }
// it will auto create new vector of ValuebucketInfo if the key is not found.
if (bucket.valueIndex.size() > 0) {
- auto& bucketList = mPastBuckets[slice.first];
+ auto& bucketList = mPastBuckets[metricDimensionKey];
bucketList.push_back(bucket);
bucketHasData = true;
}
@@ -1023,11 +1080,18 @@ void ValueMetricProducer::flushCurrentBucketLocked(const int64_t& eventTimeNs,
buildDropEvent(eventTimeNs, BucketDropReason::NO_DATA));
mSkippedBuckets.emplace_back(bucketInGap);
}
-
appendToFullBucket(eventTimeNs > fullBucketEndTimeNs);
initCurrentSlicedBucket(nextBucketStartTimeNs);
// Update the condition timer again, in case we skipped buckets.
mConditionTimer.newBucketStart(nextBucketStartTimeNs);
+
+ // NOTE: Update the condition timers in `mCurrentSlicedBucket` only when slicing
+ // by state. Otherwise, the "global" condition timer will be used.
+ if (!mSlicedStateAtoms.empty()) {
+ for (auto& [metricDimensionKey, currentValueBucket] : mCurrentSlicedBucket) {
+ currentValueBucket.conditionTimer.newBucketStart(nextBucketStartTimeNs);
+ }
+ }
mCurrentBucketNum += numBucketsForward;
}
@@ -1069,6 +1133,17 @@ void ValueMetricProducer::initCurrentSlicedBucket(int64_t nextBucketStartTimeNs)
interval.seenNewData = false;
}
+ if (obsolete && !mSlicedStateAtoms.empty()) {
+ // When slicing by state, only delete the MetricDimensionKey when the
+ // state key in the MetricDimensionKey is not the current state key.
+ const HashableDimensionKey& dimensionInWhatKey = it->first.getDimensionKeyInWhat();
+ const auto& currentBaseInfoItr = mCurrentBaseInfo.find(dimensionInWhatKey);
+
+ if ((currentBaseInfoItr != mCurrentBaseInfo.end()) &&
+ (it->first.getStateValuesKey() == currentBaseInfoItr->second.currentState)) {
+ obsolete = false;
+ }
+ }
if (obsolete) {
it = mCurrentSlicedBucket.erase(it);
} else {
@@ -1104,7 +1179,7 @@ void ValueMetricProducer::appendToFullBucket(const bool isFullBucketReached) {
// Accumulate partial buckets with current value and then send to anomaly tracker.
if (mCurrentFullBucket.size() > 0) {
for (const auto& slice : mCurrentSlicedBucket) {
- if (hitFullBucketGuardRailLocked(slice.first)) {
+ if (hitFullBucketGuardRailLocked(slice.first) || slice.second.intervals.empty()) {
continue;
}
// TODO: fix this when anomaly can accept double values
@@ -1125,7 +1200,7 @@ void ValueMetricProducer::appendToFullBucket(const bool isFullBucketReached) {
// Skip aggregating the partial buckets since there's no previous partial bucket.
for (const auto& slice : mCurrentSlicedBucket) {
for (auto& tracker : mAnomalyTrackers) {
- if (tracker != nullptr) {
+ if (tracker != nullptr && !slice.second.intervals.empty()) {
// TODO: fix this when anomaly can accept double values
auto& interval = slice.second.intervals[0];
if (interval.hasValue) {
@@ -1139,10 +1214,12 @@ void ValueMetricProducer::appendToFullBucket(const bool isFullBucketReached) {
} else {
// Accumulate partial bucket.
for (const auto& slice : mCurrentSlicedBucket) {
- // TODO: fix this when anomaly can accept double values
- auto& interval = slice.second.intervals[0];
- if (interval.hasValue) {
- mCurrentFullBucket[slice.first] += interval.value.long_value;
+ if (!slice.second.intervals.empty()) {
+ // TODO: fix this when anomaly can accept double values
+ auto& interval = slice.second.intervals[0];
+ if (interval.hasValue) {
+ mCurrentFullBucket[slice.first] += interval.value.long_value;
+ }
}
}
}
diff --git a/cmds/statsd/src/metrics/ValueMetricProducer.h b/cmds/statsd/src/metrics/ValueMetricProducer.h
index 4b2599bdb517..67de214e655c 100644
--- a/cmds/statsd/src/metrics/ValueMetricProducer.h
+++ b/cmds/statsd/src/metrics/ValueMetricProducer.h
@@ -193,8 +193,14 @@ private:
// Internal state of an ongoing aggregation bucket.
typedef struct CurrentValueBucket {
+ // If the `MetricDimensionKey` state key is the current state key, then
+ // the condition timer will be updated later (e.g. condition/state/active
+ // state change) with the correct condition and time.
+ CurrentValueBucket() : intervals(), conditionTimer(ConditionTimer(false, 0)) {}
// Value information for each value field of the metric.
std::vector<Interval> intervals;
+ // Tracks how long the condition is true.
+ ConditionTimer conditionTimer;
} CurrentValueBucket;
// Holds base information for diffing values from one value field.
@@ -206,7 +212,10 @@ private:
} BaseInfo;
// State key and base information for a specific DimensionsInWhat key.
- typedef struct {
+ typedef struct DimensionsInWhatInfo {
+ DimensionsInWhatInfo(const HashableDimensionKey& stateKey)
+ : baseInfos(), currentState(stateKey), hasCurrentState(false) {
+ }
std::vector<BaseInfo> baseInfos;
// Last seen state value(s).
HashableDimensionKey currentState;
@@ -252,6 +261,10 @@ private:
// Reset diff base and mHasGlobalBase
void resetBase();
+ // Updates the condition timers in the current sliced bucket when there is a
+ // condition change or an active state change.
+ void updateCurrentSlicedBucketConditionTimers(bool newCondition, int64_t eventTimeNs);
+
static const size_t kBucketSize = sizeof(PastValueBucket{});
const size_t mDimensionSoftLimit;
@@ -337,6 +350,11 @@ private:
FRIEND_TEST(ValueMetricProducerTest, TestTrimUnusedDimensionKey);
FRIEND_TEST(ValueMetricProducerTest, TestUseZeroDefaultBase);
FRIEND_TEST(ValueMetricProducerTest, TestUseZeroDefaultBaseWithPullFailures);
+ FRIEND_TEST(ValueMetricProducerTest, TestSlicedStateWithMultipleDimensions);
+ FRIEND_TEST(ValueMetricProducerTest, TestSlicedStateWithMissingDataInStateChange);
+ FRIEND_TEST(ValueMetricProducerTest, TestSlicedStateWithDataMissingInConditionChange);
+ FRIEND_TEST(ValueMetricProducerTest, TestSlicedStateWithMissingDataThenFlushBucket);
+ FRIEND_TEST(ValueMetricProducerTest, TestSlicedStateWithNoPullOnBucketBoundary);
FRIEND_TEST(ValueMetricProducerTest_BucketDrop, TestInvalidBucketWhenOneConditionFailed);
FRIEND_TEST(ValueMetricProducerTest_BucketDrop, TestInvalidBucketWhenInitialPullFailed);
diff --git a/cmds/statsd/src/metrics/parsing_utils/config_update_utils.cpp b/cmds/statsd/src/metrics/parsing_utils/config_update_utils.cpp
index bd60b6bfcb8e..2ae57638791e 100644
--- a/cmds/statsd/src/metrics/parsing_utils/config_update_utils.cpp
+++ b/cmds/statsd/src/metrics/parsing_utils/config_update_utils.cpp
@@ -21,8 +21,11 @@
#include "external/StatsPullerManager.h"
#include "hash.h"
+#include "matchers/EventMatcherWizard.h"
#include "metrics_manager_util.h"
+using google::protobuf::MessageLite;
+
namespace android {
namespace os {
namespace statsd {
@@ -394,6 +397,248 @@ bool updateConditions(const ConfigKey& key, const StatsdConfig& config,
return true;
}
+// Returns true if any matchers in the metric activation were replaced.
+bool metricActivationDepsChange(const StatsdConfig& config,
+ const unordered_map<int64_t, int>& metricToActivationMap,
+ const int64_t metricId, const set<int64_t>& replacedMatchers) {
+ const auto& metricActivationIt = metricToActivationMap.find(metricId);
+ if (metricActivationIt == metricToActivationMap.end()) {
+ return false;
+ }
+ const MetricActivation& metricActivation = config.metric_activation(metricActivationIt->second);
+ for (int i = 0; i < metricActivation.event_activation_size(); i++) {
+ const EventActivation& activation = metricActivation.event_activation(i);
+ if (replacedMatchers.find(activation.atom_matcher_id()) != replacedMatchers.end()) {
+ return true;
+ }
+ if (activation.has_deactivation_atom_matcher_id()) {
+ if (replacedMatchers.find(activation.deactivation_atom_matcher_id()) !=
+ replacedMatchers.end()) {
+ return true;
+ }
+ }
+ }
+ return false;
+}
+
+bool determineMetricUpdateStatus(
+ const StatsdConfig& config, const MessageLite& metric, const int64_t metricId,
+ const MetricType metricType, const set<int64_t>& matcherDependencies,
+ const set<int64_t>& conditionDependencies,
+ const ::google::protobuf::RepeatedField<int64_t>& stateDependencies,
+ const ::google::protobuf::RepeatedPtrField<MetricConditionLink>& conditionLinks,
+ const unordered_map<int64_t, int>& oldMetricProducerMap,
+ const vector<sp<MetricProducer>>& oldMetricProducers,
+ const unordered_map<int64_t, int>& metricToActivationMap,
+ const set<int64_t>& replacedMatchers, const set<int64_t>& replacedConditions,
+ const set<int64_t>& replacedStates, UpdateStatus& updateStatus) {
+ // Check if new metric
+ const auto& oldMetricProducerIt = oldMetricProducerMap.find(metricId);
+ if (oldMetricProducerIt == oldMetricProducerMap.end()) {
+ updateStatus = UPDATE_NEW;
+ return true;
+ }
+
+ // This is an existing metric, check if it has changed.
+ uint64_t metricHash;
+ if (!getMetricProtoHash(config, metric, metricId, metricToActivationMap, metricHash)) {
+ return false;
+ }
+ const sp<MetricProducer> oldMetricProducer = oldMetricProducers[oldMetricProducerIt->second];
+ if (oldMetricProducer->getMetricType() != metricType ||
+ oldMetricProducer->getProtoHash() != metricHash) {
+ updateStatus = UPDATE_REPLACE;
+ return true;
+ }
+
+ // Take intersections of the matchers/predicates/states that the metric
+ // depends on with those that have been replaced. If a metric depends on any
+ // replaced component, it too must be replaced.
+ set<int64_t> intersection;
+ set_intersection(matcherDependencies.begin(), matcherDependencies.end(),
+ replacedMatchers.begin(), replacedMatchers.end(),
+ inserter(intersection, intersection.begin()));
+ if (intersection.size() > 0) {
+ updateStatus = UPDATE_REPLACE;
+ return true;
+ }
+ set_intersection(conditionDependencies.begin(), conditionDependencies.end(),
+ replacedConditions.begin(), replacedConditions.end(),
+ inserter(intersection, intersection.begin()));
+ if (intersection.size() > 0) {
+ updateStatus = UPDATE_REPLACE;
+ return true;
+ }
+ set_intersection(stateDependencies.begin(), stateDependencies.end(), replacedStates.begin(),
+ replacedStates.end(), inserter(intersection, intersection.begin()));
+ if (intersection.size() > 0) {
+ updateStatus = UPDATE_REPLACE;
+ return true;
+ }
+
+ for (const auto& metricConditionLink : conditionLinks) {
+ if (replacedConditions.find(metricConditionLink.condition()) != replacedConditions.end()) {
+ updateStatus = UPDATE_REPLACE;
+ return true;
+ }
+ }
+
+ if (metricActivationDepsChange(config, metricToActivationMap, metricId, replacedMatchers)) {
+ updateStatus = UPDATE_REPLACE;
+ return true;
+ }
+
+ updateStatus = UPDATE_PRESERVE;
+ return true;
+}
+
+bool determineAllMetricUpdateStatuses(const StatsdConfig& config,
+ const unordered_map<int64_t, int>& oldMetricProducerMap,
+ const vector<sp<MetricProducer>>& oldMetricProducers,
+ const unordered_map<int64_t, int>& metricToActivationMap,
+ const set<int64_t>& replacedMatchers,
+ const set<int64_t>& replacedConditions,
+ const set<int64_t>& replacedStates,
+ vector<UpdateStatus>& metricsToUpdate) {
+ int metricIndex = 0;
+ for (int i = 0; i < config.event_metric_size(); i++, metricIndex++) {
+ const EventMetric& metric = config.event_metric(i);
+ set<int64_t> conditionDependencies;
+ if (metric.has_condition()) {
+ conditionDependencies.insert(metric.condition());
+ }
+ if (!determineMetricUpdateStatus(
+ config, metric, metric.id(), METRIC_TYPE_EVENT, {metric.what()},
+ conditionDependencies, ::google::protobuf::RepeatedField<int64_t>(),
+ metric.links(), oldMetricProducerMap, oldMetricProducers, metricToActivationMap,
+ replacedMatchers, replacedConditions, replacedStates,
+ metricsToUpdate[metricIndex])) {
+ return false;
+ }
+ }
+ // TODO: determine update status for count, gauge, value, duration metrics.
+ return true;
+}
+
+bool updateMetrics(const ConfigKey& key, const StatsdConfig& config, const int64_t timeBaseNs,
+ const int64_t currentTimeNs, const sp<StatsPullerManager>& pullerManager,
+ const unordered_map<int64_t, int>& oldAtomMatchingTrackerMap,
+ const unordered_map<int64_t, int>& newAtomMatchingTrackerMap,
+ const set<int64_t>& replacedMatchers,
+ const vector<sp<AtomMatchingTracker>>& allAtomMatchingTrackers,
+ const unordered_map<int64_t, int>& conditionTrackerMap,
+ const set<int64_t>& replacedConditions,
+ vector<sp<ConditionTracker>>& allConditionTrackers,
+ const vector<ConditionState>& initialConditionCache,
+ const unordered_map<int64_t, int>& stateAtomIdMap,
+ const unordered_map<int64_t, unordered_map<int, int64_t>>& allStateGroupMaps,
+ const set<int64_t>& replacedStates,
+ const unordered_map<int64_t, int>& oldMetricProducerMap,
+ const vector<sp<MetricProducer>>& oldMetricProducers,
+ unordered_map<int64_t, int>& newMetricProducerMap,
+ vector<sp<MetricProducer>>& newMetricProducers,
+ unordered_map<int, vector<int>>& conditionToMetricMap,
+ unordered_map<int, vector<int>>& trackerToMetricMap,
+ set<int64_t>& noReportMetricIds,
+ unordered_map<int, vector<int>>& activationAtomTrackerToMetricMap,
+ unordered_map<int, vector<int>>& deactivationAtomTrackerToMetricMap,
+ vector<int>& metricsWithActivation) {
+ sp<ConditionWizard> wizard = new ConditionWizard(allConditionTrackers);
+ sp<EventMatcherWizard> matcherWizard = new EventMatcherWizard(allAtomMatchingTrackers);
+ const int allMetricsCount = config.count_metric_size() + config.duration_metric_size() +
+ config.event_metric_size() + config.gauge_metric_size() +
+ config.value_metric_size();
+ newMetricProducers.reserve(allMetricsCount);
+
+ // Construct map from metric id to metric activation index. The map will be used to determine
+ // the metric activation corresponding to a metric.
+ unordered_map<int64_t, int> metricToActivationMap;
+ for (int i = 0; i < config.metric_activation_size(); i++) {
+ const MetricActivation& metricActivation = config.metric_activation(i);
+ int64_t metricId = metricActivation.metric_id();
+ if (metricToActivationMap.find(metricId) != metricToActivationMap.end()) {
+ ALOGE("Metric %lld has multiple MetricActivations", (long long)metricId);
+ return false;
+ }
+ metricToActivationMap.insert({metricId, i});
+ }
+
+ vector<UpdateStatus> metricsToUpdate(allMetricsCount, UPDATE_UNKNOWN);
+ if (!determineAllMetricUpdateStatuses(config, oldMetricProducerMap, oldMetricProducers,
+ metricToActivationMap, replacedMatchers,
+ replacedConditions, replacedStates, metricsToUpdate)) {
+ return false;
+ }
+
+ // Now, perform the update. Must iterate the metric types in the same order
+ int metricIndex = 0;
+ for (int i = 0; i < config.event_metric_size(); i++, metricIndex++) {
+ newMetricProducerMap[config.event_metric(i).id()] = metricIndex;
+ const EventMetric& metric = config.event_metric(i);
+ switch (metricsToUpdate[metricIndex]) {
+ case UPDATE_PRESERVE: {
+ const auto& oldMetricProducerIt = oldMetricProducerMap.find(metric.id());
+ if (oldMetricProducerIt == oldMetricProducerMap.end()) {
+ ALOGE("Could not find Metric %lld in the previous config, but expected it "
+ "to be there",
+ (long long)metric.id());
+ return false;
+ }
+ const int oldIndex = oldMetricProducerIt->second;
+ sp<MetricProducer> producer = oldMetricProducers[oldIndex];
+ producer->onConfigUpdated(
+ config, i, metricIndex, allAtomMatchingTrackers, oldAtomMatchingTrackerMap,
+ newAtomMatchingTrackerMap, matcherWizard, allConditionTrackers,
+ conditionTrackerMap, wizard, metricToActivationMap, trackerToMetricMap,
+ conditionToMetricMap, activationAtomTrackerToMetricMap,
+ deactivationAtomTrackerToMetricMap, metricsWithActivation);
+ newMetricProducers.push_back(producer);
+ break;
+ }
+ case UPDATE_REPLACE:
+ case UPDATE_NEW: {
+ sp<MetricProducer> producer = createEventMetricProducerAndUpdateMetadata(
+ key, config, timeBaseNs, metric, metricIndex, allAtomMatchingTrackers,
+ newAtomMatchingTrackerMap, allConditionTrackers, conditionTrackerMap,
+ initialConditionCache, wizard, metricToActivationMap, trackerToMetricMap,
+ conditionToMetricMap, activationAtomTrackerToMetricMap,
+ deactivationAtomTrackerToMetricMap, metricsWithActivation);
+ if (producer == nullptr) {
+ return false;
+ }
+ newMetricProducers.push_back(producer);
+ break;
+ }
+ default: {
+ ALOGE("Metric \"%lld\" update state is unknown. This should never happen",
+ (long long)metric.id());
+ return false;
+ }
+ }
+ }
+ // TODO: perform update for count, gauge, value, duration metric.
+
+ const set<int> atomsAllowedFromAnyUid(config.whitelisted_atom_ids().begin(),
+ config.whitelisted_atom_ids().end());
+ for (int i = 0; i < allMetricsCount; i++) {
+ sp<MetricProducer> producer = newMetricProducers[i];
+ // Register metrics to StateTrackers
+ for (int atomId : producer->getSlicedStateAtoms()) {
+ // Register listener for atoms that use allowed_log_sources.
+ // Using atoms allowed from any uid as a sliced state atom is not allowed.
+ // Redo this check for all metrics in case the atoms allowed from any uid changed.
+ if (atomsAllowedFromAnyUid.find(atomId) != atomsAllowedFromAnyUid.end()) {
+ return false;
+ // Preserved metrics should've already registered.`
+ } else if (metricsToUpdate[i] != UPDATE_PRESERVE) {
+ StateManager::getInstance().registerListener(atomId, producer);
+ }
+ }
+ }
+
+ return true;
+}
+
bool updateStatsdConfig(const ConfigKey& key, const StatsdConfig& config, const sp<UidMap>& uidMap,
const sp<StatsPullerManager>& pullerManager,
const sp<AlarmMonitor>& anomalyAlarmMonitor,
@@ -403,15 +648,28 @@ bool updateStatsdConfig(const ConfigKey& key, const StatsdConfig& config, const
const unordered_map<int64_t, int>& oldAtomMatchingTrackerMap,
const vector<sp<ConditionTracker>>& oldConditionTrackers,
const unordered_map<int64_t, int>& oldConditionTrackerMap,
- set<int>& allTagIds,
+ const vector<sp<MetricProducer>>& oldMetricProducers,
+ const unordered_map<int64_t, int>& oldMetricProducerMap,
+ const map<int64_t, uint64_t>& oldStateProtoHashes, set<int>& allTagIds,
vector<sp<AtomMatchingTracker>>& newAtomMatchingTrackers,
unordered_map<int64_t, int>& newAtomMatchingTrackerMap,
vector<sp<ConditionTracker>>& newConditionTrackers,
unordered_map<int64_t, int>& newConditionTrackerMap,
- unordered_map<int, vector<int>>& trackerToConditionMap) {
+ vector<sp<MetricProducer>>& newMetricProducers,
+ unordered_map<int64_t, int>& newMetricProducerMap,
+ unordered_map<int, vector<int>>& conditionToMetricMap,
+ unordered_map<int, vector<int>>& trackerToMetricMap,
+ unordered_map<int, vector<int>>& trackerToConditionMap,
+ unordered_map<int, vector<int>>& activationTrackerToMetricMap,
+ unordered_map<int, vector<int>>& deactivationTrackerToMetricMap,
+ vector<int>& metricsWithActivation,
+ map<int64_t, uint64_t>& newStateProtoHashes,
+ set<int64_t>& noReportMetricIds) {
set<int64_t> replacedMatchers;
set<int64_t> replacedConditions;
vector<ConditionState> conditionCache;
+ unordered_map<int64_t, int> stateAtomIdMap;
+ unordered_map<int64_t, unordered_map<int, int64_t>> allStateGroupMaps;
if (!updateAtomMatchingTrackers(config, uidMap, oldAtomMatchingTrackerMap,
oldAtomMatchingTrackers, allTagIds, newAtomMatchingTrackerMap,
@@ -430,6 +688,31 @@ bool updateStatsdConfig(const ConfigKey& key, const StatsdConfig& config, const
}
VLOG("updateConditions succeeded");
+ // Share with metrics_manager_util,
+ if (!initStates(config, stateAtomIdMap, allStateGroupMaps, newStateProtoHashes)) {
+ ALOGE("initStates failed");
+ return false;
+ }
+
+ set<int64_t> replacedStates;
+ for (const auto& [stateId, stateHash] : oldStateProtoHashes) {
+ const auto& it = newStateProtoHashes.find(stateId);
+ if (it != newStateProtoHashes.end() && it->second != stateHash) {
+ replacedStates.insert(stateId);
+ }
+ }
+ if (!updateMetrics(key, config, timeBaseNs, currentTimeNs, pullerManager,
+ oldAtomMatchingTrackerMap, newAtomMatchingTrackerMap, replacedMatchers,
+ newAtomMatchingTrackers, newConditionTrackerMap, replacedConditions,
+ newConditionTrackers, conditionCache, stateAtomIdMap, allStateGroupMaps,
+ replacedStates, oldMetricProducerMap, oldMetricProducers,
+ newMetricProducerMap, newMetricProducers, conditionToMetricMap,
+ trackerToMetricMap, noReportMetricIds, activationTrackerToMetricMap,
+ deactivationTrackerToMetricMap, metricsWithActivation)) {
+ ALOGE("initMetricProducers failed");
+ return false;
+ }
+
return true;
}
diff --git a/cmds/statsd/src/metrics/parsing_utils/config_update_utils.h b/cmds/statsd/src/metrics/parsing_utils/config_update_utils.h
index 7ba684a65e88..34d7e9c7de9e 100644
--- a/cmds/statsd/src/metrics/parsing_utils/config_update_utils.h
+++ b/cmds/statsd/src/metrics/parsing_utils/config_update_utils.h
@@ -22,6 +22,7 @@
#include "condition/ConditionTracker.h"
#include "external/StatsPullerManager.h"
#include "matchers/AtomMatchingTracker.h"
+#include "metrics/MetricProducer.h"
namespace android {
namespace os {
@@ -112,7 +113,7 @@ bool determineConditionUpdateStatus(const StatsdConfig& config, const int condit
// [trackerToConditionMap]: contains the mapping from the index of an atom matcher
// to indices of condition trackers that use the matcher
// [conditionCache]: stores the current conditions for each ConditionTracker
-// [replacedConditions]: set of matcher ids that have changed and have been replaced
+// [replacedConditions]: set of condition ids that have changed and have been replaced
bool updateConditions(const ConfigKey& key, const StatsdConfig& config,
const std::unordered_map<int64_t, int>& atomMatchingTrackerMap,
const std::set<int64_t>& replacedMatchers,
@@ -124,6 +125,70 @@ bool updateConditions(const ConfigKey& key, const StatsdConfig& config,
std::vector<ConditionState>& conditionCache,
std::set<int64_t>& replacedConditions);
+// Function to determine the update status (preserve/replace/new) of all metrics in the config.
+// [config]: the input StatsdConfig
+// [oldMetricProducerMap]: metric id to index mapping in the existing MetricsManager
+// [oldMetricProducers]: stores the existing MetricProducers
+// [metricToActivationMap]: map from metric id to metric activation index
+// [replacedMatchers]: set of replaced matcher ids. metrics using these matchers must be replaced
+// [replacedConditions]: set of replaced conditions. metrics using these conditions must be replaced
+// [replacedStates]: set of replaced state ids. metrics using these states must be replaced
+// output:
+// [metricsToUpdate]: update status of each metric. Will be changed from UPDATE_UNKNOWN
+// Returns whether the function was successful or not.
+bool determineAllMetricUpdateStatuses(const StatsdConfig& config,
+ const unordered_map<int64_t, int>& oldMetricProducerMap,
+ const vector<sp<MetricProducer>>& oldMetricProducers,
+ const unordered_map<int64_t, int>& metricToActivationMap,
+ const set<int64_t>& replacedMatchers,
+ const set<int64_t>& replacedConditions,
+ const set<int64_t>& replacedStates,
+ vector<UpdateStatus>& metricsToUpdate);
+
+// Update MetricProducers.
+// input:
+// [key]: the config key that this config belongs to
+// [config]: the input config
+// [timeBaseNs]: start time base for all metrics
+// [currentTimeNs]: time of the config update
+// [atomMatchingTrackerMap]: AtomMatchingTracker name to index mapping from previous step.
+// [replacedMatchers]: ids of replaced matchers. Metrics depending on these must also be replaced
+// [allAtomMatchingTrackers]: stores the sp of the atom matchers.
+// [conditionTrackerMap]: condition name to index mapping
+// [replacedConditions]: set of condition ids that have changed and have been replaced
+// [stateAtomIdMap]: contains the mapping from state ids to atom ids
+// [allStateGroupMaps]: contains the mapping from atom ids and state values to
+// state group ids for all states
+// output:
+// [allMetricProducers]: contains the list of sp to the MetricProducers created.
+// [conditionToMetricMap]: contains the mapping from condition tracker index to
+// the list of MetricProducer index
+// [trackerToMetricMap]: contains the mapping from log tracker to MetricProducer index.
+bool updateMetrics(
+ const ConfigKey& key, const StatsdConfig& config, const int64_t timeBaseNs,
+ const int64_t currentTimeNs, const sp<StatsPullerManager>& pullerManager,
+ const std::unordered_map<int64_t, int>& oldAtomMatchingTrackerMap,
+ const std::unordered_map<int64_t, int>& newAtomMatchingTrackerMap,
+ const std::set<int64_t>& replacedMatchers,
+ const std::vector<sp<AtomMatchingTracker>>& allAtomMatchingTrackers,
+ const std::unordered_map<int64_t, int>& conditionTrackerMap,
+ const std::set<int64_t>& replacedConditions,
+ std::vector<sp<ConditionTracker>>& allConditionTrackers,
+ const std::vector<ConditionState>& initialConditionCache,
+ const std::unordered_map<int64_t, int>& stateAtomIdMap,
+ const std::unordered_map<int64_t, std::unordered_map<int, int64_t>>& allStateGroupMaps,
+ const std::set<int64_t>& replacedStates,
+ const std::unordered_map<int64_t, int>& oldMetricProducerMap,
+ const std::vector<sp<MetricProducer>>& oldMetricProducers,
+ std::unordered_map<int64_t, int>& newMetricProducerMap,
+ std::vector<sp<MetricProducer>>& newMetricProducers,
+ std::unordered_map<int, std::vector<int>>& conditionToMetricMap,
+ std::unordered_map<int, std::vector<int>>& trackerToMetricMap,
+ std::set<int64_t>& noReportMetricIds,
+ std::unordered_map<int, std::vector<int>>& activationAtomTrackerToMetricMap,
+ std::unordered_map<int, std::vector<int>>& deactivationAtomTrackerToMetricMap,
+ std::vector<int>& metricsWithActivation);
+
// Updates the existing MetricsManager from a new StatsdConfig.
// Parameters are the members of MetricsManager. See MetricsManager for declaration.
bool updateStatsdConfig(const ConfigKey& key, const StatsdConfig& config, const sp<UidMap>& uidMap,
@@ -135,12 +200,24 @@ bool updateStatsdConfig(const ConfigKey& key, const StatsdConfig& config, const
const std::unordered_map<int64_t, int>& oldAtomMatchingTrackerMap,
const std::vector<sp<ConditionTracker>>& oldConditionTrackers,
const std::unordered_map<int64_t, int>& oldConditionTrackerMap,
+ const std::vector<sp<MetricProducer>>& oldMetricProducers,
+ const std::unordered_map<int64_t, int>& oldMetricProducerMap,
+ const std::map<int64_t, uint64_t>& oldStateProtoHashes,
std::set<int>& allTagIds,
std::vector<sp<AtomMatchingTracker>>& newAtomMatchingTrackers,
std::unordered_map<int64_t, int>& newAtomMatchingTrackerMap,
std::vector<sp<ConditionTracker>>& newConditionTrackers,
std::unordered_map<int64_t, int>& newConditionTrackerMap,
- std::unordered_map<int, std::vector<int>>& trackerToConditionMap);
+ std::vector<sp<MetricProducer>>& newMetricProducers,
+ std::unordered_map<int64_t, int>& newMetricProducerMap,
+ std::unordered_map<int, std::vector<int>>& conditionToMetricMap,
+ std::unordered_map<int, std::vector<int>>& trackerToMetricMap,
+ std::unordered_map<int, std::vector<int>>& trackerToConditionMap,
+ std::unordered_map<int, std::vector<int>>& activationTrackerToMetricMap,
+ std::unordered_map<int, std::vector<int>>& deactivationTrackerToMetricMap,
+ std::vector<int>& metricsWithActivation,
+ std::map<int64_t, uint64_t>& newStateProtoHashes,
+ std::set<int64_t>& noReportMetricIds);
} // namespace statsd
} // namespace os
diff --git a/cmds/statsd/src/metrics/parsing_utils/metrics_manager_util.cpp b/cmds/statsd/src/metrics/parsing_utils/metrics_manager_util.cpp
index 3f40c90d515a..51df7df5174e 100644
--- a/cmds/statsd/src/metrics/parsing_utils/metrics_manager_util.cpp
+++ b/cmds/statsd/src/metrics/parsing_utils/metrics_manager_util.cpp
@@ -61,20 +61,6 @@ bool hasLeafNode(const FieldMatcher& matcher) {
return true;
}
-bool getMetricProtoHash(const MessageLite& metric, const int64_t id, const bool hasActivation,
- const uint64_t activationHash, uint64_t& metricHash) {
- string serializedMetric;
- if (!metric.SerializeToString(&serializedMetric)) {
- ALOGE("Unable to serialize metric %lld", (long long)id);
- return false;
- }
- metricHash = Hash64(serializedMetric);
- if (hasActivation) {
- metricHash = Hash64(to_string(metricHash).append(to_string(activationHash)));
- }
- return true;
-}
-
} // namespace
sp<AtomMatchingTracker> createAtomMatchingTracker(const AtomMatcher& logMatcher, const int index,
@@ -120,43 +106,45 @@ sp<ConditionTracker> createConditionTracker(
}
}
-bool handleMetricWithAtomMatchingTrackers(
- const int64_t what, const int metricIndex, const bool usedForDimension,
- const vector<sp<AtomMatchingTracker>>& allAtomMatchingTrackers,
- const unordered_map<int64_t, int>& atomMatchingTrackerMap,
- unordered_map<int, std::vector<int>>& trackerToMetricMap, int& logTrackerIndex) {
- auto logTrackerIt = atomMatchingTrackerMap.find(what);
- if (logTrackerIt == atomMatchingTrackerMap.end()) {
- ALOGW("cannot find the AtomMatcher \"%lld\" in config", (long long)what);
+bool getMetricProtoHash(const StatsdConfig& config, const MessageLite& metric, const int64_t id,
+ const unordered_map<int64_t, int>& metricToActivationMap,
+ uint64_t& metricHash) {
+ string serializedMetric;
+ if (!metric.SerializeToString(&serializedMetric)) {
+ ALOGE("Unable to serialize metric %lld", (long long)id);
return false;
}
- if (usedForDimension &&
- allAtomMatchingTrackers[logTrackerIt->second]->getAtomIds().size() > 1) {
- ALOGE("AtomMatcher \"%lld\" has more than one tag ids. When a metric has dimension, "
- "the \"what\" can only about one atom type.",
- (long long)what);
- return false;
+ metricHash = Hash64(serializedMetric);
+
+ // Combine with activation hash, if applicable
+ const auto& metricActivationIt = metricToActivationMap.find(id);
+ if (metricActivationIt != metricToActivationMap.end()) {
+ string serializedActivation;
+ const MetricActivation& activation = config.metric_activation(metricActivationIt->second);
+ if (!activation.SerializeToString(&serializedActivation)) {
+ ALOGE("Unable to serialize metric activation for metric %lld", (long long)id);
+ return false;
+ }
+ metricHash = Hash64(to_string(metricHash).append(to_string(Hash64(serializedActivation))));
}
- logTrackerIndex = logTrackerIt->second;
- auto& metric_list = trackerToMetricMap[logTrackerIndex];
- metric_list.push_back(metricIndex);
return true;
}
-bool handlePullMetricTriggerWithAtomMatchingTrackers(
- const int64_t trigger, const int metricIndex,
+bool handleMetricWithAtomMatchingTrackers(
+ const int64_t matcherId, const int metricIndex, const bool enforceOneAtom,
const vector<sp<AtomMatchingTracker>>& allAtomMatchingTrackers,
const unordered_map<int64_t, int>& atomMatchingTrackerMap,
- unordered_map<int, std::vector<int>>& trackerToMetricMap, int& logTrackerIndex) {
- auto logTrackerIt = atomMatchingTrackerMap.find(trigger);
+ unordered_map<int, vector<int>>& trackerToMetricMap, int& logTrackerIndex) {
+ auto logTrackerIt = atomMatchingTrackerMap.find(matcherId);
if (logTrackerIt == atomMatchingTrackerMap.end()) {
- ALOGW("cannot find the AtomMatcher \"%lld\" in config", (long long)trigger);
+ ALOGW("cannot find the AtomMatcher \"%lld\" in config", (long long)matcherId);
return false;
}
- if (allAtomMatchingTrackers[logTrackerIt->second]->getAtomIds().size() > 1) {
- ALOGE("AtomMatcher \"%lld\" has more than one tag ids."
- "Trigger can only be one atom type.",
- (long long)trigger);
+ if (enforceOneAtom && allAtomMatchingTrackers[logTrackerIt->second]->getAtomIds().size() > 1) {
+ ALOGE("AtomMatcher \"%lld\" has more than one tag ids. When a metric has dimension, "
+ "the \"what\" can only be about one atom type. trigger_event matchers can also only "
+ "be about one atom type.",
+ (long long)matcherId);
return false;
}
logTrackerIndex = logTrackerIt->second;
@@ -170,8 +158,8 @@ bool handleMetricWithConditions(
const unordered_map<int64_t, int>& conditionTrackerMap,
const ::google::protobuf::RepeatedPtrField<::android::os::statsd::MetricConditionLink>&
links,
- vector<sp<ConditionTracker>>& allConditionTrackers, int& conditionIndex,
- unordered_map<int, std::vector<int>>& conditionToMetricMap) {
+ const vector<sp<ConditionTracker>>& allConditionTrackers, int& conditionIndex,
+ unordered_map<int, vector<int>>& conditionToMetricMap) {
auto condition_it = conditionTrackerMap.find(condition);
if (condition_it == conditionTrackerMap.end()) {
ALOGW("cannot find Predicate \"%lld\" in the config", (long long)condition);
@@ -243,31 +231,21 @@ bool handleMetricWithStateLink(const FieldMatcher& stateMatcher,
bool handleMetricActivation(
const StatsdConfig& config, const int64_t metricId, const int metricIndex,
const unordered_map<int64_t, int>& metricToActivationMap,
- const unordered_map<int64_t, int>& atomMatchingTrackerMap, bool& hasActivation,
+ const unordered_map<int64_t, int>& atomMatchingTrackerMap,
unordered_map<int, vector<int>>& activationAtomTrackerToMetricMap,
unordered_map<int, vector<int>>& deactivationAtomTrackerToMetricMap,
vector<int>& metricsWithActivation,
unordered_map<int, shared_ptr<Activation>>& eventActivationMap,
- unordered_map<int, vector<shared_ptr<Activation>>>& eventDeactivationMap,
- uint64_t& activationHash) {
+ unordered_map<int, vector<shared_ptr<Activation>>>& eventDeactivationMap) {
// Check if metric has an associated activation
auto itr = metricToActivationMap.find(metricId);
if (itr == metricToActivationMap.end()) {
- hasActivation = false;
return true;
}
- hasActivation = true;
int activationIndex = itr->second;
const MetricActivation& metricActivation = config.metric_activation(activationIndex);
- string serializedActivation;
- if (!metricActivation.SerializeToString(&serializedActivation)) {
- ALOGE("Unable to serialize metric activation for metric %lld", (long long)metricId);
- return false;
- }
- activationHash = Hash64(serializedActivation);
-
for (int i = 0; i < metricActivation.event_activation_size(); i++) {
const EventActivation& activation = metricActivation.event_activation(i);
@@ -303,6 +281,130 @@ bool handleMetricActivation(
return true;
}
+// Validates a metricActivation and populates state.
+// Fills the new event activation/deactivation maps, preserving the existing activations
+// Returns false if there are errors.
+bool handleMetricActivationOnConfigUpdate(
+ const StatsdConfig& config, const int64_t metricId, const int metricIndex,
+ const unordered_map<int64_t, int>& metricToActivationMap,
+ const unordered_map<int64_t, int>& oldAtomMatchingTrackerMap,
+ const unordered_map<int64_t, int>& newAtomMatchingTrackerMap,
+ const unordered_map<int, shared_ptr<Activation>>& oldEventActivationMap,
+ unordered_map<int, vector<int>>& activationAtomTrackerToMetricMap,
+ unordered_map<int, vector<int>>& deactivationAtomTrackerToMetricMap,
+ vector<int>& metricsWithActivation,
+ unordered_map<int, shared_ptr<Activation>>& newEventActivationMap,
+ unordered_map<int, vector<shared_ptr<Activation>>>& newEventDeactivationMap) {
+ // Check if metric has an associated activation.
+ const auto& itr = metricToActivationMap.find(metricId);
+ if (itr == metricToActivationMap.end()) {
+ return true;
+ }
+
+ int activationIndex = itr->second;
+ const MetricActivation& metricActivation = config.metric_activation(activationIndex);
+
+ for (int i = 0; i < metricActivation.event_activation_size(); i++) {
+ const int64_t activationMatcherId = metricActivation.event_activation(i).atom_matcher_id();
+
+ const auto& newActivationIt = newAtomMatchingTrackerMap.find(activationMatcherId);
+ if (newActivationIt == newAtomMatchingTrackerMap.end()) {
+ ALOGE("Atom matcher not found in new config for event activation.");
+ return false;
+ }
+ int newActivationMatcherIndex = newActivationIt->second;
+
+ // Find the old activation struct and copy it over.
+ const auto& oldActivationIt = oldAtomMatchingTrackerMap.find(activationMatcherId);
+ if (oldActivationIt == oldAtomMatchingTrackerMap.end()) {
+ ALOGE("Atom matcher not found in existing config for event activation.");
+ return false;
+ }
+ int oldActivationMatcherIndex = oldActivationIt->second;
+ const auto& oldEventActivationIt = oldEventActivationMap.find(oldActivationMatcherIndex);
+ if (oldEventActivationIt == oldEventActivationMap.end()) {
+ ALOGE("Could not find existing event activation to update");
+ return false;
+ }
+ newEventActivationMap.emplace(newActivationMatcherIndex, oldEventActivationIt->second);
+ activationAtomTrackerToMetricMap[newActivationMatcherIndex].push_back(metricIndex);
+
+ if (metricActivation.event_activation(i).has_deactivation_atom_matcher_id()) {
+ const int64_t deactivationMatcherId =
+ metricActivation.event_activation(i).deactivation_atom_matcher_id();
+ const auto& newDeactivationIt = newAtomMatchingTrackerMap.find(deactivationMatcherId);
+ if (newDeactivationIt == newAtomMatchingTrackerMap.end()) {
+ ALOGE("Deactivation atom matcher not found in new config for event activation.");
+ return false;
+ }
+ int newDeactivationMatcherIndex = newDeactivationIt->second;
+ newEventDeactivationMap[newDeactivationMatcherIndex].push_back(
+ oldEventActivationIt->second);
+ deactivationAtomTrackerToMetricMap[newDeactivationMatcherIndex].push_back(metricIndex);
+ }
+ }
+
+ metricsWithActivation.push_back(metricIndex);
+ return true;
+}
+
+sp<MetricProducer> createEventMetricProducerAndUpdateMetadata(
+ const ConfigKey& key, const StatsdConfig& config, const int64_t timeBaseNs,
+ const EventMetric& metric, const int metricIndex,
+ const vector<sp<AtomMatchingTracker>>& allAtomMatchingTrackers,
+ const unordered_map<int64_t, int>& atomMatchingTrackerMap,
+ vector<sp<ConditionTracker>>& allConditionTrackers,
+ const unordered_map<int64_t, int>& conditionTrackerMap,
+ const vector<ConditionState>& initialConditionCache, const sp<ConditionWizard>& wizard,
+ const unordered_map<int64_t, int>& metricToActivationMap,
+ unordered_map<int, vector<int>>& trackerToMetricMap,
+ unordered_map<int, vector<int>>& conditionToMetricMap,
+ unordered_map<int, vector<int>>& activationAtomTrackerToMetricMap,
+ unordered_map<int, vector<int>>& deactivationAtomTrackerToMetricMap,
+ vector<int>& metricsWithActivation) {
+ if (!metric.has_id() || !metric.has_what()) {
+ ALOGW("cannot find the metric name or what in config");
+ return nullptr;
+ }
+ int trackerIndex;
+ if (!handleMetricWithAtomMatchingTrackers(metric.what(), metricIndex, false,
+ allAtomMatchingTrackers, atomMatchingTrackerMap,
+ trackerToMetricMap, trackerIndex)) {
+ return nullptr;
+ }
+
+ int conditionIndex = -1;
+ if (metric.has_condition()) {
+ if (!handleMetricWithConditions(metric.condition(), metricIndex, conditionTrackerMap,
+ metric.links(), allConditionTrackers, conditionIndex,
+ conditionToMetricMap)) {
+ return nullptr;
+ }
+ } else {
+ if (metric.links_size() > 0) {
+ ALOGW("metrics has a MetricConditionLink but doesn't have a condition");
+ return nullptr;
+ }
+ }
+
+ unordered_map<int, shared_ptr<Activation>> eventActivationMap;
+ unordered_map<int, vector<shared_ptr<Activation>>> eventDeactivationMap;
+ bool success = handleMetricActivation(config, metric.id(), metricIndex, metricToActivationMap,
+ atomMatchingTrackerMap, activationAtomTrackerToMetricMap,
+ deactivationAtomTrackerToMetricMap, metricsWithActivation,
+ eventActivationMap, eventDeactivationMap);
+ if (!success) return nullptr;
+
+ uint64_t metricHash;
+ if (!getMetricProtoHash(config, metric, metric.id(), metricToActivationMap, metricHash)) {
+ return nullptr;
+ }
+
+ return new EventMetricProducer(key, metric, conditionIndex, initialConditionCache, wizard,
+ metricHash, timeBaseNs, eventActivationMap,
+ eventDeactivationMap);
+}
+
bool initAtomMatchingTrackers(const StatsdConfig& config, const sp<UidMap>& uidMap,
unordered_map<int64_t, int>& atomMatchingTrackerMap,
vector<sp<AtomMatchingTracker>>& allAtomMatchingTrackers,
@@ -492,18 +594,16 @@ bool initMetrics(const ConfigKey& key, const StatsdConfig& config, const int64_t
}
}
- bool hasActivation = false;
unordered_map<int, shared_ptr<Activation>> eventActivationMap;
unordered_map<int, vector<shared_ptr<Activation>>> eventDeactivationMap;
- uint64_t activationHash;
bool success = handleMetricActivation(
config, metric.id(), metricIndex, metricToActivationMap, atomMatchingTrackerMap,
- hasActivation, activationAtomTrackerToMetricMap, deactivationAtomTrackerToMetricMap,
- metricsWithActivation, eventActivationMap, eventDeactivationMap, activationHash);
+ activationAtomTrackerToMetricMap, deactivationAtomTrackerToMetricMap,
+ metricsWithActivation, eventActivationMap, eventDeactivationMap);
if (!success) return false;
uint64_t metricHash;
- if (!getMetricProtoHash(metric, metric.id(), hasActivation, activationHash, metricHash)) {
+ if (!getMetricProtoHash(config, metric, metric.id(), metricToActivationMap, metricHash)) {
return false;
}
@@ -608,18 +708,16 @@ bool initMetrics(const ConfigKey& key, const StatsdConfig& config, const int64_t
}
}
- bool hasActivation = false;
unordered_map<int, shared_ptr<Activation>> eventActivationMap;
unordered_map<int, vector<shared_ptr<Activation>>> eventDeactivationMap;
- uint64_t activationHash;
bool success = handleMetricActivation(
config, metric.id(), metricIndex, metricToActivationMap, atomMatchingTrackerMap,
- hasActivation, activationAtomTrackerToMetricMap, deactivationAtomTrackerToMetricMap,
- metricsWithActivation, eventActivationMap, eventDeactivationMap, activationHash);
+ activationAtomTrackerToMetricMap, deactivationAtomTrackerToMetricMap,
+ metricsWithActivation, eventActivationMap, eventDeactivationMap);
if (!success) return false;
uint64_t metricHash;
- if (!getMetricProtoHash(metric, metric.id(), hasActivation, activationHash, metricHash)) {
+ if (!getMetricProtoHash(config, metric, metric.id(), metricToActivationMap, metricHash)) {
return false;
}
@@ -637,52 +735,16 @@ bool initMetrics(const ConfigKey& key, const StatsdConfig& config, const int64_t
int metricIndex = allMetricProducers.size();
const EventMetric& metric = config.event_metric(i);
metricMap.insert({metric.id(), metricIndex});
- if (!metric.has_id() || !metric.has_what()) {
- ALOGW("cannot find the metric name or what in config");
- return false;
- }
- int trackerIndex;
- if (!handleMetricWithAtomMatchingTrackers(metric.what(), metricIndex, false,
- allAtomMatchingTrackers, atomMatchingTrackerMap,
- trackerToMetricMap, trackerIndex)) {
- return false;
- }
-
- int conditionIndex = -1;
- if (metric.has_condition()) {
- bool good = handleMetricWithConditions(
- metric.condition(), metricIndex, conditionTrackerMap, metric.links(),
- allConditionTrackers, conditionIndex, conditionToMetricMap);
- if (!good) {
- return false;
- }
- } else {
- if (metric.links_size() > 0) {
- ALOGW("metrics has a MetricConditionLink but doesn't have a condition");
- return false;
- }
- }
-
- bool hasActivation = false;
- unordered_map<int, shared_ptr<Activation>> eventActivationMap;
- unordered_map<int, vector<shared_ptr<Activation>>> eventDeactivationMap;
- uint64_t activationHash;
- bool success = handleMetricActivation(
- config, metric.id(), metricIndex, metricToActivationMap, atomMatchingTrackerMap,
- hasActivation, activationAtomTrackerToMetricMap, deactivationAtomTrackerToMetricMap,
- metricsWithActivation, eventActivationMap, eventDeactivationMap, activationHash);
- if (!success) return false;
-
- uint64_t metricHash;
- if (!getMetricProtoHash(metric, metric.id(), hasActivation, activationHash, metricHash)) {
+ sp<MetricProducer> producer = createEventMetricProducerAndUpdateMetadata(
+ key, config, timeBaseTimeNs, metric, metricIndex, allAtomMatchingTrackers,
+ atomMatchingTrackerMap, allConditionTrackers, conditionTrackerMap,
+ initialConditionCache, wizard, metricToActivationMap, trackerToMetricMap,
+ conditionToMetricMap, activationAtomTrackerToMetricMap,
+ deactivationAtomTrackerToMetricMap, metricsWithActivation);
+ if (producer == nullptr) {
return false;
}
-
- sp<MetricProducer> eventMetric = new EventMetricProducer(
- key, metric, conditionIndex, initialConditionCache, wizard, metricHash,
- timeBaseTimeNs, eventActivationMap, eventDeactivationMap);
-
- allMetricProducers.push_back(eventMetric);
+ allMetricProducers.push_back(producer);
}
// build ValueMetricProducer
@@ -759,18 +821,16 @@ bool initMetrics(const ConfigKey& key, const StatsdConfig& config, const int64_t
}
}
- bool hasActivation = false;
unordered_map<int, shared_ptr<Activation>> eventActivationMap;
unordered_map<int, vector<shared_ptr<Activation>>> eventDeactivationMap;
- uint64_t activationHash;
bool success = handleMetricActivation(
config, metric.id(), metricIndex, metricToActivationMap, atomMatchingTrackerMap,
- hasActivation, activationAtomTrackerToMetricMap, deactivationAtomTrackerToMetricMap,
- metricsWithActivation, eventActivationMap, eventDeactivationMap, activationHash);
+ activationAtomTrackerToMetricMap, deactivationAtomTrackerToMetricMap,
+ metricsWithActivation, eventActivationMap, eventDeactivationMap);
if (!success) return false;
uint64_t metricHash;
- if (!getMetricProtoHash(metric, metric.id(), hasActivation, activationHash, metricHash)) {
+ if (!getMetricProtoHash(config, metric, metric.id(), metricToActivationMap, metricHash)) {
return false;
}
@@ -832,9 +892,10 @@ bool initMetrics(const ConfigKey& key, const StatsdConfig& config, const int64_t
if (metric.sampling_type() != GaugeMetric::FIRST_N_SAMPLES) {
return false;
}
- if (!handlePullMetricTriggerWithAtomMatchingTrackers(
- metric.trigger_event(), metricIndex, allAtomMatchingTrackers,
- atomMatchingTrackerMap, trackerToMetricMap, triggerTrackerIndex)) {
+ if (!handleMetricWithAtomMatchingTrackers(
+ metric.trigger_event(), metricIndex, /*enforceOneAtom=*/true,
+ allAtomMatchingTrackers, atomMatchingTrackerMap, trackerToMetricMap,
+ triggerTrackerIndex)) {
return false;
}
sp<AtomMatchingTracker> triggerAtomMatcher =
@@ -863,18 +924,16 @@ bool initMetrics(const ConfigKey& key, const StatsdConfig& config, const int64_t
}
}
- bool hasActivation = false;
unordered_map<int, shared_ptr<Activation>> eventActivationMap;
unordered_map<int, vector<shared_ptr<Activation>>> eventDeactivationMap;
- uint64_t activationHash;
bool success = handleMetricActivation(
config, metric.id(), metricIndex, metricToActivationMap, atomMatchingTrackerMap,
- hasActivation, activationAtomTrackerToMetricMap, deactivationAtomTrackerToMetricMap,
- metricsWithActivation, eventActivationMap, eventDeactivationMap, activationHash);
+ activationAtomTrackerToMetricMap, deactivationAtomTrackerToMetricMap,
+ metricsWithActivation, eventActivationMap, eventDeactivationMap);
if (!success) return false;
uint64_t metricHash;
- if (!getMetricProtoHash(metric, metric.id(), hasActivation, activationHash, metricHash)) {
+ if (!getMetricProtoHash(config, metric, metric.id(), metricToActivationMap, metricHash)) {
return false;
}
diff --git a/cmds/statsd/src/metrics/parsing_utils/metrics_manager_util.h b/cmds/statsd/src/metrics/parsing_utils/metrics_manager_util.h
index 4979c3051133..fdfe5cfa6491 100644
--- a/cmds/statsd/src/metrics/parsing_utils/metrics_manager_util.h
+++ b/cmds/statsd/src/metrics/parsing_utils/metrics_manager_util.h
@@ -30,7 +30,7 @@ namespace android {
namespace os {
namespace statsd {
-// Helper functions for creating individual config components from StatsdConfig.
+// Helper functions for creating, validating, and updating config components from StatsdConfig.
// Should only be called from metrics_manager_util and config_update_utils.
// Create a AtomMatchingTracker.
@@ -53,6 +53,61 @@ sp<ConditionTracker> createConditionTracker(
const ConfigKey& key, const Predicate& predicate, const int index,
const unordered_map<int64_t, int>& atomMatchingTrackerMap);
+// Get the hash of a metric, combining the activation if the metric has one.
+bool getMetricProtoHash(const StatsdConfig& config, const google::protobuf::MessageLite& metric,
+ const int64_t id,
+ const std::unordered_map<int64_t, int>& metricToActivationMap,
+ uint64_t& metricHash);
+
+// 1. Validates matcher existence
+// 2. Enforces matchers with dimensions and those used for trigger_event are about one atom
+// 3. Gets matcher index and updates tracker to metric map
+bool handleMetricWithAtomMatchingTrackers(
+ const int64_t matcherId, const int metricIndex, const bool enforceOneAtom,
+ const std::vector<sp<AtomMatchingTracker>>& allAtomMatchingTrackers,
+ const std::unordered_map<int64_t, int>& atomMatchingTrackerMap,
+ std::unordered_map<int, std::vector<int>>& trackerToMetricMap, int& logTrackerIndex);
+
+// 1. Validates condition existence, including those in links
+// 2. Gets condition index and updates condition to metric map
+bool handleMetricWithConditions(
+ const int64_t condition, const int metricIndex,
+ const std::unordered_map<int64_t, int>& conditionTrackerMap,
+ const ::google::protobuf::RepeatedPtrField<::android::os::statsd::MetricConditionLink>&
+ links,
+ const std::vector<sp<ConditionTracker>>& allConditionTrackers, int& conditionIndex,
+ std::unordered_map<int, std::vector<int>>& conditionToMetricMap);
+
+// Validates a metricActivation and populates state.
+// Fills the new event activation/deactivation maps, preserving the existing activations.
+// Returns false if there are errors.
+bool handleMetricActivationOnConfigUpdate(
+ const StatsdConfig& config, const int64_t metricId, const int metricIndex,
+ const std::unordered_map<int64_t, int>& metricToActivationMap,
+ const std::unordered_map<int64_t, int>& oldAtomMatchingTrackerMap,
+ const std::unordered_map<int64_t, int>& newAtomMatchingTrackerMap,
+ const std::unordered_map<int, shared_ptr<Activation>>& oldEventActivationMap,
+ std::unordered_map<int, std::vector<int>>& activationAtomTrackerToMetricMap,
+ std::unordered_map<int, std::vector<int>>& deactivationAtomTrackerToMetricMap,
+ std::vector<int>& metricsWithActivation,
+ std::unordered_map<int, shared_ptr<Activation>>& newEventActivationMap,
+ std::unordered_map<int, std::vector<shared_ptr<Activation>>>& newEventDeactivationMap);
+
+sp<MetricProducer> createEventMetricProducerAndUpdateMetadata(
+ const ConfigKey& key, const StatsdConfig& config, const int64_t timeBaseNs,
+ const EventMetric& metric, const int metricIndex,
+ const std::vector<sp<AtomMatchingTracker>>& allAtomMatchingTrackers,
+ const std::unordered_map<int64_t, int>& atomMatchingTrackerMap,
+ std::vector<sp<ConditionTracker>>& allConditionTrackers,
+ const std::unordered_map<int64_t, int>& conditionTrackerMap,
+ const std::vector<ConditionState>& initialConditionCache, const sp<ConditionWizard>& wizard,
+ const std::unordered_map<int64_t, int>& metricToActivationMap,
+ std::unordered_map<int, std::vector<int>>& trackerToMetricMap,
+ std::unordered_map<int, std::vector<int>>& conditionToMetricMap,
+ std::unordered_map<int, std::vector<int>>& activationAtomTrackerToMetricMap,
+ std::unordered_map<int, std::vector<int>>& deactivationAtomTrackerToMetricMap,
+ std::vector<int>& metricsWithActivation);
+
// Helper functions for MetricsManager to initialize from StatsdConfig.
// *Note*: only initStatsdConfig() should be called from outside.
// All other functions are intermediate
@@ -154,10 +209,10 @@ bool initStatsdConfig(const ConfigKey& key, const StatsdConfig& config, const sp
std::unordered_map<int, std::vector<int>>& conditionToMetricMap,
std::unordered_map<int, std::vector<int>>& trackerToMetricMap,
std::unordered_map<int, std::vector<int>>& trackerToConditionMap,
- unordered_map<int, std::vector<int>>& activationAtomTrackerToMetricMap,
- unordered_map<int, std::vector<int>>& deactivationAtomTrackerToMetricMap,
+ std::unordered_map<int, std::vector<int>>& activationAtomTrackerToMetricMap,
+ std::unordered_map<int, std::vector<int>>& deactivationAtomTrackerToMetricMap,
std::unordered_map<int64_t, int>& alertTrackerMap,
- vector<int>& metricsWithActivation,
+ std::vector<int>& metricsWithActivation,
std::map<int64_t, uint64_t>& stateProtoHashes,
std::set<int64_t>& noReportMetricIds);
diff --git a/cmds/statsd/tests/condition/ConditionTimer_test.cpp b/cmds/statsd/tests/condition/ConditionTimer_test.cpp
index ea02cd3a5ee1..46dc9a9d381f 100644
--- a/cmds/statsd/tests/condition/ConditionTimer_test.cpp
+++ b/cmds/statsd/tests/condition/ConditionTimer_test.cpp
@@ -35,11 +35,11 @@ TEST(ConditionTimerTest, TestTimer_Inital_False) {
EXPECT_EQ(0, timer.mTimerNs);
timer.onConditionChanged(true, ct_start_time + 5);
- EXPECT_EQ(ct_start_time + 5, timer.mLastConditionTrueTimestampNs);
+ EXPECT_EQ(ct_start_time + 5, timer.mLastConditionChangeTimestampNs);
EXPECT_EQ(true, timer.mCondition);
EXPECT_EQ(95, timer.newBucketStart(ct_start_time + 100));
- EXPECT_EQ(ct_start_time + 100, timer.mLastConditionTrueTimestampNs);
+ EXPECT_EQ(ct_start_time + 100, timer.mLastConditionChangeTimestampNs);
EXPECT_EQ(true, timer.mCondition);
}
@@ -51,7 +51,7 @@ TEST(ConditionTimerTest, TestTimer_Inital_True) {
EXPECT_EQ(ct_start_time - time_base, timer.newBucketStart(ct_start_time));
EXPECT_EQ(true, timer.mCondition);
EXPECT_EQ(0, timer.mTimerNs);
- EXPECT_EQ(ct_start_time, timer.mLastConditionTrueTimestampNs);
+ EXPECT_EQ(ct_start_time, timer.mLastConditionChangeTimestampNs);
timer.onConditionChanged(false, ct_start_time + 5);
EXPECT_EQ(5, timer.mTimerNs);
diff --git a/cmds/statsd/tests/metrics/ValueMetricProducer_test.cpp b/cmds/statsd/tests/metrics/ValueMetricProducer_test.cpp
index b166cc1fe04e..6cf4192b41fd 100644
--- a/cmds/statsd/tests/metrics/ValueMetricProducer_test.cpp
+++ b/cmds/statsd/tests/metrics/ValueMetricProducer_test.cpp
@@ -93,6 +93,13 @@ static void assertPastBucketValuesSingleKey(
}
}
+static void assertConditionTimer(const ConditionTimer& conditionTimer, bool condition,
+ int64_t timerNs, int64_t lastConditionTrueTimestampNs) {
+ EXPECT_EQ(condition, conditionTimer.mCondition);
+ EXPECT_EQ(timerNs, conditionTimer.mTimerNs);
+ EXPECT_EQ(lastConditionTrueTimestampNs, conditionTimer.mLastConditionChangeTimestampNs);
+}
+
} // anonymous namespace
class ValueMetricProducerTestHelper {
@@ -3967,33 +3974,37 @@ TEST(ValueMetricProducerTest, TestSlicedState) {
// Screen state change to ON.
.WillOnce(Invoke([](int tagId, const ConfigKey&, const int64_t eventTimeNs,
vector<std::shared_ptr<LogEvent>>* data) {
- EXPECT_EQ(eventTimeNs, bucketStartTimeNs + 5);
+ EXPECT_EQ(eventTimeNs, bucketStartTimeNs + 5 * NS_PER_SEC);
data->clear();
- data->push_back(CreateRepeatedValueLogEvent(tagId, bucketStartTimeNs + 5, 5));
+ data->push_back(
+ CreateRepeatedValueLogEvent(tagId, bucketStartTimeNs + 5 * NS_PER_SEC, 5));
return true;
}))
// Screen state change to OFF.
.WillOnce(Invoke([](int tagId, const ConfigKey&, const int64_t eventTimeNs,
vector<std::shared_ptr<LogEvent>>* data) {
- EXPECT_EQ(eventTimeNs, bucketStartTimeNs + 10);
+ EXPECT_EQ(eventTimeNs, bucketStartTimeNs + 10 * NS_PER_SEC);
data->clear();
- data->push_back(CreateRepeatedValueLogEvent(tagId, bucketStartTimeNs + 10, 9));
+ data->push_back(
+ CreateRepeatedValueLogEvent(tagId, bucketStartTimeNs + 10 * NS_PER_SEC, 9));
return true;
}))
// Screen state change to ON.
.WillOnce(Invoke([](int tagId, const ConfigKey&, const int64_t eventTimeNs,
vector<std::shared_ptr<LogEvent>>* data) {
- EXPECT_EQ(eventTimeNs, bucketStartTimeNs + 15);
+ EXPECT_EQ(eventTimeNs, bucketStartTimeNs + 15 * NS_PER_SEC);
data->clear();
- data->push_back(CreateRepeatedValueLogEvent(tagId, bucketStartTimeNs + 15, 21));
+ data->push_back(CreateRepeatedValueLogEvent(
+ tagId, bucketStartTimeNs + 15 * NS_PER_SEC, 21));
return true;
}))
// Dump report requested.
.WillOnce(Invoke([](int tagId, const ConfigKey&, const int64_t eventTimeNs,
vector<std::shared_ptr<LogEvent>>* data) {
- EXPECT_EQ(eventTimeNs, bucketStartTimeNs + 50);
+ EXPECT_EQ(eventTimeNs, bucketStartTimeNs + 50 * NS_PER_SEC);
data->clear();
- data->push_back(CreateRepeatedValueLogEvent(tagId, bucketStartTimeNs + 50, 30));
+ data->push_back(CreateRepeatedValueLogEvent(
+ tagId, bucketStartTimeNs + 50 * NS_PER_SEC, 30));
return true;
}));
@@ -4025,12 +4036,13 @@ TEST(ValueMetricProducerTest, TestSlicedState) {
EXPECT_EQ(-1 /* StateTracker::kStateUnknown */,
it->first.getStateValuesKey().getValues()[0].mValue.int_value);
EXPECT_FALSE(it->second.intervals[0].hasValue);
+ assertConditionTimer(it->second.conditionTimer, true, 0, bucketStartTimeNs);
// Bucket status after screen state change kStateUnknown->ON.
auto screenEvent = CreateScreenStateChangedEvent(
- bucketStartTimeNs + 5, android::view::DisplayStateEnum::DISPLAY_STATE_ON);
+ bucketStartTimeNs + 5 * NS_PER_SEC, android::view::DisplayStateEnum::DISPLAY_STATE_ON);
StateManager::getInstance().onLogEvent(*screenEvent);
- ASSERT_EQ(1UL, valueProducer->mCurrentSlicedBucket.size());
+ ASSERT_EQ(2UL, valueProducer->mCurrentSlicedBucket.size());
// Base for dimension key {}
it = valueProducer->mCurrentSlicedBucket.begin();
itBase = valueProducer->mCurrentBaseInfo.find(it->first.getDimensionKeyInWhat());
@@ -4040,19 +4052,29 @@ TEST(ValueMetricProducerTest, TestSlicedState) {
ASSERT_EQ(1, itBase->second.currentState.getValues().size());
EXPECT_EQ(android::view::DisplayStateEnum::DISPLAY_STATE_ON,
itBase->second.currentState.getValues()[0].mValue.int_value);
+ // Value for dimension, state key {{}, ON}
+ EXPECT_EQ(0, it->first.getDimensionKeyInWhat().getValues().size());
+ ASSERT_EQ(1, it->first.getStateValuesKey().getValues().size());
+ EXPECT_EQ(android::view::DisplayStateEnum::DISPLAY_STATE_ON,
+ it->first.getStateValuesKey().getValues()[0].mValue.int_value);
+ EXPECT_EQ(0, it->second.intervals.size());
+ assertConditionTimer(it->second.conditionTimer, true, 0, bucketStartTimeNs + 5 * NS_PER_SEC);
// Value for dimension, state key {{}, kStateUnknown}
+ it++;
EXPECT_EQ(0, it->first.getDimensionKeyInWhat().getValues().size());
ASSERT_EQ(1, it->first.getStateValuesKey().getValues().size());
EXPECT_EQ(-1 /* StateTracker::kStateUnknown */,
it->first.getStateValuesKey().getValues()[0].mValue.int_value);
EXPECT_TRUE(it->second.intervals[0].hasValue);
EXPECT_EQ(2, it->second.intervals[0].value.long_value);
+ assertConditionTimer(it->second.conditionTimer, false, 5 * NS_PER_SEC,
+ bucketStartTimeNs + 5 * NS_PER_SEC);
// Bucket status after screen state change ON->OFF.
- screenEvent = CreateScreenStateChangedEvent(bucketStartTimeNs + 10,
+ screenEvent = CreateScreenStateChangedEvent(bucketStartTimeNs + 10 * NS_PER_SEC,
android::view::DisplayStateEnum::DISPLAY_STATE_OFF);
StateManager::getInstance().onLogEvent(*screenEvent);
- ASSERT_EQ(2UL, valueProducer->mCurrentSlicedBucket.size());
+ ASSERT_EQ(3UL, valueProducer->mCurrentSlicedBucket.size());
// Base for dimension key {}
it = valueProducer->mCurrentSlicedBucket.begin();
itBase = valueProducer->mCurrentBaseInfo.find(it->first.getDimensionKeyInWhat());
@@ -4061,13 +4083,23 @@ TEST(ValueMetricProducerTest, TestSlicedState) {
EXPECT_TRUE(itBase->second.hasCurrentState);
EXPECT_EQ(android::view::DisplayStateEnum::DISPLAY_STATE_OFF,
itBase->second.currentState.getValues()[0].mValue.int_value);
+ // Value for dimension, state key {{}, OFF}
+ EXPECT_EQ(0, it->first.getDimensionKeyInWhat().getValues().size());
+ ASSERT_EQ(1, it->first.getStateValuesKey().getValues().size());
+ EXPECT_EQ(android::view::DisplayStateEnum::DISPLAY_STATE_OFF,
+ it->first.getStateValuesKey().getValues()[0].mValue.int_value);
+ EXPECT_EQ(0, it->second.intervals.size());
+ assertConditionTimer(it->second.conditionTimer, true, 0, bucketStartTimeNs + 10 * NS_PER_SEC);
// Value for dimension, state key {{}, ON}
+ it++;
EXPECT_EQ(0, it->first.getDimensionKeyInWhat().getValues().size());
ASSERT_EQ(1, it->first.getStateValuesKey().getValues().size());
EXPECT_EQ(android::view::DisplayStateEnum::DISPLAY_STATE_ON,
it->first.getStateValuesKey().getValues()[0].mValue.int_value);
EXPECT_TRUE(it->second.intervals[0].hasValue);
EXPECT_EQ(4, it->second.intervals[0].value.long_value);
+ assertConditionTimer(it->second.conditionTimer, false, 5 * NS_PER_SEC,
+ bucketStartTimeNs + 10 * NS_PER_SEC);
// Value for dimension, state key {{}, kStateUnknown}
it++;
EXPECT_EQ(0, it->first.getDimensionKeyInWhat().getValues().size());
@@ -4076,9 +4108,11 @@ TEST(ValueMetricProducerTest, TestSlicedState) {
it->first.getStateValuesKey().getValues()[0].mValue.int_value);
EXPECT_TRUE(it->second.intervals[0].hasValue);
EXPECT_EQ(2, it->second.intervals[0].value.long_value);
+ assertConditionTimer(it->second.conditionTimer, false, 5 * NS_PER_SEC,
+ bucketStartTimeNs + 5 * NS_PER_SEC);
// Bucket status after screen state change OFF->ON.
- screenEvent = CreateScreenStateChangedEvent(bucketStartTimeNs + 15,
+ screenEvent = CreateScreenStateChangedEvent(bucketStartTimeNs + 15 * NS_PER_SEC,
android::view::DisplayStateEnum::DISPLAY_STATE_ON);
StateManager::getInstance().onLogEvent(*screenEvent);
ASSERT_EQ(3UL, valueProducer->mCurrentSlicedBucket.size());
@@ -4098,6 +4132,8 @@ TEST(ValueMetricProducerTest, TestSlicedState) {
it->first.getStateValuesKey().getValues()[0].mValue.int_value);
EXPECT_TRUE(it->second.intervals[0].hasValue);
EXPECT_EQ(12, it->second.intervals[0].value.long_value);
+ assertConditionTimer(it->second.conditionTimer, false, 5 * NS_PER_SEC,
+ bucketStartTimeNs + 15 * NS_PER_SEC);
// Value for dimension, state key {{}, ON}
it++;
EXPECT_EQ(0, it->first.getDimensionKeyInWhat().getValues().size());
@@ -4106,6 +4142,8 @@ TEST(ValueMetricProducerTest, TestSlicedState) {
it->first.getStateValuesKey().getValues()[0].mValue.int_value);
EXPECT_TRUE(it->second.intervals[0].hasValue);
EXPECT_EQ(4, it->second.intervals[0].value.long_value);
+ assertConditionTimer(it->second.conditionTimer, true, 5 * NS_PER_SEC,
+ bucketStartTimeNs + 15 * NS_PER_SEC);
// Value for dimension, state key {{}, kStateUnknown}
it++;
EXPECT_EQ(0, it->first.getDimensionKeyInWhat().getValues().size());
@@ -4114,37 +4152,46 @@ TEST(ValueMetricProducerTest, TestSlicedState) {
it->first.getStateValuesKey().getValues()[0].mValue.int_value);
EXPECT_TRUE(it->second.intervals[0].hasValue);
EXPECT_EQ(2, it->second.intervals[0].value.long_value);
+ assertConditionTimer(it->second.conditionTimer, false, 5 * NS_PER_SEC,
+ bucketStartTimeNs + 5 * NS_PER_SEC);
// Start dump report and check output.
ProtoOutputStream output;
std::set<string> strSet;
- valueProducer->onDumpReport(bucketStartTimeNs + 50, true /* include recent buckets */, true,
- NO_TIME_CONSTRAINTS, &strSet, &output);
+ valueProducer->onDumpReport(bucketStartTimeNs + 50 * NS_PER_SEC,
+ true /* include recent buckets */, true, NO_TIME_CONSTRAINTS,
+ &strSet, &output);
StatsLogReport report = outputStreamToProto(&output);
EXPECT_TRUE(report.has_value_metrics());
ASSERT_EQ(3, report.value_metrics().data_size());
+ // {{}, kStateUnknown}
auto data = report.value_metrics().data(0);
ASSERT_EQ(1, data.bucket_info_size());
EXPECT_EQ(2, report.value_metrics().data(0).bucket_info(0).values(0).value_long());
EXPECT_EQ(SCREEN_STATE_ATOM_ID, data.slice_by_state(0).atom_id());
EXPECT_TRUE(data.slice_by_state(0).has_value());
EXPECT_EQ(-1 /* StateTracker::kStateUnknown */, data.slice_by_state(0).value());
+ EXPECT_EQ(5 * NS_PER_SEC, data.bucket_info(0).condition_true_nanos());
+ // {{}, ON}
data = report.value_metrics().data(1);
ASSERT_EQ(1, report.value_metrics().data(1).bucket_info_size());
EXPECT_EQ(13, report.value_metrics().data(1).bucket_info(0).values(0).value_long());
EXPECT_EQ(SCREEN_STATE_ATOM_ID, data.slice_by_state(0).atom_id());
EXPECT_TRUE(data.slice_by_state(0).has_value());
EXPECT_EQ(android::view::DisplayStateEnum::DISPLAY_STATE_ON, data.slice_by_state(0).value());
+ EXPECT_EQ(40 * NS_PER_SEC, data.bucket_info(0).condition_true_nanos());
+ // {{}, OFF}
data = report.value_metrics().data(2);
ASSERT_EQ(1, report.value_metrics().data(2).bucket_info_size());
EXPECT_EQ(12, report.value_metrics().data(2).bucket_info(0).values(0).value_long());
EXPECT_EQ(SCREEN_STATE_ATOM_ID, data.slice_by_state(0).atom_id());
EXPECT_TRUE(data.slice_by_state(0).has_value());
EXPECT_EQ(android::view::DisplayStateEnum::DISPLAY_STATE_OFF, data.slice_by_state(0).value());
+ EXPECT_EQ(5 * NS_PER_SEC, data.bucket_info(0).condition_true_nanos());
}
/*
@@ -4169,9 +4216,10 @@ TEST(ValueMetricProducerTest, TestSlicedStateWithMap) {
// Screen state change to ON.
.WillOnce(Invoke([](int tagId, const ConfigKey&, const int64_t eventTimeNs,
vector<std::shared_ptr<LogEvent>>* data) {
- EXPECT_EQ(eventTimeNs, bucketStartTimeNs + 5);
+ EXPECT_EQ(eventTimeNs, bucketStartTimeNs + 5 * NS_PER_SEC);
data->clear();
- data->push_back(CreateRepeatedValueLogEvent(tagId, bucketStartTimeNs + 5, 5));
+ data->push_back(
+ CreateRepeatedValueLogEvent(tagId, bucketStartTimeNs + 5 * NS_PER_SEC, 5));
return true;
}))
// Screen state change to VR has no pull because it is in the same
@@ -4183,17 +4231,19 @@ TEST(ValueMetricProducerTest, TestSlicedStateWithMap) {
// Screen state change to OFF.
.WillOnce(Invoke([](int tagId, const ConfigKey&, const int64_t eventTimeNs,
vector<std::shared_ptr<LogEvent>>* data) {
- EXPECT_EQ(eventTimeNs, bucketStartTimeNs + 15);
+ EXPECT_EQ(eventTimeNs, bucketStartTimeNs + 15 * NS_PER_SEC);
data->clear();
- data->push_back(CreateRepeatedValueLogEvent(tagId, bucketStartTimeNs + 15, 21));
+ data->push_back(CreateRepeatedValueLogEvent(
+ tagId, bucketStartTimeNs + 15 * NS_PER_SEC, 21));
return true;
}))
// Dump report requested.
.WillOnce(Invoke([](int tagId, const ConfigKey&, const int64_t eventTimeNs,
vector<std::shared_ptr<LogEvent>>* data) {
- EXPECT_EQ(eventTimeNs, bucketStartTimeNs + 50);
+ EXPECT_EQ(eventTimeNs, bucketStartTimeNs + 50 * NS_PER_SEC);
data->clear();
- data->push_back(CreateRepeatedValueLogEvent(tagId, bucketStartTimeNs + 50, 30));
+ data->push_back(CreateRepeatedValueLogEvent(
+ tagId, bucketStartTimeNs + 50 * NS_PER_SEC, 30));
return true;
}));
@@ -4236,12 +4286,13 @@ TEST(ValueMetricProducerTest, TestSlicedStateWithMap) {
EXPECT_EQ(-1 /* StateTracker::kStateUnknown */,
it->first.getStateValuesKey().getValues()[0].mValue.int_value);
EXPECT_FALSE(it->second.intervals[0].hasValue);
+ assertConditionTimer(it->second.conditionTimer, true, 0, bucketStartTimeNs);
// Bucket status after screen state change kStateUnknown->ON.
auto screenEvent = CreateScreenStateChangedEvent(
- bucketStartTimeNs + 5, android::view::DisplayStateEnum::DISPLAY_STATE_ON);
+ bucketStartTimeNs + 5 * NS_PER_SEC, android::view::DisplayStateEnum::DISPLAY_STATE_ON);
StateManager::getInstance().onLogEvent(*screenEvent);
- ASSERT_EQ(1UL, valueProducer->mCurrentSlicedBucket.size());
+ ASSERT_EQ(2UL, valueProducer->mCurrentSlicedBucket.size());
// Base for dimension key {}
it = valueProducer->mCurrentSlicedBucket.begin();
itBase = valueProducer->mCurrentBaseInfo.find(it->first.getDimensionKeyInWhat());
@@ -4251,20 +4302,29 @@ TEST(ValueMetricProducerTest, TestSlicedStateWithMap) {
ASSERT_EQ(1, itBase->second.currentState.getValues().size());
EXPECT_EQ(screenOnGroup.group_id(),
itBase->second.currentState.getValues()[0].mValue.long_value);
+ // Value for dimension, state key {{}, ON GROUP}
+ EXPECT_EQ(0, it->first.getDimensionKeyInWhat().getValues().size());
+ ASSERT_EQ(1, it->first.getStateValuesKey().getValues().size());
+ EXPECT_EQ(screenOnGroup.group_id(),
+ it->first.getStateValuesKey().getValues()[0].mValue.int_value);
+ assertConditionTimer(it->second.conditionTimer, true, 0, bucketStartTimeNs + 5 * NS_PER_SEC);
// Value for dimension, state key {{}, kStateUnknown}
+ it++;
EXPECT_EQ(0, it->first.getDimensionKeyInWhat().getValues().size());
ASSERT_EQ(1, it->first.getStateValuesKey().getValues().size());
EXPECT_EQ(-1 /* StateTracker::kStateUnknown */,
it->first.getStateValuesKey().getValues()[0].mValue.int_value);
EXPECT_TRUE(it->second.intervals[0].hasValue);
EXPECT_EQ(2, it->second.intervals[0].value.long_value);
+ assertConditionTimer(it->second.conditionTimer, false, 5 * NS_PER_SEC,
+ bucketStartTimeNs + 5 * NS_PER_SEC);
// Bucket status after screen state change ON->VR.
// Both ON and VR are in the same state group, so the base should not change.
- screenEvent = CreateScreenStateChangedEvent(bucketStartTimeNs + 10,
+ screenEvent = CreateScreenStateChangedEvent(bucketStartTimeNs + 10 * NS_PER_SEC,
android::view::DisplayStateEnum::DISPLAY_STATE_VR);
StateManager::getInstance().onLogEvent(*screenEvent);
- ASSERT_EQ(1UL, valueProducer->mCurrentSlicedBucket.size());
+ ASSERT_EQ(2UL, valueProducer->mCurrentSlicedBucket.size());
// Base for dimension key {}
it = valueProducer->mCurrentSlicedBucket.begin();
itBase = valueProducer->mCurrentBaseInfo.find(it->first.getDimensionKeyInWhat());
@@ -4274,20 +4334,29 @@ TEST(ValueMetricProducerTest, TestSlicedStateWithMap) {
ASSERT_EQ(1, itBase->second.currentState.getValues().size());
EXPECT_EQ(screenOnGroup.group_id(),
itBase->second.currentState.getValues()[0].mValue.int_value);
+ // Value for dimension, state key {{}, ON GROUP}
+ EXPECT_EQ(0, it->first.getDimensionKeyInWhat().getValues().size());
+ ASSERT_EQ(1, it->first.getStateValuesKey().getValues().size());
+ EXPECT_EQ(screenOnGroup.group_id(),
+ it->first.getStateValuesKey().getValues()[0].mValue.int_value);
+ assertConditionTimer(it->second.conditionTimer, true, 0, bucketStartTimeNs + 5 * NS_PER_SEC);
// Value for dimension, state key {{}, kStateUnknown}
+ it++;
EXPECT_EQ(0, it->first.getDimensionKeyInWhat().getValues().size());
ASSERT_EQ(1, it->first.getStateValuesKey().getValues().size());
EXPECT_EQ(-1 /* StateTracker::kStateUnknown */,
it->first.getStateValuesKey().getValues()[0].mValue.int_value);
EXPECT_TRUE(it->second.intervals[0].hasValue);
EXPECT_EQ(2, it->second.intervals[0].value.long_value);
+ assertConditionTimer(it->second.conditionTimer, false, 5 * NS_PER_SEC,
+ bucketStartTimeNs + 5 * NS_PER_SEC);
// Bucket status after screen state change VR->ON.
// Both ON and VR are in the same state group, so the base should not change.
- screenEvent = CreateScreenStateChangedEvent(bucketStartTimeNs + 12,
+ screenEvent = CreateScreenStateChangedEvent(bucketStartTimeNs + 12 * NS_PER_SEC,
android::view::DisplayStateEnum::DISPLAY_STATE_ON);
StateManager::getInstance().onLogEvent(*screenEvent);
- ASSERT_EQ(1UL, valueProducer->mCurrentSlicedBucket.size());
+ ASSERT_EQ(2UL, valueProducer->mCurrentSlicedBucket.size());
// Base for dimension key {}
it = valueProducer->mCurrentSlicedBucket.begin();
itBase = valueProducer->mCurrentBaseInfo.find(it->first.getDimensionKeyInWhat());
@@ -4297,19 +4366,28 @@ TEST(ValueMetricProducerTest, TestSlicedStateWithMap) {
ASSERT_EQ(1, itBase->second.currentState.getValues().size());
EXPECT_EQ(screenOnGroup.group_id(),
itBase->second.currentState.getValues()[0].mValue.int_value);
+ // Value for dimension, state key {{}, ON GROUP}
+ EXPECT_EQ(0, it->first.getDimensionKeyInWhat().getValues().size());
+ ASSERT_EQ(1, it->first.getStateValuesKey().getValues().size());
+ EXPECT_EQ(screenOnGroup.group_id(),
+ it->first.getStateValuesKey().getValues()[0].mValue.int_value);
+ assertConditionTimer(it->second.conditionTimer, true, 0, bucketStartTimeNs + 5 * NS_PER_SEC);
// Value for dimension, state key {{}, kStateUnknown}
+ it++;
EXPECT_EQ(0, it->first.getDimensionKeyInWhat().getValues().size());
ASSERT_EQ(1, it->first.getStateValuesKey().getValues().size());
EXPECT_EQ(-1 /* StateTracker::kStateUnknown */,
it->first.getStateValuesKey().getValues()[0].mValue.int_value);
EXPECT_TRUE(it->second.intervals[0].hasValue);
EXPECT_EQ(2, it->second.intervals[0].value.long_value);
+ assertConditionTimer(it->second.conditionTimer, false, 5 * NS_PER_SEC,
+ bucketStartTimeNs + 5 * NS_PER_SEC);
// Bucket status after screen state change VR->OFF.
- screenEvent = CreateScreenStateChangedEvent(bucketStartTimeNs + 15,
+ screenEvent = CreateScreenStateChangedEvent(bucketStartTimeNs + 15 * NS_PER_SEC,
android::view::DisplayStateEnum::DISPLAY_STATE_OFF);
StateManager::getInstance().onLogEvent(*screenEvent);
- ASSERT_EQ(2UL, valueProducer->mCurrentSlicedBucket.size());
+ ASSERT_EQ(3UL, valueProducer->mCurrentSlicedBucket.size());
// Base for dimension key {}
it = valueProducer->mCurrentSlicedBucket.begin();
itBase = valueProducer->mCurrentBaseInfo.find(it->first.getDimensionKeyInWhat());
@@ -4319,13 +4397,22 @@ TEST(ValueMetricProducerTest, TestSlicedStateWithMap) {
ASSERT_EQ(1, itBase->second.currentState.getValues().size());
EXPECT_EQ(screenOffGroup.group_id(),
itBase->second.currentState.getValues()[0].mValue.int_value);
+ // Value for dimension, state key {{}, OFF GROUP}
+ EXPECT_EQ(0, it->first.getDimensionKeyInWhat().getValues().size());
+ ASSERT_EQ(1, it->first.getStateValuesKey().getValues().size());
+ EXPECT_EQ(screenOffGroup.group_id(),
+ it->first.getStateValuesKey().getValues()[0].mValue.long_value);
+ assertConditionTimer(it->second.conditionTimer, true, 0, bucketStartTimeNs + 15 * NS_PER_SEC);
// Value for dimension, state key {{}, ON GROUP}
+ it++;
EXPECT_EQ(0, it->first.getDimensionKeyInWhat().getValues().size());
ASSERT_EQ(1, it->first.getStateValuesKey().getValues().size());
EXPECT_EQ(screenOnGroup.group_id(),
it->first.getStateValuesKey().getValues()[0].mValue.long_value);
EXPECT_TRUE(it->second.intervals[0].hasValue);
EXPECT_EQ(16, it->second.intervals[0].value.long_value);
+ assertConditionTimer(it->second.conditionTimer, false, 10 * NS_PER_SEC,
+ bucketStartTimeNs + 15 * NS_PER_SEC);
// Value for dimension, state key {{}, kStateUnknown}
it++;
EXPECT_EQ(0, it->first.getDimensionKeyInWhat().getValues().size());
@@ -4334,37 +4421,46 @@ TEST(ValueMetricProducerTest, TestSlicedStateWithMap) {
it->first.getStateValuesKey().getValues()[0].mValue.int_value);
EXPECT_TRUE(it->second.intervals[0].hasValue);
EXPECT_EQ(2, it->second.intervals[0].value.long_value);
+ assertConditionTimer(it->second.conditionTimer, false, 5 * NS_PER_SEC,
+ bucketStartTimeNs + 5 * NS_PER_SEC);
// Start dump report and check output.
ProtoOutputStream output;
std::set<string> strSet;
- valueProducer->onDumpReport(bucketStartTimeNs + 50, true /* include recent buckets */, true,
- NO_TIME_CONSTRAINTS, &strSet, &output);
+ valueProducer->onDumpReport(bucketStartTimeNs + 50 * NS_PER_SEC,
+ true /* include recent buckets */, true, NO_TIME_CONSTRAINTS,
+ &strSet, &output);
StatsLogReport report = outputStreamToProto(&output);
EXPECT_TRUE(report.has_value_metrics());
ASSERT_EQ(3, report.value_metrics().data_size());
+ // {{}, kStateUnknown}
auto data = report.value_metrics().data(0);
ASSERT_EQ(1, data.bucket_info_size());
EXPECT_EQ(2, report.value_metrics().data(0).bucket_info(0).values(0).value_long());
EXPECT_EQ(SCREEN_STATE_ATOM_ID, data.slice_by_state(0).atom_id());
EXPECT_TRUE(data.slice_by_state(0).has_value());
EXPECT_EQ(-1 /*StateTracker::kStateUnknown*/, data.slice_by_state(0).value());
+ EXPECT_EQ(5 * NS_PER_SEC, data.bucket_info(0).condition_true_nanos());
+ // {{}, ON GROUP}
data = report.value_metrics().data(1);
ASSERT_EQ(1, report.value_metrics().data(1).bucket_info_size());
EXPECT_EQ(16, report.value_metrics().data(1).bucket_info(0).values(0).value_long());
EXPECT_EQ(SCREEN_STATE_ATOM_ID, data.slice_by_state(0).atom_id());
EXPECT_TRUE(data.slice_by_state(0).has_group_id());
EXPECT_EQ(screenOnGroup.group_id(), data.slice_by_state(0).group_id());
+ EXPECT_EQ(10 * NS_PER_SEC, data.bucket_info(0).condition_true_nanos());
+ // {{}, OFF GROUP}
data = report.value_metrics().data(2);
ASSERT_EQ(1, report.value_metrics().data(2).bucket_info_size());
EXPECT_EQ(9, report.value_metrics().data(2).bucket_info(0).values(0).value_long());
EXPECT_EQ(SCREEN_STATE_ATOM_ID, data.slice_by_state(0).atom_id());
EXPECT_TRUE(data.slice_by_state(0).has_group_id());
EXPECT_EQ(screenOffGroup.group_id(), data.slice_by_state(0).group_id());
+ EXPECT_EQ(35 * NS_PER_SEC, data.bucket_info(0).condition_true_nanos());
}
/*
@@ -4386,6 +4482,35 @@ TEST(ValueMetricProducerTest, TestSlicedStateWithPrimaryField_WithDimensions) {
auto fieldsInState = stateLink->mutable_fields_in_state();
*fieldsInState = CreateDimensions(UID_PROCESS_STATE_ATOM_ID, {1 /* uid */});
+ /*
+ NOTE: "1" denotes uid 1 and "2" denotes uid 2.
+ bucket # 1 bucket # 2
+ 10 20 30 40 50 60 70 80 90 100 110 120 (seconds)
+ |------------------------------------------|---------------------------------|--
+
+ (kStateUnknown)
+ 1
+ |-------------|
+ 20
+
+ 2
+ |----------------------------|
+ 40
+
+ (FOREGROUND)
+ 1 1
+ |----------------------------|-------------| |------|
+ 40 20 10
+
+
+ (BACKGROUND)
+ 1
+ |------------|
+ 20
+ 2
+ |-------------|---------------------------------|
+ 20 50
+ */
sp<MockStatsPullerManager> pullerManager = new StrictMock<MockStatsPullerManager>();
EXPECT_CALL(*pullerManager, Pull(tagId, kConfigKey, _, _))
// ValueMetricProducer initialized.
@@ -4400,64 +4525,64 @@ TEST(ValueMetricProducerTest, TestSlicedStateWithPrimaryField_WithDimensions) {
// Uid 1 process state change from kStateUnknown -> Foreground
.WillOnce(Invoke([](int tagId, const ConfigKey&, const int64_t eventTimeNs,
vector<std::shared_ptr<LogEvent>>* data) {
- EXPECT_EQ(eventTimeNs, bucketStartTimeNs + 20);
+ EXPECT_EQ(eventTimeNs, bucketStartTimeNs + 20 * NS_PER_SEC);
data->clear();
- data->push_back(
- CreateTwoValueLogEvent(tagId, bucketStartTimeNs + 20, 1 /*uid*/, 6));
+ data->push_back(CreateTwoValueLogEvent(tagId, bucketStartTimeNs + 20 * NS_PER_SEC,
+ 1 /*uid*/, 6));
// This event should be skipped.
- data->push_back(
- CreateTwoValueLogEvent(tagId, bucketStartTimeNs + 20, 2 /*uid*/, 8));
+ data->push_back(CreateTwoValueLogEvent(tagId, bucketStartTimeNs + 20 * NS_PER_SEC,
+ 2 /*uid*/, 8));
return true;
}))
// Uid 2 process state change from kStateUnknown -> Background
.WillOnce(Invoke([](int tagId, const ConfigKey&, const int64_t eventTimeNs,
vector<std::shared_ptr<LogEvent>>* data) {
- EXPECT_EQ(eventTimeNs, bucketStartTimeNs + 40);
+ EXPECT_EQ(eventTimeNs, bucketStartTimeNs + 40 * NS_PER_SEC);
data->clear();
- data->push_back(
- CreateTwoValueLogEvent(tagId, bucketStartTimeNs + 40, 2 /*uid*/, 9));
+ data->push_back(CreateTwoValueLogEvent(tagId, bucketStartTimeNs + 40 * NS_PER_SEC,
+ 2 /*uid*/, 9));
// This event should be skipped.
- data->push_back(
- CreateTwoValueLogEvent(tagId, bucketStartTimeNs + 40, 1 /*uid*/, 12));
+ data->push_back(CreateTwoValueLogEvent(tagId, bucketStartTimeNs + 40 * NS_PER_SEC,
+ 1 /*uid*/, 12));
return true;
}))
// Uid 1 process state change from Foreground -> Background
.WillOnce(Invoke([](int tagId, const ConfigKey&, const int64_t eventTimeNs,
vector<std::shared_ptr<LogEvent>>* data) {
- EXPECT_EQ(eventTimeNs, bucket2StartTimeNs + 20);
+ EXPECT_EQ(eventTimeNs, bucket2StartTimeNs + 20 * NS_PER_SEC);
data->clear();
- data->push_back(
- CreateTwoValueLogEvent(tagId, bucket2StartTimeNs + 20, 1 /*uid*/, 13));
+ data->push_back(CreateTwoValueLogEvent(tagId, bucket2StartTimeNs + 20 * NS_PER_SEC,
+ 1 /*uid*/, 13));
// This event should be skipped.
- data->push_back(
- CreateTwoValueLogEvent(tagId, bucket2StartTimeNs + 20, 2 /*uid*/, 11));
+ data->push_back(CreateTwoValueLogEvent(tagId, bucket2StartTimeNs + 20 * NS_PER_SEC,
+ 2 /*uid*/, 11));
return true;
}))
// Uid 1 process state change from Background -> Foreground
.WillOnce(Invoke([](int tagId, const ConfigKey&, const int64_t eventTimeNs,
vector<std::shared_ptr<LogEvent>>* data) {
- EXPECT_EQ(eventTimeNs, bucket2StartTimeNs + 40);
+ EXPECT_EQ(eventTimeNs, bucket2StartTimeNs + 40 * NS_PER_SEC);
data->clear();
- data->push_back(
- CreateTwoValueLogEvent(tagId, bucket2StartTimeNs + 40, 1 /*uid*/, 17));
+ data->push_back(CreateTwoValueLogEvent(tagId, bucket2StartTimeNs + 40 * NS_PER_SEC,
+ 1 /*uid*/, 17));
// This event should be skipped.
- data->push_back(
- CreateTwoValueLogEvent(tagId, bucket2StartTimeNs + 40, 2 /*uid */, 15));
+ data->push_back(CreateTwoValueLogEvent(tagId, bucket2StartTimeNs + 40 * NS_PER_SEC,
+ 2 /*uid */, 15));
return true;
}))
// Dump report pull.
.WillOnce(Invoke([](int tagId, const ConfigKey&, const int64_t eventTimeNs,
vector<std::shared_ptr<LogEvent>>* data) {
- EXPECT_EQ(eventTimeNs, bucket2StartTimeNs + 50);
+ EXPECT_EQ(eventTimeNs, bucket2StartTimeNs + 50 * NS_PER_SEC);
data->clear();
- data->push_back(
- CreateTwoValueLogEvent(tagId, bucket2StartTimeNs + 50, 2 /*uid*/, 20));
- data->push_back(
- CreateTwoValueLogEvent(tagId, bucket2StartTimeNs + 50, 1 /*uid*/, 21));
+ data->push_back(CreateTwoValueLogEvent(tagId, bucket2StartTimeNs + 50 * NS_PER_SEC,
+ 2 /*uid*/, 20));
+ data->push_back(CreateTwoValueLogEvent(tagId, bucket2StartTimeNs + 50 * NS_PER_SEC,
+ 1 /*uid*/, 21));
return true;
}));
@@ -4489,6 +4614,7 @@ TEST(ValueMetricProducerTest, TestSlicedStateWithPrimaryField_WithDimensions) {
EXPECT_EQ(-1 /* StateTracker::kStateUnknown */,
it->first.getStateValuesKey().getValues()[0].mValue.int_value);
EXPECT_FALSE(it->second.intervals[0].hasValue);
+ assertConditionTimer(it->second.conditionTimer, true, 0, bucketStartTimeNs);
// Base for dimension key {uid 2}
it++;
itBase = valueProducer->mCurrentBaseInfo.find(it->first.getDimensionKeyInWhat());
@@ -4505,12 +4631,14 @@ TEST(ValueMetricProducerTest, TestSlicedStateWithPrimaryField_WithDimensions) {
EXPECT_EQ(-1 /* StateTracker::kStateUnknown */,
it->first.getStateValuesKey().getValues()[0].mValue.int_value);
EXPECT_FALSE(it->second.intervals[0].hasValue);
+ assertConditionTimer(it->second.conditionTimer, true, 0, bucketStartTimeNs);
// Bucket status after uid 1 process state change kStateUnknown -> Foreground.
- auto uidProcessEvent = CreateUidProcessStateChangedEvent(
- bucketStartTimeNs + 20, 1 /* uid */, android::app::PROCESS_STATE_IMPORTANT_FOREGROUND);
+ auto uidProcessEvent =
+ CreateUidProcessStateChangedEvent(bucketStartTimeNs + 20 * NS_PER_SEC, 1 /* uid */,
+ android::app::PROCESS_STATE_IMPORTANT_FOREGROUND);
StateManager::getInstance().onLogEvent(*uidProcessEvent);
- ASSERT_EQ(2UL, valueProducer->mCurrentSlicedBucket.size());
+ ASSERT_EQ(3UL, valueProducer->mCurrentSlicedBucket.size());
// Base for dimension key {uid 1}.
it = valueProducer->mCurrentSlicedBucket.begin();
itBase = valueProducer->mCurrentBaseInfo.find(it->first.getDimensionKeyInWhat());
@@ -4528,8 +4656,18 @@ TEST(ValueMetricProducerTest, TestSlicedStateWithPrimaryField_WithDimensions) {
it->first.getStateValuesKey().getValues()[0].mValue.int_value);
EXPECT_TRUE(it->second.intervals[0].hasValue);
EXPECT_EQ(3, it->second.intervals[0].value.long_value);
+ assertConditionTimer(it->second.conditionTimer, false, 20 * NS_PER_SEC,
+ bucketStartTimeNs + 20 * NS_PER_SEC);
+ // Value for key {uid 1, FOREGROUND}.
+ it++;
+ ASSERT_EQ(1, it->first.getDimensionKeyInWhat().getValues().size());
+ EXPECT_EQ(1, it->first.getDimensionKeyInWhat().getValues()[0].mValue.int_value);
+ ASSERT_EQ(1, it->first.getStateValuesKey().getValues().size());
+ EXPECT_EQ(android::app::PROCESS_STATE_IMPORTANT_FOREGROUND,
+ it->first.getStateValuesKey().getValues()[0].mValue.int_value);
+ assertConditionTimer(it->second.conditionTimer, true, 0, bucketStartTimeNs + 20 * NS_PER_SEC);
- // Base for dimension key {uid 2}
+ // Base for dimension key {uid 2}.
it++;
itBase = valueProducer->mCurrentBaseInfo.find(it->first.getDimensionKeyInWhat());
EXPECT_TRUE(itBase->second.baseInfos[0].hasBase);
@@ -4538,22 +4676,42 @@ TEST(ValueMetricProducerTest, TestSlicedStateWithPrimaryField_WithDimensions) {
ASSERT_EQ(1, itBase->second.currentState.getValues().size());
EXPECT_EQ(-1 /* StateTracker::kStateUnknown */,
itBase->second.currentState.getValues()[0].mValue.int_value);
- // Value for key {uid 2, kStateUnknown}
+ // Value for key {uid 2, kStateUnknown}.
ASSERT_EQ(1, it->first.getDimensionKeyInWhat().getValues().size());
EXPECT_EQ(2, it->first.getDimensionKeyInWhat().getValues()[0].mValue.int_value);
ASSERT_EQ(1, it->first.getStateValuesKey().getValues().size());
- EXPECT_EQ(-1, it->first.getStateValuesKey().getValues()[0].mValue.int_value);
+ EXPECT_EQ(-1 /* StateTracker::kStateUnknown */,
+ it->first.getStateValuesKey().getValues()[0].mValue.int_value);
EXPECT_FALSE(it->second.intervals[0].hasValue);
+ assertConditionTimer(it->second.conditionTimer, true, 0, bucketStartTimeNs);
// Bucket status after uid 2 process state change kStateUnknown -> Background.
- uidProcessEvent = CreateUidProcessStateChangedEvent(
- bucketStartTimeNs + 40, 2 /* uid */, android::app::PROCESS_STATE_IMPORTANT_BACKGROUND);
+ uidProcessEvent =
+ CreateUidProcessStateChangedEvent(bucketStartTimeNs + 40 * NS_PER_SEC, 2 /* uid */,
+ android::app::PROCESS_STATE_IMPORTANT_BACKGROUND);
StateManager::getInstance().onLogEvent(*uidProcessEvent);
- ASSERT_EQ(2UL, valueProducer->mCurrentSlicedBucket.size());
- // Base for dimension key {uid 1}.
+ ASSERT_EQ(4UL, valueProducer->mCurrentSlicedBucket.size());
+ // Base for dimension key {uid 2}.
it = valueProducer->mCurrentSlicedBucket.begin();
itBase = valueProducer->mCurrentBaseInfo.find(it->first.getDimensionKeyInWhat());
EXPECT_TRUE(itBase->second.baseInfos[0].hasBase);
+ EXPECT_EQ(9, itBase->second.baseInfos[0].base.long_value);
+ EXPECT_TRUE(itBase->second.hasCurrentState);
+ ASSERT_EQ(1, itBase->second.currentState.getValues().size());
+ EXPECT_EQ(android::app::PROCESS_STATE_IMPORTANT_BACKGROUND,
+ itBase->second.currentState.getValues()[0].mValue.int_value);
+ // Value for key {uid 2, BACKGROUND}.
+ ASSERT_EQ(1, it->first.getDimensionKeyInWhat().getValues().size());
+ EXPECT_EQ(2, it->first.getDimensionKeyInWhat().getValues()[0].mValue.int_value);
+ ASSERT_EQ(1, it->first.getStateValuesKey().getValues().size());
+ EXPECT_EQ(android::app::PROCESS_STATE_IMPORTANT_BACKGROUND,
+ it->first.getStateValuesKey().getValues()[0].mValue.int_value);
+ assertConditionTimer(it->second.conditionTimer, true, 0, bucketStartTimeNs + 40 * NS_PER_SEC);
+
+ // Base for dimension key {uid 1}.
+ it++;
+ itBase = valueProducer->mCurrentBaseInfo.find(it->first.getDimensionKeyInWhat());
+ EXPECT_TRUE(itBase->second.baseInfos[0].hasBase);
EXPECT_EQ(6, itBase->second.baseInfos[0].base.long_value);
EXPECT_TRUE(itBase->second.hasCurrentState);
ASSERT_EQ(1, itBase->second.currentState.getValues().size());
@@ -4563,26 +4721,33 @@ TEST(ValueMetricProducerTest, TestSlicedStateWithPrimaryField_WithDimensions) {
ASSERT_EQ(1, it->first.getDimensionKeyInWhat().getValues().size());
EXPECT_EQ(1, it->first.getDimensionKeyInWhat().getValues()[0].mValue.int_value);
ASSERT_EQ(1, it->first.getStateValuesKey().getValues().size());
- EXPECT_EQ(-1, it->first.getStateValuesKey().getValues()[0].mValue.int_value);
+ EXPECT_EQ(-1 /* StateTracker::kStateUnknown */,
+ it->first.getStateValuesKey().getValues()[0].mValue.int_value);
EXPECT_TRUE(it->second.intervals[0].hasValue);
EXPECT_EQ(3, it->second.intervals[0].value.long_value);
+ assertConditionTimer(it->second.conditionTimer, false, 20 * NS_PER_SEC,
+ bucketStartTimeNs + 20 * NS_PER_SEC);
- // Base for dimension key {uid 2}
+ // Value for key {uid 1, FOREGROUND}.
it++;
- itBase = valueProducer->mCurrentBaseInfo.find(it->first.getDimensionKeyInWhat());
- EXPECT_TRUE(itBase->second.baseInfos[0].hasBase);
- EXPECT_EQ(9, itBase->second.baseInfos[0].base.long_value);
- EXPECT_TRUE(itBase->second.hasCurrentState);
- ASSERT_EQ(1, itBase->second.currentState.getValues().size());
- EXPECT_EQ(android::app::PROCESS_STATE_IMPORTANT_BACKGROUND,
- itBase->second.currentState.getValues()[0].mValue.int_value);
+ ASSERT_EQ(1, it->first.getDimensionKeyInWhat().getValues().size());
+ EXPECT_EQ(1, it->first.getDimensionKeyInWhat().getValues()[0].mValue.int_value);
+ ASSERT_EQ(1, it->first.getStateValuesKey().getValues().size());
+ EXPECT_EQ(android::app::PROCESS_STATE_IMPORTANT_FOREGROUND,
+ it->first.getStateValuesKey().getValues()[0].mValue.int_value);
+ assertConditionTimer(it->second.conditionTimer, true, 0, bucketStartTimeNs + 20 * NS_PER_SEC);
+
// Value for key {uid 2, kStateUnknown}
+ it++;
ASSERT_EQ(1, it->first.getDimensionKeyInWhat().getValues().size());
EXPECT_EQ(2, it->first.getDimensionKeyInWhat().getValues()[0].mValue.int_value);
ASSERT_EQ(1, it->first.getStateValuesKey().getValues().size());
- EXPECT_EQ(-1, it->first.getStateValuesKey().getValues()[0].mValue.int_value);
+ EXPECT_EQ(-1 /* StateTracker::kStateUnknown */,
+ it->first.getStateValuesKey().getValues()[0].mValue.int_value);
EXPECT_TRUE(it->second.intervals[0].hasValue);
EXPECT_EQ(2, it->second.intervals[0].value.long_value);
+ assertConditionTimer(it->second.conditionTimer, false, 40 * NS_PER_SEC,
+ bucketStartTimeNs + 40 * NS_PER_SEC);
// Pull at end of first bucket.
vector<shared_ptr<LogEvent>> allData;
@@ -4612,6 +4777,8 @@ TEST(ValueMetricProducerTest, TestSlicedStateWithPrimaryField_WithDimensions) {
EXPECT_EQ(android::app::PROCESS_STATE_IMPORTANT_BACKGROUND,
it->first.getStateValuesKey().getValues()[0].mValue.int_value);
EXPECT_FALSE(it->second.intervals[0].hasValue);
+ assertConditionTimer(it->second.conditionTimer, true, 0, bucket2StartTimeNs);
+ EXPECT_EQ(20 * NS_PER_SEC, valueProducer->mPastBuckets[it->first][0].mConditionTrueNs);
// Base for dimension key {uid 1}
it++;
@@ -4629,6 +4796,8 @@ TEST(ValueMetricProducerTest, TestSlicedStateWithPrimaryField_WithDimensions) {
EXPECT_EQ(-1 /* kStateTracker::kUnknown */,
it->first.getStateValuesKey().getValues()[0].mValue.int_value);
EXPECT_FALSE(it->second.intervals[0].hasValue);
+ assertConditionTimer(it->second.conditionTimer, false, 0, bucketStartTimeNs + 20 * NS_PER_SEC);
+ EXPECT_EQ(20 * NS_PER_SEC, valueProducer->mPastBuckets[it->first][0].mConditionTrueNs);
// Value for key {uid 1, FOREGROUND}
it++;
@@ -4638,6 +4807,8 @@ TEST(ValueMetricProducerTest, TestSlicedStateWithPrimaryField_WithDimensions) {
EXPECT_EQ(android::app::PROCESS_STATE_IMPORTANT_FOREGROUND,
it->first.getStateValuesKey().getValues()[0].mValue.int_value);
EXPECT_FALSE(it->second.intervals[0].hasValue);
+ assertConditionTimer(it->second.conditionTimer, true, 0, bucket2StartTimeNs);
+ EXPECT_EQ(40 * NS_PER_SEC, valueProducer->mPastBuckets[it->first][0].mConditionTrueNs);
// Value for key {uid 2, kStateUnknown}
it++;
@@ -4647,13 +4818,16 @@ TEST(ValueMetricProducerTest, TestSlicedStateWithPrimaryField_WithDimensions) {
EXPECT_EQ(-1 /* kStateTracker::kUnknown */,
it->first.getStateValuesKey().getValues()[0].mValue.int_value);
EXPECT_FALSE(it->second.intervals[0].hasValue);
+ assertConditionTimer(it->second.conditionTimer, false, 0, bucketStartTimeNs + 40 * NS_PER_SEC);
+ EXPECT_EQ(40 * NS_PER_SEC, valueProducer->mPastBuckets[it->first][0].mConditionTrueNs);
// Bucket status after uid 1 process state change from Foreground -> Background.
- uidProcessEvent = CreateUidProcessStateChangedEvent(
- bucket2StartTimeNs + 20, 1 /* uid */, android::app::PROCESS_STATE_IMPORTANT_BACKGROUND);
+ uidProcessEvent =
+ CreateUidProcessStateChangedEvent(bucket2StartTimeNs + 20 * NS_PER_SEC, 1 /* uid */,
+ android::app::PROCESS_STATE_IMPORTANT_BACKGROUND);
StateManager::getInstance().onLogEvent(*uidProcessEvent);
- ASSERT_EQ(4UL, valueProducer->mCurrentSlicedBucket.size());
+ ASSERT_EQ(5UL, valueProducer->mCurrentSlicedBucket.size());
ASSERT_EQ(4UL, valueProducer->mPastBuckets.size());
ASSERT_EQ(2UL, valueProducer->mCurrentBaseInfo.size());
// Base for dimension key {uid 2}.
@@ -4672,6 +4846,8 @@ TEST(ValueMetricProducerTest, TestSlicedStateWithPrimaryField_WithDimensions) {
EXPECT_EQ(android::app::PROCESS_STATE_IMPORTANT_BACKGROUND,
it->first.getStateValuesKey().getValues()[0].mValue.int_value);
EXPECT_FALSE(it->second.intervals[0].hasValue);
+ assertConditionTimer(it->second.conditionTimer, true, 0, bucket2StartTimeNs);
+
// Base for dimension key {uid 1}
it++;
itBase = valueProducer->mCurrentBaseInfo.find(it->first.getDimensionKeyInWhat());
@@ -4688,6 +4864,17 @@ TEST(ValueMetricProducerTest, TestSlicedStateWithPrimaryField_WithDimensions) {
EXPECT_EQ(-1 /* StateTracker::kStateUnknown */,
it->first.getStateValuesKey().getValues()[0].mValue.int_value);
EXPECT_FALSE(it->second.intervals[0].hasValue);
+ assertConditionTimer(it->second.conditionTimer, false, 0, bucketStartTimeNs + 20 * NS_PER_SEC);
+
+ // Value for key {uid 1, BACKGROUND}
+ it++;
+ ASSERT_EQ(1, it->first.getDimensionKeyInWhat().getValues().size());
+ EXPECT_EQ(1, it->first.getDimensionKeyInWhat().getValues()[0].mValue.int_value);
+ ASSERT_EQ(1, it->first.getStateValuesKey().getValues().size());
+ EXPECT_EQ(android::app::PROCESS_STATE_IMPORTANT_BACKGROUND,
+ it->first.getStateValuesKey().getValues()[0].mValue.int_value);
+ assertConditionTimer(it->second.conditionTimer, true, 0, bucket2StartTimeNs + 20 * NS_PER_SEC);
+
// Value for key {uid 1, FOREGROUND}
it++;
ASSERT_EQ(1, it->first.getDimensionKeyInWhat().getValues().size());
@@ -4697,6 +4884,9 @@ TEST(ValueMetricProducerTest, TestSlicedStateWithPrimaryField_WithDimensions) {
it->first.getStateValuesKey().getValues()[0].mValue.int_value);
EXPECT_TRUE(it->second.intervals[0].hasValue);
EXPECT_EQ(3, it->second.intervals[0].value.long_value);
+ assertConditionTimer(it->second.conditionTimer, false, 20 * NS_PER_SEC,
+ bucket2StartTimeNs + 20 * NS_PER_SEC);
+
// Value for key {uid 2, kStateUnknown}
it++;
ASSERT_EQ(1, it->first.getDimensionKeyInWhat().getValues().size());
@@ -4705,10 +4895,12 @@ TEST(ValueMetricProducerTest, TestSlicedStateWithPrimaryField_WithDimensions) {
EXPECT_EQ(-1 /* StateTracker::kStateUnknown */,
it->first.getStateValuesKey().getValues()[0].mValue.int_value);
EXPECT_FALSE(it->second.intervals[0].hasValue);
+ assertConditionTimer(it->second.conditionTimer, false, 0, bucketStartTimeNs + 40 * NS_PER_SEC);
// Bucket status after uid 1 process state change Background->Foreground.
- uidProcessEvent = CreateUidProcessStateChangedEvent(
- bucket2StartTimeNs + 40, 1 /* uid */, android::app::PROCESS_STATE_IMPORTANT_FOREGROUND);
+ uidProcessEvent =
+ CreateUidProcessStateChangedEvent(bucket2StartTimeNs + 40 * NS_PER_SEC, 1 /* uid */,
+ android::app::PROCESS_STATE_IMPORTANT_FOREGROUND);
StateManager::getInstance().onLogEvent(*uidProcessEvent);
ASSERT_EQ(5UL, valueProducer->mCurrentSlicedBucket.size());
@@ -4729,6 +4921,7 @@ TEST(ValueMetricProducerTest, TestSlicedStateWithPrimaryField_WithDimensions) {
EXPECT_EQ(android::app::PROCESS_STATE_IMPORTANT_BACKGROUND,
it->first.getStateValuesKey().getValues()[0].mValue.int_value);
EXPECT_FALSE(it->second.intervals[0].hasValue);
+ assertConditionTimer(it->second.conditionTimer, true, 0, bucket2StartTimeNs);
// Base for dimension key {uid 1}
it++;
@@ -4746,6 +4939,7 @@ TEST(ValueMetricProducerTest, TestSlicedStateWithPrimaryField_WithDimensions) {
EXPECT_EQ(-1 /* StateTracker::kStateUnknown */,
it->first.getStateValuesKey().getValues()[0].mValue.int_value);
EXPECT_FALSE(it->second.intervals[0].hasValue);
+ assertConditionTimer(it->second.conditionTimer, false, 0, bucketStartTimeNs + 20 * NS_PER_SEC);
// Value for key {uid 1, BACKGROUND}
it++;
@@ -4756,6 +4950,8 @@ TEST(ValueMetricProducerTest, TestSlicedStateWithPrimaryField_WithDimensions) {
it->first.getStateValuesKey().getValues()[0].mValue.int_value);
EXPECT_TRUE(it->second.intervals[0].hasValue);
EXPECT_EQ(4, it->second.intervals[0].value.long_value);
+ assertConditionTimer(it->second.conditionTimer, false, 20 * NS_PER_SEC,
+ bucket2StartTimeNs + 40 * NS_PER_SEC);
// Value for key {uid 1, FOREGROUND}
it++;
@@ -4766,6 +4962,8 @@ TEST(ValueMetricProducerTest, TestSlicedStateWithPrimaryField_WithDimensions) {
it->first.getStateValuesKey().getValues()[0].mValue.int_value);
EXPECT_TRUE(it->second.intervals[0].hasValue);
EXPECT_EQ(3, it->second.intervals[0].value.long_value);
+ assertConditionTimer(it->second.conditionTimer, true, 20 * NS_PER_SEC,
+ bucket2StartTimeNs + 40 * NS_PER_SEC);
// Value for key {uid 2, kStateUnknown}
it++;
@@ -4774,17 +4972,20 @@ TEST(ValueMetricProducerTest, TestSlicedStateWithPrimaryField_WithDimensions) {
ASSERT_EQ(1, it->first.getStateValuesKey().getValues().size());
EXPECT_EQ(-1 /* StateTracker::kStateUnknown */,
it->first.getStateValuesKey().getValues()[0].mValue.int_value);
+ assertConditionTimer(it->second.conditionTimer, false, 0, bucketStartTimeNs + 40 * NS_PER_SEC);
// Start dump report and check output.
ProtoOutputStream output;
std::set<string> strSet;
- valueProducer->onDumpReport(bucket2StartTimeNs + 50, true /* include recent buckets */, true,
- NO_TIME_CONSTRAINTS, &strSet, &output);
+ valueProducer->onDumpReport(bucket2StartTimeNs + 50 * NS_PER_SEC,
+ true /* include recent buckets */, true, NO_TIME_CONSTRAINTS,
+ &strSet, &output);
StatsLogReport report = outputStreamToProto(&output);
EXPECT_TRUE(report.has_value_metrics());
ASSERT_EQ(5, report.value_metrics().data_size());
+ // {uid 1, BACKGROUND}
auto data = report.value_metrics().data(0);
ASSERT_EQ(1, data.bucket_info_size());
EXPECT_EQ(4, report.value_metrics().data(0).bucket_info(0).values(0).value_long());
@@ -4792,14 +4993,18 @@ TEST(ValueMetricProducerTest, TestSlicedStateWithPrimaryField_WithDimensions) {
EXPECT_TRUE(data.slice_by_state(0).has_value());
EXPECT_EQ(android::app::ProcessStateEnum::PROCESS_STATE_IMPORTANT_BACKGROUND,
data.slice_by_state(0).value());
+ EXPECT_EQ(20 * NS_PER_SEC, data.bucket_info(0).condition_true_nanos());
+ // {uid 2, kStateUnknown}
data = report.value_metrics().data(1);
ASSERT_EQ(1, report.value_metrics().data(1).bucket_info_size());
EXPECT_EQ(2, report.value_metrics().data(1).bucket_info(0).values(0).value_long());
EXPECT_EQ(UID_PROCESS_STATE_ATOM_ID, data.slice_by_state(0).atom_id());
EXPECT_TRUE(data.slice_by_state(0).has_value());
EXPECT_EQ(-1 /*StateTracker::kStateUnknown*/, data.slice_by_state(0).value());
+ EXPECT_EQ(40 * NS_PER_SEC, data.bucket_info(0).condition_true_nanos());
+ // {uid 1, FOREGROUND}
data = report.value_metrics().data(2);
EXPECT_EQ(UID_PROCESS_STATE_ATOM_ID, data.slice_by_state(0).atom_id());
EXPECT_TRUE(data.slice_by_state(0).has_value());
@@ -4808,14 +5013,19 @@ TEST(ValueMetricProducerTest, TestSlicedStateWithPrimaryField_WithDimensions) {
ASSERT_EQ(2, report.value_metrics().data(2).bucket_info_size());
EXPECT_EQ(4, report.value_metrics().data(2).bucket_info(0).values(0).value_long());
EXPECT_EQ(7, report.value_metrics().data(2).bucket_info(1).values(0).value_long());
+ EXPECT_EQ(40 * NS_PER_SEC, data.bucket_info(0).condition_true_nanos());
+ EXPECT_EQ(30 * NS_PER_SEC, data.bucket_info(1).condition_true_nanos());
+ // {uid 1, kStateUnknown}
data = report.value_metrics().data(3);
ASSERT_EQ(1, report.value_metrics().data(3).bucket_info_size());
EXPECT_EQ(3, report.value_metrics().data(3).bucket_info(0).values(0).value_long());
EXPECT_EQ(UID_PROCESS_STATE_ATOM_ID, data.slice_by_state(0).atom_id());
EXPECT_TRUE(data.slice_by_state(0).has_value());
EXPECT_EQ(-1 /*StateTracker::kStateUnknown*/, data.slice_by_state(0).value());
+ EXPECT_EQ(20 * NS_PER_SEC, data.bucket_info(0).condition_true_nanos());
+ // {uid 2, BACKGROUND}
data = report.value_metrics().data(4);
EXPECT_EQ(UID_PROCESS_STATE_ATOM_ID, data.slice_by_state(0).atom_id());
EXPECT_TRUE(data.slice_by_state(0).has_value());
@@ -4824,6 +5034,1630 @@ TEST(ValueMetricProducerTest, TestSlicedStateWithPrimaryField_WithDimensions) {
ASSERT_EQ(2, report.value_metrics().data(4).bucket_info_size());
EXPECT_EQ(6, report.value_metrics().data(4).bucket_info(0).values(0).value_long());
EXPECT_EQ(5, report.value_metrics().data(4).bucket_info(1).values(0).value_long());
+ EXPECT_EQ(20 * NS_PER_SEC, data.bucket_info(0).condition_true_nanos());
+ EXPECT_EQ(50 * NS_PER_SEC, data.bucket_info(1).condition_true_nanos());
+}
+
+/*
+ * Test slicing condition_true_nanos by state for metric that slices by state when data is not
+ * present in pulled data during a state change.
+ */
+TEST(ValueMetricProducerTest, TestSlicedStateWithMissingDataInStateChange) {
+ // Set up ValueMetricProducer.
+ ValueMetric metric =
+ ValueMetricProducerTestHelper::createMetricWithState("BATTERY_SAVER_MODE_STATE");
+ sp<MockStatsPullerManager> pullerManager = new StrictMock<MockStatsPullerManager>();
+ /*
+ NOTE: "-" means that the data was not present in the pulled data.
+
+ bucket # 1
+ 10 20 30 40 50 60 (seconds)
+ |-------------------------------------------------------|--
+ x (kStateUnknown)
+ |-----------|
+ 10
+
+ x x (ON)
+ |---------------------| |-----------|
+ 20 10
+
+ - (OFF)
+ */
+ EXPECT_CALL(*pullerManager, Pull(tagId, kConfigKey, _, _))
+ // ValueMetricProducer initialized.
+ .WillOnce(Invoke([](int tagId, const ConfigKey&, const int64_t eventTimeNs,
+ vector<std::shared_ptr<LogEvent>>* data) {
+ EXPECT_EQ(eventTimeNs, bucketStartTimeNs);
+ data->clear();
+ data->push_back(CreateRepeatedValueLogEvent(tagId, bucketStartTimeNs, 3));
+ return true;
+ }))
+ // Battery saver mode state changed to ON.
+ .WillOnce(Invoke([](int tagId, const ConfigKey&, const int64_t eventTimeNs,
+ vector<std::shared_ptr<LogEvent>>* data) {
+ EXPECT_EQ(eventTimeNs, bucketStartTimeNs + 10 * NS_PER_SEC);
+ data->clear();
+ data->push_back(
+ CreateRepeatedValueLogEvent(tagId, bucketStartTimeNs + 10 * NS_PER_SEC, 5));
+ return true;
+ }))
+ // Battery saver mode state changed to OFF but data for dimension key {} is not present
+ // in the pulled data.
+ .WillOnce(Invoke([](int tagId, const ConfigKey&, const int64_t eventTimeNs,
+ vector<std::shared_ptr<LogEvent>>* data) {
+ EXPECT_EQ(eventTimeNs, bucketStartTimeNs + 30 * NS_PER_SEC);
+ data->clear();
+ return true;
+ }))
+ // Battery saver mode state changed to ON.
+ .WillOnce(Invoke([](int tagId, const ConfigKey&, const int64_t eventTimeNs,
+ vector<std::shared_ptr<LogEvent>>* data) {
+ EXPECT_EQ(eventTimeNs, bucketStartTimeNs + 40 * NS_PER_SEC);
+ data->clear();
+ data->push_back(
+ CreateRepeatedValueLogEvent(tagId, bucketStartTimeNs + 40 * NS_PER_SEC, 7));
+ return true;
+ }))
+ // Dump report pull.
+ .WillOnce(Invoke([](int tagId, const ConfigKey&, const int64_t eventTimeNs,
+ vector<std::shared_ptr<LogEvent>>* data) {
+ EXPECT_EQ(eventTimeNs, bucketStartTimeNs + 50 * NS_PER_SEC);
+ data->clear();
+ data->push_back(CreateRepeatedValueLogEvent(
+ tagId, bucketStartTimeNs + 50 * NS_PER_SEC, 15));
+ return true;
+ }));
+
+ StateManager::getInstance().clear();
+ sp<ValueMetricProducer> valueProducer =
+ ValueMetricProducerTestHelper::createValueProducerWithState(
+ pullerManager, metric, {util::BATTERY_SAVER_MODE_STATE_CHANGED}, {});
+ EXPECT_EQ(1, valueProducer->mSlicedStateAtoms.size());
+
+ // Set up StateManager and check that StateTrackers are initialized.
+ StateManager::getInstance().registerListener(util::BATTERY_SAVER_MODE_STATE_CHANGED,
+ valueProducer);
+ EXPECT_EQ(1, StateManager::getInstance().getStateTrackersCount());
+ EXPECT_EQ(1, StateManager::getInstance().getListenersCount(
+ util::BATTERY_SAVER_MODE_STATE_CHANGED));
+
+ // Bucket status after metric initialized.
+ ASSERT_EQ(1UL, valueProducer->mCurrentSlicedBucket.size());
+ // Base for dimension key {}
+ auto it = valueProducer->mCurrentSlicedBucket.begin();
+ auto itBase = valueProducer->mCurrentBaseInfo.find(it->first.getDimensionKeyInWhat());
+ EXPECT_TRUE(itBase->second.hasCurrentState);
+ ASSERT_EQ(1, itBase->second.currentState.getValues().size());
+ EXPECT_EQ(-1 /* StateTracker::kStateUnknown */,
+ itBase->second.currentState.getValues()[0].mValue.int_value);
+ // Value for dimension, state key {{}, kStateUnknown}
+ EXPECT_EQ(0, it->first.getDimensionKeyInWhat().getValues().size());
+ ASSERT_EQ(1, it->first.getStateValuesKey().getValues().size());
+ EXPECT_EQ(-1 /* StateTracker::kStateUnknown */,
+ it->first.getStateValuesKey().getValues()[0].mValue.int_value);
+ assertConditionTimer(it->second.conditionTimer, true, 0, bucketStartTimeNs);
+
+ // Bucket status after battery saver mode ON event.
+ unique_ptr<LogEvent> batterySaverOnEvent =
+ CreateBatterySaverOnEvent(/*timestamp=*/bucketStartTimeNs + 10 * NS_PER_SEC);
+ StateManager::getInstance().onLogEvent(*batterySaverOnEvent);
+
+ // Base for dimension key {}
+
+ ASSERT_EQ(1UL, valueProducer->mCurrentBaseInfo.size());
+ ASSERT_EQ(2UL, valueProducer->mCurrentSlicedBucket.size());
+ it = valueProducer->mCurrentSlicedBucket.begin();
+ itBase = valueProducer->mCurrentBaseInfo.find(it->first.getDimensionKeyInWhat());
+ EXPECT_TRUE(itBase->second.hasCurrentState);
+ ASSERT_EQ(1, itBase->second.currentState.getValues().size());
+ EXPECT_EQ(BatterySaverModeStateChanged::ON,
+ itBase->second.currentState.getValues()[0].mValue.int_value);
+ // Value for key {{}, ON}
+ EXPECT_EQ(0, it->first.getDimensionKeyInWhat().getValues().size());
+ ASSERT_EQ(1, it->first.getStateValuesKey().getValues().size());
+ EXPECT_EQ(BatterySaverModeStateChanged::ON,
+ it->first.getStateValuesKey().getValues()[0].mValue.int_value);
+ assertConditionTimer(it->second.conditionTimer, true, 0, bucketStartTimeNs + 10 * NS_PER_SEC);
+
+ // Value for key {{}, -1}
+ it++;
+ EXPECT_EQ(0, it->first.getDimensionKeyInWhat().getValues().size());
+ ASSERT_EQ(1, it->first.getStateValuesKey().getValues().size());
+ EXPECT_EQ(-1 /*StateTracker::kUnknown*/,
+ it->first.getStateValuesKey().getValues()[0].mValue.int_value);
+ assertConditionTimer(it->second.conditionTimer, false, 10 * NS_PER_SEC,
+ bucketStartTimeNs + 10 * NS_PER_SEC);
+
+ // Bucket status after battery saver mode OFF event which is not present
+ // in the pulled data.
+ unique_ptr<LogEvent> batterySaverOffEvent =
+ CreateBatterySaverOffEvent(/*timestamp=*/bucketStartTimeNs + 30 * NS_PER_SEC);
+ StateManager::getInstance().onLogEvent(*batterySaverOffEvent);
+
+ // Base for dimension key {}
+ ASSERT_EQ(1UL, valueProducer->mCurrentBaseInfo.size());
+ ASSERT_EQ(2UL, valueProducer->mCurrentSlicedBucket.size());
+ it = valueProducer->mCurrentSlicedBucket.begin();
+ itBase = valueProducer->mCurrentBaseInfo.find(it->first.getDimensionKeyInWhat());
+ EXPECT_FALSE(itBase->second.hasCurrentState);
+ ASSERT_EQ(1, itBase->second.currentState.getValues().size());
+ EXPECT_EQ(BatterySaverModeStateChanged::ON,
+ itBase->second.currentState.getValues()[0].mValue.int_value);
+ // Value for key {{}, ON}
+ EXPECT_EQ(0, it->first.getDimensionKeyInWhat().getValues().size());
+ ASSERT_EQ(1, it->first.getStateValuesKey().getValues().size());
+ EXPECT_EQ(BatterySaverModeStateChanged::ON,
+ it->first.getStateValuesKey().getValues()[0].mValue.int_value);
+ assertConditionTimer(it->second.conditionTimer, false, 20 * NS_PER_SEC,
+ bucketStartTimeNs + 30 * NS_PER_SEC);
+
+ // Value for key {{}, -1}
+ it++;
+ EXPECT_EQ(0, it->first.getDimensionKeyInWhat().getValues().size());
+ ASSERT_EQ(1, it->first.getStateValuesKey().getValues().size());
+ EXPECT_EQ(-1 /*StateTracker::kUnknown*/,
+ it->first.getStateValuesKey().getValues()[0].mValue.int_value);
+ assertConditionTimer(it->second.conditionTimer, false, 10 * NS_PER_SEC,
+ bucketStartTimeNs + 10 * NS_PER_SEC);
+
+ // Bucket status after battery saver mode ON event.
+ batterySaverOnEvent =
+ CreateBatterySaverOnEvent(/*timestamp=*/bucketStartTimeNs + 40 * NS_PER_SEC);
+ StateManager::getInstance().onLogEvent(*batterySaverOnEvent);
+
+ // Base for dimension key {}
+ ASSERT_EQ(1UL, valueProducer->mCurrentBaseInfo.size());
+ ASSERT_EQ(2UL, valueProducer->mCurrentSlicedBucket.size());
+ it = valueProducer->mCurrentSlicedBucket.begin();
+ itBase = valueProducer->mCurrentBaseInfo.find(it->first.getDimensionKeyInWhat());
+ ASSERT_EQ(1, itBase->second.currentState.getValues().size());
+ EXPECT_EQ(BatterySaverModeStateChanged::ON,
+ itBase->second.currentState.getValues()[0].mValue.int_value);
+ // Value for key {{}, ON}
+ EXPECT_EQ(0, it->first.getDimensionKeyInWhat().getValues().size());
+ ASSERT_EQ(1, it->first.getStateValuesKey().getValues().size());
+ EXPECT_EQ(BatterySaverModeStateChanged::ON,
+ it->first.getStateValuesKey().getValues()[0].mValue.int_value);
+ assertConditionTimer(it->second.conditionTimer, true, 20 * NS_PER_SEC,
+ bucketStartTimeNs + 40 * NS_PER_SEC);
+
+ // Value for key {{}, -1}
+ it++;
+ EXPECT_EQ(0, it->first.getDimensionKeyInWhat().getValues().size());
+ ASSERT_EQ(1, it->first.getStateValuesKey().getValues().size());
+ EXPECT_EQ(-1 /*StateTracker::kUnknown*/,
+ it->first.getStateValuesKey().getValues()[0].mValue.int_value);
+ assertConditionTimer(it->second.conditionTimer, false, 10 * NS_PER_SEC,
+ bucketStartTimeNs + 10 * NS_PER_SEC);
+
+ // Start dump report and check output.
+ ProtoOutputStream output;
+ std::set<string> strSet;
+ valueProducer->onDumpReport(bucketStartTimeNs + 50 * NS_PER_SEC,
+ true /* include recent buckets */, true, NO_TIME_CONSTRAINTS,
+ &strSet, &output);
+
+ StatsLogReport report = outputStreamToProto(&output);
+ EXPECT_TRUE(report.has_value_metrics());
+ ASSERT_EQ(2, report.value_metrics().data_size());
+
+ // {{}, kStateUnknown}
+ ValueMetricData data = report.value_metrics().data(0);
+ EXPECT_EQ(util::BATTERY_SAVER_MODE_STATE_CHANGED, data.slice_by_state(0).atom_id());
+ EXPECT_EQ(-1 /*StateTracker::kUnknown*/, data.slice_by_state(0).value());
+ ASSERT_EQ(1, data.bucket_info_size());
+ EXPECT_EQ(10 * NS_PER_SEC, data.bucket_info(0).condition_true_nanos());
+
+ // {{}, ON}
+ data = report.value_metrics().data(1);
+ EXPECT_EQ(util::BATTERY_SAVER_MODE_STATE_CHANGED, data.slice_by_state(0).atom_id());
+ EXPECT_EQ(BatterySaverModeStateChanged::ON, data.slice_by_state(0).value());
+ ASSERT_EQ(1, data.bucket_info_size());
+ EXPECT_EQ(30 * NS_PER_SEC, data.bucket_info(0).condition_true_nanos());
+}
+
+/*
+ * Test for metric that slices by state when data is not present in pulled data
+ * during an event and then a flush occurs for the current bucket. With the new
+ * condition timer behavior, a "new" MetricDimensionKey is inserted into
+ * `mCurrentSlicedBucket` before intervals are closed/added to that new
+ * MetricDimensionKey.
+ */
+TEST(ValueMetricProducerTest, TestSlicedStateWithMissingDataThenFlushBucket) {
+ // Set up ValueMetricProducer.
+ ValueMetric metric =
+ ValueMetricProducerTestHelper::createMetricWithState("BATTERY_SAVER_MODE_STATE");
+ sp<MockStatsPullerManager> pullerManager = new StrictMock<MockStatsPullerManager>();
+ /*
+ NOTE: "-" means that the data was not present in the pulled data.
+
+ bucket # 1
+ 10 20 30 40 50 60 (seconds)
+ |-------------------------------------------------------|--
+ - (kStateUnknown)
+
+ - (ON)
+ */
+ EXPECT_CALL(*pullerManager, Pull(tagId, kConfigKey, _, _))
+ // ValueMetricProducer initialized but data for dimension key {} is not present
+ // in the pulled data..
+ .WillOnce(Invoke([](int tagId, const ConfigKey&, const int64_t eventTimeNs,
+ vector<std::shared_ptr<LogEvent>>* data) {
+ EXPECT_EQ(eventTimeNs, bucketStartTimeNs);
+ data->clear();
+ return true;
+ }))
+ // Battery saver mode state changed to ON but data for dimension key {} is not present
+ // in the pulled data.
+ .WillOnce(Invoke([](int tagId, const ConfigKey&, const int64_t eventTimeNs,
+ vector<std::shared_ptr<LogEvent>>* data) {
+ EXPECT_EQ(eventTimeNs, bucketStartTimeNs + 10 * NS_PER_SEC);
+ data->clear();
+ return true;
+ }))
+ // Dump report pull.
+ .WillOnce(Invoke([](int tagId, const ConfigKey&, const int64_t eventTimeNs,
+ vector<std::shared_ptr<LogEvent>>* data) {
+ EXPECT_EQ(eventTimeNs, bucketStartTimeNs + 50 * NS_PER_SEC);
+ data->clear();
+ data->push_back(CreateRepeatedValueLogEvent(
+ tagId, bucketStartTimeNs + 50 * NS_PER_SEC, 15));
+ return true;
+ }));
+
+ StateManager::getInstance().clear();
+ sp<ValueMetricProducer> valueProducer =
+ ValueMetricProducerTestHelper::createValueProducerWithState(
+ pullerManager, metric, {util::BATTERY_SAVER_MODE_STATE_CHANGED}, {});
+ EXPECT_EQ(1, valueProducer->mSlicedStateAtoms.size());
+
+ // Set up StateManager and check that StateTrackers are initialized.
+ StateManager::getInstance().registerListener(util::BATTERY_SAVER_MODE_STATE_CHANGED,
+ valueProducer);
+ EXPECT_EQ(1, StateManager::getInstance().getStateTrackersCount());
+ EXPECT_EQ(1, StateManager::getInstance().getListenersCount(
+ util::BATTERY_SAVER_MODE_STATE_CHANGED));
+
+ // Bucket status after metric initialized.
+ ASSERT_EQ(0UL, valueProducer->mCurrentSlicedBucket.size());
+ ASSERT_EQ(0UL, valueProducer->mCurrentBaseInfo.size());
+
+ // Bucket status after battery saver mode ON event which is not present
+ // in the pulled data.
+ unique_ptr<LogEvent> batterySaverOnEvent =
+ CreateBatterySaverOnEvent(/*timestamp=*/bucketStartTimeNs + 10 * NS_PER_SEC);
+ StateManager::getInstance().onLogEvent(*batterySaverOnEvent);
+
+ ASSERT_EQ(0UL, valueProducer->mCurrentBaseInfo.size());
+ ASSERT_EQ(0UL, valueProducer->mCurrentSlicedBucket.size());
+
+ // Start dump report and check output.
+ ProtoOutputStream output;
+ std::set<string> strSet;
+ valueProducer->onDumpReport(bucketStartTimeNs + 50 * NS_PER_SEC,
+ true /* include recent buckets */, true, NO_TIME_CONSTRAINTS,
+ &strSet, &output);
+
+ StatsLogReport report = outputStreamToProto(&output);
+ EXPECT_TRUE(report.has_value_metrics());
+ ASSERT_EQ(0, report.value_metrics().data_size());
+ ASSERT_EQ(2UL, valueProducer->mCurrentSlicedBucket.size());
+ ASSERT_EQ(1UL, valueProducer->mCurrentBaseInfo.size());
+}
+
+TEST(ValueMetricProducerTest, TestSlicedStateWithNoPullOnBucketBoundary) {
+ // Set up ValueMetricProducer.
+ ValueMetric metric =
+ ValueMetricProducerTestHelper::createMetricWithState("BATTERY_SAVER_MODE_STATE");
+ sp<MockStatsPullerManager> pullerManager = new StrictMock<MockStatsPullerManager>();
+ /*
+ bucket # 1 bucket # 2
+ 10 20 30 40 50 60 70 80 90 100 110 120 (seconds)
+ |------------------------------------|---------------------------|--
+ x (kStateUnknown)
+ |-----|
+ 10
+ x x (ON)
+ |-----| |-----------|
+ 10 20
+ x (OFF)
+ |------------------------|
+ 40
+ */
+ EXPECT_CALL(*pullerManager, Pull(tagId, kConfigKey, _, _))
+ // ValueMetricProducer initialized.
+ .WillOnce(Invoke([](int tagId, const ConfigKey&, const int64_t eventTimeNs,
+ vector<std::shared_ptr<LogEvent>>* data) {
+ EXPECT_EQ(eventTimeNs, bucketStartTimeNs);
+ data->clear();
+ data->push_back(CreateRepeatedValueLogEvent(tagId, bucketStartTimeNs, 3));
+ return true;
+ }))
+ // Battery saver mode state changed to ON.
+ .WillOnce(Invoke([](int tagId, const ConfigKey&, const int64_t eventTimeNs,
+ vector<std::shared_ptr<LogEvent>>* data) {
+ EXPECT_EQ(eventTimeNs, bucketStartTimeNs + 10 * NS_PER_SEC);
+ data->clear();
+ data->push_back(
+ CreateRepeatedValueLogEvent(tagId, bucketStartTimeNs + 10 * NS_PER_SEC, 5));
+ return true;
+ }))
+ // Battery saver mode state changed to OFF.
+ .WillOnce(Invoke([](int tagId, const ConfigKey&, const int64_t eventTimeNs,
+ vector<std::shared_ptr<LogEvent>>* data) {
+ EXPECT_EQ(eventTimeNs, bucketStartTimeNs + 20 * NS_PER_SEC);
+ data->clear();
+ data->push_back(
+ CreateRepeatedValueLogEvent(tagId, bucketStartTimeNs + 20 * NS_PER_SEC, 7));
+ return true;
+ }))
+ // Battery saver mode state changed to ON.
+ .WillOnce(Invoke([](int tagId, const ConfigKey&, const int64_t eventTimeNs,
+ vector<std::shared_ptr<LogEvent>>* data) {
+ EXPECT_EQ(eventTimeNs, bucket2StartTimeNs + 30 * NS_PER_SEC);
+ data->clear();
+ data->push_back(CreateRepeatedValueLogEvent(
+ tagId, bucket2StartTimeNs + 30 * NS_PER_SEC, 10));
+ return true;
+ }))
+ // Dump report pull.
+ .WillOnce(Invoke([](int tagId, const ConfigKey&, const int64_t eventTimeNs,
+ vector<std::shared_ptr<LogEvent>>* data) {
+ EXPECT_EQ(eventTimeNs, bucket2StartTimeNs + 50 * NS_PER_SEC);
+ data->clear();
+ data->push_back(CreateRepeatedValueLogEvent(
+ tagId, bucket2StartTimeNs + 50 * NS_PER_SEC, 15));
+ return true;
+ }));
+
+ StateManager::getInstance().clear();
+ sp<ValueMetricProducer> valueProducer =
+ ValueMetricProducerTestHelper::createValueProducerWithState(
+ pullerManager, metric, {util::BATTERY_SAVER_MODE_STATE_CHANGED}, {});
+ EXPECT_EQ(1, valueProducer->mSlicedStateAtoms.size());
+
+ // Set up StateManager and check that StateTrackers are initialized.
+ StateManager::getInstance().registerListener(util::BATTERY_SAVER_MODE_STATE_CHANGED,
+ valueProducer);
+ EXPECT_EQ(1, StateManager::getInstance().getStateTrackersCount());
+ EXPECT_EQ(1, StateManager::getInstance().getListenersCount(
+ util::BATTERY_SAVER_MODE_STATE_CHANGED));
+
+ // Bucket status after metric initialized.
+ ASSERT_EQ(1UL, valueProducer->mCurrentSlicedBucket.size());
+ ASSERT_EQ(1UL, valueProducer->mCurrentBaseInfo.size());
+ auto it = valueProducer->mCurrentSlicedBucket.begin();
+ auto itBase = valueProducer->mCurrentBaseInfo.find(it->first.getDimensionKeyInWhat());
+ EXPECT_TRUE(itBase->second.hasCurrentState);
+ ASSERT_EQ(1, itBase->second.currentState.getValues().size());
+ EXPECT_EQ(-1 /* StateTracker::kStateUnknown */,
+ itBase->second.currentState.getValues()[0].mValue.int_value);
+ // Value for dimension, state key {{}, kStateUnknown}
+ EXPECT_EQ(0, it->first.getDimensionKeyInWhat().getValues().size());
+ ASSERT_EQ(1, it->first.getStateValuesKey().getValues().size());
+ EXPECT_EQ(-1 /* StateTracker::kStateUnknown */,
+ it->first.getStateValuesKey().getValues()[0].mValue.int_value);
+ assertConditionTimer(it->second.conditionTimer, true, 0, bucketStartTimeNs);
+
+ // Bucket status after battery saver mode ON event.
+ unique_ptr<LogEvent> batterySaverOnEvent =
+ CreateBatterySaverOnEvent(/*timestamp=*/bucketStartTimeNs + 10 * NS_PER_SEC);
+ StateManager::getInstance().onLogEvent(*batterySaverOnEvent);
+
+ ASSERT_EQ(1UL, valueProducer->mCurrentBaseInfo.size());
+ ASSERT_EQ(2UL, valueProducer->mCurrentSlicedBucket.size());
+ it = valueProducer->mCurrentSlicedBucket.begin();
+ itBase = valueProducer->mCurrentBaseInfo.find(it->first.getDimensionKeyInWhat());
+ ASSERT_EQ(1, itBase->second.currentState.getValues().size());
+ EXPECT_EQ(BatterySaverModeStateChanged::ON,
+ itBase->second.currentState.getValues()[0].mValue.int_value);
+ // Value for key {{}, ON}
+ EXPECT_EQ(0, it->first.getDimensionKeyInWhat().getValues().size());
+ ASSERT_EQ(1, it->first.getStateValuesKey().getValues().size());
+ EXPECT_EQ(BatterySaverModeStateChanged::ON,
+ it->first.getStateValuesKey().getValues()[0].mValue.int_value);
+ assertConditionTimer(it->second.conditionTimer, true, 0, bucketStartTimeNs + 10 * NS_PER_SEC);
+
+ // Value for key {{}, -1}
+ it++;
+ EXPECT_EQ(0, it->first.getDimensionKeyInWhat().getValues().size());
+ ASSERT_EQ(1, it->first.getStateValuesKey().getValues().size());
+ EXPECT_EQ(-1 /*StateTracker::kUnknown*/,
+ it->first.getStateValuesKey().getValues()[0].mValue.int_value);
+ assertConditionTimer(it->second.conditionTimer, false, 10 * NS_PER_SEC,
+ bucketStartTimeNs + 10 * NS_PER_SEC);
+
+ // Bucket status after battery saver mode OFF event.
+ unique_ptr<LogEvent> batterySaverOffEvent =
+ CreateBatterySaverOffEvent(/*timestamp=*/bucketStartTimeNs + 20 * NS_PER_SEC);
+ StateManager::getInstance().onLogEvent(*batterySaverOffEvent);
+
+ ASSERT_EQ(1UL, valueProducer->mCurrentBaseInfo.size());
+ ASSERT_EQ(3UL, valueProducer->mCurrentSlicedBucket.size());
+ it = valueProducer->mCurrentSlicedBucket.begin();
+ itBase = valueProducer->mCurrentBaseInfo.find(it->first.getDimensionKeyInWhat());
+ ASSERT_EQ(1, itBase->second.currentState.getValues().size());
+ EXPECT_EQ(BatterySaverModeStateChanged::OFF,
+ itBase->second.currentState.getValues()[0].mValue.int_value);
+ // Value for key {{}, OFF}
+ EXPECT_EQ(0, it->first.getDimensionKeyInWhat().getValues().size());
+ ASSERT_EQ(1, it->first.getStateValuesKey().getValues().size());
+ EXPECT_EQ(BatterySaverModeStateChanged::OFF,
+ it->first.getStateValuesKey().getValues()[0].mValue.int_value);
+ assertConditionTimer(it->second.conditionTimer, true, 0, bucketStartTimeNs + 20 * NS_PER_SEC);
+
+ // Value for key {{}, ON}
+ it++;
+ EXPECT_EQ(0, it->first.getDimensionKeyInWhat().getValues().size());
+ ASSERT_EQ(1, it->first.getStateValuesKey().getValues().size());
+ EXPECT_EQ(BatterySaverModeStateChanged::ON,
+ it->first.getStateValuesKey().getValues()[0].mValue.int_value);
+ assertConditionTimer(it->second.conditionTimer, false, 10 * NS_PER_SEC,
+ bucketStartTimeNs + 20 * NS_PER_SEC);
+
+ // Value for key {{}, -1}
+ it++;
+ EXPECT_EQ(0, it->first.getDimensionKeyInWhat().getValues().size());
+ ASSERT_EQ(1, it->first.getStateValuesKey().getValues().size());
+ EXPECT_EQ(-1 /*StateTracker::kUnknown*/,
+ it->first.getStateValuesKey().getValues()[0].mValue.int_value);
+ assertConditionTimer(it->second.conditionTimer, false, 10 * NS_PER_SEC,
+ bucketStartTimeNs + 10 * NS_PER_SEC);
+
+ // Bucket status after battery saver mode ON event.
+ batterySaverOnEvent =
+ CreateBatterySaverOnEvent(/*timestamp=*/bucket2StartTimeNs + 30 * NS_PER_SEC);
+ StateManager::getInstance().onLogEvent(*batterySaverOnEvent);
+
+ ASSERT_EQ(1UL, valueProducer->mCurrentBaseInfo.size());
+ ASSERT_EQ(3UL, valueProducer->mCurrentSlicedBucket.size());
+ it = valueProducer->mCurrentSlicedBucket.begin();
+ itBase = valueProducer->mCurrentBaseInfo.find(it->first.getDimensionKeyInWhat());
+ ASSERT_EQ(1, itBase->second.currentState.getValues().size());
+ EXPECT_EQ(BatterySaverModeStateChanged::ON,
+ itBase->second.currentState.getValues()[0].mValue.int_value);
+ // Value for key {{}, OFF}
+ EXPECT_EQ(0, it->first.getDimensionKeyInWhat().getValues().size());
+ ASSERT_EQ(1, it->first.getStateValuesKey().getValues().size());
+ EXPECT_EQ(BatterySaverModeStateChanged::OFF,
+ it->first.getStateValuesKey().getValues()[0].mValue.int_value);
+ assertConditionTimer(it->second.conditionTimer, false, 30 * NS_PER_SEC,
+ bucket2StartTimeNs + 30 * NS_PER_SEC);
+
+ // Value for key {{}, ON}
+ it++;
+ EXPECT_EQ(0, it->first.getDimensionKeyInWhat().getValues().size());
+ ASSERT_EQ(1, it->first.getStateValuesKey().getValues().size());
+ EXPECT_EQ(BatterySaverModeStateChanged::ON,
+ it->first.getStateValuesKey().getValues()[0].mValue.int_value);
+ assertConditionTimer(it->second.conditionTimer, true, 0, bucket2StartTimeNs + 30 * NS_PER_SEC);
+
+ // Value for key {{}, -1}
+ it++;
+ EXPECT_EQ(0, it->first.getDimensionKeyInWhat().getValues().size());
+ ASSERT_EQ(1, it->first.getStateValuesKey().getValues().size());
+ EXPECT_EQ(-1 /*StateTracker::kUnknown*/,
+ it->first.getStateValuesKey().getValues()[0].mValue.int_value);
+ assertConditionTimer(it->second.conditionTimer, false, 0, bucketStartTimeNs + 10 * NS_PER_SEC);
+
+ // Start dump report and check output.
+ ProtoOutputStream output;
+ std::set<string> strSet;
+ valueProducer->onDumpReport(bucket2StartTimeNs + 50 * NS_PER_SEC,
+ true /* include recent buckets */, true, NO_TIME_CONSTRAINTS,
+ &strSet, &output);
+
+ StatsLogReport report = outputStreamToProto(&output);
+ EXPECT_TRUE(report.has_value_metrics());
+ ASSERT_EQ(3, report.value_metrics().data_size());
+
+ // {{}, kStateUnknown}
+ ValueMetricData data = report.value_metrics().data(0);
+ EXPECT_EQ(util::BATTERY_SAVER_MODE_STATE_CHANGED, data.slice_by_state(0).atom_id());
+ EXPECT_EQ(-1 /*StateTracker::kUnknown*/, data.slice_by_state(0).value());
+ ASSERT_EQ(1, data.bucket_info_size());
+ EXPECT_EQ(10 * NS_PER_SEC, data.bucket_info(0).condition_true_nanos());
+
+ // {{}, ON}
+ data = report.value_metrics().data(1);
+ EXPECT_EQ(util::BATTERY_SAVER_MODE_STATE_CHANGED, data.slice_by_state(0).atom_id());
+ EXPECT_EQ(BatterySaverModeStateChanged::ON, data.slice_by_state(0).value());
+ ASSERT_EQ(2, data.bucket_info_size());
+ EXPECT_EQ(10 * NS_PER_SEC, data.bucket_info(0).condition_true_nanos());
+ EXPECT_EQ(20 * NS_PER_SEC, data.bucket_info(1).condition_true_nanos());
+
+ // {{}, OFF}
+ data = report.value_metrics().data(2);
+ EXPECT_EQ(util::BATTERY_SAVER_MODE_STATE_CHANGED, data.slice_by_state(0).atom_id());
+ EXPECT_EQ(BatterySaverModeStateChanged::OFF, data.slice_by_state(0).value());
+ ASSERT_EQ(1, data.bucket_info_size());
+ EXPECT_EQ(40 * NS_PER_SEC, data.bucket_info(0).condition_true_nanos());
+}
+
+/*
+ * Test slicing condition_true_nanos by state for metric that slices by state when data is not
+ * present in pulled data during a condition change.
+ */
+TEST(ValueMetricProducerTest, TestSlicedStateWithDataMissingInConditionChange) {
+ // Set up ValueMetricProducer.
+ ValueMetric metric = ValueMetricProducerTestHelper::createMetricWithConditionAndState(
+ "BATTERY_SAVER_MODE_STATE");
+ sp<MockStatsPullerManager> pullerManager = new StrictMock<MockStatsPullerManager>();
+ /*
+ NOTE: "-" means that the data was not present in the pulled data.
+
+ bucket # 1
+ 10 20 30 40 50 60 (seconds)
+ |-------------------------------------------------------|--
+
+ T F T (Condition)
+ x (ON)
+ |----------------------| -
+ 20
+ */
+ EXPECT_CALL(*pullerManager, Pull(tagId, kConfigKey, _, _))
+ // Battery saver mode state changed to ON.
+ .WillOnce(Invoke([](int tagId, const ConfigKey&, const int64_t eventTimeNs,
+ vector<std::shared_ptr<LogEvent>>* data) {
+ EXPECT_EQ(eventTimeNs, bucketStartTimeNs + 10 * NS_PER_SEC);
+ data->clear();
+ data->push_back(
+ CreateRepeatedValueLogEvent(tagId, bucketStartTimeNs + 10 * NS_PER_SEC, 3));
+ return true;
+ }))
+ // Condition changed to false.
+ .WillOnce(Invoke([](int tagId, const ConfigKey&, const int64_t eventTimeNs,
+ vector<std::shared_ptr<LogEvent>>* data) {
+ EXPECT_EQ(eventTimeNs, bucketStartTimeNs + 30 * NS_PER_SEC);
+ data->clear();
+ data->push_back(
+ CreateRepeatedValueLogEvent(tagId, bucketStartTimeNs + 30 * NS_PER_SEC, 5));
+ return true;
+ }))
+ // Condition changed to true but data for dimension key {} is not present in the
+ // pulled data.
+ .WillOnce(Invoke([](int tagId, const ConfigKey&, const int64_t eventTimeNs,
+ vector<std::shared_ptr<LogEvent>>* data) {
+ EXPECT_EQ(eventTimeNs, bucketStartTimeNs + 40 * NS_PER_SEC);
+ data->clear();
+ return true;
+ }))
+ // Dump report pull.
+ .WillOnce(Invoke([](int tagId, const ConfigKey&, const int64_t eventTimeNs,
+ vector<std::shared_ptr<LogEvent>>* data) {
+ EXPECT_EQ(eventTimeNs, bucketStartTimeNs + 50 * NS_PER_SEC);
+ data->clear();
+ data->push_back(CreateRepeatedValueLogEvent(
+ tagId, bucketStartTimeNs + 50 * NS_PER_SEC, 20));
+ return true;
+ }));
+
+ StateManager::getInstance().clear();
+ sp<ValueMetricProducer> valueProducer =
+ ValueMetricProducerTestHelper::createValueProducerWithConditionAndState(
+ pullerManager, metric, {util::BATTERY_SAVER_MODE_STATE_CHANGED}, {},
+ ConditionState::kTrue);
+ EXPECT_EQ(1, valueProducer->mSlicedStateAtoms.size());
+
+ // Set up StateManager and check that StateTrackers are initialized.
+ StateManager::getInstance().registerListener(util::BATTERY_SAVER_MODE_STATE_CHANGED,
+ valueProducer);
+ EXPECT_EQ(1, StateManager::getInstance().getStateTrackersCount());
+ EXPECT_EQ(1, StateManager::getInstance().getListenersCount(
+ util::BATTERY_SAVER_MODE_STATE_CHANGED));
+
+ // Bucket status after battery saver mode ON event.
+ unique_ptr<LogEvent> batterySaverOnEvent =
+ CreateBatterySaverOnEvent(/*timestamp=*/bucketStartTimeNs + 10 * NS_PER_SEC);
+ StateManager::getInstance().onLogEvent(*batterySaverOnEvent);
+ // Base for dimension key {}
+ ASSERT_EQ(1UL, valueProducer->mCurrentBaseInfo.size());
+ ASSERT_EQ(2UL, valueProducer->mCurrentSlicedBucket.size());
+ auto it = valueProducer->mCurrentSlicedBucket.begin();
+ auto itBase = valueProducer->mCurrentBaseInfo.find(it->first.getDimensionKeyInWhat());
+ EXPECT_TRUE(itBase->second.hasCurrentState);
+ ASSERT_EQ(1, itBase->second.currentState.getValues().size());
+ EXPECT_EQ(BatterySaverModeStateChanged::ON,
+ itBase->second.currentState.getValues()[0].mValue.int_value);
+ // Value for key {{}, ON}
+ EXPECT_EQ(0, it->first.getDimensionKeyInWhat().getValues().size());
+ ASSERT_EQ(1, it->first.getStateValuesKey().getValues().size());
+ EXPECT_EQ(BatterySaverModeStateChanged::ON,
+ it->first.getStateValuesKey().getValues()[0].mValue.int_value);
+ assertConditionTimer(it->second.conditionTimer, true, 0, bucketStartTimeNs + 10 * NS_PER_SEC);
+
+ // Value for key {{}, -1}
+ it++;
+ EXPECT_EQ(0, it->first.getDimensionKeyInWhat().getValues().size());
+ ASSERT_EQ(1, it->first.getStateValuesKey().getValues().size());
+ EXPECT_EQ(-1 /*StateTracker::kUnknown*/,
+ it->first.getStateValuesKey().getValues()[0].mValue.int_value);
+ assertConditionTimer(it->second.conditionTimer, false, 0, 0);
+
+ // Bucket status after condition change to false.
+ valueProducer->onConditionChanged(false, bucketStartTimeNs + 30 * NS_PER_SEC);
+ // Base for dimension key {}
+ ASSERT_EQ(1UL, valueProducer->mCurrentBaseInfo.size());
+ ASSERT_EQ(2UL, valueProducer->mCurrentSlicedBucket.size());
+ it = valueProducer->mCurrentSlicedBucket.begin();
+ itBase = valueProducer->mCurrentBaseInfo.find(it->first.getDimensionKeyInWhat());
+ EXPECT_TRUE(itBase->second.hasCurrentState);
+ ASSERT_EQ(1, itBase->second.currentState.getValues().size());
+ EXPECT_EQ(BatterySaverModeStateChanged::ON,
+ itBase->second.currentState.getValues()[0].mValue.int_value);
+ // Value for key {{}, ON}
+ EXPECT_EQ(0, it->first.getDimensionKeyInWhat().getValues().size());
+ ASSERT_EQ(1, it->first.getStateValuesKey().getValues().size());
+ EXPECT_EQ(BatterySaverModeStateChanged::ON,
+ it->first.getStateValuesKey().getValues()[0].mValue.int_value);
+ assertConditionTimer(it->second.conditionTimer, false, 20 * NS_PER_SEC,
+ bucketStartTimeNs + 30 * NS_PER_SEC);
+
+ // Value for key {{}, -1}
+ it++;
+ EXPECT_EQ(0, it->first.getDimensionKeyInWhat().getValues().size());
+ ASSERT_EQ(1, it->first.getStateValuesKey().getValues().size());
+ EXPECT_EQ(-1 /*StateTracker::kUnknown*/,
+ it->first.getStateValuesKey().getValues()[0].mValue.int_value);
+ assertConditionTimer(it->second.conditionTimer, false, 0, 0);
+
+ // Bucket status after condition change to true.
+ valueProducer->onConditionChanged(true, bucketStartTimeNs + 40 * NS_PER_SEC);
+ // Base for dimension key {}
+ ASSERT_EQ(1UL, valueProducer->mCurrentBaseInfo.size());
+ ASSERT_EQ(2UL, valueProducer->mCurrentSlicedBucket.size());
+ it = valueProducer->mCurrentSlicedBucket.begin();
+ itBase = valueProducer->mCurrentBaseInfo.find(it->first.getDimensionKeyInWhat());
+ EXPECT_FALSE(itBase->second.hasCurrentState);
+ ASSERT_EQ(1, itBase->second.currentState.getValues().size());
+ EXPECT_EQ(BatterySaverModeStateChanged::ON,
+ itBase->second.currentState.getValues()[0].mValue.int_value);
+ // Value for key {{}, ON}
+ EXPECT_EQ(0, it->first.getDimensionKeyInWhat().getValues().size());
+ ASSERT_EQ(1, it->first.getStateValuesKey().getValues().size());
+ EXPECT_EQ(BatterySaverModeStateChanged::ON,
+ it->first.getStateValuesKey().getValues()[0].mValue.int_value);
+ assertConditionTimer(it->second.conditionTimer, false, 20 * NS_PER_SEC,
+ bucketStartTimeNs + 30 * NS_PER_SEC);
+
+ // Value for key {{}, -1}
+ it++;
+ EXPECT_EQ(0, it->first.getDimensionKeyInWhat().getValues().size());
+ ASSERT_EQ(1, it->first.getStateValuesKey().getValues().size());
+ EXPECT_EQ(-1 /*StateTracker::kUnknown*/,
+ it->first.getStateValuesKey().getValues()[0].mValue.int_value);
+ assertConditionTimer(it->second.conditionTimer, false, 0, 0);
+
+ // Start dump report and check output.
+ ProtoOutputStream output;
+ std::set<string> strSet;
+ valueProducer->onDumpReport(bucketStartTimeNs + 50 * NS_PER_SEC,
+ true /* include recent buckets */, true, NO_TIME_CONSTRAINTS,
+ &strSet, &output);
+
+ StatsLogReport report = outputStreamToProto(&output);
+ EXPECT_TRUE(report.has_value_metrics());
+ ASSERT_EQ(1, report.value_metrics().data_size());
+
+ // {{}, ON}
+ ValueMetricData data = report.value_metrics().data(0);
+ EXPECT_EQ(util::BATTERY_SAVER_MODE_STATE_CHANGED, data.slice_by_state(0).atom_id());
+ EXPECT_EQ(BatterySaverModeStateChanged::ON, data.slice_by_state(0).value());
+ ASSERT_EQ(1, data.bucket_info_size());
+ EXPECT_EQ(20 * NS_PER_SEC, data.bucket_info(0).condition_true_nanos());
+}
+
+/*
+ * Test slicing condition_true_nanos by state for metric that slices by state with a primary field,
+ * condition, and has multiple dimensions.
+ */
+TEST(ValueMetricProducerTest, TestSlicedStateWithMultipleDimensions) {
+ // Set up ValueMetricProducer.
+ ValueMetric metric =
+ ValueMetricProducerTestHelper::createMetricWithConditionAndState("UID_PROCESS_STATE");
+ metric.mutable_dimensions_in_what()->set_field(tagId);
+ metric.mutable_dimensions_in_what()->add_child()->set_field(1);
+ metric.mutable_dimensions_in_what()->add_child()->set_field(3);
+
+ MetricStateLink* stateLink = metric.add_state_link();
+ stateLink->set_state_atom_id(UID_PROCESS_STATE_ATOM_ID);
+ auto fieldsInWhat = stateLink->mutable_fields_in_what();
+ *fieldsInWhat = CreateDimensions(tagId, {1 /* uid */});
+ auto fieldsInState = stateLink->mutable_fields_in_state();
+ *fieldsInState = CreateDimensions(UID_PROCESS_STATE_ATOM_ID, {1 /* uid */});
+
+ /*
+ bucket # 1 bucket # 2
+ 10 20 30 40 50 60 70 80 90 100 110 120 (seconds)
+ |------------------------------------------|---------------------------------|--
+
+ T F T (Condition)
+ (FOREGROUND)
+ x {1, 14}
+ |------|
+ 10
+
+ x {1, 16}
+ |------|
+ 10
+ x {2, 8}
+ |-------------|
+ 20
+
+ (BACKGROUND)
+ x {1, 14}
+ |-------------| |----------|---------------------------------|
+ 20 15 50
+
+ x {1, 16}
+ |-------------| |----------|---------------------------------|
+ 20 15 50
+
+ x {2, 8}
+ |----------| |----------|-------------------|
+ 15 15 30
+ */
+ sp<MockStatsPullerManager> pullerManager = new StrictMock<MockStatsPullerManager>();
+ EXPECT_CALL(*pullerManager, Pull(tagId, kConfigKey, _, _))
+ // Uid 1 process state change from kStateUnknown -> Foreground
+ .WillOnce(Invoke([](int tagId, const ConfigKey&, const int64_t eventTimeNs,
+ vector<std::shared_ptr<LogEvent>>* data) {
+ EXPECT_EQ(eventTimeNs, bucketStartTimeNs + 10 * NS_PER_SEC);
+ data->clear();
+ data->push_back(CreateThreeValueLogEvent(tagId, bucketStartTimeNs + 10 * NS_PER_SEC,
+ 1 /*uid*/, 3, 14 /*tag*/));
+ data->push_back(CreateThreeValueLogEvent(tagId, bucketStartTimeNs + 10 * NS_PER_SEC,
+ 1 /*uid*/, 3, 16 /*tag*/));
+
+ // This event should be skipped.
+ data->push_back(CreateThreeValueLogEvent(tagId, bucketStartTimeNs + 10 * NS_PER_SEC,
+ 2 /*uid*/, 5, 8 /*tag*/));
+ return true;
+ }))
+ // Uid 1 process state change from Foreground -> Background
+ .WillOnce(Invoke([](int tagId, const ConfigKey&, const int64_t eventTimeNs,
+ vector<std::shared_ptr<LogEvent>>* data) {
+ EXPECT_EQ(eventTimeNs, bucketStartTimeNs + 20 * NS_PER_SEC);
+ data->clear();
+ data->push_back(CreateThreeValueLogEvent(tagId, bucketStartTimeNs + 20 * NS_PER_SEC,
+ 1 /*uid*/, 5, 14 /*tag*/));
+ data->push_back(CreateThreeValueLogEvent(tagId, bucketStartTimeNs + 20 * NS_PER_SEC,
+ 1 /*uid*/, 5, 16 /*tag*/));
+
+ // This event should be skipped.
+ data->push_back(CreateThreeValueLogEvent(tagId, bucketStartTimeNs + 20 * NS_PER_SEC,
+ 2 /*uid*/, 7, 8 /*tag*/));
+
+ return true;
+ }))
+ // Uid 2 process state change from kStateUnknown -> Background
+ .WillOnce(Invoke([](int tagId, const ConfigKey&, const int64_t eventTimeNs,
+ vector<std::shared_ptr<LogEvent>>* data) {
+ EXPECT_EQ(eventTimeNs, bucketStartTimeNs + 25 * NS_PER_SEC);
+ data->clear();
+ data->push_back(CreateThreeValueLogEvent(tagId, bucketStartTimeNs + 25 * NS_PER_SEC,
+ 2 /*uid*/, 9, 8 /*tag*/));
+
+ // This event should be skipped.
+ data->push_back(CreateThreeValueLogEvent(tagId, bucketStartTimeNs + 25 * NS_PER_SEC,
+ 1 /*uid*/, 9, 14 /* tag */));
+
+ // This event should be skipped.
+ data->push_back(CreateThreeValueLogEvent(tagId, bucketStartTimeNs + 25 * NS_PER_SEC,
+ 1 /*uid*/, 9, 16 /* tag */));
+
+ return true;
+ }))
+ // Condition changed to false.
+ .WillOnce(Invoke([](int tagId, const ConfigKey&, const int64_t eventTimeNs,
+ vector<std::shared_ptr<LogEvent>>* data) {
+ EXPECT_EQ(eventTimeNs, bucketStartTimeNs + 40 * NS_PER_SEC);
+ data->clear();
+ data->push_back(CreateThreeValueLogEvent(tagId, bucketStartTimeNs + 40 * NS_PER_SEC,
+ 1 /*uid*/, 11, 14 /* tag */));
+ data->push_back(CreateThreeValueLogEvent(tagId, bucketStartTimeNs + 40 * NS_PER_SEC,
+ 1 /*uid*/, 11, 16 /* tag */));
+ data->push_back(CreateThreeValueLogEvent(tagId, bucketStartTimeNs + 40 * NS_PER_SEC,
+ 2 /*uid*/, 11, 8 /*tag*/));
+
+ return true;
+ }))
+ // Condition changed to true.
+ .WillOnce(Invoke([](int tagId, const ConfigKey&, const int64_t eventTimeNs,
+ vector<std::shared_ptr<LogEvent>>* data) {
+ EXPECT_EQ(eventTimeNs, bucketStartTimeNs + 45 * NS_PER_SEC);
+ data->clear();
+ data->push_back(CreateThreeValueLogEvent(tagId, bucketStartTimeNs + 45 * NS_PER_SEC,
+ 1 /*uid*/, 13, 14 /* tag */));
+ data->push_back(CreateThreeValueLogEvent(tagId, bucketStartTimeNs + 45 * NS_PER_SEC,
+ 1 /*uid*/, 13, 16 /* tag */));
+ data->push_back(CreateThreeValueLogEvent(tagId, bucketStartTimeNs + 45 * NS_PER_SEC,
+ 2 /*uid*/, 13, 8 /*tag*/));
+ return true;
+ }))
+ // Uid 2 process state change from Background -> Foreground
+ .WillOnce(Invoke([](int tagId, const ConfigKey&, const int64_t eventTimeNs,
+ vector<std::shared_ptr<LogEvent>>* data) {
+ EXPECT_EQ(eventTimeNs, bucket2StartTimeNs + 30 * NS_PER_SEC);
+ data->clear();
+ data->push_back(CreateThreeValueLogEvent(
+ tagId, bucket2StartTimeNs + 30 * NS_PER_SEC, 2 /*uid*/, 18, 8 /*tag*/));
+
+ // This event should be skipped.
+ data->push_back(CreateThreeValueLogEvent(
+ tagId, bucket2StartTimeNs + 30 * NS_PER_SEC, 1 /*uid*/, 18, 14 /* tag */));
+ // This event should be skipped.
+ data->push_back(CreateThreeValueLogEvent(
+ tagId, bucket2StartTimeNs + 30 * NS_PER_SEC, 1 /*uid*/, 18, 16 /* tag */));
+
+ return true;
+ }))
+ // Dump report pull.
+ .WillOnce(Invoke([](int tagId, const ConfigKey&, const int64_t eventTimeNs,
+ vector<std::shared_ptr<LogEvent>>* data) {
+ EXPECT_EQ(eventTimeNs, bucket2StartTimeNs + 50 * NS_PER_SEC);
+ data->clear();
+ data->push_back(CreateThreeValueLogEvent(
+ tagId, bucket2StartTimeNs + 50 * NS_PER_SEC, 1 /*uid*/, 21, 14 /* tag */));
+ data->push_back(CreateThreeValueLogEvent(
+ tagId, bucket2StartTimeNs + 50 * NS_PER_SEC, 1 /*uid*/, 21, 16 /* tag */));
+ data->push_back(CreateThreeValueLogEvent(
+ tagId, bucket2StartTimeNs + 50 * NS_PER_SEC, 2 /*uid*/, 21, 8 /*tag*/));
+ return true;
+ }));
+
+ StateManager::getInstance().clear();
+ sp<ValueMetricProducer> valueProducer =
+ ValueMetricProducerTestHelper::createValueProducerWithConditionAndState(
+ pullerManager, metric, {UID_PROCESS_STATE_ATOM_ID}, {}, ConditionState::kTrue);
+ EXPECT_EQ(1, valueProducer->mSlicedStateAtoms.size());
+
+ // Set up StateManager and check that StateTrackers are initialized.
+ StateManager::getInstance().registerListener(UID_PROCESS_STATE_ATOM_ID, valueProducer);
+ EXPECT_EQ(1, StateManager::getInstance().getStateTrackersCount());
+ EXPECT_EQ(1, StateManager::getInstance().getListenersCount(UID_PROCESS_STATE_ATOM_ID));
+
+ // Condition is true.
+ // Bucket status after uid 1 process state change kStateUnknown -> Foreground.
+ auto uidProcessEvent =
+ CreateUidProcessStateChangedEvent(bucketStartTimeNs + 10 * NS_PER_SEC, 1 /* uid */,
+ android::app::PROCESS_STATE_IMPORTANT_FOREGROUND);
+ StateManager::getInstance().onLogEvent(*uidProcessEvent);
+ ASSERT_EQ(2UL, valueProducer->mCurrentBaseInfo.size());
+ ASSERT_EQ(4UL, valueProducer->mCurrentSlicedBucket.size());
+ // Base for dimension {uid 1, tag 16}.
+ auto it = valueProducer->mCurrentSlicedBucket.begin();
+ auto itBase = valueProducer->mCurrentBaseInfo.find(it->first.getDimensionKeyInWhat());
+ ASSERT_EQ(1, itBase->second.currentState.getValues().size());
+ EXPECT_EQ(android::app::PROCESS_STATE_IMPORTANT_FOREGROUND,
+ itBase->second.currentState.getValues()[0].mValue.int_value);
+ // Value for key {{uid 1, uid 16}, FOREGROUND}.
+ ASSERT_EQ(2, it->first.getDimensionKeyInWhat().getValues().size());
+ EXPECT_EQ(1, it->first.getDimensionKeyInWhat().getValues()[0].mValue.int_value);
+ EXPECT_EQ(16, it->first.getDimensionKeyInWhat().getValues()[1].mValue.int_value);
+ ASSERT_EQ(1, it->first.getStateValuesKey().getValues().size());
+ EXPECT_EQ(android::app::PROCESS_STATE_IMPORTANT_FOREGROUND,
+ it->first.getStateValuesKey().getValues()[0].mValue.int_value);
+ assertConditionTimer(it->second.conditionTimer, true, 0, bucketStartTimeNs + 10 * NS_PER_SEC);
+ // Value for key {{uid 1, tag 16}, kStateUnknown}.
+ it++;
+ ASSERT_EQ(2, it->first.getDimensionKeyInWhat().getValues().size());
+ EXPECT_EQ(1, it->first.getDimensionKeyInWhat().getValues()[0].mValue.int_value);
+ EXPECT_EQ(16, it->first.getDimensionKeyInWhat().getValues()[1].mValue.int_value);
+ ASSERT_EQ(1, it->first.getStateValuesKey().getValues().size());
+ EXPECT_EQ(-1 /* StateTracker::kStateUnknown */,
+ it->first.getStateValuesKey().getValues()[0].mValue.int_value);
+ assertConditionTimer(it->second.conditionTimer, false, 0, 0);
+
+ // Base for dimension key {uid 1, tag 14}.
+ it++;
+ itBase = valueProducer->mCurrentBaseInfo.find(it->first.getDimensionKeyInWhat());
+ ASSERT_EQ(1, itBase->second.currentState.getValues().size());
+ EXPECT_EQ(android::app::PROCESS_STATE_IMPORTANT_FOREGROUND,
+ itBase->second.currentState.getValues()[0].mValue.int_value);
+ // Value for key {{uid 1, tag 14}, FOREGROUND}.
+ ASSERT_EQ(2, it->first.getDimensionKeyInWhat().getValues().size());
+ EXPECT_EQ(1, it->first.getDimensionKeyInWhat().getValues()[0].mValue.int_value);
+ EXPECT_EQ(14, it->first.getDimensionKeyInWhat().getValues()[1].mValue.int_value);
+ ASSERT_EQ(1, it->first.getStateValuesKey().getValues().size());
+ EXPECT_EQ(android::app::PROCESS_STATE_IMPORTANT_FOREGROUND,
+ it->first.getStateValuesKey().getValues()[0].mValue.int_value);
+ assertConditionTimer(it->second.conditionTimer, true, 0, bucketStartTimeNs + 10 * NS_PER_SEC);
+ // Value for key {{uid 1, tag 14}, kStateUnknown}.
+ it++;
+ ASSERT_EQ(2, it->first.getDimensionKeyInWhat().getValues().size());
+ EXPECT_EQ(1, it->first.getDimensionKeyInWhat().getValues()[0].mValue.int_value);
+ EXPECT_EQ(14, it->first.getDimensionKeyInWhat().getValues()[1].mValue.int_value);
+ ASSERT_EQ(1, it->first.getStateValuesKey().getValues().size());
+ EXPECT_EQ(-1 /* StateTracker::kStateUnknown */,
+ it->first.getStateValuesKey().getValues()[0].mValue.int_value);
+ assertConditionTimer(it->second.conditionTimer, false, 0, 0);
+
+ // Bucket status after uid 1 process state change Foreground -> Background.
+ uidProcessEvent =
+ CreateUidProcessStateChangedEvent(bucketStartTimeNs + 20 * NS_PER_SEC, 1 /* uid */,
+ android::app::PROCESS_STATE_IMPORTANT_BACKGROUND);
+ StateManager::getInstance().onLogEvent(*uidProcessEvent);
+ ASSERT_EQ(2UL, valueProducer->mCurrentBaseInfo.size());
+ ASSERT_EQ(6UL, valueProducer->mCurrentSlicedBucket.size());
+ // Base for dimension {uid 1, tag 16}.
+ it = valueProducer->mCurrentSlicedBucket.begin();
+ itBase = valueProducer->mCurrentBaseInfo.find(it->first.getDimensionKeyInWhat());
+ ASSERT_EQ(1, itBase->second.currentState.getValues().size());
+ EXPECT_EQ(android::app::PROCESS_STATE_IMPORTANT_BACKGROUND,
+ itBase->second.currentState.getValues()[0].mValue.int_value);
+ // Value for key {{uid 1, uid 16}, BACKGROUND}.
+ ASSERT_EQ(2, it->first.getDimensionKeyInWhat().getValues().size());
+ EXPECT_EQ(1, it->first.getDimensionKeyInWhat().getValues()[0].mValue.int_value);
+ EXPECT_EQ(16, it->first.getDimensionKeyInWhat().getValues()[1].mValue.int_value);
+ ASSERT_EQ(1, it->first.getStateValuesKey().getValues().size());
+ EXPECT_EQ(android::app::PROCESS_STATE_IMPORTANT_BACKGROUND,
+ it->first.getStateValuesKey().getValues()[0].mValue.int_value);
+ assertConditionTimer(it->second.conditionTimer, true, 0, bucketStartTimeNs + 20 * NS_PER_SEC);
+
+ // Base for dimension key {uid 1, tag 14}.
+ it++;
+ itBase = valueProducer->mCurrentBaseInfo.find(it->first.getDimensionKeyInWhat());
+ ASSERT_EQ(1, itBase->second.currentState.getValues().size());
+ EXPECT_EQ(android::app::PROCESS_STATE_IMPORTANT_BACKGROUND,
+ itBase->second.currentState.getValues()[0].mValue.int_value);
+ // Value for key {{uid 1, tag 14}, BACKGROUND}.
+ ASSERT_EQ(2, it->first.getDimensionKeyInWhat().getValues().size());
+ EXPECT_EQ(1, it->first.getDimensionKeyInWhat().getValues()[0].mValue.int_value);
+ EXPECT_EQ(14, it->first.getDimensionKeyInWhat().getValues()[1].mValue.int_value);
+ ASSERT_EQ(1, it->first.getStateValuesKey().getValues().size());
+ EXPECT_EQ(android::app::PROCESS_STATE_IMPORTANT_BACKGROUND,
+ it->first.getStateValuesKey().getValues()[0].mValue.int_value);
+ assertConditionTimer(it->second.conditionTimer, true, 0, bucketStartTimeNs + 20 * NS_PER_SEC);
+
+ // Value for key {{uid 1, uid 16}, FOREGROUND}.
+ it++;
+ ASSERT_EQ(2, it->first.getDimensionKeyInWhat().getValues().size());
+ EXPECT_EQ(1, it->first.getDimensionKeyInWhat().getValues()[0].mValue.int_value);
+ EXPECT_EQ(16, it->first.getDimensionKeyInWhat().getValues()[1].mValue.int_value);
+ ASSERT_EQ(1, it->first.getStateValuesKey().getValues().size());
+ EXPECT_EQ(android::app::PROCESS_STATE_IMPORTANT_FOREGROUND,
+ it->first.getStateValuesKey().getValues()[0].mValue.int_value);
+ assertConditionTimer(it->second.conditionTimer, false, 10 * NS_PER_SEC,
+ bucketStartTimeNs + 20 * NS_PER_SEC);
+
+ // Value for key {{uid 1, tag 16}, kStateUnknown}.
+ it++;
+ ASSERT_EQ(2, it->first.getDimensionKeyInWhat().getValues().size());
+ EXPECT_EQ(1, it->first.getDimensionKeyInWhat().getValues()[0].mValue.int_value);
+ EXPECT_EQ(16, it->first.getDimensionKeyInWhat().getValues()[1].mValue.int_value);
+ ASSERT_EQ(1, it->first.getStateValuesKey().getValues().size());
+ EXPECT_EQ(-1 /* StateTracker::kStateUnknown */,
+ it->first.getStateValuesKey().getValues()[0].mValue.int_value);
+ assertConditionTimer(it->second.conditionTimer, false, 0, 0);
+
+ // Value for key {{uid 1, tag 14}, FOREGROUND}.
+ it++;
+ ASSERT_EQ(2, it->first.getDimensionKeyInWhat().getValues().size());
+ EXPECT_EQ(1, it->first.getDimensionKeyInWhat().getValues()[0].mValue.int_value);
+ EXPECT_EQ(14, it->first.getDimensionKeyInWhat().getValues()[1].mValue.int_value);
+ ASSERT_EQ(1, it->first.getStateValuesKey().getValues().size());
+ EXPECT_EQ(android::app::PROCESS_STATE_IMPORTANT_FOREGROUND,
+ it->first.getStateValuesKey().getValues()[0].mValue.int_value);
+ assertConditionTimer(it->second.conditionTimer, false, 10 * NS_PER_SEC,
+ bucketStartTimeNs + 20 * NS_PER_SEC);
+
+ // Value for key {{uid 1, tag 14}, kStateUnknown}.
+ it++;
+ ASSERT_EQ(2, it->first.getDimensionKeyInWhat().getValues().size());
+ EXPECT_EQ(1, it->first.getDimensionKeyInWhat().getValues()[0].mValue.int_value);
+ EXPECT_EQ(14, it->first.getDimensionKeyInWhat().getValues()[1].mValue.int_value);
+ ASSERT_EQ(1, it->first.getStateValuesKey().getValues().size());
+ EXPECT_EQ(-1 /* StateTracker::kStateUnknown */,
+ it->first.getStateValuesKey().getValues()[0].mValue.int_value);
+ assertConditionTimer(it->second.conditionTimer, false, 0, 0);
+
+ // Bucket status after uid 2 process state change kStateUnknown -> Background.
+ uidProcessEvent =
+ CreateUidProcessStateChangedEvent(bucketStartTimeNs + 25 * NS_PER_SEC, 2 /* uid */,
+ android::app::PROCESS_STATE_IMPORTANT_BACKGROUND);
+ StateManager::getInstance().onLogEvent(*uidProcessEvent);
+ ASSERT_EQ(3UL, valueProducer->mCurrentBaseInfo.size());
+ ASSERT_EQ(8UL, valueProducer->mCurrentSlicedBucket.size());
+ // Base for dimension {uid 2, tag 8}.
+ it = valueProducer->mCurrentSlicedBucket.begin();
+ itBase = valueProducer->mCurrentBaseInfo.find(it->first.getDimensionKeyInWhat());
+ ASSERT_EQ(1, itBase->second.currentState.getValues().size());
+ EXPECT_EQ(android::app::PROCESS_STATE_IMPORTANT_BACKGROUND,
+ itBase->second.currentState.getValues()[0].mValue.int_value);
+ // Value for key {{uid 2, uid 8}, BACKGROUND}.
+ ASSERT_EQ(2, it->first.getDimensionKeyInWhat().getValues().size());
+ EXPECT_EQ(2, it->first.getDimensionKeyInWhat().getValues()[0].mValue.int_value);
+ EXPECT_EQ(8, it->first.getDimensionKeyInWhat().getValues()[1].mValue.int_value);
+ ASSERT_EQ(1, it->first.getStateValuesKey().getValues().size());
+ EXPECT_EQ(android::app::PROCESS_STATE_IMPORTANT_BACKGROUND,
+ it->first.getStateValuesKey().getValues()[0].mValue.int_value);
+ assertConditionTimer(it->second.conditionTimer, true, 0, bucketStartTimeNs + 25 * NS_PER_SEC);
+
+ // Value for key {{uid 2, uid 8}, kStateUnknown}.
+ it++;
+ ASSERT_EQ(2, it->first.getDimensionKeyInWhat().getValues().size());
+ EXPECT_EQ(2, it->first.getDimensionKeyInWhat().getValues()[0].mValue.int_value);
+ EXPECT_EQ(8, it->first.getDimensionKeyInWhat().getValues()[1].mValue.int_value);
+ ASSERT_EQ(1, it->first.getStateValuesKey().getValues().size());
+ EXPECT_EQ(-1 /* StateTracker::kStateUnknown */,
+ it->first.getStateValuesKey().getValues()[0].mValue.int_value);
+ assertConditionTimer(it->second.conditionTimer, false, 0, 0);
+
+ // Base for dimension {uid 1, tag 16}.
+ it++;
+ itBase = valueProducer->mCurrentBaseInfo.find(it->first.getDimensionKeyInWhat());
+ ASSERT_EQ(1, itBase->second.currentState.getValues().size());
+ EXPECT_EQ(android::app::PROCESS_STATE_IMPORTANT_BACKGROUND,
+ itBase->second.currentState.getValues()[0].mValue.int_value);
+ // Value for key {{uid 1, uid 16}, BACKGROUND}.
+ ASSERT_EQ(2, it->first.getDimensionKeyInWhat().getValues().size());
+ EXPECT_EQ(1, it->first.getDimensionKeyInWhat().getValues()[0].mValue.int_value);
+ EXPECT_EQ(16, it->first.getDimensionKeyInWhat().getValues()[1].mValue.int_value);
+ ASSERT_EQ(1, it->first.getStateValuesKey().getValues().size());
+ EXPECT_EQ(android::app::PROCESS_STATE_IMPORTANT_BACKGROUND,
+ it->first.getStateValuesKey().getValues()[0].mValue.int_value);
+ assertConditionTimer(it->second.conditionTimer, true, 0, bucketStartTimeNs + 20 * NS_PER_SEC);
+
+ // Base for dimension key {uid 1, tag 14}.
+ it++;
+ itBase = valueProducer->mCurrentBaseInfo.find(it->first.getDimensionKeyInWhat());
+ ASSERT_EQ(1, itBase->second.currentState.getValues().size());
+ EXPECT_EQ(android::app::PROCESS_STATE_IMPORTANT_BACKGROUND,
+ itBase->second.currentState.getValues()[0].mValue.int_value);
+ // Value for key {{uid 1, tag 14}, BACKGROUND}.
+ ASSERT_EQ(2, it->first.getDimensionKeyInWhat().getValues().size());
+ EXPECT_EQ(1, it->first.getDimensionKeyInWhat().getValues()[0].mValue.int_value);
+ EXPECT_EQ(14, it->first.getDimensionKeyInWhat().getValues()[1].mValue.int_value);
+ ASSERT_EQ(1, it->first.getStateValuesKey().getValues().size());
+ EXPECT_EQ(android::app::PROCESS_STATE_IMPORTANT_BACKGROUND,
+ it->first.getStateValuesKey().getValues()[0].mValue.int_value);
+ assertConditionTimer(it->second.conditionTimer, true, 0, bucketStartTimeNs + 20 * NS_PER_SEC);
+
+ // Value for key {{uid 1, uid 16}, FOREGROUND}.
+ it++;
+ ASSERT_EQ(2, it->first.getDimensionKeyInWhat().getValues().size());
+ EXPECT_EQ(1, it->first.getDimensionKeyInWhat().getValues()[0].mValue.int_value);
+ EXPECT_EQ(16, it->first.getDimensionKeyInWhat().getValues()[1].mValue.int_value);
+ ASSERT_EQ(1, it->first.getStateValuesKey().getValues().size());
+ EXPECT_EQ(android::app::PROCESS_STATE_IMPORTANT_FOREGROUND,
+ it->first.getStateValuesKey().getValues()[0].mValue.int_value);
+ assertConditionTimer(it->second.conditionTimer, false, 10 * NS_PER_SEC,
+ bucketStartTimeNs + 20 * NS_PER_SEC);
+
+ // Value for key {{uid 1, tag 16}, kStateUnknown}.
+ it++;
+ ASSERT_EQ(2, it->first.getDimensionKeyInWhat().getValues().size());
+ EXPECT_EQ(1, it->first.getDimensionKeyInWhat().getValues()[0].mValue.int_value);
+ EXPECT_EQ(16, it->first.getDimensionKeyInWhat().getValues()[1].mValue.int_value);
+ ASSERT_EQ(1, it->first.getStateValuesKey().getValues().size());
+ EXPECT_EQ(-1 /* StateTracker::kStateUnknown */,
+ it->first.getStateValuesKey().getValues()[0].mValue.int_value);
+ assertConditionTimer(it->second.conditionTimer, false, 0, 0);
+
+ // Value for key {{uid 1, tag 14}, FOREGROUND}.
+ it++;
+ ASSERT_EQ(2, it->first.getDimensionKeyInWhat().getValues().size());
+ EXPECT_EQ(1, it->first.getDimensionKeyInWhat().getValues()[0].mValue.int_value);
+ EXPECT_EQ(14, it->first.getDimensionKeyInWhat().getValues()[1].mValue.int_value);
+ ASSERT_EQ(1, it->first.getStateValuesKey().getValues().size());
+ EXPECT_EQ(android::app::PROCESS_STATE_IMPORTANT_FOREGROUND,
+ it->first.getStateValuesKey().getValues()[0].mValue.int_value);
+ assertConditionTimer(it->second.conditionTimer, false, 10 * NS_PER_SEC,
+ bucketStartTimeNs + 20 * NS_PER_SEC);
+
+ // Value for key {{uid 1, tag 14}, kStateUnknown}.
+ it++;
+ ASSERT_EQ(2, it->first.getDimensionKeyInWhat().getValues().size());
+ EXPECT_EQ(1, it->first.getDimensionKeyInWhat().getValues()[0].mValue.int_value);
+ EXPECT_EQ(14, it->first.getDimensionKeyInWhat().getValues()[1].mValue.int_value);
+ ASSERT_EQ(1, it->first.getStateValuesKey().getValues().size());
+ EXPECT_EQ(-1 /* StateTracker::kStateUnknown */,
+ it->first.getStateValuesKey().getValues()[0].mValue.int_value);
+ assertConditionTimer(it->second.conditionTimer, false, 0, 0);
+
+ // Bucket 1 status after condition change to false.
+ // All condition timers should be turned off.
+ valueProducer->onConditionChanged(false, bucketStartTimeNs + 40 * NS_PER_SEC);
+ ASSERT_EQ(3UL, valueProducer->mCurrentBaseInfo.size());
+ ASSERT_EQ(8UL, valueProducer->mCurrentSlicedBucket.size());
+ // Base for dimension {uid 2, tag 8}.
+ it = valueProducer->mCurrentSlicedBucket.begin();
+ itBase = valueProducer->mCurrentBaseInfo.find(it->first.getDimensionKeyInWhat());
+ ASSERT_EQ(1, itBase->second.currentState.getValues().size());
+ EXPECT_EQ(android::app::PROCESS_STATE_IMPORTANT_BACKGROUND,
+ itBase->second.currentState.getValues()[0].mValue.int_value);
+ // Value for key {{uid 2, uid 8}, BACKGROUND}.
+ ASSERT_EQ(2, it->first.getDimensionKeyInWhat().getValues().size());
+ EXPECT_EQ(2, it->first.getDimensionKeyInWhat().getValues()[0].mValue.int_value);
+ EXPECT_EQ(8, it->first.getDimensionKeyInWhat().getValues()[1].mValue.int_value);
+ ASSERT_EQ(1, it->first.getStateValuesKey().getValues().size());
+ EXPECT_EQ(android::app::PROCESS_STATE_IMPORTANT_BACKGROUND,
+ it->first.getStateValuesKey().getValues()[0].mValue.int_value);
+ assertConditionTimer(it->second.conditionTimer, false, 15 * NS_PER_SEC,
+ bucketStartTimeNs + 40 * NS_PER_SEC);
+
+ // Value for key {{uid 2, uid 8}, kStateUnknown}.
+ it++;
+ ASSERT_EQ(2, it->first.getDimensionKeyInWhat().getValues().size());
+ EXPECT_EQ(2, it->first.getDimensionKeyInWhat().getValues()[0].mValue.int_value);
+ EXPECT_EQ(8, it->first.getDimensionKeyInWhat().getValues()[1].mValue.int_value);
+ ASSERT_EQ(1, it->first.getStateValuesKey().getValues().size());
+ EXPECT_EQ(-1 /* StateTracker::kStateUnknown */,
+ it->first.getStateValuesKey().getValues()[0].mValue.int_value);
+ assertConditionTimer(it->second.conditionTimer, false, 0, 0);
+
+ // Base for dimension {uid 1, tag 16}.
+ it++;
+ itBase = valueProducer->mCurrentBaseInfo.find(it->first.getDimensionKeyInWhat());
+ ASSERT_EQ(1, itBase->second.currentState.getValues().size());
+ EXPECT_EQ(android::app::PROCESS_STATE_IMPORTANT_BACKGROUND,
+ itBase->second.currentState.getValues()[0].mValue.int_value);
+ // Value for key {{uid 1, uid 16}, BACKGROUND}.
+ ASSERT_EQ(2, it->first.getDimensionKeyInWhat().getValues().size());
+ EXPECT_EQ(1, it->first.getDimensionKeyInWhat().getValues()[0].mValue.int_value);
+ EXPECT_EQ(16, it->first.getDimensionKeyInWhat().getValues()[1].mValue.int_value);
+ ASSERT_EQ(1, it->first.getStateValuesKey().getValues().size());
+ EXPECT_EQ(android::app::PROCESS_STATE_IMPORTANT_BACKGROUND,
+ it->first.getStateValuesKey().getValues()[0].mValue.int_value);
+ assertConditionTimer(it->second.conditionTimer, false, 20 * NS_PER_SEC,
+ bucketStartTimeNs + 40 * NS_PER_SEC);
+
+ // Base for dimension key {uid 1, tag 14}.
+ it++;
+ itBase = valueProducer->mCurrentBaseInfo.find(it->first.getDimensionKeyInWhat());
+ ASSERT_EQ(1, itBase->second.currentState.getValues().size());
+ EXPECT_EQ(android::app::PROCESS_STATE_IMPORTANT_BACKGROUND,
+ itBase->second.currentState.getValues()[0].mValue.int_value);
+ // Value for key {{uid 1, tag 14}, BACKGROUND}.
+ ASSERT_EQ(2, it->first.getDimensionKeyInWhat().getValues().size());
+ EXPECT_EQ(1, it->first.getDimensionKeyInWhat().getValues()[0].mValue.int_value);
+ EXPECT_EQ(14, it->first.getDimensionKeyInWhat().getValues()[1].mValue.int_value);
+ ASSERT_EQ(1, it->first.getStateValuesKey().getValues().size());
+ EXPECT_EQ(android::app::PROCESS_STATE_IMPORTANT_BACKGROUND,
+ it->first.getStateValuesKey().getValues()[0].mValue.int_value);
+ assertConditionTimer(it->second.conditionTimer, false, 20 * NS_PER_SEC,
+ bucketStartTimeNs + 40 * NS_PER_SEC);
+
+ // Value for key {{uid 1, uid 16}, FOREGROUND}.
+ it++;
+ ASSERT_EQ(2, it->first.getDimensionKeyInWhat().getValues().size());
+ EXPECT_EQ(1, it->first.getDimensionKeyInWhat().getValues()[0].mValue.int_value);
+ EXPECT_EQ(16, it->first.getDimensionKeyInWhat().getValues()[1].mValue.int_value);
+ ASSERT_EQ(1, it->first.getStateValuesKey().getValues().size());
+ EXPECT_EQ(android::app::PROCESS_STATE_IMPORTANT_FOREGROUND,
+ it->first.getStateValuesKey().getValues()[0].mValue.int_value);
+ assertConditionTimer(it->second.conditionTimer, false, 10 * NS_PER_SEC,
+ bucketStartTimeNs + 20 * NS_PER_SEC);
+
+ // Value for key {{uid 1, tag 16}, kStateUnknown}.
+ it++;
+ ASSERT_EQ(2, it->first.getDimensionKeyInWhat().getValues().size());
+ EXPECT_EQ(1, it->first.getDimensionKeyInWhat().getValues()[0].mValue.int_value);
+ EXPECT_EQ(16, it->first.getDimensionKeyInWhat().getValues()[1].mValue.int_value);
+ ASSERT_EQ(1, it->first.getStateValuesKey().getValues().size());
+ EXPECT_EQ(-1 /* StateTracker::kStateUnknown */,
+ it->first.getStateValuesKey().getValues()[0].mValue.int_value);
+ assertConditionTimer(it->second.conditionTimer, false, 0, 0);
+
+ // Value for key {{uid 1, tag 14}, FOREGROUND}.
+ it++;
+ ASSERT_EQ(2, it->first.getDimensionKeyInWhat().getValues().size());
+ EXPECT_EQ(1, it->first.getDimensionKeyInWhat().getValues()[0].mValue.int_value);
+ EXPECT_EQ(14, it->first.getDimensionKeyInWhat().getValues()[1].mValue.int_value);
+ ASSERT_EQ(1, it->first.getStateValuesKey().getValues().size());
+ EXPECT_EQ(android::app::PROCESS_STATE_IMPORTANT_FOREGROUND,
+ it->first.getStateValuesKey().getValues()[0].mValue.int_value);
+ assertConditionTimer(it->second.conditionTimer, false, 10 * NS_PER_SEC,
+ bucketStartTimeNs + 20 * NS_PER_SEC);
+
+ // Value for key {{uid 1, tag 14}, kStateUnknown}.
+ it++;
+ ASSERT_EQ(2, it->first.getDimensionKeyInWhat().getValues().size());
+ EXPECT_EQ(1, it->first.getDimensionKeyInWhat().getValues()[0].mValue.int_value);
+ EXPECT_EQ(14, it->first.getDimensionKeyInWhat().getValues()[1].mValue.int_value);
+ ASSERT_EQ(1, it->first.getStateValuesKey().getValues().size());
+ EXPECT_EQ(-1 /* StateTracker::kStateUnknown */,
+ it->first.getStateValuesKey().getValues()[0].mValue.int_value);
+ assertConditionTimer(it->second.conditionTimer, false, 0, 0);
+
+ // Bucket 1 status after condition change to true.
+ valueProducer->onConditionChanged(true, bucketStartTimeNs + 45 * NS_PER_SEC);
+ ASSERT_EQ(3UL, valueProducer->mCurrentBaseInfo.size());
+ ASSERT_EQ(8UL, valueProducer->mCurrentSlicedBucket.size());
+ // Base for dimension {uid 2, tag 8}.
+ it = valueProducer->mCurrentSlicedBucket.begin();
+ itBase = valueProducer->mCurrentBaseInfo.find(it->first.getDimensionKeyInWhat());
+ ASSERT_EQ(1, itBase->second.currentState.getValues().size());
+ EXPECT_EQ(android::app::PROCESS_STATE_IMPORTANT_BACKGROUND,
+ itBase->second.currentState.getValues()[0].mValue.int_value);
+ // Value for key {{uid 2, uid 8}, BACKGROUND}.
+ ASSERT_EQ(2, it->first.getDimensionKeyInWhat().getValues().size());
+ EXPECT_EQ(2, it->first.getDimensionKeyInWhat().getValues()[0].mValue.int_value);
+ EXPECT_EQ(8, it->first.getDimensionKeyInWhat().getValues()[1].mValue.int_value);
+ ASSERT_EQ(1, it->first.getStateValuesKey().getValues().size());
+ EXPECT_EQ(android::app::PROCESS_STATE_IMPORTANT_BACKGROUND,
+ it->first.getStateValuesKey().getValues()[0].mValue.int_value);
+ assertConditionTimer(it->second.conditionTimer, true, 15 * NS_PER_SEC,
+ bucketStartTimeNs + 45 * NS_PER_SEC);
+
+ // Value for key {{uid 2, uid 8}, kStateUnknown}.
+ it++;
+ ASSERT_EQ(2, it->first.getDimensionKeyInWhat().getValues().size());
+ EXPECT_EQ(2, it->first.getDimensionKeyInWhat().getValues()[0].mValue.int_value);
+ EXPECT_EQ(8, it->first.getDimensionKeyInWhat().getValues()[1].mValue.int_value);
+ ASSERT_EQ(1, it->first.getStateValuesKey().getValues().size());
+ EXPECT_EQ(-1 /* StateTracker::kStateUnknown */,
+ it->first.getStateValuesKey().getValues()[0].mValue.int_value);
+ assertConditionTimer(it->second.conditionTimer, false, 0, 0);
+
+ // Base for dimension {uid 1, tag 16}.
+ it++;
+ itBase = valueProducer->mCurrentBaseInfo.find(it->first.getDimensionKeyInWhat());
+ ASSERT_EQ(1, itBase->second.currentState.getValues().size());
+ EXPECT_EQ(android::app::PROCESS_STATE_IMPORTANT_BACKGROUND,
+ itBase->second.currentState.getValues()[0].mValue.int_value);
+ // Value for key {{uid 1, uid 16}, BACKGROUND}.
+ ASSERT_EQ(2, it->first.getDimensionKeyInWhat().getValues().size());
+ EXPECT_EQ(1, it->first.getDimensionKeyInWhat().getValues()[0].mValue.int_value);
+ EXPECT_EQ(16, it->first.getDimensionKeyInWhat().getValues()[1].mValue.int_value);
+ ASSERT_EQ(1, it->first.getStateValuesKey().getValues().size());
+ EXPECT_EQ(android::app::PROCESS_STATE_IMPORTANT_BACKGROUND,
+ it->first.getStateValuesKey().getValues()[0].mValue.int_value);
+ assertConditionTimer(it->second.conditionTimer, true, 20 * NS_PER_SEC,
+ bucketStartTimeNs + 45 * NS_PER_SEC);
+
+ // Base for dimension key {uid 1, tag 14}.
+ it++;
+ itBase = valueProducer->mCurrentBaseInfo.find(it->first.getDimensionKeyInWhat());
+ EXPECT_EQ(android::app::PROCESS_STATE_IMPORTANT_BACKGROUND,
+ itBase->second.currentState.getValues()[0].mValue.int_value);
+ // Value for key {{uid 1, tag 14}, BACKGROUND}.
+ ASSERT_EQ(2, it->first.getDimensionKeyInWhat().getValues().size());
+ EXPECT_EQ(1, it->first.getDimensionKeyInWhat().getValues()[0].mValue.int_value);
+ EXPECT_EQ(14, it->first.getDimensionKeyInWhat().getValues()[1].mValue.int_value);
+ ASSERT_EQ(1, it->first.getStateValuesKey().getValues().size());
+ EXPECT_EQ(android::app::PROCESS_STATE_IMPORTANT_BACKGROUND,
+ it->first.getStateValuesKey().getValues()[0].mValue.int_value);
+ assertConditionTimer(it->second.conditionTimer, true, 20 * NS_PER_SEC,
+ bucketStartTimeNs + 45 * NS_PER_SEC);
+
+ // Value for key {{uid 1, uid 16}, FOREGROUND}.
+ it++;
+ ASSERT_EQ(2, it->first.getDimensionKeyInWhat().getValues().size());
+ EXPECT_EQ(1, it->first.getDimensionKeyInWhat().getValues()[0].mValue.int_value);
+ EXPECT_EQ(16, it->first.getDimensionKeyInWhat().getValues()[1].mValue.int_value);
+ ASSERT_EQ(1, it->first.getStateValuesKey().getValues().size());
+ EXPECT_EQ(android::app::PROCESS_STATE_IMPORTANT_FOREGROUND,
+ it->first.getStateValuesKey().getValues()[0].mValue.int_value);
+ assertConditionTimer(it->second.conditionTimer, false, 10 * NS_PER_SEC,
+ bucketStartTimeNs + 20 * NS_PER_SEC);
+
+ // Value for key {{uid 1, tag 16}, kStateUnknown}.
+ it++;
+ ASSERT_EQ(2, it->first.getDimensionKeyInWhat().getValues().size());
+ EXPECT_EQ(1, it->first.getDimensionKeyInWhat().getValues()[0].mValue.int_value);
+ EXPECT_EQ(16, it->first.getDimensionKeyInWhat().getValues()[1].mValue.int_value);
+ ASSERT_EQ(1, it->first.getStateValuesKey().getValues().size());
+ EXPECT_EQ(-1 /* StateTracker::kStateUnknown */,
+ it->first.getStateValuesKey().getValues()[0].mValue.int_value);
+ assertConditionTimer(it->second.conditionTimer, false, 0, 0);
+
+ // Value for key {{uid 1, tag 14}, FOREGROUND}.
+ it++;
+ ASSERT_EQ(2, it->first.getDimensionKeyInWhat().getValues().size());
+ EXPECT_EQ(1, it->first.getDimensionKeyInWhat().getValues()[0].mValue.int_value);
+ EXPECT_EQ(14, it->first.getDimensionKeyInWhat().getValues()[1].mValue.int_value);
+ ASSERT_EQ(1, it->first.getStateValuesKey().getValues().size());
+ EXPECT_EQ(android::app::PROCESS_STATE_IMPORTANT_FOREGROUND,
+ it->first.getStateValuesKey().getValues()[0].mValue.int_value);
+ assertConditionTimer(it->second.conditionTimer, false, 10 * NS_PER_SEC,
+ bucketStartTimeNs + 20 * NS_PER_SEC);
+
+ // Value for key {{uid 1, tag 14}, kStateUnknown}.
+ it++;
+ ASSERT_EQ(2, it->first.getDimensionKeyInWhat().getValues().size());
+ EXPECT_EQ(1, it->first.getDimensionKeyInWhat().getValues()[0].mValue.int_value);
+ EXPECT_EQ(14, it->first.getDimensionKeyInWhat().getValues()[1].mValue.int_value);
+ ASSERT_EQ(1, it->first.getStateValuesKey().getValues().size());
+ EXPECT_EQ(-1 /* StateTracker::kStateUnknown */,
+ it->first.getStateValuesKey().getValues()[0].mValue.int_value);
+ assertConditionTimer(it->second.conditionTimer, false, 0, 0);
+
+ // Pull at end of first bucket.
+ vector<shared_ptr<LogEvent>> allData;
+ allData.push_back(
+ CreateThreeValueLogEvent(tagId, bucket2StartTimeNs, 1 /*uid*/, 13, 14 /* tag */));
+ allData.push_back(
+ CreateThreeValueLogEvent(tagId, bucket2StartTimeNs, 1 /*uid*/, 13, 16 /* tag */));
+ allData.push_back(
+ CreateThreeValueLogEvent(tagId, bucket2StartTimeNs, 2 /*uid*/, 13, 8 /*tag*/));
+ valueProducer->onDataPulled(allData, /** succeeds */ true, bucket2StartTimeNs + 1);
+
+ // Buckets flushed after end of first bucket.
+ // All condition timers' behavior should rollover to bucket 2.
+ ASSERT_EQ(8UL, valueProducer->mCurrentSlicedBucket.size());
+ ASSERT_EQ(5UL, valueProducer->mPastBuckets.size());
+ ASSERT_EQ(3UL, valueProducer->mCurrentBaseInfo.size());
+ // Base for dimension {uid 2, tag 8}.
+ it = valueProducer->mCurrentSlicedBucket.begin();
+ itBase = valueProducer->mCurrentBaseInfo.find(it->first.getDimensionKeyInWhat());
+ ASSERT_EQ(1, itBase->second.currentState.getValues().size());
+ EXPECT_EQ(android::app::PROCESS_STATE_IMPORTANT_BACKGROUND,
+ itBase->second.currentState.getValues()[0].mValue.int_value);
+ // Value for key {{uid 2, uid 8}, BACKGROUND}.
+ ASSERT_EQ(2, it->first.getDimensionKeyInWhat().getValues().size());
+ EXPECT_EQ(2, it->first.getDimensionKeyInWhat().getValues()[0].mValue.int_value);
+ EXPECT_EQ(8, it->first.getDimensionKeyInWhat().getValues()[1].mValue.int_value);
+ ASSERT_EQ(1, it->first.getStateValuesKey().getValues().size());
+ EXPECT_EQ(android::app::PROCESS_STATE_IMPORTANT_BACKGROUND,
+ it->first.getStateValuesKey().getValues()[0].mValue.int_value);
+ assertConditionTimer(it->second.conditionTimer, true, 0, bucket2StartTimeNs);
+ ASSERT_EQ(1, valueProducer->mPastBuckets[it->first].size());
+ EXPECT_EQ(30 * NS_PER_SEC, valueProducer->mPastBuckets[it->first][0].mConditionTrueNs);
+
+ // Value for key {{uid 2, uid 8}, kStateUnknown}.
+ it++;
+ ASSERT_EQ(2, it->first.getDimensionKeyInWhat().getValues().size());
+ EXPECT_EQ(2, it->first.getDimensionKeyInWhat().getValues()[0].mValue.int_value);
+ EXPECT_EQ(8, it->first.getDimensionKeyInWhat().getValues()[1].mValue.int_value);
+ ASSERT_EQ(1, it->first.getStateValuesKey().getValues().size());
+ EXPECT_EQ(-1 /* StateTracker::kStateUnknown */,
+ it->first.getStateValuesKey().getValues()[0].mValue.int_value);
+ assertConditionTimer(it->second.conditionTimer, false, 0, 0);
+
+ // Base for dimension {uid 1, tag 16}.
+ it++;
+ itBase = valueProducer->mCurrentBaseInfo.find(it->first.getDimensionKeyInWhat());
+ ASSERT_EQ(1, itBase->second.currentState.getValues().size());
+ EXPECT_EQ(android::app::PROCESS_STATE_IMPORTANT_BACKGROUND,
+ itBase->second.currentState.getValues()[0].mValue.int_value);
+ // Value for key {{uid 1, uid 16}, BACKGROUND}.
+ ASSERT_EQ(2, it->first.getDimensionKeyInWhat().getValues().size());
+ EXPECT_EQ(1, it->first.getDimensionKeyInWhat().getValues()[0].mValue.int_value);
+ EXPECT_EQ(16, it->first.getDimensionKeyInWhat().getValues()[1].mValue.int_value);
+ ASSERT_EQ(1, it->first.getStateValuesKey().getValues().size());
+ EXPECT_EQ(android::app::PROCESS_STATE_IMPORTANT_BACKGROUND,
+ it->first.getStateValuesKey().getValues()[0].mValue.int_value);
+ assertConditionTimer(it->second.conditionTimer, true, 0, bucket2StartTimeNs);
+ ASSERT_EQ(1, valueProducer->mPastBuckets[it->first].size());
+ EXPECT_EQ(35 * NS_PER_SEC, valueProducer->mPastBuckets[it->first][0].mConditionTrueNs);
+
+ // Base for dimension key {uid 1, tag 14}.
+ it++;
+ itBase = valueProducer->mCurrentBaseInfo.find(it->first.getDimensionKeyInWhat());
+ EXPECT_EQ(android::app::PROCESS_STATE_IMPORTANT_BACKGROUND,
+ itBase->second.currentState.getValues()[0].mValue.int_value);
+ // Value for key {{uid 1, tag 14}, BACKGROUND}.
+ ASSERT_EQ(2, it->first.getDimensionKeyInWhat().getValues().size());
+ EXPECT_EQ(1, it->first.getDimensionKeyInWhat().getValues()[0].mValue.int_value);
+ EXPECT_EQ(14, it->first.getDimensionKeyInWhat().getValues()[1].mValue.int_value);
+ ASSERT_EQ(1, it->first.getStateValuesKey().getValues().size());
+ EXPECT_EQ(android::app::PROCESS_STATE_IMPORTANT_BACKGROUND,
+ it->first.getStateValuesKey().getValues()[0].mValue.int_value);
+ assertConditionTimer(it->second.conditionTimer, true, 0, bucket2StartTimeNs);
+ ASSERT_EQ(1, valueProducer->mPastBuckets[it->first].size());
+ EXPECT_EQ(35 * NS_PER_SEC, valueProducer->mPastBuckets[it->first][0].mConditionTrueNs);
+
+ // Value for key {{uid 1, uid 16}, FOREGROUND}.
+ it++;
+ ASSERT_EQ(2, it->first.getDimensionKeyInWhat().getValues().size());
+ EXPECT_EQ(1, it->first.getDimensionKeyInWhat().getValues()[0].mValue.int_value);
+ EXPECT_EQ(16, it->first.getDimensionKeyInWhat().getValues()[1].mValue.int_value);
+ ASSERT_EQ(1, it->first.getStateValuesKey().getValues().size());
+ EXPECT_EQ(android::app::PROCESS_STATE_IMPORTANT_FOREGROUND,
+ it->first.getStateValuesKey().getValues()[0].mValue.int_value);
+ assertConditionTimer(it->second.conditionTimer, false, 0, bucketStartTimeNs + 20 * NS_PER_SEC);
+ ASSERT_EQ(1, valueProducer->mPastBuckets[it->first].size());
+ EXPECT_EQ(10 * NS_PER_SEC, valueProducer->mPastBuckets[it->first][0].mConditionTrueNs);
+
+ // Value for key {{uid 1, tag 16}, kStateUnknown}.
+ it++;
+ ASSERT_EQ(2, it->first.getDimensionKeyInWhat().getValues().size());
+ EXPECT_EQ(1, it->first.getDimensionKeyInWhat().getValues()[0].mValue.int_value);
+ EXPECT_EQ(16, it->first.getDimensionKeyInWhat().getValues()[1].mValue.int_value);
+ ASSERT_EQ(1, it->first.getStateValuesKey().getValues().size());
+ EXPECT_EQ(-1 /* StateTracker::kStateUnknown */,
+ it->first.getStateValuesKey().getValues()[0].mValue.int_value);
+ assertConditionTimer(it->second.conditionTimer, false, 0, 0);
+
+ // Value for key {{uid 1, tag 14}, FOREGROUND}.
+ it++;
+ ASSERT_EQ(2, it->first.getDimensionKeyInWhat().getValues().size());
+ EXPECT_EQ(1, it->first.getDimensionKeyInWhat().getValues()[0].mValue.int_value);
+ EXPECT_EQ(14, it->first.getDimensionKeyInWhat().getValues()[1].mValue.int_value);
+ ASSERT_EQ(1, it->first.getStateValuesKey().getValues().size());
+ EXPECT_EQ(android::app::PROCESS_STATE_IMPORTANT_FOREGROUND,
+ it->first.getStateValuesKey().getValues()[0].mValue.int_value);
+ assertConditionTimer(it->second.conditionTimer, false, 0, bucketStartTimeNs + 20 * NS_PER_SEC);
+ ASSERT_EQ(1, valueProducer->mPastBuckets[it->first].size());
+ EXPECT_EQ(10 * NS_PER_SEC, valueProducer->mPastBuckets[it->first][0].mConditionTrueNs);
+
+ // Value for key {{uid 1, tag 14}, kStateUnknown}.
+ it++;
+ ASSERT_EQ(2, it->first.getDimensionKeyInWhat().getValues().size());
+ EXPECT_EQ(1, it->first.getDimensionKeyInWhat().getValues()[0].mValue.int_value);
+ EXPECT_EQ(14, it->first.getDimensionKeyInWhat().getValues()[1].mValue.int_value);
+ ASSERT_EQ(1, it->first.getStateValuesKey().getValues().size());
+ EXPECT_EQ(-1 /* StateTracker::kStateUnknown */,
+ it->first.getStateValuesKey().getValues()[0].mValue.int_value);
+ assertConditionTimer(it->second.conditionTimer, false, 0, 0);
+
+ // Bucket 2 status after uid 2 process state change Background->Foreground.
+ uidProcessEvent =
+ CreateUidProcessStateChangedEvent(bucket2StartTimeNs + 30 * NS_PER_SEC, 2 /* uid */,
+ android::app::PROCESS_STATE_IMPORTANT_FOREGROUND);
+ StateManager::getInstance().onLogEvent(*uidProcessEvent);
+
+ ASSERT_EQ(9UL, valueProducer->mCurrentSlicedBucket.size());
+ ASSERT_EQ(3UL, valueProducer->mCurrentBaseInfo.size());
+ // Base for dimension {uid 2, tag 8}.
+ it = valueProducer->mCurrentSlicedBucket.begin();
+ itBase = valueProducer->mCurrentBaseInfo.find(it->first.getDimensionKeyInWhat());
+ ASSERT_EQ(1, itBase->second.currentState.getValues().size());
+ EXPECT_EQ(android::app::PROCESS_STATE_IMPORTANT_FOREGROUND,
+ itBase->second.currentState.getValues()[0].mValue.int_value);
+ // Value for key {{uid 2, uid 8}, FOREGROUND}.
+ ASSERT_EQ(2, it->first.getDimensionKeyInWhat().getValues().size());
+ EXPECT_EQ(2, it->first.getDimensionKeyInWhat().getValues()[0].mValue.int_value);
+ EXPECT_EQ(8, it->first.getDimensionKeyInWhat().getValues()[1].mValue.int_value);
+ ASSERT_EQ(1, it->first.getStateValuesKey().getValues().size());
+ EXPECT_EQ(android::app::PROCESS_STATE_IMPORTANT_FOREGROUND,
+ it->first.getStateValuesKey().getValues()[0].mValue.int_value);
+ assertConditionTimer(it->second.conditionTimer, true, 0, bucket2StartTimeNs + 30 * NS_PER_SEC);
+
+ // Value for key {{uid 2, uid 8}, BACKGROUND}.
+ it++;
+ ASSERT_EQ(2, it->first.getDimensionKeyInWhat().getValues().size());
+ EXPECT_EQ(2, it->first.getDimensionKeyInWhat().getValues()[0].mValue.int_value);
+ EXPECT_EQ(8, it->first.getDimensionKeyInWhat().getValues()[1].mValue.int_value);
+ ASSERT_EQ(1, it->first.getStateValuesKey().getValues().size());
+ EXPECT_EQ(android::app::PROCESS_STATE_IMPORTANT_BACKGROUND,
+ it->first.getStateValuesKey().getValues()[0].mValue.int_value);
+ assertConditionTimer(it->second.conditionTimer, false, 30 * NS_PER_SEC,
+ bucket2StartTimeNs + 30 * NS_PER_SEC);
+
+ // Value for key {{uid 2, uid 8}, kStateUnknown}.
+ it++;
+ ASSERT_EQ(2, it->first.getDimensionKeyInWhat().getValues().size());
+ EXPECT_EQ(2, it->first.getDimensionKeyInWhat().getValues()[0].mValue.int_value);
+ EXPECT_EQ(8, it->first.getDimensionKeyInWhat().getValues()[1].mValue.int_value);
+ ASSERT_EQ(1, it->first.getStateValuesKey().getValues().size());
+ EXPECT_EQ(-1 /* StateTracker::kStateUnknown */,
+ it->first.getStateValuesKey().getValues()[0].mValue.int_value);
+ assertConditionTimer(it->second.conditionTimer, false, 0, 0);
+
+ // Base for dimension {uid 1, tag 16}.
+ it++;
+ itBase = valueProducer->mCurrentBaseInfo.find(it->first.getDimensionKeyInWhat());
+ ASSERT_EQ(1, itBase->second.currentState.getValues().size());
+ EXPECT_EQ(android::app::PROCESS_STATE_IMPORTANT_BACKGROUND,
+ itBase->second.currentState.getValues()[0].mValue.int_value);
+ // Value for key {{uid 1, uid 16}, BACKGROUND}.
+ ASSERT_EQ(2, it->first.getDimensionKeyInWhat().getValues().size());
+ EXPECT_EQ(1, it->first.getDimensionKeyInWhat().getValues()[0].mValue.int_value);
+ EXPECT_EQ(16, it->first.getDimensionKeyInWhat().getValues()[1].mValue.int_value);
+ ASSERT_EQ(1, it->first.getStateValuesKey().getValues().size());
+ EXPECT_EQ(android::app::PROCESS_STATE_IMPORTANT_BACKGROUND,
+ it->first.getStateValuesKey().getValues()[0].mValue.int_value);
+ assertConditionTimer(it->second.conditionTimer, true, 0, bucket2StartTimeNs);
+
+ // Base for dimension key {uid 1, tag 14}.
+ it++;
+ itBase = valueProducer->mCurrentBaseInfo.find(it->first.getDimensionKeyInWhat());
+ ASSERT_EQ(1, itBase->second.currentState.getValues().size());
+ EXPECT_EQ(android::app::PROCESS_STATE_IMPORTANT_BACKGROUND,
+ itBase->second.currentState.getValues()[0].mValue.int_value);
+ // Value for key {{uid 1, tag 14}, BACKGROUND}.
+ ASSERT_EQ(2, it->first.getDimensionKeyInWhat().getValues().size());
+ EXPECT_EQ(1, it->first.getDimensionKeyInWhat().getValues()[0].mValue.int_value);
+ EXPECT_EQ(14, it->first.getDimensionKeyInWhat().getValues()[1].mValue.int_value);
+ ASSERT_EQ(1, it->first.getStateValuesKey().getValues().size());
+ EXPECT_EQ(android::app::PROCESS_STATE_IMPORTANT_BACKGROUND,
+ it->first.getStateValuesKey().getValues()[0].mValue.int_value);
+ assertConditionTimer(it->second.conditionTimer, true, 0, bucket2StartTimeNs);
+
+ // Value for key {{uid 1, uid 16}, FOREGROUND}.
+ it++;
+ ASSERT_EQ(2, it->first.getDimensionKeyInWhat().getValues().size());
+ EXPECT_EQ(1, it->first.getDimensionKeyInWhat().getValues()[0].mValue.int_value);
+ EXPECT_EQ(16, it->first.getDimensionKeyInWhat().getValues()[1].mValue.int_value);
+ ASSERT_EQ(1, it->first.getStateValuesKey().getValues().size());
+ EXPECT_EQ(android::app::PROCESS_STATE_IMPORTANT_FOREGROUND,
+ it->first.getStateValuesKey().getValues()[0].mValue.int_value);
+ assertConditionTimer(it->second.conditionTimer, false, 0, bucketStartTimeNs + 20 * NS_PER_SEC);
+
+ // Value for key {{uid 1, tag 16}, kStateUnknown}.
+ it++;
+ ASSERT_EQ(2, it->first.getDimensionKeyInWhat().getValues().size());
+ EXPECT_EQ(1, it->first.getDimensionKeyInWhat().getValues()[0].mValue.int_value);
+ EXPECT_EQ(16, it->first.getDimensionKeyInWhat().getValues()[1].mValue.int_value);
+ ASSERT_EQ(1, it->first.getStateValuesKey().getValues().size());
+ EXPECT_EQ(-1 /* StateTracker::kStateUnknown */,
+ it->first.getStateValuesKey().getValues()[0].mValue.int_value);
+ assertConditionTimer(it->second.conditionTimer, false, 0, 0);
+
+ // Value for key {{uid 1, tag 14}, FOREGROUND}.
+ it++;
+ ASSERT_EQ(2, it->first.getDimensionKeyInWhat().getValues().size());
+ EXPECT_EQ(1, it->first.getDimensionKeyInWhat().getValues()[0].mValue.int_value);
+ EXPECT_EQ(14, it->first.getDimensionKeyInWhat().getValues()[1].mValue.int_value);
+ ASSERT_EQ(1, it->first.getStateValuesKey().getValues().size());
+ EXPECT_EQ(android::app::PROCESS_STATE_IMPORTANT_FOREGROUND,
+ it->first.getStateValuesKey().getValues()[0].mValue.int_value);
+ assertConditionTimer(it->second.conditionTimer, false, 0, bucketStartTimeNs + 20 * NS_PER_SEC);
+
+ // Value for key {{uid 1, tag 14}, kStateUnknown}.
+ it++;
+ ASSERT_EQ(2, it->first.getDimensionKeyInWhat().getValues().size());
+ EXPECT_EQ(1, it->first.getDimensionKeyInWhat().getValues()[0].mValue.int_value);
+ EXPECT_EQ(14, it->first.getDimensionKeyInWhat().getValues()[1].mValue.int_value);
+ ASSERT_EQ(1, it->first.getStateValuesKey().getValues().size());
+ EXPECT_EQ(-1 /* StateTracker::kStateUnknown */,
+ it->first.getStateValuesKey().getValues()[0].mValue.int_value);
+ assertConditionTimer(it->second.conditionTimer, false, 0, 0);
+
+ // Start dump report and check output.
+ ProtoOutputStream output;
+ std::set<string> strSet;
+ valueProducer->onDumpReport(bucket2StartTimeNs + 50 * NS_PER_SEC,
+ true /* include recent buckets */, true, NO_TIME_CONSTRAINTS,
+ &strSet, &output);
+
+ StatsLogReport report = outputStreamToProto(&output);
+ EXPECT_TRUE(report.has_value_metrics());
+ ASSERT_EQ(6, report.value_metrics().data_size());
+
+ // {{uid 1, tag 14}, FOREGROUND}.
+ auto data = report.value_metrics().data(0);
+ EXPECT_EQ(UID_PROCESS_STATE_ATOM_ID, data.slice_by_state(0).atom_id());
+ EXPECT_EQ(android::app::ProcessStateEnum::PROCESS_STATE_IMPORTANT_FOREGROUND,
+ data.slice_by_state(0).value());
+ ASSERT_EQ(1, data.bucket_info_size());
+ EXPECT_EQ(10 * NS_PER_SEC, data.bucket_info(0).condition_true_nanos());
+
+ // {{uid 1, tag 16}, BACKGROUND}.
+ data = report.value_metrics().data(1);
+ EXPECT_EQ(UID_PROCESS_STATE_ATOM_ID, data.slice_by_state(0).atom_id());
+ EXPECT_EQ(android::app::ProcessStateEnum::PROCESS_STATE_IMPORTANT_BACKGROUND,
+ data.slice_by_state(0).value());
+ ASSERT_EQ(2, data.bucket_info_size());
+ EXPECT_EQ(35 * NS_PER_SEC, data.bucket_info(0).condition_true_nanos());
+ EXPECT_EQ(50 * NS_PER_SEC, data.bucket_info(1).condition_true_nanos());
+
+ // {{uid 1, tag 14}, BACKGROUND}.
+ data = report.value_metrics().data(2);
+ EXPECT_EQ(UID_PROCESS_STATE_ATOM_ID, data.slice_by_state(0).atom_id());
+ EXPECT_EQ(android::app::ProcessStateEnum::PROCESS_STATE_IMPORTANT_BACKGROUND,
+ data.slice_by_state(0).value());
+ ASSERT_EQ(2, data.bucket_info_size());
+ EXPECT_EQ(35 * NS_PER_SEC, data.bucket_info(0).condition_true_nanos());
+ EXPECT_EQ(50 * NS_PER_SEC, data.bucket_info(1).condition_true_nanos());
+
+ // {{uid 1, tag 16}, FOREGROUND}.
+ data = report.value_metrics().data(3);
+ EXPECT_EQ(UID_PROCESS_STATE_ATOM_ID, data.slice_by_state(0).atom_id());
+ EXPECT_EQ(android::app::ProcessStateEnum::PROCESS_STATE_IMPORTANT_FOREGROUND,
+ data.slice_by_state(0).value());
+ ASSERT_EQ(1, data.bucket_info_size());
+ EXPECT_EQ(10 * NS_PER_SEC, data.bucket_info(0).condition_true_nanos());
+
+ // {{uid 2, tag 8}, FOREGROUND}.
+ data = report.value_metrics().data(4);
+ EXPECT_EQ(UID_PROCESS_STATE_ATOM_ID, data.slice_by_state(0).atom_id());
+ EXPECT_EQ(android::app::ProcessStateEnum::PROCESS_STATE_IMPORTANT_FOREGROUND,
+ data.slice_by_state(0).value());
+ ASSERT_EQ(1, data.bucket_info_size());
+ EXPECT_EQ(20 * NS_PER_SEC, data.bucket_info(0).condition_true_nanos());
+
+ // {{uid 2, tag 8}, BACKGROUND}.
+ data = report.value_metrics().data(5);
+ EXPECT_EQ(UID_PROCESS_STATE_ATOM_ID, data.slice_by_state(0).atom_id());
+ EXPECT_EQ(android::app::ProcessStateEnum::PROCESS_STATE_IMPORTANT_BACKGROUND,
+ data.slice_by_state(0).value());
+ ASSERT_EQ(2, data.bucket_info_size());
+ EXPECT_EQ(30 * NS_PER_SEC, data.bucket_info(0).condition_true_nanos());
+ EXPECT_EQ(30 * NS_PER_SEC, data.bucket_info(1).condition_true_nanos());
}
TEST(ValueMetricProducerTest, TestSlicedStateWithCondition) {
@@ -4894,15 +6728,23 @@ TEST(ValueMetricProducerTest, TestSlicedStateWithCondition) {
ASSERT_EQ(1, itBase->second.currentState.getValues().size());
EXPECT_EQ(BatterySaverModeStateChanged::ON,
itBase->second.currentState.getValues()[0].mValue.int_value);
- // Value for key {{}, -1}
- ASSERT_EQ(1UL, valueProducer->mCurrentSlicedBucket.size());
+ // Value for key {{}, ON}
+ ASSERT_EQ(2UL, valueProducer->mCurrentSlicedBucket.size());
std::unordered_map<MetricDimensionKey, ValueMetricProducer::CurrentValueBucket>::iterator it =
valueProducer->mCurrentSlicedBucket.begin();
EXPECT_EQ(0, it->first.getDimensionKeyInWhat().getValues().size());
ASSERT_EQ(1, it->first.getStateValuesKey().getValues().size());
+ EXPECT_EQ(BatterySaverModeStateChanged::ON,
+ it->first.getStateValuesKey().getValues()[0].mValue.int_value);
+ assertConditionTimer(it->second.conditionTimer, true, 0, bucketStartTimeNs + 20 * NS_PER_SEC);
+ // Value for key {{}, -1}
+ it++;
+ EXPECT_EQ(0, it->first.getDimensionKeyInWhat().getValues().size());
+ ASSERT_EQ(1, it->first.getStateValuesKey().getValues().size());
EXPECT_EQ(-1 /*StateTracker::kUnknown*/,
it->first.getStateValuesKey().getValues()[0].mValue.int_value);
EXPECT_FALSE(it->second.intervals[0].hasValue);
+ assertConditionTimer(it->second.conditionTimer, false, 0, 0);
// Bucket status after battery saver mode OFF event.
unique_ptr<LogEvent> batterySaverOffEvent =
@@ -4917,15 +6759,27 @@ TEST(ValueMetricProducerTest, TestSlicedStateWithCondition) {
ASSERT_EQ(1, itBase->second.currentState.getValues().size());
EXPECT_EQ(BatterySaverModeStateChanged::OFF,
itBase->second.currentState.getValues()[0].mValue.int_value);
- // Value for key {{}, ON}
- ASSERT_EQ(2UL, valueProducer->mCurrentSlicedBucket.size());
+ // Value for key {{}, OFF}
+ ASSERT_EQ(3UL, valueProducer->mCurrentSlicedBucket.size());
it = valueProducer->mCurrentSlicedBucket.begin();
EXPECT_EQ(0, it->first.getDimensionKeyInWhat().getValues().size());
ASSERT_EQ(1, it->first.getStateValuesKey().getValues().size());
+ EXPECT_EQ(BatterySaverModeStateChanged::OFF,
+ it->first.getStateValuesKey().getValues()[0].mValue.int_value);
+ assertConditionTimer(it->second.conditionTimer, true, 0, bucketStartTimeNs + 30 * NS_PER_SEC);
+ // Value for key {{}, ON}
+ it++;
+ EXPECT_EQ(0, it->first.getDimensionKeyInWhat().getValues().size());
+ ASSERT_EQ(1, it->first.getStateValuesKey().getValues().size());
EXPECT_EQ(BatterySaverModeStateChanged::ON,
it->first.getStateValuesKey().getValues()[0].mValue.int_value);
EXPECT_TRUE(it->second.intervals[0].hasValue);
EXPECT_EQ(2, it->second.intervals[0].value.long_value);
+ assertConditionTimer(it->second.conditionTimer, false, 10 * NS_PER_SEC,
+ bucketStartTimeNs + 30 * NS_PER_SEC);
+ // Value for key {{}, -1}
+ it++;
+ assertConditionTimer(it->second.conditionTimer, false, 0, 0);
// Pull at end of first bucket.
vector<shared_ptr<LogEvent>> allData;
@@ -4944,6 +6798,15 @@ TEST(ValueMetricProducerTest, TestSlicedStateWithCondition) {
ASSERT_EQ(1, itBase->second.currentState.getValues().size());
EXPECT_EQ(BatterySaverModeStateChanged::OFF,
itBase->second.currentState.getValues()[0].mValue.int_value);
+ // Value for key {{}, OFF}
+ it = valueProducer->mCurrentSlicedBucket.begin();
+ assertConditionTimer(it->second.conditionTimer, true, 0, bucket2StartTimeNs);
+ // Value for key {{}, ON}
+ it++;
+ assertConditionTimer(it->second.conditionTimer, false, 0, bucketStartTimeNs + 30 * NS_PER_SEC);
+ // Value for key {{}, -1}
+ it++;
+ assertConditionTimer(it->second.conditionTimer, false, 0, 0);
// Bucket 2 status after condition change to false.
valueProducer->onConditionChanged(false, bucket2StartTimeNs + 10 * NS_PER_SEC);
@@ -4964,6 +6827,19 @@ TEST(ValueMetricProducerTest, TestSlicedStateWithCondition) {
it->first.getStateValuesKey().getValues()[0].mValue.int_value);
EXPECT_TRUE(it->second.intervals[0].hasValue);
EXPECT_EQ(4, it->second.intervals[0].value.long_value);
+ assertConditionTimer(it->second.conditionTimer, false, 10 * NS_PER_SEC,
+ bucket2StartTimeNs + 10 * NS_PER_SEC);
+ // Value for key {{}, ON}
+ it++;
+ EXPECT_EQ(0, it->first.getDimensionKeyInWhat().getValues().size());
+ ASSERT_EQ(1, it->first.getStateValuesKey().getValues().size());
+ EXPECT_EQ(BatterySaverModeStateChanged::ON,
+ it->first.getStateValuesKey().getValues()[0].mValue.int_value);
+ EXPECT_FALSE(it->second.intervals[0].hasValue);
+ assertConditionTimer(it->second.conditionTimer, false, 0, bucketStartTimeNs + 30 * NS_PER_SEC);
+ // Value for key {{}, -1}
+ it++;
+ assertConditionTimer(it->second.conditionTimer, false, 0, 0);
// Start dump report and check output.
ProtoOutputStream output;
@@ -4982,6 +6858,7 @@ TEST(ValueMetricProducerTest, TestSlicedStateWithCondition) {
EXPECT_EQ(BatterySaverModeStateChanged::ON, data.slice_by_state(0).value());
ASSERT_EQ(1, data.bucket_info_size());
EXPECT_EQ(2, data.bucket_info(0).values(0).value_long());
+ EXPECT_EQ(10 * NS_PER_SEC, data.bucket_info(0).condition_true_nanos());
data = report.value_metrics().data(1);
EXPECT_EQ(util::BATTERY_SAVER_MODE_STATE_CHANGED, data.slice_by_state(0).atom_id());
@@ -4990,6 +6867,8 @@ TEST(ValueMetricProducerTest, TestSlicedStateWithCondition) {
ASSERT_EQ(2, data.bucket_info_size());
EXPECT_EQ(6, data.bucket_info(0).values(0).value_long());
EXPECT_EQ(4, data.bucket_info(1).values(0).value_long());
+ EXPECT_EQ(30 * NS_PER_SEC, data.bucket_info(0).condition_true_nanos());
+ EXPECT_EQ(10 * NS_PER_SEC, data.bucket_info(1).condition_true_nanos());
}
/*
diff --git a/cmds/statsd/tests/metrics/parsing_utils/config_update_utils_test.cpp b/cmds/statsd/tests/metrics/parsing_utils/config_update_utils_test.cpp
index 843d836a2c0b..65e5875e39bf 100644
--- a/cmds/statsd/tests/metrics/parsing_utils/config_update_utils_test.cpp
+++ b/cmds/statsd/tests/metrics/parsing_utils/config_update_utils_test.cpp
@@ -34,6 +34,8 @@ using namespace testing;
using android::sp;
using android::os::statsd::Predicate;
using std::map;
+using std::nullopt;
+using std::optional;
using std::set;
using std::unordered_map;
using std::vector;
@@ -61,11 +63,11 @@ vector<sp<MetricProducer>> oldMetricProducers;
unordered_map<int64_t, int> oldMetricProducerMap;
std::vector<sp<AnomalyTracker>> oldAnomalyTrackers;
std::vector<sp<AlarmTracker>> oldAlarmTrackers;
-unordered_map<int, std::vector<int>> conditionToMetricMap;
-unordered_map<int, std::vector<int>> trackerToMetricMap;
-unordered_map<int, std::vector<int>> trackerToConditionMap;
-unordered_map<int, std::vector<int>> activationAtomTrackerToMetricMap;
-unordered_map<int, std::vector<int>> deactivationAtomTrackerToMetricMap;
+unordered_map<int, std::vector<int>> tmpConditionToMetricMap;
+unordered_map<int, std::vector<int>> tmpTrackerToMetricMap;
+unordered_map<int, std::vector<int>> tmpTrackerToConditionMap;
+unordered_map<int, std::vector<int>> tmpActivationAtomTrackerToMetricMap;
+unordered_map<int, std::vector<int>> tmpDeactivationAtomTrackerToMetricMap;
unordered_map<int64_t, int> alertTrackerMap;
vector<int> metricsWithActivation;
map<int64_t, uint64_t> oldStateHashes;
@@ -86,11 +88,11 @@ public:
oldMetricProducerMap.clear();
oldAnomalyTrackers.clear();
oldAlarmTrackers.clear();
- conditionToMetricMap.clear();
- trackerToMetricMap.clear();
- trackerToConditionMap.clear();
- activationAtomTrackerToMetricMap.clear();
- deactivationAtomTrackerToMetricMap.clear();
+ tmpConditionToMetricMap.clear();
+ tmpTrackerToMetricMap.clear();
+ tmpTrackerToConditionMap.clear();
+ tmpActivationAtomTrackerToMetricMap.clear();
+ tmpDeactivationAtomTrackerToMetricMap.clear();
alertTrackerMap.clear();
metricsWithActivation.clear();
oldStateHashes.clear();
@@ -103,12 +105,22 @@ bool initConfig(const StatsdConfig& config) {
key, config, uidMap, pullerManager, anomalyAlarmMonitor, periodicAlarmMonitor,
timeBaseNs, timeBaseNs, allTagIds, oldAtomMatchingTrackers, oldAtomMatchingTrackerMap,
oldConditionTrackers, oldConditionTrackerMap, oldMetricProducers, oldMetricProducerMap,
- oldAnomalyTrackers, oldAlarmTrackers, conditionToMetricMap, trackerToMetricMap,
- trackerToConditionMap, activationAtomTrackerToMetricMap,
- deactivationAtomTrackerToMetricMap, alertTrackerMap, metricsWithActivation,
+ oldAnomalyTrackers, oldAlarmTrackers, tmpConditionToMetricMap, tmpTrackerToMetricMap,
+ tmpTrackerToConditionMap, tmpActivationAtomTrackerToMetricMap,
+ tmpDeactivationAtomTrackerToMetricMap, alertTrackerMap, metricsWithActivation,
oldStateHashes, noReportMetricIds);
}
+EventMetric createEventMetric(string name, int64_t what, optional<int64_t> condition) {
+ EventMetric metric;
+ metric.set_id(StringToId(name));
+ metric.set_what(what);
+ if (condition) {
+ metric.set_condition(condition.value());
+ }
+ return metric;
+}
+
} // anonymous namespace
TEST_F(ConfigUpdateTest, TestSimpleMatcherPreserve) {
@@ -660,7 +672,6 @@ TEST_F(ConfigUpdateTest, TestCombinationConditionDepsChange) {
TEST_F(ConfigUpdateTest, TestUpdateConditions) {
StatsdConfig config;
-
// Add atom matchers. These are mostly needed for initStatsdConfig
AtomMatcher matcher1 = CreateScreenTurnedOnAtomMatcher();
int64_t matcher1Id = matcher1.id();
@@ -734,7 +745,7 @@ TEST_F(ConfigUpdateTest, TestUpdateConditions) {
set<int64_t> replacedMatchers;
replacedMatchers.insert(matcher6Id);
- // Change the condition of simple1 to true.
+ // Change the condition of simple1 to false.
ASSERT_EQ(oldConditionTrackers[0]->getConditionId(), simple1Id);
LogEvent event(/*uid=*/0, /*pid=*/0); // Empty event is fine since there are no dimensions.
// Mark the stop matcher as matched, condition should be false.
@@ -747,7 +758,7 @@ TEST_F(ConfigUpdateTest, TestUpdateConditions) {
EXPECT_EQ(tmpConditionCache[0], ConditionState::kFalse);
EXPECT_EQ(conditionChangeCache[0], true);
- // New combination matcher. Should have an initial condition of true since it is NOT(simple1).
+ // New combination predicate. Should have an initial condition of true since it is NOT(simple1).
Predicate combination4;
combination4.set_id(StringToId("COMBINATION4"));
combination4.mutable_combination()->set_operation(LogicalOperation::NOT);
@@ -888,6 +899,556 @@ TEST_F(ConfigUpdateTest, TestUpdateConditions) {
UnorderedElementsAre(simple1Index, simple2Index));
EXPECT_THAT(combinationTracker1->mSlicedChildren, IsEmpty());
}
+
+TEST_F(ConfigUpdateTest, TestEventMetricPreserve) {
+ StatsdConfig config;
+ AtomMatcher startMatcher = CreateScreenTurnedOnAtomMatcher();
+ *config.add_atom_matcher() = startMatcher;
+ AtomMatcher stopMatcher = CreateScreenTurnedOffAtomMatcher();
+ *config.add_atom_matcher() = stopMatcher;
+ AtomMatcher whatMatcher = CreateScreenBrightnessChangedAtomMatcher();
+ *config.add_atom_matcher() = whatMatcher;
+
+ Predicate predicate = CreateScreenIsOnPredicate();
+ *config.add_predicate() = predicate;
+
+ EventMetric* metric = config.add_event_metric();
+ metric->set_id(12345);
+ metric->set_what(whatMatcher.id());
+ metric->set_condition(predicate.id());
+
+ // Create an initial config.
+ EXPECT_TRUE(initConfig(config));
+
+ unordered_map<int64_t, int> metricToActivationMap;
+ vector<UpdateStatus> metricsToUpdate(1, UPDATE_UNKNOWN);
+ EXPECT_TRUE(determineAllMetricUpdateStatuses(config, oldMetricProducerMap, oldMetricProducers,
+ metricToActivationMap,
+ /*replacedMatchers*/ {}, /*replacedConditions=*/{},
+ /*replacedStates=*/{}, metricsToUpdate));
+ EXPECT_EQ(metricsToUpdate[0], UPDATE_PRESERVE);
+}
+
+TEST_F(ConfigUpdateTest, TestEventMetricActivationAdded) {
+ StatsdConfig config;
+ AtomMatcher startMatcher = CreateScreenTurnedOnAtomMatcher();
+ *config.add_atom_matcher() = startMatcher;
+ AtomMatcher stopMatcher = CreateScreenTurnedOffAtomMatcher();
+ *config.add_atom_matcher() = stopMatcher;
+ AtomMatcher whatMatcher = CreateScreenBrightnessChangedAtomMatcher();
+ *config.add_atom_matcher() = whatMatcher;
+
+ Predicate predicate = CreateScreenIsOnPredicate();
+ *config.add_predicate() = predicate;
+
+ EventMetric* metric = config.add_event_metric();
+ metric->set_id(12345);
+ metric->set_what(whatMatcher.id());
+ metric->set_condition(predicate.id());
+
+ // Create an initial config.
+ EXPECT_TRUE(initConfig(config));
+
+ // Add a metric activation, which should change the proto, causing replacement.
+ MetricActivation* activation = config.add_metric_activation();
+ activation->set_metric_id(12345);
+ EventActivation* eventActivation = activation->add_event_activation();
+ eventActivation->set_atom_matcher_id(startMatcher.id());
+ eventActivation->set_ttl_seconds(5);
+
+ unordered_map<int64_t, int> metricToActivationMap = {{12345, 0}};
+ vector<UpdateStatus> metricsToUpdate(1, UPDATE_UNKNOWN);
+ EXPECT_TRUE(determineAllMetricUpdateStatuses(config, oldMetricProducerMap, oldMetricProducers,
+ metricToActivationMap,
+ /*replacedMatchers*/ {}, /*replacedConditions=*/{},
+ /*replacedStates=*/{}, metricsToUpdate));
+ EXPECT_EQ(metricsToUpdate[0], UPDATE_REPLACE);
+}
+
+TEST_F(ConfigUpdateTest, TestEventMetricWhatChanged) {
+ StatsdConfig config;
+ AtomMatcher startMatcher = CreateScreenTurnedOnAtomMatcher();
+ *config.add_atom_matcher() = startMatcher;
+ AtomMatcher stopMatcher = CreateScreenTurnedOffAtomMatcher();
+ *config.add_atom_matcher() = stopMatcher;
+ AtomMatcher whatMatcher = CreateScreenBrightnessChangedAtomMatcher();
+ *config.add_atom_matcher() = whatMatcher;
+
+ Predicate predicate = CreateScreenIsOnPredicate();
+ *config.add_predicate() = predicate;
+
+ EventMetric* metric = config.add_event_metric();
+ metric->set_id(12345);
+ metric->set_what(whatMatcher.id());
+ metric->set_condition(predicate.id());
+
+ // Create an initial config.
+ EXPECT_TRUE(initConfig(config));
+
+ unordered_map<int64_t, int> metricToActivationMap;
+ vector<UpdateStatus> metricsToUpdate(1, UPDATE_UNKNOWN);
+ EXPECT_TRUE(determineAllMetricUpdateStatuses(
+ config, oldMetricProducerMap, oldMetricProducers, metricToActivationMap,
+ /*replacedMatchers*/ {whatMatcher.id()}, /*replacedConditions=*/{},
+ /*replacedStates=*/{}, metricsToUpdate));
+ EXPECT_EQ(metricsToUpdate[0], UPDATE_REPLACE);
+}
+
+TEST_F(ConfigUpdateTest, TestEventMetricConditionChanged) {
+ StatsdConfig config;
+ AtomMatcher startMatcher = CreateScreenTurnedOnAtomMatcher();
+ *config.add_atom_matcher() = startMatcher;
+ AtomMatcher stopMatcher = CreateScreenTurnedOffAtomMatcher();
+ *config.add_atom_matcher() = stopMatcher;
+ AtomMatcher whatMatcher = CreateScreenBrightnessChangedAtomMatcher();
+ *config.add_atom_matcher() = whatMatcher;
+
+ Predicate predicate = CreateScreenIsOnPredicate();
+ *config.add_predicate() = predicate;
+
+ EventMetric* metric = config.add_event_metric();
+ metric->set_id(12345);
+ metric->set_what(whatMatcher.id());
+ metric->set_condition(predicate.id());
+
+ // Create an initial config.
+ EXPECT_TRUE(initConfig(config));
+
+ unordered_map<int64_t, int> metricToActivationMap;
+ vector<UpdateStatus> metricsToUpdate(1, UPDATE_UNKNOWN);
+ EXPECT_TRUE(determineAllMetricUpdateStatuses(
+ config, oldMetricProducerMap, oldMetricProducers, metricToActivationMap,
+ /*replacedMatchers*/ {}, /*replacedConditions=*/{predicate.id()},
+ /*replacedStates=*/{}, metricsToUpdate));
+ EXPECT_EQ(metricsToUpdate[0], UPDATE_REPLACE);
+}
+
+TEST_F(ConfigUpdateTest, TestMetricConditionLinkDepsChanged) {
+ StatsdConfig config;
+ AtomMatcher startMatcher = CreateScreenTurnedOnAtomMatcher();
+ *config.add_atom_matcher() = startMatcher;
+ AtomMatcher stopMatcher = CreateScreenTurnedOffAtomMatcher();
+ *config.add_atom_matcher() = stopMatcher;
+ AtomMatcher whatMatcher = CreateScreenBrightnessChangedAtomMatcher();
+ *config.add_atom_matcher() = whatMatcher;
+
+ Predicate predicate = CreateScreenIsOnPredicate();
+ *config.add_predicate() = predicate;
+
+ Predicate linkPredicate = CreateScreenIsOffPredicate();
+ *config.add_predicate() = linkPredicate;
+
+ EventMetric* metric = config.add_event_metric();
+ metric->set_id(12345);
+ metric->set_what(whatMatcher.id());
+ metric->set_condition(predicate.id());
+ // Doesn't make sense as a real metric definition, but suffices as a separate predicate
+ // From the one in the condition.
+ MetricConditionLink* link = metric->add_links();
+ link->set_condition(linkPredicate.id());
+
+ // Create an initial config.
+ EXPECT_TRUE(initConfig(config));
+
+ unordered_map<int64_t, int> metricToActivationMap;
+ vector<UpdateStatus> metricsToUpdate(1, UPDATE_UNKNOWN);
+ EXPECT_TRUE(determineAllMetricUpdateStatuses(
+ config, oldMetricProducerMap, oldMetricProducers, metricToActivationMap,
+ /*replacedMatchers*/ {}, /*replacedConditions=*/{linkPredicate.id()},
+ /*replacedStates=*/{}, metricsToUpdate));
+ EXPECT_EQ(metricsToUpdate[0], UPDATE_REPLACE);
+}
+
+TEST_F(ConfigUpdateTest, TestEventMetricActivationDepsChange) {
+ StatsdConfig config;
+ AtomMatcher startMatcher = CreateScreenTurnedOnAtomMatcher();
+ *config.add_atom_matcher() = startMatcher;
+ AtomMatcher stopMatcher = CreateScreenTurnedOffAtomMatcher();
+ *config.add_atom_matcher() = stopMatcher;
+ AtomMatcher whatMatcher = CreateScreenBrightnessChangedAtomMatcher();
+ *config.add_atom_matcher() = whatMatcher;
+
+ Predicate predicate = CreateScreenIsOnPredicate();
+ *config.add_predicate() = predicate;
+
+ EventMetric* metric = config.add_event_metric();
+ metric->set_id(12345);
+ metric->set_what(whatMatcher.id());
+ metric->set_condition(predicate.id());
+
+ MetricActivation* activation = config.add_metric_activation();
+ activation->set_metric_id(12345);
+ EventActivation* eventActivation = activation->add_event_activation();
+ eventActivation->set_atom_matcher_id(startMatcher.id());
+ eventActivation->set_ttl_seconds(5);
+
+ // Create an initial config.
+ EXPECT_TRUE(initConfig(config));
+
+ unordered_map<int64_t, int> metricToActivationMap = {{12345, 0}};
+ vector<UpdateStatus> metricsToUpdate(1, UPDATE_UNKNOWN);
+ EXPECT_TRUE(determineAllMetricUpdateStatuses(
+ config, oldMetricProducerMap, oldMetricProducers, metricToActivationMap,
+ /*replacedMatchers*/ {startMatcher.id()}, /*replacedConditions=*/{},
+ /*replacedStates=*/{}, metricsToUpdate));
+ EXPECT_EQ(metricsToUpdate[0], UPDATE_REPLACE);
+}
+
+TEST_F(ConfigUpdateTest, TestUpdateEventMetrics) {
+ StatsdConfig config;
+
+ // Add atom matchers/predicates. These are mostly needed for initStatsdConfig
+ AtomMatcher matcher1 = CreateScreenTurnedOnAtomMatcher();
+ int64_t matcher1Id = matcher1.id();
+ *config.add_atom_matcher() = matcher1;
+
+ AtomMatcher matcher2 = CreateScreenTurnedOffAtomMatcher();
+ int64_t matcher2Id = matcher2.id();
+ *config.add_atom_matcher() = matcher2;
+
+ AtomMatcher matcher3 = CreateStartScheduledJobAtomMatcher();
+ int64_t matcher3Id = matcher3.id();
+ *config.add_atom_matcher() = matcher3;
+
+ AtomMatcher matcher4 = CreateFinishScheduledJobAtomMatcher();
+ int64_t matcher4Id = matcher4.id();
+ *config.add_atom_matcher() = matcher4;
+
+ AtomMatcher matcher5 = CreateBatterySaverModeStartAtomMatcher();
+ int64_t matcher5Id = matcher5.id();
+ *config.add_atom_matcher() = matcher5;
+
+ Predicate predicate1 = CreateScreenIsOnPredicate();
+ int64_t predicate1Id = predicate1.id();
+ *config.add_predicate() = predicate1;
+
+ Predicate predicate2 = CreateScheduledJobPredicate();
+ int64_t predicate2Id = predicate2.id();
+ *config.add_predicate() = predicate2;
+
+ // Add a few event metrics.
+ // Will be preserved.
+ EventMetric event1 = createEventMetric("EVENT1", matcher1Id, predicate2Id);
+ int64_t event1Id = event1.id();
+ *config.add_event_metric() = event1;
+
+ // Will be replaced.
+ EventMetric event2 = createEventMetric("EVENT2", matcher2Id, nullopt);
+ int64_t event2Id = event2.id();
+ *config.add_event_metric() = event2;
+
+ // Will be replaced.
+ EventMetric event3 = createEventMetric("EVENT3", matcher3Id, nullopt);
+ int64_t event3Id = event3.id();
+ *config.add_event_metric() = event3;
+
+ MetricActivation event3Activation;
+ event3Activation.set_metric_id(event3Id);
+ EventActivation* eventActivation = event3Activation.add_event_activation();
+ eventActivation->set_atom_matcher_id(matcher5Id);
+ eventActivation->set_ttl_seconds(5);
+ *config.add_metric_activation() = event3Activation;
+
+ // Will be replaced.
+ EventMetric event4 = createEventMetric("EVENT4", matcher4Id, predicate1Id);
+ int64_t event4Id = event4.id();
+ *config.add_event_metric() = event4;
+
+ // Will be deleted.
+ EventMetric event5 = createEventMetric("EVENT5", matcher5Id, nullopt);
+ int64_t event5Id = event5.id();
+ *config.add_event_metric() = event5;
+
+ EXPECT_TRUE(initConfig(config));
+
+ // Used later to ensure the condition wizard is replaced. Get it before doing the update.
+ sp<ConditionWizard> oldConditionWizard = oldMetricProducers[0]->mWizard;
+ EXPECT_EQ(oldConditionWizard->getStrongCount(), oldMetricProducers.size() + 1);
+
+ // Add a condition to event2, causing it to be replaced.
+ event2.set_condition(predicate1Id);
+
+ // Mark matcher 5 as replaced. Causes event3 to be replaced.
+ set<int64_t> replacedMatchers;
+ replacedMatchers.insert(matcher5Id);
+
+ // Mark predicate 1 as replaced. Causes event4 to be replaced.
+ set<int64_t> replacedConditions;
+ replacedConditions.insert(predicate1Id);
+
+ // Fake that predicate 2 is true.
+ ASSERT_EQ(oldMetricProducers[0]->getMetricId(), event1Id);
+ oldMetricProducers[0]->onConditionChanged(true, /*timestamp=*/0);
+ EXPECT_EQ(oldMetricProducers[0]->mCondition, ConditionState::kTrue);
+
+ // New event metric. Should have an initial condition of true since it depends on predicate2.
+ EventMetric event6 = createEventMetric("EVENT6", matcher3Id, predicate2Id);
+ int64_t event6Id = event6.id();
+ MetricActivation event6Activation;
+ event6Activation.set_metric_id(event6Id);
+ eventActivation = event6Activation.add_event_activation();
+ eventActivation->set_atom_matcher_id(matcher5Id);
+ eventActivation->set_ttl_seconds(20);
+
+ // Map the matchers and predicates in reverse order to force the indices to change.
+ std::unordered_map<int64_t, int> newAtomMatchingTrackerMap;
+ const int matcher5Index = 0;
+ newAtomMatchingTrackerMap[matcher5Id] = 0;
+ const int matcher4Index = 1;
+ newAtomMatchingTrackerMap[matcher4Id] = 1;
+ const int matcher3Index = 2;
+ newAtomMatchingTrackerMap[matcher3Id] = 2;
+ const int matcher2Index = 3;
+ newAtomMatchingTrackerMap[matcher2Id] = 3;
+ const int matcher1Index = 4;
+ newAtomMatchingTrackerMap[matcher1Id] = 4;
+ // Use the existing matchers. A bit hacky, but saves code and we don't rely on them.
+ vector<sp<AtomMatchingTracker>> newAtomMatchingTrackers(5);
+ std::reverse_copy(oldAtomMatchingTrackers.begin(), oldAtomMatchingTrackers.end(),
+ newAtomMatchingTrackers.begin());
+
+ std::unordered_map<int64_t, int> newConditionTrackerMap;
+ const int predicate2Index = 0;
+ newConditionTrackerMap[predicate2Id] = 0;
+ const int predicate1Index = 1;
+ newConditionTrackerMap[predicate1Id] = 1;
+ // Use the existing conditionTrackers. A bit hacky, but saves code and we don't rely on them.
+ vector<sp<ConditionTracker>> newConditionTrackers(2);
+ std::reverse_copy(oldConditionTrackers.begin(), oldConditionTrackers.end(),
+ newConditionTrackers.begin());
+ // Fake that predicate2 is true.
+ vector<ConditionState> conditionCache = {ConditionState::kTrue, ConditionState::kUnknown};
+
+ StatsdConfig newConfig;
+ *newConfig.add_event_metric() = event6;
+ const int event6Index = 0;
+ *newConfig.add_event_metric() = event3;
+ const int event3Index = 1;
+ *newConfig.add_event_metric() = event1;
+ const int event1Index = 2;
+ *newConfig.add_event_metric() = event4;
+ const int event4Index = 3;
+ *newConfig.add_event_metric() = event2;
+ const int event2Index = 4;
+ *newConfig.add_metric_activation() = event3Activation;
+ *newConfig.add_metric_activation() = event6Activation;
+
+ // Output data structures to validate.
+ unordered_map<int64_t, int> newMetricProducerMap;
+ vector<sp<MetricProducer>> newMetricProducers;
+ unordered_map<int, vector<int>> conditionToMetricMap;
+ unordered_map<int, vector<int>> trackerToMetricMap;
+ set<int64_t> noReportMetricIds;
+ unordered_map<int, vector<int>> activationAtomTrackerToMetricMap;
+ unordered_map<int, vector<int>> deactivationAtomTrackerToMetricMap;
+ vector<int> metricsWithActivation;
+ EXPECT_TRUE(updateMetrics(
+ key, newConfig, /*timeBaseNs=*/123, /*currentTimeNs=*/12345, new StatsPullerManager(),
+ oldAtomMatchingTrackerMap, newAtomMatchingTrackerMap, replacedMatchers,
+ newAtomMatchingTrackers, newConditionTrackerMap, replacedConditions,
+ newConditionTrackers, conditionCache, /*stateAtomIdMap=*/{}, /*allStateGroupMaps=*/{},
+ /*replacedStates=*/{}, oldMetricProducerMap, oldMetricProducers, newMetricProducerMap,
+ newMetricProducers, conditionToMetricMap, trackerToMetricMap, noReportMetricIds,
+ activationAtomTrackerToMetricMap, deactivationAtomTrackerToMetricMap,
+ metricsWithActivation));
+
+ unordered_map<int64_t, int> expectedMetricProducerMap = {
+ {event1Id, event1Index}, {event2Id, event2Index}, {event3Id, event3Index},
+ {event4Id, event4Index}, {event6Id, event6Index},
+ };
+ EXPECT_THAT(newMetricProducerMap, ContainerEq(expectedMetricProducerMap));
+
+ // Make sure preserved metrics are the same.
+ ASSERT_EQ(newMetricProducers.size(), 5);
+ EXPECT_EQ(oldMetricProducers[oldMetricProducerMap.at(event1Id)],
+ newMetricProducers[newMetricProducerMap.at(event1Id)]);
+
+ // Make sure replaced conditions are different and included in replacedConditions.
+ EXPECT_NE(oldMetricProducers[oldMetricProducerMap.at(event2Id)],
+ newMetricProducers[newMetricProducerMap.at(event2Id)]);
+ EXPECT_NE(oldMetricProducers[oldMetricProducerMap.at(event3Id)],
+ newMetricProducers[newMetricProducerMap.at(event3Id)]);
+ EXPECT_NE(oldMetricProducers[oldMetricProducerMap.at(event4Id)],
+ newMetricProducers[newMetricProducerMap.at(event4Id)]);
+
+ // Verify the conditionToMetricMap.
+ ASSERT_EQ(conditionToMetricMap.size(), 2);
+ const vector<int>& condition1Metrics = conditionToMetricMap[predicate1Index];
+ EXPECT_THAT(condition1Metrics, UnorderedElementsAre(event2Index, event4Index));
+ const vector<int>& condition2Metrics = conditionToMetricMap[predicate2Index];
+ EXPECT_THAT(condition2Metrics, UnorderedElementsAre(event1Index, event6Index));
+
+ // Verify the trackerToMetricMap.
+ ASSERT_EQ(trackerToMetricMap.size(), 4);
+ const vector<int>& matcher1Metrics = trackerToMetricMap[matcher1Index];
+ EXPECT_THAT(matcher1Metrics, UnorderedElementsAre(event1Index));
+ const vector<int>& matcher2Metrics = trackerToMetricMap[matcher2Index];
+ EXPECT_THAT(matcher2Metrics, UnorderedElementsAre(event2Index));
+ const vector<int>& matcher3Metrics = trackerToMetricMap[matcher3Index];
+ EXPECT_THAT(matcher3Metrics, UnorderedElementsAre(event3Index, event6Index));
+ const vector<int>& matcher4Metrics = trackerToMetricMap[matcher4Index];
+ EXPECT_THAT(matcher4Metrics, UnorderedElementsAre(event4Index));
+
+ // Verify event activation/deactivation maps.
+ ASSERT_EQ(activationAtomTrackerToMetricMap.size(), 1);
+ EXPECT_THAT(activationAtomTrackerToMetricMap[matcher5Index],
+ UnorderedElementsAre(event3Index, event6Index));
+ ASSERT_EQ(deactivationAtomTrackerToMetricMap.size(), 0);
+ ASSERT_EQ(metricsWithActivation.size(), 2);
+ EXPECT_THAT(metricsWithActivation, UnorderedElementsAre(event3Index, event6Index));
+
+ // Verify tracker indices/ids/conditions are correct.
+ EXPECT_EQ(newMetricProducers[event1Index]->getMetricId(), event1Id);
+ EXPECT_EQ(newMetricProducers[event1Index]->mConditionTrackerIndex, predicate2Index);
+ EXPECT_EQ(newMetricProducers[event1Index]->mCondition, ConditionState::kTrue);
+ EXPECT_EQ(newMetricProducers[event2Index]->getMetricId(), event2Id);
+ EXPECT_EQ(newMetricProducers[event2Index]->mConditionTrackerIndex, predicate1Index);
+ EXPECT_EQ(newMetricProducers[event2Index]->mCondition, ConditionState::kUnknown);
+ EXPECT_EQ(newMetricProducers[event3Index]->getMetricId(), event3Id);
+ EXPECT_EQ(newMetricProducers[event3Index]->mConditionTrackerIndex, -1);
+ EXPECT_EQ(newMetricProducers[event3Index]->mCondition, ConditionState::kTrue);
+ EXPECT_EQ(newMetricProducers[event4Index]->getMetricId(), event4Id);
+ EXPECT_EQ(newMetricProducers[event4Index]->mConditionTrackerIndex, predicate1Index);
+ EXPECT_EQ(newMetricProducers[event4Index]->mCondition, ConditionState::kUnknown);
+ EXPECT_EQ(newMetricProducers[event6Index]->getMetricId(), event6Id);
+ EXPECT_EQ(newMetricProducers[event6Index]->mConditionTrackerIndex, predicate2Index);
+ EXPECT_EQ(newMetricProducers[event6Index]->mCondition, ConditionState::kTrue);
+
+ sp<ConditionWizard> newConditionWizard = newMetricProducers[0]->mWizard;
+ EXPECT_NE(newConditionWizard, oldConditionWizard);
+ EXPECT_EQ(newConditionWizard->getStrongCount(), newMetricProducers.size() + 1);
+ oldMetricProducers.clear();
+ // Only reference to the old wizard should be the one in the test.
+ EXPECT_EQ(oldConditionWizard->getStrongCount(), 1);
+}
+
+TEST_F(ConfigUpdateTest, TestUpdateMetricActivations) {
+ StatsdConfig config;
+ // Add atom matchers
+ AtomMatcher matcher1 = CreateScreenTurnedOnAtomMatcher();
+ int64_t matcher1Id = matcher1.id();
+ *config.add_atom_matcher() = matcher1;
+
+ AtomMatcher matcher2 = CreateScreenTurnedOffAtomMatcher();
+ int64_t matcher2Id = matcher2.id();
+ *config.add_atom_matcher() = matcher2;
+
+ AtomMatcher matcher3 = CreateStartScheduledJobAtomMatcher();
+ int64_t matcher3Id = matcher3.id();
+ *config.add_atom_matcher() = matcher3;
+
+ AtomMatcher matcher4 = CreateFinishScheduledJobAtomMatcher();
+ int64_t matcher4Id = matcher4.id();
+ *config.add_atom_matcher() = matcher4;
+
+ // Add an event metric with multiple activations.
+ EventMetric event1 = createEventMetric("EVENT1", matcher1Id, nullopt);
+ int64_t event1Id = event1.id();
+ *config.add_event_metric() = event1;
+
+ int64_t matcher2TtlSec = 2, matcher3TtlSec = 3, matcher4TtlSec = 4;
+ MetricActivation metricActivation;
+ metricActivation.set_metric_id(event1Id);
+ EventActivation* activation = metricActivation.add_event_activation();
+ activation->set_atom_matcher_id(matcher2Id);
+ activation->set_ttl_seconds(matcher2TtlSec);
+ activation->set_activation_type(ACTIVATE_IMMEDIATELY);
+ activation->set_deactivation_atom_matcher_id(matcher1Id);
+ activation = metricActivation.add_event_activation();
+ activation->set_atom_matcher_id(matcher3Id);
+ activation->set_ttl_seconds(matcher3TtlSec);
+ activation->set_activation_type(ACTIVATE_ON_BOOT);
+ activation->set_deactivation_atom_matcher_id(matcher1Id);
+ activation = metricActivation.add_event_activation();
+ activation->set_atom_matcher_id(matcher4Id);
+ activation->set_ttl_seconds(matcher4TtlSec);
+ activation->set_activation_type(ACTIVATE_IMMEDIATELY);
+ activation->set_deactivation_atom_matcher_id(matcher2Id);
+ *config.add_metric_activation() = metricActivation;
+
+ EXPECT_TRUE(initConfig(config));
+
+ // Activate some of the event activations.
+ ASSERT_EQ(oldMetricProducers[0]->getMetricId(), event1Id);
+ int64_t matcher2StartNs = 12345;
+ oldMetricProducers[0]->activate(oldAtomMatchingTrackerMap[matcher2Id], matcher2StartNs);
+ int64_t matcher3StartNs = 23456;
+ oldMetricProducers[0]->activate(oldAtomMatchingTrackerMap[matcher3Id], matcher3StartNs);
+ EXPECT_TRUE(oldMetricProducers[0]->isActive());
+
+ // Map the matchers and predicates in reverse order to force the indices to change.
+ std::unordered_map<int64_t, int> newAtomMatchingTrackerMap;
+ const int matcher4Index = 0;
+ newAtomMatchingTrackerMap[matcher4Id] = 0;
+ const int matcher3Index = 1;
+ newAtomMatchingTrackerMap[matcher3Id] = 1;
+ const int matcher2Index = 2;
+ newAtomMatchingTrackerMap[matcher2Id] = 2;
+ const int matcher1Index = 3;
+ newAtomMatchingTrackerMap[matcher1Id] = 3;
+ // Use the existing matchers. A bit hacky, but saves code and we don't rely on them.
+ vector<sp<AtomMatchingTracker>> newAtomMatchingTrackers(4);
+ std::reverse_copy(oldAtomMatchingTrackers.begin(), oldAtomMatchingTrackers.end(),
+ newAtomMatchingTrackers.begin());
+ set<int64_t> replacedMatchers;
+
+ unordered_map<int64_t, int> newConditionTrackerMap;
+ vector<sp<ConditionTracker>> newConditionTrackers;
+ set<int64_t> replacedConditions;
+ vector<ConditionState> conditionCache;
+ unordered_map<int64_t, int> newMetricProducerMap;
+ vector<sp<MetricProducer>> newMetricProducers;
+ unordered_map<int, vector<int>> conditionToMetricMap;
+ unordered_map<int, vector<int>> trackerToMetricMap;
+ set<int64_t> noReportMetricIds;
+ unordered_map<int, vector<int>> activationAtomTrackerToMetricMap;
+ unordered_map<int, vector<int>> deactivationAtomTrackerToMetricMap;
+ vector<int> metricsWithActivation;
+ EXPECT_TRUE(updateMetrics(
+ key, config, /*timeBaseNs=*/123, /*currentTimeNs=*/12345, new StatsPullerManager(),
+ oldAtomMatchingTrackerMap, newAtomMatchingTrackerMap, replacedMatchers,
+ newAtomMatchingTrackers, newConditionTrackerMap, replacedConditions,
+ newConditionTrackers, conditionCache, /*stateAtomIdMap=*/{}, /*allStateGroupMaps=*/{},
+ /*replacedStates=*/{}, oldMetricProducerMap, oldMetricProducers, newMetricProducerMap,
+ newMetricProducers, conditionToMetricMap, trackerToMetricMap, noReportMetricIds,
+ activationAtomTrackerToMetricMap, deactivationAtomTrackerToMetricMap,
+ metricsWithActivation));
+
+ // Verify event activation/deactivation maps.
+ ASSERT_EQ(activationAtomTrackerToMetricMap.size(), 3);
+ EXPECT_THAT(activationAtomTrackerToMetricMap[matcher2Index], UnorderedElementsAre(0));
+ EXPECT_THAT(activationAtomTrackerToMetricMap[matcher3Index], UnorderedElementsAre(0));
+ EXPECT_THAT(activationAtomTrackerToMetricMap[matcher4Index], UnorderedElementsAre(0));
+ ASSERT_EQ(deactivationAtomTrackerToMetricMap.size(), 2);
+ EXPECT_THAT(deactivationAtomTrackerToMetricMap[matcher1Index], UnorderedElementsAre(0, 0));
+ EXPECT_THAT(deactivationAtomTrackerToMetricMap[matcher2Index], UnorderedElementsAre(0));
+ ASSERT_EQ(metricsWithActivation.size(), 1);
+ EXPECT_THAT(metricsWithActivation, UnorderedElementsAre(0));
+
+ // Verify mEventActivation and mEventDeactivation map of the producer.
+ sp<MetricProducer> producer = newMetricProducers[0];
+ EXPECT_TRUE(producer->isActive());
+ ASSERT_EQ(producer->mEventActivationMap.size(), 3);
+ shared_ptr<Activation> matcher2Activation = producer->mEventActivationMap[matcher2Index];
+ EXPECT_EQ(matcher2Activation->ttl_ns, matcher2TtlSec * NS_PER_SEC);
+ EXPECT_EQ(matcher2Activation->activationType, ACTIVATE_IMMEDIATELY);
+ EXPECT_EQ(matcher2Activation->state, kActive);
+ EXPECT_EQ(matcher2Activation->start_ns, matcher2StartNs);
+ shared_ptr<Activation> matcher3Activation = producer->mEventActivationMap[matcher3Index];
+ EXPECT_EQ(matcher3Activation->ttl_ns, matcher3TtlSec * NS_PER_SEC);
+ EXPECT_EQ(matcher3Activation->activationType, ACTIVATE_ON_BOOT);
+ EXPECT_EQ(matcher3Activation->state, kActiveOnBoot);
+ shared_ptr<Activation> matcher4Activation = producer->mEventActivationMap[matcher4Index];
+ EXPECT_EQ(matcher4Activation->ttl_ns, matcher4TtlSec * NS_PER_SEC);
+ EXPECT_EQ(matcher4Activation->activationType, ACTIVATE_IMMEDIATELY);
+ EXPECT_EQ(matcher4Activation->state, kNotActive);
+
+ ASSERT_EQ(producer->mEventDeactivationMap.size(), 2);
+ EXPECT_THAT(producer->mEventDeactivationMap[matcher1Index],
+ UnorderedElementsAre(matcher2Activation, matcher3Activation));
+ EXPECT_THAT(producer->mEventDeactivationMap[matcher2Index],
+ UnorderedElementsAre(matcher4Activation));
+}
} // namespace statsd
} // namespace os
} // namespace android
diff --git a/core/java/android/app/ActivityThread.java b/core/java/android/app/ActivityThread.java
index 0fd0e02a224a..312d122d0ba4 100644
--- a/core/java/android/app/ActivityThread.java
+++ b/core/java/android/app/ActivityThread.java
@@ -614,7 +614,7 @@ public final class ActivityThread extends ClientTransactionHandler {
throw new IllegalStateException(
"Received config update for non-existing activity");
}
- activity.mMainThread.handleActivityConfigurationChanged(token, overrideConfig,
+ activity.mMainThread.handleActivityConfigurationChanged(this, overrideConfig,
newDisplayId);
};
}
@@ -3475,13 +3475,9 @@ public final class ActivityThread extends ClientTransactionHandler {
}
@Override
- public void handleStartActivity(IBinder token, PendingTransactionActions pendingActions) {
- final ActivityClientRecord r = mActivities.get(token);
+ public void handleStartActivity(ActivityClientRecord r,
+ PendingTransactionActions pendingActions) {
final Activity activity = r.activity;
- if (r.activity == null) {
- // TODO(lifecycler): What do we do in this case?
- return;
- }
if (!r.stopped) {
throw new IllegalStateException("Can't start activity that is not stopped.");
}
@@ -3687,12 +3683,7 @@ public final class ActivityThread extends ClientTransactionHandler {
}
@Override
- public void handleNewIntent(IBinder token, List<ReferrerIntent> intents) {
- final ActivityClientRecord r = mActivities.get(token);
- if (r == null) {
- return;
- }
-
+ public void handleNewIntent(ActivityClientRecord r, List<ReferrerIntent> intents) {
checkAndBlockForNetworkAccess();
deliverNewIntents(r, intents);
}
@@ -3881,13 +3872,7 @@ public final class ActivityThread extends ClientTransactionHandler {
}
@Override
- public void handlePictureInPictureRequested(IBinder token) {
- final ActivityClientRecord r = mActivities.get(token);
- if (r == null) {
- Log.w(TAG, "Activity to request PIP to no longer exists");
- return;
- }
-
+ public void handlePictureInPictureRequested(ActivityClientRecord r) {
final boolean receivedByApp = r.activity.onPictureInPictureRequested();
if (!receivedByApp) {
// Previous recommendation was for apps to enter picture-in-picture in
@@ -4417,22 +4402,21 @@ public final class ActivityThread extends ClientTransactionHandler {
/**
* Resume the activity.
- * @param token Target activity token.
+ * @param r Target activity record.
* @param finalStateRequest Flag indicating if this is part of final state resolution for a
* transaction.
* @param reason Reason for performing the action.
*
- * @return The {@link ActivityClientRecord} that was resumed, {@code null} otherwise.
+ * @return {@code true} that was resumed, {@code false} otherwise.
*/
@VisibleForTesting
- public ActivityClientRecord performResumeActivity(IBinder token, boolean finalStateRequest,
+ public boolean performResumeActivity(ActivityClientRecord r, boolean finalStateRequest,
String reason) {
- final ActivityClientRecord r = mActivities.get(token);
if (localLOGV) {
Slog.v(TAG, "Performing resume of " + r + " finished=" + r.activity.mFinished);
}
- if (r == null || r.activity.mFinished) {
- return null;
+ if (r.activity.mFinished) {
+ return false;
}
if (r.getLifecycleState() == ON_RESUME) {
if (!finalStateRequest) {
@@ -4446,7 +4430,7 @@ public final class ActivityThread extends ClientTransactionHandler {
// handle two resume requests for the final state. For cases other than this
// one, we don't expect it to happen.
}
- return null;
+ return false;
}
if (finalStateRequest) {
r.hideForNow = false;
@@ -4477,7 +4461,7 @@ public final class ActivityThread extends ClientTransactionHandler {
+ r.intent.getComponent().toShortString() + ": " + e.toString(), e);
}
}
- return r;
+ return true;
}
static final void cleanUpPendingRemoveWindows(ActivityClientRecord r, boolean force) {
@@ -4498,20 +4482,19 @@ public final class ActivityThread extends ClientTransactionHandler {
}
@Override
- public void handleResumeActivity(IBinder token, boolean finalStateRequest, boolean isForward,
- String reason) {
+ public void handleResumeActivity(ActivityClientRecord r, boolean finalStateRequest,
+ boolean isForward, String reason) {
// If we are getting ready to gc after going to the background, well
// we are back active so skip it.
unscheduleGcIdler();
mSomeActivitiesChanged = true;
// TODO Push resumeArgs into the activity for consideration
- final ActivityClientRecord r = performResumeActivity(token, finalStateRequest, reason);
- if (r == null) {
- // We didn't actually resume the activity, so skipping any follow-up actions.
+ // skip below steps for double-resume and r.mFinish = true case.
+ if (!performResumeActivity(r, finalStateRequest, reason)) {
return;
}
- if (mActivitiesToBeDestroyed.containsKey(token)) {
+ if (mActivitiesToBeDestroyed.containsKey(r.token)) {
// Although the activity is resumed, it is going to be destroyed. So the following
// UI operations are unnecessary and also prevents exception because its token may
// be gone that window manager cannot recognize it. All necessary cleanup actions
@@ -4629,13 +4612,8 @@ public final class ActivityThread extends ClientTransactionHandler {
@Override
- public void handleTopResumedActivityChanged(IBinder token, boolean onTop, String reason) {
- ActivityClientRecord r = mActivities.get(token);
- if (r == null || r.activity == null) {
- Slog.w(TAG, "Not found target activity to report position change for token: " + token);
- return;
- }
-
+ public void handleTopResumedActivityChanged(ActivityClientRecord r, boolean onTop,
+ String reason) {
if (DEBUG_ORDER) {
Slog.d(TAG, "Received position change to top: " + onTop + " for activity: " + r);
}
@@ -4668,23 +4646,20 @@ public final class ActivityThread extends ClientTransactionHandler {
}
@Override
- public void handlePauseActivity(IBinder token, boolean finished, boolean userLeaving,
+ public void handlePauseActivity(ActivityClientRecord r, boolean finished, boolean userLeaving,
int configChanges, PendingTransactionActions pendingActions, String reason) {
- ActivityClientRecord r = mActivities.get(token);
- if (r != null) {
- if (userLeaving) {
- performUserLeavingActivity(r);
- }
+ if (userLeaving) {
+ performUserLeavingActivity(r);
+ }
- r.activity.mConfigChangeFlags |= configChanges;
- performPauseActivity(r, finished, reason, pendingActions);
+ r.activity.mConfigChangeFlags |= configChanges;
+ performPauseActivity(r, finished, reason, pendingActions);
- // Make sure any pending writes are now committed.
- if (r.isPreHoneycomb()) {
- QueuedWork.waitToFinish();
- }
- mSomeActivitiesChanged = true;
+ // Make sure any pending writes are now committed.
+ if (r.isPreHoneycomb()) {
+ QueuedWork.waitToFinish();
}
+ mSomeActivitiesChanged = true;
}
final void performUserLeavingActivity(ActivityClientRecord r) {
@@ -4781,8 +4756,11 @@ public final class ActivityThread extends ClientTransactionHandler {
r.setState(ON_PAUSE);
}
+ // TODO(b/127877792): Make LocalActivityManager call performStopActivityInner. We cannot remove
+ // this since it's a high usage hidden API.
/** Called from {@link LocalActivityManager}. */
- @UnsupportedAppUsage
+ @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 127877792,
+ publicAlternatives = "{@code N/A}")
final void performStopActivity(IBinder token, boolean saveState, String reason) {
ActivityClientRecord r = mActivities.get(token);
performStopActivityInner(r, null /* stopInfo */, saveState, false /* finalStateRequest */,
@@ -4823,44 +4801,42 @@ public final class ActivityThread extends ClientTransactionHandler {
private void performStopActivityInner(ActivityClientRecord r, StopInfo info,
boolean saveState, boolean finalStateRequest, String reason) {
if (localLOGV) Slog.v(TAG, "Performing stop of " + r);
- if (r != null) {
- if (r.stopped) {
- if (r.activity.mFinished) {
- // If we are finishing, we won't call onResume() in certain
- // cases. So here we likewise don't want to call onStop()
- // if the activity isn't resumed.
- return;
- }
- if (!finalStateRequest) {
- final RuntimeException e = new RuntimeException(
- "Performing stop of activity that is already stopped: "
- + r.intent.getComponent().toShortString());
- Slog.e(TAG, e.getMessage(), e);
- Slog.e(TAG, r.getStateString());
- }
+ if (r.stopped) {
+ if (r.activity.mFinished) {
+ // If we are finishing, we won't call onResume() in certain
+ // cases. So here we likewise don't want to call onStop()
+ // if the activity isn't resumed.
+ return;
}
+ if (!finalStateRequest) {
+ final RuntimeException e = new RuntimeException(
+ "Performing stop of activity that is already stopped: "
+ + r.intent.getComponent().toShortString());
+ Slog.e(TAG, e.getMessage(), e);
+ Slog.e(TAG, r.getStateString());
+ }
+ }
- // One must first be paused before stopped...
- performPauseActivityIfNeeded(r, reason);
+ // One must first be paused before stopped...
+ performPauseActivityIfNeeded(r, reason);
- if (info != null) {
- try {
- // First create a thumbnail for the activity...
- // For now, don't create the thumbnail here; we are
- // doing that by doing a screen snapshot.
- info.setDescription(r.activity.onCreateDescription());
- } catch (Exception e) {
- if (!mInstrumentation.onException(r.activity, e)) {
- throw new RuntimeException(
- "Unable to save state of activity "
- + r.intent.getComponent().toShortString()
- + ": " + e.toString(), e);
- }
+ if (info != null) {
+ try {
+ // First create a thumbnail for the activity...
+ // For now, don't create the thumbnail here; we are
+ // doing that by doing a screen snapshot.
+ info.setDescription(r.activity.onCreateDescription());
+ } catch (Exception e) {
+ if (!mInstrumentation.onException(r.activity, e)) {
+ throw new RuntimeException(
+ "Unable to save state of activity "
+ + r.intent.getComponent().toShortString()
+ + ": " + e.toString(), e);
}
}
-
- callActivityOnStop(r, saveState, reason);
}
+
+ callActivityOnStop(r, saveState, reason);
}
/**
@@ -4927,9 +4903,8 @@ public final class ActivityThread extends ClientTransactionHandler {
}
@Override
- public void handleStopActivity(IBinder token, int configChanges,
+ public void handleStopActivity(ActivityClientRecord r, int configChanges,
PendingTransactionActions pendingActions, boolean finalStateRequest, String reason) {
- final ActivityClientRecord r = mActivities.get(token);
r.activity.mConfigChangeFlags |= configChanges;
final StopInfo stopInfo = new StopInfo();
@@ -4965,8 +4940,7 @@ public final class ActivityThread extends ClientTransactionHandler {
}
@Override
- public void performRestartActivity(IBinder token, boolean start) {
- ActivityClientRecord r = mActivities.get(token);
+ public void performRestartActivity(ActivityClientRecord r, boolean start) {
if (r.stopped) {
r.activity.performRestart(start, "performRestartActivity");
if (start) {
@@ -5053,110 +5027,98 @@ public final class ActivityThread extends ClientTransactionHandler {
}
@Override
- public void handleSendResult(IBinder token, List<ResultInfo> results, String reason) {
- ActivityClientRecord r = mActivities.get(token);
+ public void handleSendResult(ActivityClientRecord r, List<ResultInfo> results, String reason) {
if (DEBUG_RESULTS) Slog.v(TAG, "Handling send result to " + r);
- if (r != null) {
- final boolean resumed = !r.paused;
- if (!r.activity.mFinished && r.activity.mDecor != null
- && r.hideForNow && resumed) {
- // We had hidden the activity because it started another
- // one... we have gotten a result back and we are not
- // paused, so make sure our window is visible.
- updateVisibility(r, true);
- }
- if (resumed) {
- try {
- // Now we are idle.
- r.activity.mCalled = false;
- mInstrumentation.callActivityOnPause(r.activity);
- if (!r.activity.mCalled) {
- throw new SuperNotCalledException(
- "Activity " + r.intent.getComponent().toShortString()
- + " did not call through to super.onPause()");
- }
- } catch (SuperNotCalledException e) {
- throw e;
- } catch (Exception e) {
- if (!mInstrumentation.onException(r.activity, e)) {
- throw new RuntimeException(
- "Unable to pause activity "
- + r.intent.getComponent().toShortString()
- + ": " + e.toString(), e);
- }
+ final boolean resumed = !r.paused;
+ if (!r.activity.mFinished && r.activity.mDecor != null
+ && r.hideForNow && resumed) {
+ // We had hidden the activity because it started another
+ // one... we have gotten a result back and we are not
+ // paused, so make sure our window is visible.
+ updateVisibility(r, true);
+ }
+ if (resumed) {
+ try {
+ // Now we are idle.
+ r.activity.mCalled = false;
+ mInstrumentation.callActivityOnPause(r.activity);
+ if (!r.activity.mCalled) {
+ throw new SuperNotCalledException(
+ "Activity " + r.intent.getComponent().toShortString()
+ + " did not call through to super.onPause()");
+ }
+ } catch (SuperNotCalledException e) {
+ throw e;
+ } catch (Exception e) {
+ if (!mInstrumentation.onException(r.activity, e)) {
+ throw new RuntimeException(
+ "Unable to pause activity "
+ + r.intent.getComponent().toShortString()
+ + ": " + e.toString(), e);
}
- }
- checkAndBlockForNetworkAccess();
- deliverResults(r, results, reason);
- if (resumed) {
- r.activity.performResume(false, reason);
}
}
+ checkAndBlockForNetworkAccess();
+ deliverResults(r, results, reason);
+ if (resumed) {
+ r.activity.performResume(false, reason);
+ }
}
/** Core implementation of activity destroy call. */
- ActivityClientRecord performDestroyActivity(IBinder token, boolean finishing,
+ void performDestroyActivity(ActivityClientRecord r, boolean finishing,
int configChanges, boolean getNonConfigInstance, String reason) {
- ActivityClientRecord r = mActivities.get(token);
Class<? extends Activity> activityClass = null;
if (localLOGV) Slog.v(TAG, "Performing finish of " + r);
- if (r != null) {
- activityClass = r.activity.getClass();
- r.activity.mConfigChangeFlags |= configChanges;
- if (finishing) {
- r.activity.mFinished = true;
- }
+ activityClass = r.activity.getClass();
+ r.activity.mConfigChangeFlags |= configChanges;
+ if (finishing) {
+ r.activity.mFinished = true;
+ }
- performPauseActivityIfNeeded(r, "destroy");
+ performPauseActivityIfNeeded(r, "destroy");
- if (!r.stopped) {
- callActivityOnStop(r, false /* saveState */, "destroy");
- }
- if (getNonConfigInstance) {
- try {
- r.lastNonConfigurationInstances
- = r.activity.retainNonConfigurationInstances();
- } catch (Exception e) {
- if (!mInstrumentation.onException(r.activity, e)) {
- throw new RuntimeException(
- "Unable to retain activity "
- + r.intent.getComponent().toShortString()
- + ": " + e.toString(), e);
- }
- }
- }
+ if (!r.stopped) {
+ callActivityOnStop(r, false /* saveState */, "destroy");
+ }
+ if (getNonConfigInstance) {
try {
- r.activity.mCalled = false;
- mInstrumentation.callActivityOnDestroy(r.activity);
- if (!r.activity.mCalled) {
- throw new SuperNotCalledException(
- "Activity " + safeToComponentShortString(r.intent) +
- " did not call through to super.onDestroy()");
- }
- if (r.window != null) {
- r.window.closeAllPanels();
- }
- } catch (SuperNotCalledException e) {
- throw e;
+ r.lastNonConfigurationInstances = r.activity.retainNonConfigurationInstances();
} catch (Exception e) {
if (!mInstrumentation.onException(r.activity, e)) {
- throw new RuntimeException(
- "Unable to destroy activity " + safeToComponentShortString(r.intent)
- + ": " + e.toString(), e);
+ throw new RuntimeException("Unable to retain activity "
+ + r.intent.getComponent().toShortString() + ": " + e.toString(), e);
}
}
- r.setState(ON_DESTROY);
- mLastReportedWindowingMode.remove(r.activity.getActivityToken());
}
+ try {
+ r.activity.mCalled = false;
+ mInstrumentation.callActivityOnDestroy(r.activity);
+ if (!r.activity.mCalled) {
+ throw new SuperNotCalledException("Activity " + safeToComponentShortString(r.intent)
+ + " did not call through to super.onDestroy()");
+ }
+ if (r.window != null) {
+ r.window.closeAllPanels();
+ }
+ } catch (SuperNotCalledException e) {
+ throw e;
+ } catch (Exception e) {
+ if (!mInstrumentation.onException(r.activity, e)) {
+ throw new RuntimeException("Unable to destroy activity "
+ + safeToComponentShortString(r.intent) + ": " + e.toString(), e);
+ }
+ }
+ r.setState(ON_DESTROY);
+ mLastReportedWindowingMode.remove(r.activity.getActivityToken());
schedulePurgeIdler();
// updatePendingActivityConfiguration() reads from mActivities to update
// ActivityClientRecord which runs in a different thread. Protect modifications to
// mActivities to avoid race.
synchronized (mResourcesManager) {
- mActivities.remove(token);
+ mActivities.remove(r.token);
}
StrictMode.decrementExpectedActivityCount(activityClass);
- return r;
}
private static String safeToComponentShortString(Intent intent) {
@@ -5170,70 +5132,65 @@ public final class ActivityThread extends ClientTransactionHandler {
}
@Override
- public void handleDestroyActivity(IBinder token, boolean finishing, int configChanges,
+ public void handleDestroyActivity(ActivityClientRecord r, boolean finishing, int configChanges,
boolean getNonConfigInstance, String reason) {
- ActivityClientRecord r = performDestroyActivity(token, finishing,
- configChanges, getNonConfigInstance, reason);
- if (r != null) {
- cleanUpPendingRemoveWindows(r, finishing);
- WindowManager wm = r.activity.getWindowManager();
- View v = r.activity.mDecor;
- if (v != null) {
- if (r.activity.mVisibleFromServer) {
- mNumVisibleActivities--;
- }
- IBinder wtoken = v.getWindowToken();
- if (r.activity.mWindowAdded) {
- if (r.mPreserveWindow) {
- // Hold off on removing this until the new activity's
- // window is being added.
- r.mPendingRemoveWindow = r.window;
- r.mPendingRemoveWindowManager = wm;
- // We can only keep the part of the view hierarchy that we control,
- // everything else must be removed, because it might not be able to
- // behave properly when activity is relaunching.
- r.window.clearContentView();
- } else {
- wm.removeViewImmediate(v);
- }
- }
- if (wtoken != null && r.mPendingRemoveWindow == null) {
- WindowManagerGlobal.getInstance().closeAll(wtoken,
- r.activity.getClass().getName(), "Activity");
- } else if (r.mPendingRemoveWindow != null) {
- // We're preserving only one window, others should be closed so app views
- // will be detached before the final tear down. It should be done now because
- // some components (e.g. WebView) rely on detach callbacks to perform receiver
- // unregister and other cleanup.
- WindowManagerGlobal.getInstance().closeAllExceptView(token, v,
- r.activity.getClass().getName(), "Activity");
+ performDestroyActivity(r, finishing, configChanges, getNonConfigInstance, reason);
+ cleanUpPendingRemoveWindows(r, finishing);
+ WindowManager wm = r.activity.getWindowManager();
+ View v = r.activity.mDecor;
+ if (v != null) {
+ if (r.activity.mVisibleFromServer) {
+ mNumVisibleActivities--;
+ }
+ IBinder wtoken = v.getWindowToken();
+ if (r.activity.mWindowAdded) {
+ if (r.mPreserveWindow) {
+ // Hold off on removing this until the new activity's window is being added.
+ r.mPendingRemoveWindow = r.window;
+ r.mPendingRemoveWindowManager = wm;
+ // We can only keep the part of the view hierarchy that we control,
+ // everything else must be removed, because it might not be able to
+ // behave properly when activity is relaunching.
+ r.window.clearContentView();
+ } else {
+ wm.removeViewImmediate(v);
}
- r.activity.mDecor = null;
- }
- if (r.mPendingRemoveWindow == null) {
- // If we are delaying the removal of the activity window, then
- // we can't clean up all windows here. Note that we can't do
- // so later either, which means any windows that aren't closed
- // by the app will leak. Well we try to warning them a lot
- // about leaking windows, because that is a bug, so if they are
- // using this recreate facility then they get to live with leaks.
- WindowManagerGlobal.getInstance().closeAll(token,
- r.activity.getClass().getName(), "Activity");
}
-
- // Mocked out contexts won't be participating in the normal
- // process lifecycle, but if we're running with a proper
- // ApplicationContext we need to have it tear down things
- // cleanly.
- Context c = r.activity.getBaseContext();
- if (c instanceof ContextImpl) {
- ((ContextImpl) c).scheduleFinalCleanup(
+ if (wtoken != null && r.mPendingRemoveWindow == null) {
+ WindowManagerGlobal.getInstance().closeAll(wtoken,
+ r.activity.getClass().getName(), "Activity");
+ } else if (r.mPendingRemoveWindow != null) {
+ // We're preserving only one window, others should be closed so app views
+ // will be detached before the final tear down. It should be done now because
+ // some components (e.g. WebView) rely on detach callbacks to perform receiver
+ // unregister and other cleanup.
+ WindowManagerGlobal.getInstance().closeAllExceptView(r.token, v,
r.activity.getClass().getName(), "Activity");
}
+ r.activity.mDecor = null;
+ }
+ if (r.mPendingRemoveWindow == null) {
+ // If we are delaying the removal of the activity window, then
+ // we can't clean up all windows here. Note that we can't do
+ // so later either, which means any windows that aren't closed
+ // by the app will leak. Well we try to warning them a lot
+ // about leaking windows, because that is a bug, so if they are
+ // using this recreate facility then they get to live with leaks.
+ WindowManagerGlobal.getInstance().closeAll(r.token,
+ r.activity.getClass().getName(), "Activity");
+ }
+
+ // Mocked out contexts won't be participating in the normal
+ // process lifecycle, but if we're running with a proper
+ // ApplicationContext we need to have it tear down things
+ // cleanly.
+ Context c = r.activity.getBaseContext();
+ if (c instanceof ContextImpl) {
+ ((ContextImpl) c).scheduleFinalCleanup(r.activity.getClass().getName(), "Activity");
}
if (finishing) {
try {
- ActivityTaskManager.getService().activityDestroyed(token);
+ ActivityTaskManager.getService().activityDestroyed(r.token);
} catch (RemoteException ex) {
throw ex.rethrowFromSystemServer();
}
@@ -5455,7 +5412,7 @@ public final class ActivityThread extends ClientTransactionHandler {
callActivityOnStop(r, true /* saveState */, reason);
}
- handleDestroyActivity(r.token, false, configChanges, true, reason);
+ handleDestroyActivity(r, false, configChanges, true, reason);
r.activity = null;
r.window = null;
@@ -5483,12 +5440,10 @@ public final class ActivityThread extends ClientTransactionHandler {
}
@Override
- public void reportRelaunch(IBinder token, PendingTransactionActions pendingActions) {
+ public void reportRelaunch(ActivityClientRecord r, PendingTransactionActions pendingActions) {
try {
- ActivityTaskManager.getService().activityRelaunched(token);
- final ActivityClientRecord r = mActivities.get(token);
- if (pendingActions.shouldReportRelaunchToWindowManager() && r != null
- && r.window != null) {
+ ActivityTaskManager.getService().activityRelaunched(r.token);
+ if (pendingActions.shouldReportRelaunchToWindowManager() && r.window != null) {
r.window.reportActivityRelaunched();
}
} catch (RemoteException e) {
@@ -5647,13 +5602,7 @@ public final class ActivityThread extends ClientTransactionHandler {
*/
private Configuration performActivityConfigurationChanged(Activity activity,
Configuration newConfig, Configuration amOverrideConfig, int displayId) {
- if (activity == null) {
- throw new IllegalArgumentException("No activity provided.");
- }
final IBinder activityToken = activity.getActivityToken();
- if (activityToken == null) {
- throw new IllegalArgumentException("Activity token not set. Is the activity attached?");
- }
// WindowConfiguration differences aren't considered as public, check it separately.
// multi-window / pip mode changes, if any, should be sent before the configuration
@@ -5952,20 +5901,8 @@ public final class ActivityThread extends ClientTransactionHandler {
* processing any configurations older than {@code overrideConfig}.
*/
@Override
- public void updatePendingActivityConfiguration(IBinder activityToken,
+ public void updatePendingActivityConfiguration(ActivityClientRecord r,
Configuration overrideConfig) {
- final ActivityClientRecord r;
- synchronized (mResourcesManager) {
- r = mActivities.get(activityToken);
- }
-
- if (r == null) {
- if (DEBUG_CONFIGURATION) {
- Slog.w(TAG, "Not found target activity to update its pending config.");
- }
- return;
- }
-
synchronized (r) {
if (r.mPendingOverrideConfig != null
&& !r.mPendingOverrideConfig.isOtherSeqNewer(overrideConfig)) {
@@ -5985,21 +5922,14 @@ public final class ActivityThread extends ClientTransactionHandler {
* if {@link #updatePendingActivityConfiguration(IBinder, Configuration)} has been called with
* a newer config than {@code overrideConfig}.
*
- * @param activityToken Target activity token.
+ * @param r Target activity record.
* @param overrideConfig Activity override config.
* @param displayId Id of the display where activity was moved to, -1 if there was no move and
* value didn't change.
*/
@Override
- public void handleActivityConfigurationChanged(IBinder activityToken,
+ public void handleActivityConfigurationChanged(ActivityClientRecord r,
@NonNull Configuration overrideConfig, int displayId) {
- ActivityClientRecord r = mActivities.get(activityToken);
- // Check input params.
- if (r == null || r.activity == null) {
- if (DEBUG_CONFIGURATION) Slog.w(TAG, "Not found target activity to report to: " + r);
- return;
- }
-
synchronized (r) {
if (overrideConfig.isOtherSeqNewer(r.mPendingOverrideConfig)) {
if (DEBUG_CONFIGURATION) {
diff --git a/core/java/android/app/ApplicationPackageManager.java b/core/java/android/app/ApplicationPackageManager.java
index 066a00732965..3d966c7fbc38 100644
--- a/core/java/android/app/ApplicationPackageManager.java
+++ b/core/java/android/app/ApplicationPackageManager.java
@@ -471,6 +471,19 @@ public class ApplicationPackageManager extends PackageManager {
}
@Override
+ public int getTargetSdkVersion(@NonNull String packageName) throws NameNotFoundException {
+ try {
+ int version = mPM.getTargetSdkVersion(packageName);
+ if (version != -1) {
+ return version;
+ }
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ throw new PackageManager.NameNotFoundException(packageName);
+ }
+
+ @Override
public ActivityInfo getActivityInfo(ComponentName className, int flags)
throws NameNotFoundException {
final int userId = getUserId();
diff --git a/core/java/android/app/ClientTransactionHandler.java b/core/java/android/app/ClientTransactionHandler.java
index 2df756e80fde..ac50676ff46b 100644
--- a/core/java/android/app/ClientTransactionHandler.java
+++ b/core/java/android/app/ClientTransactionHandler.java
@@ -15,6 +15,8 @@
*/
package android.app;
+import android.annotation.NonNull;
+import android.app.ActivityThread.ActivityClientRecord;
import android.app.servertransaction.ClientTransaction;
import android.app.servertransaction.ClientTransactionItem;
import android.app.servertransaction.PendingTransactionActions;
@@ -89,37 +91,38 @@ public abstract class ClientTransactionHandler {
public abstract Map<IBinder, ClientTransactionItem> getActivitiesToBeDestroyed();
/** Destroy the activity. */
- public abstract void handleDestroyActivity(IBinder token, boolean finishing, int configChanges,
- boolean getNonConfigInstance, String reason);
+ public abstract void handleDestroyActivity(@NonNull ActivityClientRecord r, boolean finishing,
+ int configChanges, boolean getNonConfigInstance, String reason);
/** Pause the activity. */
- public abstract void handlePauseActivity(IBinder token, boolean finished, boolean userLeaving,
- int configChanges, PendingTransactionActions pendingActions, String reason);
+ public abstract void handlePauseActivity(@NonNull ActivityClientRecord r, boolean finished,
+ boolean userLeaving, int configChanges, PendingTransactionActions pendingActions,
+ String reason);
/**
* Resume the activity.
- * @param token Target activity token.
+ * @param r Target activity record.
* @param finalStateRequest Flag indicating if this call is handling final lifecycle state
* request for a transaction.
* @param isForward Flag indicating if next transition is forward.
* @param reason Reason for performing this operation.
*/
- public abstract void handleResumeActivity(IBinder token, boolean finalStateRequest,
- boolean isForward, String reason);
+ public abstract void handleResumeActivity(@NonNull ActivityClientRecord r,
+ boolean finalStateRequest, boolean isForward, String reason);
/**
* Notify the activity about top resumed state change.
- * @param token Target activity token.
+ * @param r Target activity record.
* @param isTopResumedActivity Current state of the activity, {@code true} if it's the
* topmost resumed activity in the system, {@code false} otherwise.
* @param reason Reason for performing this operation.
*/
- public abstract void handleTopResumedActivityChanged(IBinder token,
+ public abstract void handleTopResumedActivityChanged(@NonNull ActivityClientRecord r,
boolean isTopResumedActivity, String reason);
/**
* Stop the activity.
- * @param token Target activity token.
+ * @param r Target activity record.
* @param configChanges Activity configuration changes.
* @param pendingActions Pending actions to be used on this or later stages of activity
* transaction.
@@ -127,38 +130,40 @@ public abstract class ClientTransactionHandler {
* request for a transaction.
* @param reason Reason for performing this operation.
*/
- public abstract void handleStopActivity(IBinder token, int configChanges,
+ public abstract void handleStopActivity(@NonNull ActivityClientRecord r, int configChanges,
PendingTransactionActions pendingActions, boolean finalStateRequest, String reason);
/** Report that activity was stopped to server. */
public abstract void reportStop(PendingTransactionActions pendingActions);
/** Restart the activity after it was stopped. */
- public abstract void performRestartActivity(IBinder token, boolean start);
+ public abstract void performRestartActivity(@NonNull ActivityClientRecord r, boolean start);
/** Set pending activity configuration in case it will be updated by other transaction item. */
- public abstract void updatePendingActivityConfiguration(IBinder activityToken,
+ public abstract void updatePendingActivityConfiguration(@NonNull ActivityClientRecord r,
Configuration overrideConfig);
/** Deliver activity (override) configuration change. */
- public abstract void handleActivityConfigurationChanged(IBinder activityToken,
+ public abstract void handleActivityConfigurationChanged(@NonNull ActivityClientRecord r,
Configuration overrideConfig, int displayId);
/** Deliver result from another activity. */
- public abstract void handleSendResult(IBinder token, List<ResultInfo> results, String reason);
+ public abstract void handleSendResult(
+ @NonNull ActivityClientRecord r, List<ResultInfo> results, String reason);
/** Deliver new intent. */
- public abstract void handleNewIntent(IBinder token, List<ReferrerIntent> intents);
+ public abstract void handleNewIntent(
+ @NonNull ActivityClientRecord r, List<ReferrerIntent> intents);
/** Request that an activity enter picture-in-picture. */
- public abstract void handlePictureInPictureRequested(IBinder token);
+ public abstract void handlePictureInPictureRequested(@NonNull ActivityClientRecord r);
/** Perform activity launch. */
- public abstract Activity handleLaunchActivity(ActivityThread.ActivityClientRecord r,
+ public abstract Activity handleLaunchActivity(@NonNull ActivityClientRecord r,
PendingTransactionActions pendingActions, Intent customIntent);
/** Perform activity start. */
- public abstract void handleStartActivity(IBinder token,
+ public abstract void handleStartActivity(@NonNull ActivityClientRecord r,
PendingTransactionActions pendingActions);
/** Get package info. */
@@ -176,7 +181,7 @@ public abstract class ClientTransactionHandler {
* Get {@link android.app.ActivityThread.ActivityClientRecord} instance that corresponds to the
* provided token.
*/
- public abstract ActivityThread.ActivityClientRecord getActivityClient(IBinder token);
+ public abstract ActivityClientRecord getActivityClient(IBinder token);
/**
* Prepare activity relaunch to update internal bookkeeping. This is used to track multiple
@@ -191,7 +196,7 @@ public abstract class ClientTransactionHandler {
* @return An initialized instance of {@link ActivityThread.ActivityClientRecord} to use during
* relaunch, or {@code null} if relaunch cancelled.
*/
- public abstract ActivityThread.ActivityClientRecord prepareRelaunchActivity(IBinder token,
+ public abstract ActivityClientRecord prepareRelaunchActivity(IBinder token,
List<ResultInfo> pendingResults, List<ReferrerIntent> pendingNewIntents,
int configChanges, MergedConfiguration config, boolean preserveWindow);
@@ -200,14 +205,15 @@ public abstract class ClientTransactionHandler {
* @param r Activity client record prepared for relaunch.
* @param pendingActions Pending actions to be used on later stages of activity transaction.
* */
- public abstract void handleRelaunchActivity(ActivityThread.ActivityClientRecord r,
+ public abstract void handleRelaunchActivity(@NonNull ActivityClientRecord r,
PendingTransactionActions pendingActions);
/**
* Report that relaunch request was handled.
- * @param token Target activity token.
+ * @param r Target activity record.
* @param pendingActions Pending actions initialized on earlier stages of activity transaction.
* Used to check if we should report relaunch to WM.
* */
- public abstract void reportRelaunch(IBinder token, PendingTransactionActions pendingActions);
+ public abstract void reportRelaunch(@NonNull ActivityClientRecord r,
+ PendingTransactionActions pendingActions);
}
diff --git a/core/java/android/app/ContextImpl.java b/core/java/android/app/ContextImpl.java
index fef8d1005e29..cf5a7b629e38 100644
--- a/core/java/android/app/ContextImpl.java
+++ b/core/java/android/app/ContextImpl.java
@@ -2413,7 +2413,6 @@ class ContextImpl extends Context {
context.setResources(createResources(mToken, mPackageInfo, mSplitName, overrideDisplayId,
overrideConfiguration, getDisplayAdjustments(displayId).getCompatibilityInfo(),
mResources.getLoaders()));
- context.mIsUiContext = isSelfOrOuterUiContext();
return context;
}
@@ -2442,6 +2441,11 @@ class ContextImpl extends Context {
// the display that would otherwise be inherited from mToken (or the global configuration if
// mToken is null).
context.mForceDisplayOverrideInResources = true;
+ // Note that even if a display context is derived from an UI context, it should not be
+ // treated as UI context because it does not handle configuration changes from the server
+ // side. If the context does need to handle configuration changes, please use
+ // Context#createWindowContext(int, Bundle).
+ context.mIsUiContext = false;
return context;
}
@@ -2806,6 +2810,7 @@ class ContextImpl extends Context {
mIsAssociatedWithDisplay = container.mIsAssociatedWithDisplay;
mIsSystemOrSystemUiContext = container.mIsSystemOrSystemUiContext;
mForceDisplayOverrideInResources = container.mForceDisplayOverrideInResources;
+ mIsUiContext = container.isSelfOrOuterUiContext();
} else {
mBasePackageName = packageInfo.mPackageName;
ApplicationInfo ainfo = packageInfo.getApplicationInfo();
diff --git a/core/java/android/app/IActivityManager.aidl b/core/java/android/app/IActivityManager.aidl
index 45b25a3bf317..c1e6f5282b2a 100644
--- a/core/java/android/app/IActivityManager.aidl
+++ b/core/java/android/app/IActivityManager.aidl
@@ -513,13 +513,6 @@ interface IActivityManager {
// descriptor.
@UnsupportedAppUsage
boolean stopBinderTrackingAndDump(in ParcelFileDescriptor fd);
- /**
- * Try to place task to provided position. The final position might be different depending on
- * current user and stacks state. The task will be moved to target stack if it's currently in
- * different stack.
- */
- @UnsupportedAppUsage
- void positionTaskInStack(int taskId, int stackId, int position);
@UnsupportedAppUsage
void suppressResizeConfigChanges(boolean suppress);
@UnsupportedAppUsage
diff --git a/core/java/android/app/IActivityTaskManager.aidl b/core/java/android/app/IActivityTaskManager.aidl
index f3c7fe9412c9..e21ef8ee23ad 100644
--- a/core/java/android/app/IActivityTaskManager.aidl
+++ b/core/java/android/app/IActivityTaskManager.aidl
@@ -295,12 +295,6 @@ interface IActivityTaskManager {
ComponentName getActivityClassForToken(in IBinder token);
String getPackageForToken(in IBinder token);
- /**
- * Try to place task to provided position. The final position might be different depending on
- * current user and stacks state. The task will be moved to target stack if it's currently in
- * different stack.
- */
- void positionTaskInStack(int taskId, int stackId, int position);
void reportSizeConfigurations(in IBinder token, in int[] horizontalSizeConfiguration,
in int[] verticalSizeConfigurations, in int[] smallestWidthConfigurations);
diff --git a/core/java/android/app/LocalActivityManager.java b/core/java/android/app/LocalActivityManager.java
index 7cdf85e0a6b8..2e7c9f11ac66 100644
--- a/core/java/android/app/LocalActivityManager.java
+++ b/core/java/android/app/LocalActivityManager.java
@@ -49,6 +49,7 @@ public class LocalActivityManager {
private static final String TAG = "LocalActivityManager";
private static final boolean localLOGV = false;
+ // TODO(b/127877792): try to remove this and use {@code ActivityClientRecord} instead.
// Internal token for an Activity being managed by LocalActivityManager.
private static class LocalActivityRecord extends Binder {
LocalActivityRecord(String _id, Intent _intent) {
@@ -136,7 +137,7 @@ public class LocalActivityManager {
// startActivity() has not yet been called, so nothing to do.
return;
}
-
+
if (r.curState == INITIALIZING) {
// Get the lastNonConfigurationInstance for the activity
HashMap<String, Object> lastNonConfigurationInstances =
@@ -177,12 +178,13 @@ public class LocalActivityManager {
pendingActions = null;
}
- mActivityThread.handleStartActivity(r, pendingActions);
+ mActivityThread.handleStartActivity(clientRecord, pendingActions);
r.curState = STARTED;
if (desiredState == RESUMED) {
if (localLOGV) Log.v(TAG, r.id + ": resuming");
- mActivityThread.performResumeActivity(r, true, "moveToState-INITIALIZING");
+ mActivityThread.performResumeActivity(clientRecord, true,
+ "moveToState-INITIALIZING");
r.curState = RESUMED;
}
@@ -194,18 +196,25 @@ public class LocalActivityManager {
// group's state catches up.
return;
}
-
+
+ final ActivityClientRecord clientRecord = mActivityThread.getActivityClient(r);
+ if (clientRecord == null) {
+ Log.w(TAG, "Can't get activity record for " + r.id);
+ return;
+ }
+
switch (r.curState) {
case CREATED:
if (desiredState == STARTED) {
if (localLOGV) Log.v(TAG, r.id + ": restarting");
- mActivityThread.performRestartActivity(r, true /* start */);
+ mActivityThread.performRestartActivity(clientRecord, true /* start */);
r.curState = STARTED;
}
if (desiredState == RESUMED) {
if (localLOGV) Log.v(TAG, r.id + ": restarting and resuming");
- mActivityThread.performRestartActivity(r, true /* start */);
- mActivityThread.performResumeActivity(r, true, "moveToState-CREATED");
+ mActivityThread.performRestartActivity(clientRecord, true /* start */);
+ mActivityThread.performResumeActivity(clientRecord, true,
+ "moveToState-CREATED");
r.curState = RESUMED;
}
return;
@@ -214,7 +223,8 @@ public class LocalActivityManager {
if (desiredState == RESUMED) {
// Need to resume it...
if (localLOGV) Log.v(TAG, r.id + ": resuming");
- mActivityThread.performResumeActivity(r, true, "moveToState-STARTED");
+ mActivityThread.performResumeActivity(clientRecord, true,
+ "moveToState-STARTED");
r.instanceState = null;
r.curState = RESUMED;
}
@@ -352,7 +362,8 @@ public class LocalActivityManager {
ArrayList<ReferrerIntent> intents = new ArrayList<>(1);
intents.add(new ReferrerIntent(intent, mParent.getPackageName()));
if (localLOGV) Log.v(TAG, r.id + ": new intent");
- mActivityThread.handleNewIntent(r, intents);
+ final ActivityClientRecord clientRecord = mActivityThread.getActivityClient(r);
+ mActivityThread.handleNewIntent(clientRecord, intents);
r.intent = intent;
moveToState(r, mCurState);
if (mSingleMode) {
@@ -399,8 +410,11 @@ public class LocalActivityManager {
performPause(r, finish);
}
if (localLOGV) Log.v(TAG, r.id + ": destroying");
- mActivityThread.performDestroyActivity(r, finish, 0 /* configChanges */,
- false /* getNonConfigInstance */, "LocalActivityManager::performDestroy");
+ final ActivityClientRecord clientRecord = mActivityThread.getActivityClient(r);
+ if (clientRecord != null) {
+ mActivityThread.performDestroyActivity(clientRecord, finish, 0 /* configChanges */,
+ false /* getNonConfigInstance */, "LocalActivityManager::performDestroy");
+ }
r.activity = null;
r.window = null;
if (finish) {
@@ -664,7 +678,12 @@ public class LocalActivityManager {
for (int i=0; i<N; i++) {
LocalActivityRecord r = mActivityArray.get(i);
if (localLOGV) Log.v(TAG, r.id + ": destroying");
- mActivityThread.performDestroyActivity(r, finishing, 0 /* configChanges */,
+ final ActivityClientRecord clientRecord = mActivityThread.getActivityClient(r);
+ if (clientRecord == null) {
+ if (localLOGV) Log.v(TAG, r.id + ": no corresponding record");
+ continue;
+ }
+ mActivityThread.performDestroyActivity(clientRecord, finishing, 0 /* configChanges */,
false /* getNonConfigInstance */, "LocalActivityManager::dispatchDestroy");
}
mActivities.clear();
diff --git a/core/java/android/app/StatusBarManager.java b/core/java/android/app/StatusBarManager.java
index 1329fa45bb50..4ff9e8d58c3a 100644
--- a/core/java/android/app/StatusBarManager.java
+++ b/core/java/android/app/StatusBarManager.java
@@ -400,6 +400,7 @@ public class StatusBarManager {
* @hide
*/
@TestApi
+ @SystemApi(client = SystemApi.Client.MODULE_LIBRARIES)
@RequiresPermission(android.Manifest.permission.STATUS_BAR)
public void setDisabledForSimNetworkLock(boolean disabled) {
try {
diff --git a/core/java/android/app/TEST_MAPPING b/core/java/android/app/TEST_MAPPING
index 4139b2faeb1b..b0307d1eb939 100644
--- a/core/java/android/app/TEST_MAPPING
+++ b/core/java/android/app/TEST_MAPPING
@@ -70,7 +70,7 @@
"name": "CtsAutoFillServiceTestCases",
"options": [
{
- "include-filter": "android.autofillservice.cts.PreSimpleSaveActivityTest"
+ "include-filter": "android.autofillservice.cts.saveui.PreSimpleSaveActivityTest"
},
{
"exclude-annotation": "androidx.test.filters.FlakyTest"
@@ -82,7 +82,7 @@
"name": "CtsAutoFillServiceTestCases",
"options": [
{
- "include-filter": "android.autofillservice.cts.SimpleSaveActivityTest"
+ "include-filter": "android.autofillservice.cts.saveui.SimpleSaveActivityTest"
},
{
"exclude-annotation": "androidx.test.filters.FlakyTest"
diff --git a/core/java/android/app/servertransaction/ActivityConfigurationChangeItem.java b/core/java/android/app/servertransaction/ActivityConfigurationChangeItem.java
index 8b52242a6b6c..8a4bee98ca87 100644
--- a/core/java/android/app/servertransaction/ActivityConfigurationChangeItem.java
+++ b/core/java/android/app/servertransaction/ActivityConfigurationChangeItem.java
@@ -20,6 +20,7 @@ import static android.os.Trace.TRACE_TAG_ACTIVITY_MANAGER;
import static android.view.Display.INVALID_DISPLAY;
import android.annotation.NonNull;
+import android.app.ActivityThread.ActivityClientRecord;
import android.app.ClientTransactionHandler;
import android.content.res.Configuration;
import android.os.IBinder;
@@ -32,23 +33,24 @@ import java.util.Objects;
* Activity configuration changed callback.
* @hide
*/
-public class ActivityConfigurationChangeItem extends ClientTransactionItem {
+public class ActivityConfigurationChangeItem extends ActivityTransactionItem {
private Configuration mConfiguration;
@Override
public void preExecute(android.app.ClientTransactionHandler client, IBinder token) {
+ final ActivityClientRecord r = getActivityClientRecord(client, token);
// Notify the client of an upcoming change in the token configuration. This ensures that
// batches of config change items only process the newest configuration.
- client.updatePendingActivityConfiguration(token, mConfiguration);
+ client.updatePendingActivityConfiguration(r, mConfiguration);
}
@Override
- public void execute(ClientTransactionHandler client, IBinder token,
+ public void execute(ClientTransactionHandler client, ActivityClientRecord r,
PendingTransactionActions pendingActions) {
// TODO(lifecycler): detect if PIP or multi-window mode changed and report it here.
Trace.traceBegin(TRACE_TAG_ACTIVITY_MANAGER, "activityConfigChanged");
- client.handleActivityConfigurationChanged(token, mConfiguration, INVALID_DISPLAY);
+ client.handleActivityConfigurationChanged(r, mConfiguration, INVALID_DISPLAY);
Trace.traceEnd(TRACE_TAG_ACTIVITY_MANAGER);
}
@@ -93,7 +95,7 @@ public class ActivityConfigurationChangeItem extends ClientTransactionItem {
mConfiguration = in.readTypedObject(Configuration.CREATOR);
}
- public static final @android.annotation.NonNull Creator<ActivityConfigurationChangeItem> CREATOR =
+ public static final @NonNull Creator<ActivityConfigurationChangeItem> CREATOR =
new Creator<ActivityConfigurationChangeItem>() {
public ActivityConfigurationChangeItem createFromParcel(Parcel in) {
return new ActivityConfigurationChangeItem(in);
diff --git a/core/java/android/app/servertransaction/ActivityLifecycleItem.java b/core/java/android/app/servertransaction/ActivityLifecycleItem.java
index c9193a9578e7..cadb6606b1be 100644
--- a/core/java/android/app/servertransaction/ActivityLifecycleItem.java
+++ b/core/java/android/app/servertransaction/ActivityLifecycleItem.java
@@ -25,7 +25,7 @@ import java.lang.annotation.RetentionPolicy;
* Request for lifecycle state that an activity should reach.
* @hide
*/
-public abstract class ActivityLifecycleItem extends ClientTransactionItem {
+public abstract class ActivityLifecycleItem extends ActivityTransactionItem {
@IntDef(prefix = { "UNDEFINED", "PRE_", "ON_" }, value = {
UNDEFINED,
diff --git a/core/java/android/app/servertransaction/ActivityRelaunchItem.java b/core/java/android/app/servertransaction/ActivityRelaunchItem.java
index 9844de7b6e88..87ea3f8db39c 100644
--- a/core/java/android/app/servertransaction/ActivityRelaunchItem.java
+++ b/core/java/android/app/servertransaction/ActivityRelaunchItem.java
@@ -18,7 +18,8 @@ package android.app.servertransaction;
import static android.app.ActivityThread.DEBUG_ORDER;
-import android.app.ActivityThread;
+import android.annotation.NonNull;
+import android.app.ActivityThread.ActivityClientRecord;
import android.app.ClientTransactionHandler;
import android.app.ResultInfo;
import android.os.IBinder;
@@ -36,7 +37,7 @@ import java.util.Objects;
* Activity relaunch callback.
* @hide
*/
-public class ActivityRelaunchItem extends ClientTransactionItem {
+public class ActivityRelaunchItem extends ActivityTransactionItem {
private static final String TAG = "ActivityRelaunchItem";
@@ -50,7 +51,7 @@ public class ActivityRelaunchItem extends ClientTransactionItem {
* A record that was properly configured for relaunch. Execution will be cancelled if not
* initialized after {@link #preExecute(ClientTransactionHandler, IBinder)}.
*/
- private ActivityThread.ActivityClientRecord mActivityClientRecord;
+ private ActivityClientRecord mActivityClientRecord;
@Override
public void preExecute(ClientTransactionHandler client, IBinder token) {
@@ -59,7 +60,7 @@ public class ActivityRelaunchItem extends ClientTransactionItem {
}
@Override
- public void execute(ClientTransactionHandler client, IBinder token,
+ public void execute(ClientTransactionHandler client, ActivityClientRecord r,
PendingTransactionActions pendingActions) {
if (mActivityClientRecord == null) {
if (DEBUG_ORDER) Slog.d(TAG, "Activity relaunch cancelled");
@@ -73,7 +74,8 @@ public class ActivityRelaunchItem extends ClientTransactionItem {
@Override
public void postExecute(ClientTransactionHandler client, IBinder token,
PendingTransactionActions pendingActions) {
- client.reportRelaunch(token, pendingActions);
+ final ActivityClientRecord r = getActivityClientRecord(client, token);
+ client.reportRelaunch(r, pendingActions);
}
// ObjectPoolItem implementation
@@ -130,16 +132,16 @@ public class ActivityRelaunchItem extends ClientTransactionItem {
mPreserveWindow = in.readBoolean();
}
- public static final @android.annotation.NonNull Creator<ActivityRelaunchItem> CREATOR =
+ public static final @NonNull Creator<ActivityRelaunchItem> CREATOR =
new Creator<ActivityRelaunchItem>() {
- public ActivityRelaunchItem createFromParcel(Parcel in) {
- return new ActivityRelaunchItem(in);
- }
-
- public ActivityRelaunchItem[] newArray(int size) {
- return new ActivityRelaunchItem[size];
- }
- };
+ public ActivityRelaunchItem createFromParcel(Parcel in) {
+ return new ActivityRelaunchItem(in);
+ }
+
+ public ActivityRelaunchItem[] newArray(int size) {
+ return new ActivityRelaunchItem[size];
+ }
+ };
@Override
public boolean equals(Object o) {
diff --git a/core/java/android/app/servertransaction/ActivityResultItem.java b/core/java/android/app/servertransaction/ActivityResultItem.java
index 4e743caccad6..47096a8257e9 100644
--- a/core/java/android/app/servertransaction/ActivityResultItem.java
+++ b/core/java/android/app/servertransaction/ActivityResultItem.java
@@ -18,10 +18,11 @@ package android.app.servertransaction;
import static android.os.Trace.TRACE_TAG_ACTIVITY_MANAGER;
+import android.annotation.NonNull;
+import android.app.ActivityThread.ActivityClientRecord;
import android.app.ClientTransactionHandler;
import android.app.ResultInfo;
import android.compat.annotation.UnsupportedAppUsage;
-import android.os.IBinder;
import android.os.Parcel;
import android.os.Parcelable;
import android.os.Trace;
@@ -33,7 +34,7 @@ import java.util.Objects;
* Activity result delivery callback.
* @hide
*/
-public class ActivityResultItem extends ClientTransactionItem {
+public class ActivityResultItem extends ActivityTransactionItem {
@UnsupportedAppUsage
private List<ResultInfo> mResultInfoList;
@@ -45,10 +46,10 @@ public class ActivityResultItem extends ClientTransactionItem {
}*/
@Override
- public void execute(ClientTransactionHandler client, IBinder token,
+ public void execute(ClientTransactionHandler client, ActivityClientRecord r,
PendingTransactionActions pendingActions) {
Trace.traceBegin(TRACE_TAG_ACTIVITY_MANAGER, "activityDeliverResult");
- client.handleSendResult(token, mResultInfoList, "ACTIVITY_RESULT");
+ client.handleSendResult(r, mResultInfoList, "ACTIVITY_RESULT");
Trace.traceEnd(TRACE_TAG_ACTIVITY_MANAGER);
}
@@ -88,7 +89,7 @@ public class ActivityResultItem extends ClientTransactionItem {
mResultInfoList = in.createTypedArrayList(ResultInfo.CREATOR);
}
- public static final @android.annotation.NonNull Parcelable.Creator<ActivityResultItem> CREATOR =
+ public static final @NonNull Parcelable.Creator<ActivityResultItem> CREATOR =
new Parcelable.Creator<ActivityResultItem>() {
public ActivityResultItem createFromParcel(Parcel in) {
return new ActivityResultItem(in);
diff --git a/core/java/android/app/servertransaction/ActivityTransactionItem.java b/core/java/android/app/servertransaction/ActivityTransactionItem.java
new file mode 100644
index 000000000000..f7d7e9d20ab9
--- /dev/null
+++ b/core/java/android/app/servertransaction/ActivityTransactionItem.java
@@ -0,0 +1,69 @@
+/*
+ * 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.app.servertransaction;
+
+import static com.android.internal.annotations.VisibleForTesting.Visibility.PACKAGE;
+
+import android.annotation.NonNull;
+import android.app.ActivityThread.ActivityClientRecord;
+import android.app.ClientTransactionHandler;
+import android.os.IBinder;
+
+import com.android.internal.annotations.VisibleForTesting;
+
+/**
+ * An activity-targeting callback message to a client that can be scheduled and executed.
+ * It also provides nullity-free version of
+ * {@link #execute(ClientTransactionHandler, IBinder, PendingTransactionActions)} for child class
+ * to inherit.
+ *
+ * @see ClientTransaction
+ * @see ClientTransactionItem
+ * @see com.android.server.wm.ClientLifecycleManager
+ * @hide
+ */
+public abstract class ActivityTransactionItem extends ClientTransactionItem {
+ @Override
+ public final void execute(ClientTransactionHandler client, IBinder token,
+ PendingTransactionActions pendingActions) {
+ final ActivityClientRecord r = getActivityClientRecord(client, token);
+
+ execute(client, r, pendingActions);
+ }
+
+ /**
+ * Like {@link #execute(ClientTransactionHandler, IBinder, PendingTransactionActions)},
+ * but take non-null {@link ActivityClientRecord} as a parameter.
+ */
+ @VisibleForTesting(visibility = PACKAGE)
+ public abstract void execute(@NonNull ClientTransactionHandler client,
+ @NonNull ActivityClientRecord r, PendingTransactionActions pendingActions);
+
+ @NonNull ActivityClientRecord getActivityClientRecord(
+ @NonNull ClientTransactionHandler client, IBinder token) {
+ final ActivityClientRecord r = client.getActivityClient(token);
+ if (r == null) {
+ throw new IllegalArgumentException("Activity client record must not be null to execute "
+ + "transaction item");
+ }
+ if (client.getActivity(token) == null) {
+ throw new IllegalArgumentException("Activity must not be null to execute "
+ + "transaction item");
+ }
+ return r;
+ }
+}
diff --git a/core/java/android/app/servertransaction/DestroyActivityItem.java b/core/java/android/app/servertransaction/DestroyActivityItem.java
index 3ee761477efd..1611369497e9 100644
--- a/core/java/android/app/servertransaction/DestroyActivityItem.java
+++ b/core/java/android/app/servertransaction/DestroyActivityItem.java
@@ -18,6 +18,8 @@ package android.app.servertransaction;
import static android.os.Trace.TRACE_TAG_ACTIVITY_MANAGER;
+import android.annotation.NonNull;
+import android.app.ActivityThread.ActivityClientRecord;
import android.app.ClientTransactionHandler;
import android.os.IBinder;
import android.os.Parcel;
@@ -38,10 +40,10 @@ public class DestroyActivityItem extends ActivityLifecycleItem {
}
@Override
- public void execute(ClientTransactionHandler client, IBinder token,
+ public void execute(ClientTransactionHandler client, ActivityClientRecord r,
PendingTransactionActions pendingActions) {
Trace.traceBegin(TRACE_TAG_ACTIVITY_MANAGER, "activityDestroy");
- client.handleDestroyActivity(token, mFinished, mConfigChanges,
+ client.handleDestroyActivity(r, mFinished, mConfigChanges,
false /* getNonConfigInstance */, "DestroyActivityItem");
Trace.traceEnd(TRACE_TAG_ACTIVITY_MANAGER);
}
@@ -92,7 +94,7 @@ public class DestroyActivityItem extends ActivityLifecycleItem {
mConfigChanges = in.readInt();
}
- public static final @android.annotation.NonNull Creator<DestroyActivityItem> CREATOR =
+ public static final @NonNull Creator<DestroyActivityItem> CREATOR =
new Creator<DestroyActivityItem>() {
public DestroyActivityItem createFromParcel(Parcel in) {
return new DestroyActivityItem(in);
diff --git a/core/java/android/app/servertransaction/EnterPipRequestedItem.java b/core/java/android/app/servertransaction/EnterPipRequestedItem.java
index b2a1276fa178..b7e81a56afad 100644
--- a/core/java/android/app/servertransaction/EnterPipRequestedItem.java
+++ b/core/java/android/app/servertransaction/EnterPipRequestedItem.java
@@ -16,20 +16,20 @@
package android.app.servertransaction;
+import android.app.ActivityThread.ActivityClientRecord;
import android.app.ClientTransactionHandler;
-import android.os.IBinder;
import android.os.Parcel;
/**
* Request an activity to enter picture-in-picture mode.
* @hide
*/
-public final class EnterPipRequestedItem extends ClientTransactionItem {
+public final class EnterPipRequestedItem extends ActivityTransactionItem {
@Override
- public void execute(ClientTransactionHandler client, IBinder token,
+ public void execute(ClientTransactionHandler client, ActivityClientRecord r,
PendingTransactionActions pendingActions) {
- client.handlePictureInPictureRequested(token);
+ client.handlePictureInPictureRequested(r);
}
// ObjectPoolItem implementation
diff --git a/core/java/android/app/servertransaction/LaunchActivityItem.java b/core/java/android/app/servertransaction/LaunchActivityItem.java
index 2e7b6262c785..77457af77340 100644
--- a/core/java/android/app/servertransaction/LaunchActivityItem.java
+++ b/core/java/android/app/servertransaction/LaunchActivityItem.java
@@ -18,6 +18,7 @@ package android.app.servertransaction;
import static android.os.Trace.TRACE_TAG_ACTIVITY_MANAGER;
+import android.annotation.NonNull;
import android.app.ActivityThread.ActivityClientRecord;
import android.app.ClientTransactionHandler;
import android.app.ProfilerInfo;
@@ -163,7 +164,7 @@ public class LaunchActivityItem extends ClientTransactionItem {
in.readTypedObject(FixedRotationAdjustments.CREATOR));
}
- public static final @android.annotation.NonNull Creator<LaunchActivityItem> CREATOR =
+ public static final @NonNull Creator<LaunchActivityItem> CREATOR =
new Creator<LaunchActivityItem>() {
public LaunchActivityItem createFromParcel(Parcel in) {
return new LaunchActivityItem(in);
diff --git a/core/java/android/app/servertransaction/MoveToDisplayItem.java b/core/java/android/app/servertransaction/MoveToDisplayItem.java
index 9a457a3aad40..32de53f189b0 100644
--- a/core/java/android/app/servertransaction/MoveToDisplayItem.java
+++ b/core/java/android/app/servertransaction/MoveToDisplayItem.java
@@ -19,6 +19,7 @@ package android.app.servertransaction;
import static android.os.Trace.TRACE_TAG_ACTIVITY_MANAGER;
import android.annotation.NonNull;
+import android.app.ActivityThread.ActivityClientRecord;
import android.app.ClientTransactionHandler;
import android.content.res.Configuration;
import android.os.IBinder;
@@ -31,23 +32,24 @@ import java.util.Objects;
* Activity move to a different display message.
* @hide
*/
-public class MoveToDisplayItem extends ClientTransactionItem {
+public class MoveToDisplayItem extends ActivityTransactionItem {
private int mTargetDisplayId;
private Configuration mConfiguration;
@Override
public void preExecute(ClientTransactionHandler client, IBinder token) {
+ final ActivityClientRecord r = getActivityClientRecord(client, token);
// Notify the client of an upcoming change in the token configuration. This ensures that
// batches of config change items only process the newest configuration.
- client.updatePendingActivityConfiguration(token, mConfiguration);
+ client.updatePendingActivityConfiguration(r, mConfiguration);
}
@Override
- public void execute(ClientTransactionHandler client, IBinder token,
+ public void execute(ClientTransactionHandler client, ActivityClientRecord r,
PendingTransactionActions pendingActions) {
Trace.traceBegin(TRACE_TAG_ACTIVITY_MANAGER, "activityMovedToDisplay");
- client.handleActivityConfigurationChanged(token, mConfiguration, mTargetDisplayId);
+ client.handleActivityConfigurationChanged(r, mConfiguration, mTargetDisplayId);
Trace.traceEnd(TRACE_TAG_ACTIVITY_MANAGER);
}
@@ -96,7 +98,8 @@ public class MoveToDisplayItem extends ClientTransactionItem {
mConfiguration = in.readTypedObject(Configuration.CREATOR);
}
- public static final @android.annotation.NonNull Creator<MoveToDisplayItem> CREATOR = new Creator<MoveToDisplayItem>() {
+ public static final @NonNull Creator<MoveToDisplayItem> CREATOR =
+ new Creator<MoveToDisplayItem>() {
public MoveToDisplayItem createFromParcel(Parcel in) {
return new MoveToDisplayItem(in);
}
diff --git a/core/java/android/app/servertransaction/NewIntentItem.java b/core/java/android/app/servertransaction/NewIntentItem.java
index 6a4996da38ca..b4e2a7bfa10f 100644
--- a/core/java/android/app/servertransaction/NewIntentItem.java
+++ b/core/java/android/app/servertransaction/NewIntentItem.java
@@ -19,9 +19,10 @@ package android.app.servertransaction;
import static android.app.servertransaction.ActivityLifecycleItem.ON_RESUME;
import static android.app.servertransaction.ActivityLifecycleItem.UNDEFINED;
+import android.annotation.NonNull;
+import android.app.ActivityThread.ActivityClientRecord;
import android.app.ClientTransactionHandler;
import android.compat.annotation.UnsupportedAppUsage;
-import android.os.IBinder;
import android.os.Parcel;
import android.os.Parcelable;
import android.os.Trace;
@@ -35,7 +36,7 @@ import java.util.Objects;
* New intent message.
* @hide
*/
-public class NewIntentItem extends ClientTransactionItem {
+public class NewIntentItem extends ActivityTransactionItem {
@UnsupportedAppUsage
private List<ReferrerIntent> mIntents;
@@ -47,10 +48,10 @@ public class NewIntentItem extends ClientTransactionItem {
}
@Override
- public void execute(ClientTransactionHandler client, IBinder token,
+ public void execute(ClientTransactionHandler client, ActivityClientRecord r,
PendingTransactionActions pendingActions) {
Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, "activityNewIntent");
- client.handleNewIntent(token, mIntents);
+ client.handleNewIntent(r, mIntents);
Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);
}
@@ -94,7 +95,7 @@ public class NewIntentItem extends ClientTransactionItem {
mIntents = in.createTypedArrayList(ReferrerIntent.CREATOR);
}
- public static final @android.annotation.NonNull Parcelable.Creator<NewIntentItem> CREATOR =
+ public static final @NonNull Parcelable.Creator<NewIntentItem> CREATOR =
new Parcelable.Creator<NewIntentItem>() {
public NewIntentItem createFromParcel(Parcel in) {
return new NewIntentItem(in);
diff --git a/core/java/android/app/servertransaction/PauseActivityItem.java b/core/java/android/app/servertransaction/PauseActivityItem.java
index f65c843ee76f..cb154e9585e6 100644
--- a/core/java/android/app/servertransaction/PauseActivityItem.java
+++ b/core/java/android/app/servertransaction/PauseActivityItem.java
@@ -18,8 +18,9 @@ package android.app.servertransaction;
import static android.os.Trace.TRACE_TAG_ACTIVITY_MANAGER;
-import android.app.ActivityManager;
+import android.annotation.NonNull;
import android.app.ActivityTaskManager;
+import android.app.ActivityThread.ActivityClientRecord;
import android.app.ClientTransactionHandler;
import android.os.IBinder;
import android.os.Parcel;
@@ -40,10 +41,10 @@ public class PauseActivityItem extends ActivityLifecycleItem {
private boolean mDontReport;
@Override
- public void execute(ClientTransactionHandler client, IBinder token,
+ public void execute(ClientTransactionHandler client, ActivityClientRecord r,
PendingTransactionActions pendingActions) {
Trace.traceBegin(TRACE_TAG_ACTIVITY_MANAGER, "activityPause");
- client.handlePauseActivity(token, mFinished, mUserLeaving, mConfigChanges, pendingActions,
+ client.handlePauseActivity(r, mFinished, mUserLeaving, mConfigChanges, pendingActions,
"PAUSE_ACTIVITY_ITEM");
Trace.traceEnd(TRACE_TAG_ACTIVITY_MANAGER);
}
@@ -130,7 +131,7 @@ public class PauseActivityItem extends ActivityLifecycleItem {
mDontReport = in.readBoolean();
}
- public static final @android.annotation.NonNull Creator<PauseActivityItem> CREATOR =
+ public static final @NonNull Creator<PauseActivityItem> CREATOR =
new Creator<PauseActivityItem>() {
public PauseActivityItem createFromParcel(Parcel in) {
return new PauseActivityItem(in);
diff --git a/core/java/android/app/servertransaction/ResumeActivityItem.java b/core/java/android/app/servertransaction/ResumeActivityItem.java
index 905076b08e69..d2a156c37c90 100644
--- a/core/java/android/app/servertransaction/ResumeActivityItem.java
+++ b/core/java/android/app/servertransaction/ResumeActivityItem.java
@@ -18,8 +18,10 @@ package android.app.servertransaction;
import static android.os.Trace.TRACE_TAG_ACTIVITY_MANAGER;
+import android.annotation.NonNull;
import android.app.ActivityManager;
import android.app.ActivityTaskManager;
+import android.app.ActivityThread.ActivityClientRecord;
import android.app.ClientTransactionHandler;
import android.os.IBinder;
import android.os.Parcel;
@@ -46,10 +48,10 @@ public class ResumeActivityItem extends ActivityLifecycleItem {
}
@Override
- public void execute(ClientTransactionHandler client, IBinder token,
+ public void execute(ClientTransactionHandler client, ActivityClientRecord r,
PendingTransactionActions pendingActions) {
Trace.traceBegin(TRACE_TAG_ACTIVITY_MANAGER, "activityResume");
- client.handleResumeActivity(token, true /* finalStateRequest */, mIsForward,
+ client.handleResumeActivity(r, true /* finalStateRequest */, mIsForward,
"RESUME_ACTIVITY");
Trace.traceEnd(TRACE_TAG_ACTIVITY_MANAGER);
}
@@ -128,7 +130,7 @@ public class ResumeActivityItem extends ActivityLifecycleItem {
mIsForward = in.readBoolean();
}
- public static final @android.annotation.NonNull Creator<ResumeActivityItem> CREATOR =
+ public static final @NonNull Creator<ResumeActivityItem> CREATOR =
new Creator<ResumeActivityItem>() {
public ResumeActivityItem createFromParcel(Parcel in) {
return new ResumeActivityItem(in);
diff --git a/core/java/android/app/servertransaction/StartActivityItem.java b/core/java/android/app/servertransaction/StartActivityItem.java
index 4fbe02b9cf76..ae0bd24218fb 100644
--- a/core/java/android/app/servertransaction/StartActivityItem.java
+++ b/core/java/android/app/servertransaction/StartActivityItem.java
@@ -18,8 +18,9 @@ package android.app.servertransaction;
import static android.os.Trace.TRACE_TAG_ACTIVITY_MANAGER;
+import android.annotation.NonNull;
+import android.app.ActivityThread.ActivityClientRecord;
import android.app.ClientTransactionHandler;
-import android.os.IBinder;
import android.os.Parcel;
import android.os.Trace;
@@ -32,10 +33,10 @@ public class StartActivityItem extends ActivityLifecycleItem {
private static final String TAG = "StartActivityItem";
@Override
- public void execute(ClientTransactionHandler client, IBinder token,
+ public void execute(ClientTransactionHandler client, ActivityClientRecord r,
PendingTransactionActions pendingActions) {
Trace.traceBegin(TRACE_TAG_ACTIVITY_MANAGER, "startActivityItem");
- client.handleStartActivity(token, pendingActions);
+ client.handleStartActivity(r, pendingActions);
Trace.traceEnd(TRACE_TAG_ACTIVITY_MANAGER);
}
@@ -79,7 +80,7 @@ public class StartActivityItem extends ActivityLifecycleItem {
// Empty
}
- public static final @android.annotation.NonNull Creator<StartActivityItem> CREATOR =
+ public static final @NonNull Creator<StartActivityItem> CREATOR =
new Creator<StartActivityItem>() {
public StartActivityItem createFromParcel(Parcel in) {
return new StartActivityItem(in);
diff --git a/core/java/android/app/servertransaction/StopActivityItem.java b/core/java/android/app/servertransaction/StopActivityItem.java
index 8668bd49c8f5..7708104da16a 100644
--- a/core/java/android/app/servertransaction/StopActivityItem.java
+++ b/core/java/android/app/servertransaction/StopActivityItem.java
@@ -18,6 +18,8 @@ package android.app.servertransaction;
import static android.os.Trace.TRACE_TAG_ACTIVITY_MANAGER;
+import android.annotation.NonNull;
+import android.app.ActivityThread.ActivityClientRecord;
import android.app.ClientTransactionHandler;
import android.os.IBinder;
import android.os.Parcel;
@@ -34,10 +36,10 @@ public class StopActivityItem extends ActivityLifecycleItem {
private int mConfigChanges;
@Override
- public void execute(ClientTransactionHandler client, IBinder token,
+ public void execute(ClientTransactionHandler client, ActivityClientRecord r,
PendingTransactionActions pendingActions) {
Trace.traceBegin(TRACE_TAG_ACTIVITY_MANAGER, "activityStop");
- client.handleStopActivity(token, mConfigChanges, pendingActions,
+ client.handleStopActivity(r, mConfigChanges, pendingActions,
true /* finalStateRequest */, "STOP_ACTIVITY_ITEM");
Trace.traceEnd(TRACE_TAG_ACTIVITY_MANAGER);
}
@@ -93,7 +95,7 @@ public class StopActivityItem extends ActivityLifecycleItem {
mConfigChanges = in.readInt();
}
- public static final @android.annotation.NonNull Creator<StopActivityItem> CREATOR =
+ public static final @NonNull Creator<StopActivityItem> CREATOR =
new Creator<StopActivityItem>() {
public StopActivityItem createFromParcel(Parcel in) {
return new StopActivityItem(in);
diff --git a/core/java/android/app/servertransaction/TopResumedActivityChangeItem.java b/core/java/android/app/servertransaction/TopResumedActivityChangeItem.java
index c7e4c3641631..345c1dd336ab 100644
--- a/core/java/android/app/servertransaction/TopResumedActivityChangeItem.java
+++ b/core/java/android/app/servertransaction/TopResumedActivityChangeItem.java
@@ -17,7 +17,9 @@ package android.app.servertransaction;
import static android.os.Trace.TRACE_TAG_ACTIVITY_MANAGER;
+import android.annotation.NonNull;
import android.app.ActivityTaskManager;
+import android.app.ActivityThread.ActivityClientRecord;
import android.app.ClientTransactionHandler;
import android.os.IBinder;
import android.os.Parcel;
@@ -28,15 +30,15 @@ import android.os.Trace;
* Top resumed activity changed callback.
* @hide
*/
-public class TopResumedActivityChangeItem extends ClientTransactionItem {
+public class TopResumedActivityChangeItem extends ActivityTransactionItem {
private boolean mOnTop;
@Override
- public void execute(ClientTransactionHandler client, IBinder token,
+ public void execute(ClientTransactionHandler client, ActivityClientRecord r,
PendingTransactionActions pendingActions) {
Trace.traceBegin(TRACE_TAG_ACTIVITY_MANAGER, "topResumedActivityChangeItem");
- client.handleTopResumedActivityChanged(token, mOnTop, "topResumedActivityChangeItem");
+ client.handleTopResumedActivityChanged(r, mOnTop, "topResumedActivityChangeItem");
Trace.traceEnd(TRACE_TAG_ACTIVITY_MANAGER);
}
@@ -97,16 +99,16 @@ public class TopResumedActivityChangeItem extends ClientTransactionItem {
mOnTop = in.readBoolean();
}
- public static final @android.annotation.NonNull Creator<TopResumedActivityChangeItem> CREATOR =
+ public static final @NonNull Creator<TopResumedActivityChangeItem> CREATOR =
new Creator<TopResumedActivityChangeItem>() {
- public TopResumedActivityChangeItem createFromParcel(Parcel in) {
- return new TopResumedActivityChangeItem(in);
- }
-
- public TopResumedActivityChangeItem[] newArray(int size) {
- return new TopResumedActivityChangeItem[size];
- }
- };
+ public TopResumedActivityChangeItem createFromParcel(Parcel in) {
+ return new TopResumedActivityChangeItem(in);
+ }
+
+ public TopResumedActivityChangeItem[] newArray(int size) {
+ return new TopResumedActivityChangeItem[size];
+ }
+ };
@Override
public boolean equals(Object o) {
diff --git a/core/java/android/app/servertransaction/TransactionExecutor.java b/core/java/android/app/servertransaction/TransactionExecutor.java
index 17fcda587322..3dcf2cb5f13e 100644
--- a/core/java/android/app/servertransaction/TransactionExecutor.java
+++ b/core/java/android/app/servertransaction/TransactionExecutor.java
@@ -218,29 +218,29 @@ public class TransactionExecutor {
null /* customIntent */);
break;
case ON_START:
- mTransactionHandler.handleStartActivity(r.token, mPendingActions);
+ mTransactionHandler.handleStartActivity(r, mPendingActions);
break;
case ON_RESUME:
- mTransactionHandler.handleResumeActivity(r.token, false /* finalStateRequest */,
+ mTransactionHandler.handleResumeActivity(r, false /* finalStateRequest */,
r.isForward, "LIFECYCLER_RESUME_ACTIVITY");
break;
case ON_PAUSE:
- mTransactionHandler.handlePauseActivity(r.token, false /* finished */,
+ mTransactionHandler.handlePauseActivity(r, false /* finished */,
false /* userLeaving */, 0 /* configChanges */, mPendingActions,
"LIFECYCLER_PAUSE_ACTIVITY");
break;
case ON_STOP:
- mTransactionHandler.handleStopActivity(r.token, 0 /* configChanges */,
+ mTransactionHandler.handleStopActivity(r, 0 /* configChanges */,
mPendingActions, false /* finalStateRequest */,
"LIFECYCLER_STOP_ACTIVITY");
break;
case ON_DESTROY:
- mTransactionHandler.handleDestroyActivity(r.token, false /* finishing */,
+ mTransactionHandler.handleDestroyActivity(r, false /* finishing */,
0 /* configChanges */, false /* getNonConfigInstance */,
"performLifecycleSequence. cycling to:" + path.get(size - 1));
break;
case ON_RESTART:
- mTransactionHandler.performRestartActivity(r.token, false /* start */);
+ mTransactionHandler.performRestartActivity(r, false /* start */);
break;
default:
throw new IllegalArgumentException("Unexpected lifecycle state: " + state);
diff --git a/core/java/android/bluetooth/BluetoothCodecConfig.java b/core/java/android/bluetooth/BluetoothCodecConfig.java
index e07bc0215a6b..a52fc891790c 100644
--- a/core/java/android/bluetooth/BluetoothCodecConfig.java
+++ b/core/java/android/bluetooth/BluetoothCodecConfig.java
@@ -51,19 +51,25 @@ public final class BluetoothCodecConfig implements Parcelable {
@Retention(RetentionPolicy.SOURCE)
public @interface SourceCodecType {}
+ @UnsupportedAppUsage
public static final int SOURCE_CODEC_TYPE_SBC = 0;
+ @UnsupportedAppUsage
public static final int SOURCE_CODEC_TYPE_AAC = 1;
+ @UnsupportedAppUsage
public static final int SOURCE_CODEC_TYPE_APTX = 2;
+ @UnsupportedAppUsage
public static final int SOURCE_CODEC_TYPE_APTX_HD = 3;
+ @UnsupportedAppUsage
public static final int SOURCE_CODEC_TYPE_LDAC = 4;
+ @UnsupportedAppUsage
public static final int SOURCE_CODEC_TYPE_MAX = 5;
-
+ @UnsupportedAppUsage
public static final int SOURCE_CODEC_TYPE_INVALID = 1000 * 1000;
/** @hide */
@@ -75,10 +81,13 @@ public final class BluetoothCodecConfig implements Parcelable {
@Retention(RetentionPolicy.SOURCE)
public @interface CodecPriority {}
+ @UnsupportedAppUsage
public static final int CODEC_PRIORITY_DISABLED = -1;
+ @UnsupportedAppUsage
public static final int CODEC_PRIORITY_DEFAULT = 0;
+ @UnsupportedAppUsage
public static final int CODEC_PRIORITY_HIGHEST = 1000 * 1000;
@@ -95,18 +104,25 @@ public final class BluetoothCodecConfig implements Parcelable {
@Retention(RetentionPolicy.SOURCE)
public @interface SampleRate {}
+ @UnsupportedAppUsage
public static final int SAMPLE_RATE_NONE = 0;
+ @UnsupportedAppUsage
public static final int SAMPLE_RATE_44100 = 0x1 << 0;
+ @UnsupportedAppUsage
public static final int SAMPLE_RATE_48000 = 0x1 << 1;
+ @UnsupportedAppUsage
public static final int SAMPLE_RATE_88200 = 0x1 << 2;
+ @UnsupportedAppUsage
public static final int SAMPLE_RATE_96000 = 0x1 << 3;
+ @UnsupportedAppUsage
public static final int SAMPLE_RATE_176400 = 0x1 << 4;
+ @UnsupportedAppUsage
public static final int SAMPLE_RATE_192000 = 0x1 << 5;
@@ -120,12 +136,16 @@ public final class BluetoothCodecConfig implements Parcelable {
@Retention(RetentionPolicy.SOURCE)
public @interface BitsPerSample {}
+ @UnsupportedAppUsage
public static final int BITS_PER_SAMPLE_NONE = 0;
+ @UnsupportedAppUsage
public static final int BITS_PER_SAMPLE_16 = 0x1 << 0;
+ @UnsupportedAppUsage
public static final int BITS_PER_SAMPLE_24 = 0x1 << 1;
+ @UnsupportedAppUsage
public static final int BITS_PER_SAMPLE_32 = 0x1 << 2;
@@ -138,10 +158,13 @@ public final class BluetoothCodecConfig implements Parcelable {
@Retention(RetentionPolicy.SOURCE)
public @interface ChannelMode {}
+ @UnsupportedAppUsage
public static final int CHANNEL_MODE_NONE = 0;
+ @UnsupportedAppUsage
public static final int CHANNEL_MODE_MONO = 0x1 << 0;
+ @UnsupportedAppUsage
public static final int CHANNEL_MODE_STEREO = 0x1 << 1;
private final @SourceCodecType int mCodecType;
@@ -154,6 +177,7 @@ public final class BluetoothCodecConfig implements Parcelable {
private final long mCodecSpecific3;
private final long mCodecSpecific4;
+ @UnsupportedAppUsage
public BluetoothCodecConfig(@SourceCodecType int codecType, @CodecPriority int codecPriority,
@SampleRate int sampleRate, @BitsPerSample int bitsPerSample,
@ChannelMode int channelMode, long codecSpecific1,
@@ -170,6 +194,7 @@ public final class BluetoothCodecConfig implements Parcelable {
mCodecSpecific4 = codecSpecific4;
}
+ @UnsupportedAppUsage
public BluetoothCodecConfig(@SourceCodecType int codecType) {
mCodecType = codecType;
mCodecPriority = BluetoothCodecConfig.CODEC_PRIORITY_DEFAULT;
@@ -391,6 +416,7 @@ public final class BluetoothCodecConfig implements Parcelable {
*
* @return the codec type
*/
+ @UnsupportedAppUsage
public @SourceCodecType int getCodecType() {
return mCodecType;
}
@@ -411,6 +437,7 @@ public final class BluetoothCodecConfig implements Parcelable {
*
* @return the codec priority
*/
+ @UnsupportedAppUsage
public @CodecPriority int getCodecPriority() {
return mCodecPriority;
}
@@ -441,6 +468,7 @@ public final class BluetoothCodecConfig implements Parcelable {
*
* @return the codec sample rate
*/
+ @UnsupportedAppUsage
public @SampleRate int getSampleRate() {
return mSampleRate;
}
@@ -455,6 +483,7 @@ public final class BluetoothCodecConfig implements Parcelable {
*
* @return the codec bits per sample
*/
+ @UnsupportedAppUsage
public @BitsPerSample int getBitsPerSample() {
return mBitsPerSample;
}
@@ -479,6 +508,7 @@ public final class BluetoothCodecConfig implements Parcelable {
*
* @return a codec specific value1.
*/
+ @UnsupportedAppUsage
public long getCodecSpecific1() {
return mCodecSpecific1;
}
diff --git a/core/java/android/bluetooth/BluetoothCodecStatus.java b/core/java/android/bluetooth/BluetoothCodecStatus.java
index 1e394b830d51..7b567b4098e7 100644
--- a/core/java/android/bluetooth/BluetoothCodecStatus.java
+++ b/core/java/android/bluetooth/BluetoothCodecStatus.java
@@ -17,6 +17,7 @@
package android.bluetooth;
import android.annotation.Nullable;
+import android.compat.annotation.UnsupportedAppUsage;
import android.os.Parcel;
import android.os.Parcelable;
@@ -38,6 +39,7 @@ public final class BluetoothCodecStatus implements Parcelable {
* This extra represents the current codec status of the A2DP
* profile.
*/
+ @UnsupportedAppUsage
public static final String EXTRA_CODEC_STATUS =
"android.bluetooth.extra.CODEC_STATUS";
@@ -196,6 +198,7 @@ public final class BluetoothCodecStatus implements Parcelable {
*
* @return the current codec configuration
*/
+ @UnsupportedAppUsage
public @Nullable BluetoothCodecConfig getCodecConfig() {
return mCodecConfig;
}
@@ -205,6 +208,7 @@ public final class BluetoothCodecStatus implements Parcelable {
*
* @return an array with the codecs local capabilities
*/
+ @UnsupportedAppUsage
public @Nullable BluetoothCodecConfig[] getCodecsLocalCapabilities() {
return mCodecsLocalCapabilities;
}
@@ -214,6 +218,7 @@ public final class BluetoothCodecStatus implements Parcelable {
*
* @return an array with the codecs selectable capabilities
*/
+ @UnsupportedAppUsage
public @Nullable BluetoothCodecConfig[] getCodecsSelectableCapabilities() {
return mCodecsSelectableCapabilities;
}
diff --git a/core/java/android/bluetooth/BluetoothPan.java b/core/java/android/bluetooth/BluetoothPan.java
index 73c38cbfbe3f..ce3c7d25a108 100644
--- a/core/java/android/bluetooth/BluetoothPan.java
+++ b/core/java/android/bluetooth/BluetoothPan.java
@@ -89,6 +89,33 @@ public final class BluetoothPan implements BluetoothProfile {
@SuppressLint("ActionValue")
public static final String EXTRA_LOCAL_ROLE = "android.bluetooth.pan.extra.LOCAL_ROLE";
+ /**
+ * Intent used to broadcast the change in tethering state of the Pan
+ * Profile
+ *
+ * <p>This intent will have 1 extra:
+ * <ul>
+ * <li> {@link #EXTRA_TETHERING_STATE} - The current state of Bluetooth
+ * tethering. </li>
+ * </ul>
+ *
+ * <p> {@link #EXTRA_TETHERING_STATE} can be any of {@link #TETHERING_STATE_OFF} or
+ * {@link #TETHERING_STATE_ON}
+ * <p>Requires {@link android.Manifest.permission#BLUETOOTH} permission to
+ * receive.
+ */
+ @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
+ public static final String ACTION_TETHERING_STATE_CHANGED =
+ "android.bluetooth.action.TETHERING_STATE_CHANGED";
+
+ /**
+ * Extra for {@link #ACTION_TETHERING_STATE_CHANGED} intent
+ * The tethering state of the PAN profile.
+ * It can be one of {@link #TETHERING_STATE_OFF} or {@link #TETHERING_STATE_ON}.
+ */
+ public static final String EXTRA_TETHERING_STATE =
+ "android.bluetooth.extra.TETHERING_STATE";
+
/** @hide */
@IntDef({PAN_ROLE_NONE, LOCAL_NAP_ROLE, LOCAL_PANU_ROLE})
@Retention(RetentionPolicy.SOURCE)
@@ -114,6 +141,14 @@ public final class BluetoothPan implements BluetoothProfile {
public static final int REMOTE_PANU_ROLE = 2;
+ /** @hide **/
+ @IntDef({TETHERING_STATE_OFF, TETHERING_STATE_ON})
+ @Retention(RetentionPolicy.SOURCE)
+ public @interface TetheringState{}
+
+ public static final int TETHERING_STATE_OFF = 1;
+
+ public static final int TETHERING_STATE_ON = 2;
/**
* Return codes for the connect and disconnect Bluez / Dbus calls.
*
diff --git a/core/java/android/content/Context.java b/core/java/android/content/Context.java
index 38bc79750631..005648ffec36 100644
--- a/core/java/android/content/Context.java
+++ b/core/java/android/content/Context.java
@@ -351,6 +351,13 @@ public abstract class Context {
* due to its foreground state such as an activity or foreground service, then this flag will
* allow the bound app to get the same capabilities, as long as it has the required permissions
* as well.
+ *
+ * If binding from a top app and its target SDK version is at or above
+ * {@link android.os.Build.VERSION_CODES#R}, the app needs to
+ * explicitly use BIND_INCLUDE_CAPABILITIES flag to pass all capabilities to the service so the
+ * other app can have while-use-use access such as location, camera, microphone from background.
+ * If binding from a top app and its target SDK version is below
+ * {@link android.os.Build.VERSION_CODES#R}, BIND_INCLUDE_CAPABILITIES is implicit.
*/
public static final int BIND_INCLUDE_CAPABILITIES = 0x000001000;
@@ -3179,8 +3186,8 @@ public abstract class Context {
* {@link #BIND_AUTO_CREATE}, {@link #BIND_DEBUG_UNBIND},
* {@link #BIND_NOT_FOREGROUND}, {@link #BIND_ABOVE_CLIENT},
* {@link #BIND_ALLOW_OOM_MANAGEMENT}, {@link #BIND_WAIVE_PRIORITY}.
- * {@link #BIND_IMPORTANT}, or
- * {@link #BIND_ADJUST_WITH_ACTIVITY}.
+ * {@link #BIND_IMPORTANT}, {@link #BIND_ADJUST_WITH_ACTIVITY},
+ * {@link #BIND_NOT_PERCEPTIBLE}, or {@link #BIND_INCLUDE_CAPABILITIES}.
* @return {@code true} if the system is in the process of bringing up a
* service that your client has permission to bind to; {@code false}
* if the system couldn't find the service or if your client doesn't
@@ -3201,6 +3208,8 @@ public abstract class Context {
* @see #BIND_WAIVE_PRIORITY
* @see #BIND_IMPORTANT
* @see #BIND_ADJUST_WITH_ACTIVITY
+ * @see #BIND_NOT_PERCEPTIBLE
+ * @see #BIND_INCLUDE_CAPABILITIES
*/
public abstract boolean bindService(@RequiresPermission Intent service,
@NonNull ServiceConnection conn, @BindServiceFlags int flags);
@@ -5820,10 +5829,19 @@ public abstract class Context {
* {@link #createWindowContext(int, Bundle)} on the returned display Context or use an
* {@link android.app.Activity}.
*
+ * <p>
+ * Note that invoking #createDisplayContext(Display) from an UI context is not regarded
+ * as an UI context. In other words, it is not suggested to access UI components (such as
+ * obtain a {@link WindowManager} by {@link #getSystemService(String)})
+ * from the context created from #createDisplayContext(Display).
+ * </p>
+ *
* @param display A {@link Display} object specifying the display for whose metrics the
* Context's resources should be tailored.
*
* @return A {@link Context} for the display.
+ *
+ * @see #getSystemService(String)
*/
@DisplayContext
public abstract Context createDisplayContext(@NonNull Display display);
diff --git a/core/java/android/content/om/TEST_MAPPING b/core/java/android/content/om/TEST_MAPPING
index d35dfdbf32c8..d8f885439b34 100644
--- a/core/java/android/content/om/TEST_MAPPING
+++ b/core/java/android/content/om/TEST_MAPPING
@@ -21,6 +21,20 @@
"include-filter": "android.appsecurity.cts.OverlayHostTest"
}
]
+ },
+ {
+ "name": "CtsContentTestCases",
+ "options": [
+ {
+ "exclude-annotation": "androidx.test.filters.FlakyTest"
+ },
+ {
+ "exclude-annotation": "org.junit.Ignore"
+ },
+ {
+ "include-filter": "android.content.om.cts"
+ }
+ ]
}
]
}
diff --git a/core/java/android/content/pm/ActivityInfo.java b/core/java/android/content/pm/ActivityInfo.java
index 31c77eeb5424..c2beab507da0 100644
--- a/core/java/android/content/pm/ActivityInfo.java
+++ b/core/java/android/content/pm/ActivityInfo.java
@@ -831,6 +831,16 @@ public class ActivityInfo extends ComponentInfo implements Parcelable {
*/
public static final int CONFIG_WINDOW_CONFIGURATION = 0x20000000;
+ /**
+ * Bit in {@link #configChanges} that indicates that the activity
+ * can itself handle changes to bold text. Set from the
+ * {@link android.R.attr#configChanges} attribute. This is
+ * not a core resource configuration, but a higher-level value, so its
+ * constant starts at the high bits.
+ */
+
+ public static final int CONFIG_FORCE_BOLD_TEXT = 0x10000000;
+
/** @hide
* Unfortunately the constants for config changes in native code are
* different from ActivityInfo. :( Here are the values we should use for the
diff --git a/core/java/android/content/pm/ApkChecksum.java b/core/java/android/content/pm/ApkChecksum.java
index e087c44d1ed1..bf678419cbef 100644
--- a/core/java/android/content/pm/ApkChecksum.java
+++ b/core/java/android/content/pm/ApkChecksum.java
@@ -50,7 +50,11 @@ public final class ApkChecksum implements Parcelable {
*/
private final @NonNull Checksum mChecksum;
/**
- * For Installer-provided checksums, certificate of the Installer/AppStore.
+ * For Installer-provided checksums, package name of the Installer.
+ */
+ private final @Nullable String mSourcePackageName;
+ /**
+ * For Installer-provided checksums, certificate of the Installer.
*/
private final @Nullable byte[] mSourceCertificate;
@@ -61,7 +65,7 @@ public final class ApkChecksum implements Parcelable {
*/
public ApkChecksum(@Nullable String splitName, @Checksum.Kind int kind,
@NonNull byte[] value) {
- this(splitName, new Checksum(kind, value), (byte[]) null);
+ this(splitName, new Checksum(kind, value), (String) null, (byte[]) null);
}
/**
@@ -69,10 +73,10 @@ public final class ApkChecksum implements Parcelable {
*
* @hide
*/
- public ApkChecksum(@Nullable String splitName, @Checksum.Kind int kind,
- @NonNull byte[] value, @Nullable Certificate sourceCertificate)
+ public ApkChecksum(@Nullable String splitName, @Checksum.Kind int kind, @NonNull byte[] value,
+ @Nullable String sourcePackageName, @Nullable Certificate sourceCertificate)
throws CertificateEncodingException {
- this(splitName, new Checksum(kind, value),
+ this(splitName, new Checksum(kind, value), sourcePackageName,
(sourceCertificate != null) ? sourceCertificate.getEncoded() : null);
}
@@ -136,19 +140,23 @@ public final class ApkChecksum implements Parcelable {
* Checksum for which split. Null indicates base.apk.
* @param checksum
* Checksum.
+ * @param sourcePackageName
+ * For Installer-provided checksums, package name of the Installer.
* @param sourceCertificate
- * For Installer-provided checksums, certificate of the Installer/AppStore.
+ * For Installer-provided checksums, certificate of the Installer.
* @hide
*/
@DataClass.Generated.Member
public ApkChecksum(
@Nullable String splitName,
@NonNull Checksum checksum,
+ @Nullable String sourcePackageName,
@Nullable byte[] sourceCertificate) {
this.mSplitName = splitName;
this.mChecksum = checksum;
com.android.internal.util.AnnotationValidations.validate(
NonNull.class, null, mChecksum);
+ this.mSourcePackageName = sourcePackageName;
this.mSourceCertificate = sourceCertificate;
// onConstructed(); // You can define this method to get a callback
@@ -162,6 +170,14 @@ public final class ApkChecksum implements Parcelable {
return mSplitName;
}
+ /**
+ * For Installer-provided checksums, package name of the Installer.
+ */
+ @DataClass.Generated.Member
+ public @Nullable String getSourcePackageName() {
+ return mSourcePackageName;
+ }
+
@Override
@DataClass.Generated.Member
public void writeToParcel(@NonNull Parcel dest, int flags) {
@@ -170,10 +186,12 @@ public final class ApkChecksum implements Parcelable {
byte flg = 0;
if (mSplitName != null) flg |= 0x1;
- if (mSourceCertificate != null) flg |= 0x4;
+ if (mSourcePackageName != null) flg |= 0x4;
+ if (mSourceCertificate != null) flg |= 0x8;
dest.writeByte(flg);
if (mSplitName != null) dest.writeString(mSplitName);
dest.writeTypedObject(mChecksum, flags);
+ if (mSourcePackageName != null) dest.writeString(mSourcePackageName);
if (mSourceCertificate != null) dest.writeByteArray(mSourceCertificate);
}
@@ -191,12 +209,14 @@ public final class ApkChecksum implements Parcelable {
byte flg = in.readByte();
String splitName = (flg & 0x1) == 0 ? null : in.readString();
Checksum checksum = (Checksum) in.readTypedObject(Checksum.CREATOR);
- byte[] sourceCertificate = (flg & 0x4) == 0 ? null : in.createByteArray();
+ String sourcePackageName = (flg & 0x4) == 0 ? null : in.readString();
+ byte[] sourceCertificate = (flg & 0x8) == 0 ? null : in.createByteArray();
this.mSplitName = splitName;
this.mChecksum = checksum;
com.android.internal.util.AnnotationValidations.validate(
NonNull.class, null, mChecksum);
+ this.mSourcePackageName = sourcePackageName;
this.mSourceCertificate = sourceCertificate;
// onConstructed(); // You can define this method to get a callback
@@ -217,10 +237,10 @@ public final class ApkChecksum implements Parcelable {
};
@DataClass.Generated(
- time = 1599845645160L,
+ time = 1600407436287L,
codegenVersion = "1.0.15",
sourceFile = "frameworks/base/core/java/android/content/pm/ApkChecksum.java",
- inputSignatures = "private final @android.annotation.Nullable java.lang.String mSplitName\nprivate final @android.annotation.NonNull android.content.pm.Checksum mChecksum\nprivate final @android.annotation.Nullable byte[] mSourceCertificate\npublic @android.content.pm.Checksum.Kind int getKind()\npublic @android.annotation.NonNull byte[] getValue()\npublic @android.annotation.Nullable byte[] getSourceCertificateBytes()\npublic @android.annotation.Nullable java.security.cert.Certificate getSourceCertificate()\nclass ApkChecksum extends java.lang.Object implements [android.os.Parcelable]\n@com.android.internal.util.DataClass(genHiddenConstructor=true)")
+ inputSignatures = "private final @android.annotation.Nullable java.lang.String mSplitName\nprivate final @android.annotation.NonNull android.content.pm.Checksum mChecksum\nprivate final @android.annotation.Nullable java.lang.String mSourcePackageName\nprivate final @android.annotation.Nullable byte[] mSourceCertificate\npublic @android.content.pm.Checksum.Kind int getKind()\npublic @android.annotation.NonNull byte[] getValue()\npublic @android.annotation.Nullable byte[] getSourceCertificateBytes()\npublic @android.annotation.Nullable java.security.cert.Certificate getSourceCertificate()\nclass ApkChecksum extends java.lang.Object implements [android.os.Parcelable]\n@com.android.internal.util.DataClass(genHiddenConstructor=true)")
@Deprecated
private void __metadata() {}
diff --git a/core/java/android/content/pm/ApplicationInfo.java b/core/java/android/content/pm/ApplicationInfo.java
index 6673cb933eff..d5f2c12e8462 100644
--- a/core/java/android/content/pm/ApplicationInfo.java
+++ b/core/java/android/content/pm/ApplicationInfo.java
@@ -146,8 +146,15 @@ public class ApplicationInfo extends PackageItemInfo implements Parcelable {
public int uiOptions = 0;
/**
- * Value for {@link #flags}: if set, this application is installed in the
- * device's system image.
+ * Value for {@link #flags}: if set, this application is installed in the device's system image.
+ * This should not be used to make security decisions. Instead, rely on
+ * {@linkplain android.content.pm.PackageManager#checkSignatures(java.lang.String,java.lang.String)
+ * signature checks} or
+ * <a href="https://developer.android.com/training/articles/security-tips#Permissions">permissions</a>.
+ *
+ * <p><b>Warning:</b> Note that does flag not behave the same as
+ * {@link android.R.attr#protectionLevel android:protectionLevel} {@code system} or
+ * {@code signatureOrSystem}.
*/
public static final int FLAG_SYSTEM = 1<<0;
diff --git a/core/java/android/content/pm/Checksum.java b/core/java/android/content/pm/Checksum.java
index 123aaddda7ce..10ca5cac5276 100644
--- a/core/java/android/content/pm/Checksum.java
+++ b/core/java/android/content/pm/Checksum.java
@@ -32,14 +32,19 @@ import java.util.List;
*
* @see PackageInstaller.Session#addChecksums(String, List)
*/
-@DataClass(genHiddenConstructor = true, genConstDefs = false)
+@DataClass(genConstDefs = false)
public final class Checksum implements Parcelable {
/**
* Root SHA256 hash of a 4K Merkle tree computed over all file bytes.
* <a href="https://source.android.com/security/apksigning/v4">See APK Signature Scheme V4</a>.
* <a href="https://git.kernel.org/pub/scm/fs/fscrypt/fscrypt.git/tree/Documentation/filesystems/fsverity.rst">See fs-verity</a>.
*
+ * Recommended for all new applications.
+ * Can be used by kernel to enforce authenticity and integrity of the APK.
+ * <a href="https://git.kernel.org/pub/scm/fs/fscrypt/fscrypt.git/tree/Documentation/filesystems/fsverity.rst#">See fs-verity for details</a>
+ *
* @see PackageManager#getChecksums
+ * @see PackageInstaller.Session#addChecksums
*/
public static final int WHOLE_MERKLE_ROOT_4K_SHA256 = 0x00000001;
@@ -47,20 +52,31 @@ public final class Checksum implements Parcelable {
* MD5 hash computed over all file bytes.
*
* @see PackageManager#getChecksums
+ * @see PackageInstaller.Session#addChecksums
+ * @deprecated Use SHA2 family of hashes (SHA256/SHA512) instead.
+ * MD5 is cryptographically broken and unsuitable for further use.
+ * Provided for completeness' sake and to support legacy usecases.
*/
+ @Deprecated
public static final int WHOLE_MD5 = 0x00000002;
/**
* SHA1 hash computed over all file bytes.
*
* @see PackageManager#getChecksums
+ * @see PackageInstaller.Session#addChecksums
+ * @deprecated Use SHA2 family of hashes (SHA256/SHA512) instead.
+ * SHA1 is broken and should not be used.
+ * Provided for completeness' sake and to support legacy usecases.
*/
+ @Deprecated
public static final int WHOLE_SHA1 = 0x00000004;
/**
* SHA256 hash computed over all file bytes.
*
* @see PackageManager#getChecksums
+ * @see PackageInstaller.Session#addChecksums
*/
public static final int WHOLE_SHA256 = 0x00000008;
@@ -68,6 +84,7 @@ public final class Checksum implements Parcelable {
* SHA512 hash computed over all file bytes.
*
* @see PackageManager#getChecksums
+ * @see PackageInstaller.Session#addChecksums
*/
public static final int WHOLE_SHA512 = 0x00000010;
@@ -77,6 +94,7 @@ public final class Checksum implements Parcelable {
* <a href="https://source.android.com/security/apksigning/v2">See APK Signature Scheme V2</a>.
*
* @see PackageManager#getChecksums
+ * @see PackageInstaller.Session#addChecksums
*/
public static final int PARTIAL_MERKLE_ROOT_1M_SHA256 = 0x00000020;
@@ -86,6 +104,7 @@ public final class Checksum implements Parcelable {
* <a href="https://source.android.com/security/apksigning/v2">See APK Signature Scheme V2</a>.
*
* @see PackageManager#getChecksums
+ * @see PackageInstaller.Session#addChecksums
*/
public static final int PARTIAL_MERKLE_ROOT_1M_SHA512 = 0x00000040;
@@ -113,6 +132,7 @@ public final class Checksum implements Parcelable {
+
// Code below generated by codegen v1.0.15.
//
// DO NOT MODIFY!
@@ -133,7 +153,6 @@ public final class Checksum implements Parcelable {
* Checksum kind.
* @param value
* Checksum value.
- * @hide
*/
@DataClass.Generated.Member
public Checksum(
@@ -214,10 +233,10 @@ public final class Checksum implements Parcelable {
};
@DataClass.Generated(
- time = 1599845646883L,
+ time = 1600717052366L,
codegenVersion = "1.0.15",
sourceFile = "frameworks/base/core/java/android/content/pm/Checksum.java",
- inputSignatures = "public static final int WHOLE_MERKLE_ROOT_4K_SHA256\npublic static final int WHOLE_MD5\npublic static final int WHOLE_SHA1\npublic static final int WHOLE_SHA256\npublic static final int WHOLE_SHA512\npublic static final int PARTIAL_MERKLE_ROOT_1M_SHA256\npublic static final int PARTIAL_MERKLE_ROOT_1M_SHA512\nprivate final @android.content.pm.Checksum.Kind int mKind\nprivate final @android.annotation.NonNull byte[] mValue\nclass Checksum extends java.lang.Object implements [android.os.Parcelable]\n@com.android.internal.util.DataClass(genHiddenConstructor=true, genConstDefs=false)")
+ inputSignatures = "public static final int WHOLE_MERKLE_ROOT_4K_SHA256\npublic static final @java.lang.Deprecated int WHOLE_MD5\npublic static final @java.lang.Deprecated int WHOLE_SHA1\npublic static final int WHOLE_SHA256\npublic static final int WHOLE_SHA512\npublic static final int PARTIAL_MERKLE_ROOT_1M_SHA256\npublic static final int PARTIAL_MERKLE_ROOT_1M_SHA512\nprivate final @android.content.pm.Checksum.Kind int mKind\nprivate final @android.annotation.NonNull byte[] mValue\nclass Checksum extends java.lang.Object implements [android.os.Parcelable]\n@com.android.internal.util.DataClass(genConstDefs=false)")
@Deprecated
private void __metadata() {}
diff --git a/core/java/android/content/pm/IPackageManager.aidl b/core/java/android/content/pm/IPackageManager.aidl
index 244cfe124381..24282365a8c7 100644
--- a/core/java/android/content/pm/IPackageManager.aidl
+++ b/core/java/android/content/pm/IPackageManager.aidl
@@ -83,6 +83,11 @@ interface IPackageManager {
@UnsupportedAppUsage
ApplicationInfo getApplicationInfo(String packageName, int flags ,int userId);
+ /**
+ * @return the target SDK for the given package name, or -1 if it cannot be retrieved
+ */
+ int getTargetSdkVersion(String packageName);
+
@UnsupportedAppUsage
ActivityInfo getActivityInfo(in ComponentName className, int flags, int userId);
@@ -788,4 +793,6 @@ interface IPackageManager {
List<String> getMimeGroup(String packageName, String group);
boolean isAutoRevokeWhitelisted(String packageName);
+
+ void grantImplicitAccess(int queryingUid, String visibleAuthority);
}
diff --git a/core/java/android/content/pm/PackageInstaller.java b/core/java/android/content/pm/PackageInstaller.java
index 75ee9b7943fe..a9f143987cd4 100644
--- a/core/java/android/content/pm/PackageInstaller.java
+++ b/core/java/android/content/pm/PackageInstaller.java
@@ -1227,7 +1227,10 @@ public class PackageInstaller {
* IntentSender)}.
* @throws SecurityException if called after the session has been
* committed or abandoned.
+ * @deprecated use platform-enforced checksums e.g.
+ * {@link Checksum#WHOLE_MERKLE_ROOT_4K_SHA256}
*/
+ @Deprecated
public void addChecksums(@NonNull String name, @NonNull List<Checksum> checksums)
throws IOException {
Objects.requireNonNull(name);
@@ -1875,6 +1878,7 @@ public class PackageInstaller {
/** {@hide} */
@SystemApi
+ @TestApi
public void setInstallAsInstantApp(boolean isInstantApp) {
if (isInstantApp) {
installFlags |= PackageManager.INSTALL_INSTANT_APP;
diff --git a/core/java/android/content/pm/PackageManager.java b/core/java/android/content/pm/PackageManager.java
index e68df3383642..4eec56c0d87b 100644
--- a/core/java/android/content/pm/PackageManager.java
+++ b/core/java/android/content/pm/PackageManager.java
@@ -4099,6 +4099,15 @@ public abstract class PackageManager {
}
/**
+ * @return The target SDK version for the given package name.
+ * @throws NameNotFoundException if a package with the given name cannot be found on the system.
+ */
+ @IntRange(from = 0)
+ public int getTargetSdkVersion(@NonNull String packageName) throws NameNotFoundException {
+ throw new UnsupportedOperationException();
+ }
+
+ /**
* Retrieve all of the information we know about a particular activity
* class.
*
@@ -7864,6 +7873,9 @@ public abstract class PackageManager {
* - enforced by installer.
* If caller needs a specific checksum kind, they can specify it as required.
*
+ * <b>Caution: Android can not verify installer-provided checksums. Make sure you specify
+ * trusted installers.</b>
+ *
* @param packageName whose checksums to return.
* @param includeSplits whether to include checksums for non-base splits.
* @param required explicitly request the checksum kinds. Will incur significant
@@ -8073,6 +8085,20 @@ public abstract class PackageManager {
"getMimeGroup not implemented in subclass");
}
+ /**
+ * Grants implicit visibility of the package that provides an authority to a querying UID.
+ *
+ * @throws SecurityException when called by a package other than the contacts provider
+ * @hide
+ */
+ public void grantImplicitAccess(int queryingUid, String visibleAuthority) {
+ try {
+ ActivityThread.getPackageManager().grantImplicitAccess(queryingUid, visibleAuthority);
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
+
// Some of the flags don't affect the query result, but let's be conservative and cache
// each combination of flags separately.
diff --git a/core/java/android/content/pm/TEST_MAPPING b/core/java/android/content/pm/TEST_MAPPING
index 1c98a1ffcbb6..953400e10e15 100644
--- a/core/java/android/content/pm/TEST_MAPPING
+++ b/core/java/android/content/pm/TEST_MAPPING
@@ -17,6 +17,20 @@
},
{
"name": "CarrierAppIntegrationTestCases"
+ },
+ {
+ "name": "CtsContentTestCases",
+ "options": [
+ {
+ "exclude-annotation": "androidx.test.filters.FlakyTest"
+ },
+ {
+ "exclude-annotation": "org.junit.Ignore"
+ },
+ {
+ "include-filter": "android.content.pm.cts"
+ }
+ ]
}
],
"postsubmit": [
diff --git a/core/java/android/content/res/Configuration.java b/core/java/android/content/res/Configuration.java
index 6407f9b3c1b8..49f81f83c39a 100644
--- a/core/java/android/content/res/Configuration.java
+++ b/core/java/android/content/res/Configuration.java
@@ -19,6 +19,7 @@ package android.content.res;
import static android.content.ConfigurationProto.COLOR_MODE;
import static android.content.ConfigurationProto.DENSITY_DPI;
import static android.content.ConfigurationProto.FONT_SCALE;
+import static android.content.ConfigurationProto.FORCE_BOLD_TEXT;
import static android.content.ConfigurationProto.HARD_KEYBOARD_HIDDEN;
import static android.content.ConfigurationProto.KEYBOARD;
import static android.content.ConfigurationProto.KEYBOARD_HIDDEN;
@@ -332,6 +333,26 @@ public final class Configuration implements Parcelable, Comparable<Configuration
public int screenLayout;
/**
+ * An undefined forceBoldText.
+ */
+ public static final int FORCE_BOLD_TEXT_UNDEFINED = 0;
+
+ /**
+ * Text is not bold.
+ */
+ public static final int FORCE_BOLD_TEXT_NO = 1;
+
+ /**
+ * Text is bold.
+ */
+ public static final int FORCE_BOLD_TEXT_YES = 2;
+
+ /**
+ * Current user preference for displaying all text in bold.
+ */
+ public int forceBoldText;
+
+ /**
* Configuration relating to the windowing state of the object associated with this
* Configuration. Contents of this field are not intended to affect resources, but need to be
* communicated and propagated at the same time as the rest of Configuration.
@@ -467,6 +488,9 @@ public final class Configuration implements Parcelable, Comparable<Configuration
if ((diff & ActivityInfo.CONFIG_WINDOW_CONFIGURATION) != 0) {
list.add("CONFIG_WINDOW_CONFIGURATION");
}
+ if ((diff & ActivityInfo.CONFIG_FORCE_BOLD_TEXT) != 0) {
+ list.add("CONFIG_AUTO_BOLD_TEXT");
+ }
StringBuilder builder = new StringBuilder("{");
for (int i = 0, n = list.size(); i < n; i++) {
builder.append(list.get(i));
@@ -957,6 +981,7 @@ public final class Configuration implements Parcelable, Comparable<Configuration
assetsSeq = o.assetsSeq;
seq = o.seq;
windowConfiguration.setTo(o.windowConfiguration);
+ forceBoldText = o.forceBoldText;
}
public String toString() {
@@ -1112,6 +1137,12 @@ public final class Configuration implements Parcelable, Comparable<Configuration
if (seq != 0) {
sb.append(" s.").append(seq);
}
+ if (forceBoldText != FORCE_BOLD_TEXT_UNDEFINED) {
+ sb.append(" boldText=");
+ sb.append(forceBoldText);
+ } else {
+ sb.append(" ?boldText");
+ }
sb.append('}');
return sb.toString();
}
@@ -1152,6 +1183,7 @@ public final class Configuration implements Parcelable, Comparable<Configuration
if (!persisted && windowConfiguration != null) {
windowConfiguration.dumpDebug(protoOutputStream, WINDOW_CONFIGURATION);
}
+ protoOutputStream.write(FORCE_BOLD_TEXT, forceBoldText);
}
protoOutputStream.write(ORIENTATION, orientation);
protoOutputStream.write(SCREEN_WIDTH_DP, screenWidthDp);
@@ -1315,6 +1347,9 @@ public final class Configuration implements Parcelable, Comparable<Configuration
Slog.e(TAG, "error parsing locale list in configuration.", e);
}
break;
+ case (int) FORCE_BOLD_TEXT:
+ forceBoldText = protoInputStream.readInt(FORCE_BOLD_TEXT);
+ break;
}
}
} finally {
@@ -1410,6 +1445,7 @@ public final class Configuration implements Parcelable, Comparable<Configuration
assetsSeq = ASSETS_SEQ_UNDEFINED;
seq = 0;
windowConfiguration.setToDefaults();
+ forceBoldText = FORCE_BOLD_TEXT_UNDEFINED;
}
/**
@@ -1607,6 +1643,12 @@ public final class Configuration implements Parcelable, Comparable<Configuration
changed |= ActivityInfo.CONFIG_WINDOW_CONFIGURATION;
}
+ if (delta.forceBoldText != FORCE_BOLD_TEXT_UNDEFINED
+ && delta.forceBoldText != forceBoldText) {
+ changed |= ActivityInfo.CONFIG_FORCE_BOLD_TEXT;
+ forceBoldText = delta.forceBoldText;
+ }
+
return changed;
}
@@ -1685,6 +1727,9 @@ public final class Configuration implements Parcelable, Comparable<Configuration
if ((mask & ActivityInfo.CONFIG_WINDOW_CONFIGURATION) != 0) {
windowConfiguration.setTo(delta.windowConfiguration, windowMask);
}
+ if ((mask & ActivityInfo.CONFIG_FORCE_BOLD_TEXT) != 0) {
+ forceBoldText = delta.forceBoldText;
+ }
}
/**
@@ -1717,6 +1762,8 @@ public final class Configuration implements Parcelable, Comparable<Configuration
* PackageManager.ActivityInfo.CONFIG_SMALLEST_SCREEN_SIZE}.
* {@link android.content.pm.ActivityInfo#CONFIG_LAYOUT_DIRECTION
* PackageManager.ActivityInfo.CONFIG_LAYOUT_DIRECTION}.
+ * {@link android.content.pm.ActivityInfo#CONFIG_FORCE_BOLD_TEXT
+ * PackageManager.ActivityInfo.CONFIG_FORCE_BOLD_TEXT.
*/
public int diff(Configuration delta) {
return diff(delta, false /* compareUndefined */, false /* publicOnly */);
@@ -1840,6 +1887,10 @@ public final class Configuration implements Parcelable, Comparable<Configuration
changed |= ActivityInfo.CONFIG_WINDOW_CONFIGURATION;
}
+ if ((compareUndefined || delta.forceBoldText != FORCE_BOLD_TEXT_UNDEFINED)
+ && forceBoldText != delta.forceBoldText) {
+ changed |= ActivityInfo.CONFIG_FORCE_BOLD_TEXT;
+ }
return changed;
}
@@ -1933,6 +1984,7 @@ public final class Configuration implements Parcelable, Comparable<Configuration
dest.writeValue(windowConfiguration);
dest.writeInt(assetsSeq);
dest.writeInt(seq);
+ dest.writeInt(forceBoldText);
}
public void readFromParcel(Parcel source) {
@@ -1964,6 +2016,7 @@ public final class Configuration implements Parcelable, Comparable<Configuration
windowConfiguration.setTo((WindowConfiguration) source.readValue(null));
assetsSeq = source.readInt();
seq = source.readInt();
+ forceBoldText = source.readInt();
}
public static final @android.annotation.NonNull Parcelable.Creator<Configuration> CREATOR
@@ -2062,6 +2115,8 @@ public final class Configuration implements Parcelable, Comparable<Configuration
if (n != 0) return n;
n = windowConfiguration.compareTo(that.windowConfiguration);
if (n != 0) return n;
+ n = this.forceBoldText - that.forceBoldText;
+ if (n != 0) return n;
// if (n != 0) return n;
return n;
@@ -2102,6 +2157,7 @@ public final class Configuration implements Parcelable, Comparable<Configuration
result = 31 * result + smallestScreenWidthDp;
result = 31 * result + densityDpi;
result = 31 * result + assetsSeq;
+ result = 31 * result + forceBoldText;
return result;
}
@@ -2674,6 +2730,10 @@ public final class Configuration implements Parcelable, Comparable<Configuration
if (!base.windowConfiguration.equals(change.windowConfiguration)) {
delta.windowConfiguration.setTo(change.windowConfiguration);
}
+
+ if (base.forceBoldText != change.forceBoldText) {
+ delta.forceBoldText = change.forceBoldText;
+ }
return delta;
}
@@ -2697,6 +2757,7 @@ public final class Configuration implements Parcelable, Comparable<Configuration
private static final String XML_ATTR_SMALLEST_WIDTH = "sw";
private static final String XML_ATTR_DENSITY = "density";
private static final String XML_ATTR_APP_BOUNDS = "app_bounds";
+ private static final String XML_ATTR_FORCE_BOLD_TEXT = "forceBoldText";
/**
* Reads the attributes corresponding to Configuration member fields from the Xml parser.
@@ -2746,6 +2807,8 @@ public final class Configuration implements Parcelable, Comparable<Configuration
SMALLEST_SCREEN_WIDTH_DP_UNDEFINED);
configOut.densityDpi = XmlUtils.readIntAttribute(parser, XML_ATTR_DENSITY,
DENSITY_DPI_UNDEFINED);
+ configOut.forceBoldText = XmlUtils.readIntAttribute(parser, XML_ATTR_FORCE_BOLD_TEXT,
+ FORCE_BOLD_TEXT_UNDEFINED);
// For persistence, we don't care about assetsSeq and WindowConfiguration, so do not read it
// out.
diff --git a/core/java/android/content/res/TEST_MAPPING b/core/java/android/content/res/TEST_MAPPING
index 9ebc9969a730..c02af59ab72e 100644
--- a/core/java/android/content/res/TEST_MAPPING
+++ b/core/java/android/content/res/TEST_MAPPING
@@ -2,6 +2,20 @@
"presubmit": [
{
"name": "CtsResourcesLoaderTests"
+ },
+ {
+ "name": "CtsContentTestCases",
+ "options": [
+ {
+ "exclude-annotation": "androidx.test.filters.FlakyTest"
+ },
+ {
+ "exclude-annotation": "org.junit.Ignore"
+ },
+ {
+ "include-filter": "android.content.res.cts"
+ }
+ ]
}
]
}
diff --git a/core/java/android/hardware/biometrics/BiometricManager.java b/core/java/android/hardware/biometrics/BiometricManager.java
index f86eb9082e01..8d0cc68056f8 100644
--- a/core/java/android/hardware/biometrics/BiometricManager.java
+++ b/core/java/android/hardware/biometrics/BiometricManager.java
@@ -103,6 +103,7 @@ public class BiometricManager {
@IntDef(flag = true, value = {
BIOMETRIC_STRONG,
BIOMETRIC_WEAK,
+ BIOMETRIC_CONVENIENCE,
DEVICE_CREDENTIAL,
})
@interface Types {}
diff --git a/core/java/android/hardware/biometrics/SensorProperties.java b/core/java/android/hardware/biometrics/SensorProperties.java
new file mode 100644
index 000000000000..0f33ac4f0568
--- /dev/null
+++ b/core/java/android/hardware/biometrics/SensorProperties.java
@@ -0,0 +1,82 @@
+/*
+ * 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.hardware.biometrics;
+
+import android.annotation.IntDef;
+import android.os.Parcel;
+import android.os.Parcelable;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+
+/**
+ * The base class containing all sensor-agnostic information. This is a superset of the
+ * {@link android.hardware.biometrics.common.CommonProps}, and provides backwards-compatible
+ * behavior with the older generation of HIDL (non-AIDL) interfaces.
+ * @hide
+ */
+public class SensorProperties implements Parcelable {
+
+ public static final int STRENGTH_CONVENIENCE = 0;
+ public static final int STRENGTH_WEAK = 1;
+ public static final int STRENGTH_STRONG = 2;
+
+ @IntDef({STRENGTH_CONVENIENCE, STRENGTH_WEAK, STRENGTH_STRONG})
+ @Retention(RetentionPolicy.SOURCE)
+ public @interface Strength {}
+
+ public final int sensorId;
+ @Strength public final int sensorStrength;
+ public final int maxEnrollmentsPerUser;
+
+ protected SensorProperties(int sensorId, @Strength int sensorStrength,
+ int maxEnrollmentsPerUser) {
+ this.sensorId = sensorId;
+ this.sensorStrength = sensorStrength;
+ this.maxEnrollmentsPerUser = maxEnrollmentsPerUser;
+ }
+
+ protected SensorProperties(Parcel in) {
+ sensorId = in.readInt();
+ sensorStrength = in.readInt();
+ maxEnrollmentsPerUser = in.readInt();
+ }
+
+ public static final Creator<SensorProperties> CREATOR = new Creator<SensorProperties>() {
+ @Override
+ public SensorProperties createFromParcel(Parcel in) {
+ return new SensorProperties(in);
+ }
+
+ @Override
+ public SensorProperties[] newArray(int size) {
+ return new SensorProperties[size];
+ }
+ };
+
+ @Override
+ public int describeContents() {
+ return 0;
+ }
+
+ @Override
+ public void writeToParcel(Parcel dest, int flags) {
+ dest.writeInt(sensorId);
+ dest.writeInt(sensorStrength);
+ dest.writeInt(maxEnrollmentsPerUser);
+ }
+}
diff --git a/core/java/android/hardware/display/DisplayManager.java b/core/java/android/hardware/display/DisplayManager.java
index 392f56212058..dda1890b0757 100644
--- a/core/java/android/hardware/display/DisplayManager.java
+++ b/core/java/android/hardware/display/DisplayManager.java
@@ -841,6 +841,30 @@ public final class DisplayManager {
}
/**
+ * When enabled the app requested mode is always selected regardless of user settings and
+ * policies for low brightness, low battery, etc.
+ *
+ * @hide
+ */
+ @TestApi
+ @RequiresPermission(Manifest.permission.OVERRIDE_DISPLAY_MODE_REQUESTS)
+ public void setShouldAlwaysRespectAppRequestedMode(boolean enabled) {
+ mGlobal.setShouldAlwaysRespectAppRequestedMode(enabled);
+ }
+
+ /**
+ * Returns whether we are running in a mode which always selects the app requested display mode
+ * and ignores user settings and policies for low brightness, low battery etc.
+ *
+ * @hide
+ */
+ @TestApi
+ @RequiresPermission(Manifest.permission.OVERRIDE_DISPLAY_MODE_REQUESTS)
+ public boolean shouldAlwaysRespectAppRequestedMode() {
+ return mGlobal.shouldAlwaysRespectAppRequestedMode();
+ }
+
+ /**
* Listens for changes in available display devices.
*/
public interface DisplayListener {
@@ -917,6 +941,7 @@ public final class DisplayManager {
*/
String KEY_PEAK_REFRESH_RATE_DEFAULT = "peak_refresh_rate_default";
+ // TODO(b/162536543): rename it once it is proved not harmful for users.
/**
* Key for controlling which packages are explicitly blocked from running at refresh rates
* higher than 60hz. An app may be added to this list if they exhibit performance issues at
diff --git a/core/java/android/hardware/display/DisplayManagerGlobal.java b/core/java/android/hardware/display/DisplayManagerGlobal.java
index 0f9c7088a1d0..b046d1df5b8c 100644
--- a/core/java/android/hardware/display/DisplayManagerGlobal.java
+++ b/core/java/android/hardware/display/DisplayManagerGlobal.java
@@ -699,6 +699,32 @@ public final class DisplayManagerGlobal {
}
}
+ /**
+ * When enabled the app requested display resolution and refresh rate is always selected
+ * in DisplayModeDirector regardless of user settings and policies for low brightness, low
+ * battery etc.
+ */
+ public void setShouldAlwaysRespectAppRequestedMode(boolean enabled) {
+ try {
+ mDm.setShouldAlwaysRespectAppRequestedMode(enabled);
+ } catch (RemoteException ex) {
+ throw ex.rethrowFromSystemServer();
+ }
+ }
+
+ /**
+ * Returns whether DisplayModeDirector is running in a mode which always selects the app
+ * requested display mode and ignores user settings and policies for low brightness, low
+ * battery etc.
+ */
+ public boolean shouldAlwaysRespectAppRequestedMode() {
+ try {
+ return mDm.shouldAlwaysRespectAppRequestedMode();
+ } catch (RemoteException ex) {
+ throw ex.rethrowFromSystemServer();
+ }
+ }
+
private final class DisplayManagerCallback extends IDisplayManagerCallback.Stub {
@Override
public void onDisplayEvent(int displayId, int event) {
diff --git a/core/java/android/hardware/display/IDisplayManager.aidl b/core/java/android/hardware/display/IDisplayManager.aidl
index c697106d0c17..85da6424377a 100644
--- a/core/java/android/hardware/display/IDisplayManager.aidl
+++ b/core/java/android/hardware/display/IDisplayManager.aidl
@@ -128,4 +128,10 @@ interface IDisplayManager {
// The wide gamut color space is returned from composition pipeline
// based on hardware capability.
int getPreferredWideGamutColorSpaceId();
+
+ // When enabled the app requested display resolution and refresh rate is always selected
+ // in DisplayModeDirector regardless of user settings and policies for low brightness, low
+ // battery etc.
+ void setShouldAlwaysRespectAppRequestedMode(boolean enabled);
+ boolean shouldAlwaysRespectAppRequestedMode();
}
diff --git a/core/java/android/hardware/face/IFaceService.aidl b/core/java/android/hardware/face/IFaceService.aidl
index 437feb13b845..e744c840c298 100644
--- a/core/java/android/hardware/face/IFaceService.aidl
+++ b/core/java/android/hardware/face/IFaceService.aidl
@@ -110,5 +110,5 @@ interface IFaceService {
String opPackageName);
// Give FaceService its ID. See AuthService.java
- void initializeConfiguration(int sensorId);
+ void initializeConfiguration(int sensorId, int strength);
}
diff --git a/core/java/android/hardware/fingerprint/FingerprintSensorProperties.java b/core/java/android/hardware/fingerprint/FingerprintSensorProperties.java
index 2fd006809046..718141a4845a 100644
--- a/core/java/android/hardware/fingerprint/FingerprintSensorProperties.java
+++ b/core/java/android/hardware/fingerprint/FingerprintSensorProperties.java
@@ -17,6 +17,7 @@
package android.hardware.fingerprint;
import android.annotation.IntDef;
+import android.hardware.biometrics.SensorProperties;
import android.hardware.face.FaceSensorProperties;
import android.os.Parcel;
import android.os.Parcelable;
@@ -28,45 +29,44 @@ import java.lang.annotation.RetentionPolicy;
* Container for fingerprint sensor properties.
* @hide
*/
-public class FingerprintSensorProperties implements Parcelable {
+public class FingerprintSensorProperties extends SensorProperties {
public static final int TYPE_UNKNOWN = 0;
public static final int TYPE_REAR = 1;
- public static final int TYPE_UDFPS = 2;
- public static final int TYPE_POWER_BUTTON = 3;
+ public static final int TYPE_UDFPS_ULTRASONIC = 2;
+ public static final int TYPE_UDFPS_OPTICAL = 3;
+ public static final int TYPE_POWER_BUTTON = 4;
+ public static final int TYPE_HOME_BUTTON = 5;
- @IntDef({
- TYPE_UNKNOWN,
+ @IntDef({TYPE_UNKNOWN,
TYPE_REAR,
- TYPE_UDFPS,
- TYPE_POWER_BUTTON})
+ TYPE_UDFPS_ULTRASONIC,
+ TYPE_UDFPS_OPTICAL,
+ TYPE_POWER_BUTTON,
+ TYPE_HOME_BUTTON})
@Retention(RetentionPolicy.SOURCE)
public @interface SensorType {}
- public final int sensorId;
public final @SensorType int sensorType;
// IBiometricsFingerprint@2.1 does not manage timeout below the HAL, so the Gatekeeper HAT
// cannot be checked
public final boolean resetLockoutRequiresHardwareAuthToken;
- // Maximum number of enrollments a user/profile can have.
- public final int maxTemplatesAllowed;
/**
* Initializes SensorProperties with specified values
*/
- public FingerprintSensorProperties(int sensorId, @SensorType int sensorType,
- boolean resetLockoutRequiresHardwareAuthToken, int maxTemplatesAllowed) {
- this.sensorId = sensorId;
+ public FingerprintSensorProperties(int sensorId, @Strength int strength,
+ int maxEnrollmentsPerUser, @SensorType int sensorType,
+ boolean resetLockoutRequiresHardwareAuthToken) {
+ super(sensorId, strength, maxEnrollmentsPerUser);
this.sensorType = sensorType;
this.resetLockoutRequiresHardwareAuthToken = resetLockoutRequiresHardwareAuthToken;
- this.maxTemplatesAllowed = maxTemplatesAllowed;
}
protected FingerprintSensorProperties(Parcel in) {
- sensorId = in.readInt();
+ super(in);
sensorType = in.readInt();
resetLockoutRequiresHardwareAuthToken = in.readBoolean();
- maxTemplatesAllowed = in.readInt();
}
public static final Creator<FingerprintSensorProperties> CREATOR =
@@ -89,9 +89,18 @@ public class FingerprintSensorProperties implements Parcelable {
@Override
public void writeToParcel(Parcel dest, int flags) {
- dest.writeInt(sensorId);
+ super.writeToParcel(dest, flags);
dest.writeInt(sensorType);
dest.writeBoolean(resetLockoutRequiresHardwareAuthToken);
- dest.writeInt(maxTemplatesAllowed);
+ }
+
+ public boolean isAnyUdfpsType() {
+ switch (sensorType) {
+ case TYPE_UDFPS_OPTICAL:
+ case TYPE_UDFPS_ULTRASONIC:
+ return true;
+ default:
+ return false;
+ }
}
}
diff --git a/core/java/android/hardware/fingerprint/IFingerprintService.aidl b/core/java/android/hardware/fingerprint/IFingerprintService.aidl
index 0fae15648e15..cc2b520b3152 100644
--- a/core/java/android/hardware/fingerprint/IFingerprintService.aidl
+++ b/core/java/android/hardware/fingerprint/IFingerprintService.aidl
@@ -118,7 +118,7 @@ interface IFingerprintService {
void removeClientActiveCallback(IFingerprintClientActiveCallback callback);
// Give FingerprintService its ID. See AuthService.java
- void initializeConfiguration(int sensorId);
+ void initializeConfiguration(int sensorId, int strength);
// Notifies about a finger touching the sensor area.
void onFingerDown(int x, int y, float minor, float major);
diff --git a/core/java/android/hardware/soundtrigger/ConversionUtil.java b/core/java/android/hardware/soundtrigger/ConversionUtil.java
index c4d123ca4382..06b5b6745bd1 100644
--- a/core/java/android/hardware/soundtrigger/ConversionUtil.java
+++ b/core/java/android/hardware/soundtrigger/ConversionUtil.java
@@ -32,10 +32,12 @@ import android.media.soundtrigger_middleware.RecognitionMode;
import android.media.soundtrigger_middleware.SoundModel;
import android.media.soundtrigger_middleware.SoundTriggerModuleDescriptor;
import android.media.soundtrigger_middleware.SoundTriggerModuleProperties;
+import android.os.ParcelFileDescriptor;
import android.os.SharedMemory;
import android.system.ErrnoException;
import java.io.FileDescriptor;
+import java.io.IOException;
import java.nio.ByteBuffer;
import java.util.Arrays;
import java.util.UUID;
@@ -109,7 +111,12 @@ class ConversionUtil {
aidlModel.type = apiModel.getType();
aidlModel.uuid = api2aidlUuid(apiModel.getUuid());
aidlModel.vendorUuid = api2aidlUuid(apiModel.getVendorUuid());
- aidlModel.data = byteArrayToSharedMemory(apiModel.getData(), "SoundTrigger SoundModel");
+ try {
+ aidlModel.data = ParcelFileDescriptor.dup(
+ byteArrayToSharedMemory(apiModel.getData(), "SoundTrigger SoundModel"));
+ } catch (IOException e) {
+ throw new RuntimeException(e);
+ }
aidlModel.dataSize = apiModel.getData().length;
return aidlModel;
}
diff --git a/core/java/android/hardware/soundtrigger/SoundTrigger.java b/core/java/android/hardware/soundtrigger/SoundTrigger.java
index 80f35a0a2e32..0f1c2a59965b 100644
--- a/core/java/android/hardware/soundtrigger/SoundTrigger.java
+++ b/core/java/android/hardware/soundtrigger/SoundTrigger.java
@@ -16,6 +16,9 @@
package android.hardware.soundtrigger;
+import static android.Manifest.permission.CAPTURE_AUDIO_HOTWORD;
+import static android.Manifest.permission.RECORD_AUDIO;
+import static android.Manifest.permission.SOUNDTRIGGER_DELEGATE_IDENTITY;
import static android.system.OsConstants.EINVAL;
import static android.system.OsConstants.ENODEV;
import static android.system.OsConstants.ENOSYS;
@@ -27,6 +30,7 @@ import static java.util.Objects.requireNonNull;
import android.annotation.IntDef;
import android.annotation.NonNull;
import android.annotation.Nullable;
+import android.annotation.RequiresPermission;
import android.annotation.SuppressLint;
import android.annotation.SystemApi;
import android.annotation.TestApi;
@@ -34,9 +38,13 @@ import android.app.ActivityThread;
import android.compat.annotation.UnsupportedAppUsage;
import android.content.Context;
import android.media.AudioFormat;
+import android.media.permission.ClearCallingIdentityContext;
+import android.media.permission.Identity;
+import android.media.permission.SafeCloseable;
import android.media.soundtrigger_middleware.ISoundTriggerMiddlewareService;
import android.media.soundtrigger_middleware.SoundTriggerModuleDescriptor;
import android.media.soundtrigger_middleware.Status;
+import android.os.Binder;
import android.os.Handler;
import android.os.IBinder;
import android.os.Looper;
@@ -1943,16 +1951,6 @@ public class SoundTrigger {
public static final int SERVICE_STATE_DISABLED = 1;
private static Object mServiceLock = new Object();
private static ISoundTriggerMiddlewareService mService;
- /**
- * @return returns current package name.
- */
- static String getCurrentOpPackageName() {
- String packageName = ActivityThread.currentOpPackageName();
- if (packageName == null) {
- return "";
- }
- return packageName;
- }
/**
* Translate an exception thrown from interaction with the underlying service to an error code.
@@ -2005,17 +2003,81 @@ public class SoundTrigger {
* - {@link #STATUS_BAD_VALUE} if modules is null
* - {@link #STATUS_DEAD_OBJECT} if the binder transaction to the native service fails
*
+ * @deprecated Please use {@link #listModulesAsOriginator(ArrayList, Identity)} or
+ * {@link #listModulesAsMiddleman(ArrayList, Identity, Identity)}, based on whether the
+ * client is acting on behalf of its own identity or a separate identity.
* @hide
*/
@UnsupportedAppUsage
public static int listModules(@NonNull ArrayList<ModuleProperties> modules) {
+ // TODO(ytai): This is a temporary hack to retain prior behavior, which makes
+ // assumptions about process affinity and Binder context, namely that the binder calling ID
+ // reliably reflects the originator identity.
+ Identity middlemanIdentity = new Identity();
+ middlemanIdentity.packageName = ActivityThread.currentOpPackageName();
+
+ Identity originatorIdentity = new Identity();
+ originatorIdentity.pid = Binder.getCallingPid();
+ originatorIdentity.uid = Binder.getCallingUid();
+
+ try (SafeCloseable ignored = ClearCallingIdentityContext.create()) {
+ return listModulesAsMiddleman(modules, middlemanIdentity, originatorIdentity);
+ }
+ }
+
+ /**
+ * Returns a list of descriptors for all hardware modules loaded.
+ * This variant is intended for use when the caller itself is the originator of the operation.
+ * @param modules A ModuleProperties array where the list will be returned.
+ * @param originatorIdentity The identity of the originator, which will be used for permission
+ * purposes.
+ * @return - {@link #STATUS_OK} in case of success
+ * - {@link #STATUS_ERROR} in case of unspecified error
+ * - {@link #STATUS_PERMISSION_DENIED} if the caller does not have system permission
+ * - {@link #STATUS_NO_INIT} if the native service cannot be reached
+ * - {@link #STATUS_BAD_VALUE} if modules is null
+ * - {@link #STATUS_DEAD_OBJECT} if the binder transaction to the native service fails
+ *
+ * @hide
+ */
+ @RequiresPermission(allOf = {RECORD_AUDIO, CAPTURE_AUDIO_HOTWORD})
+ public static int listModulesAsOriginator(@NonNull ArrayList<ModuleProperties> modules,
+ @NonNull Identity originatorIdentity) {
try {
- SoundTriggerModuleDescriptor[] descs = getService().listModules();
- modules.clear();
- modules.ensureCapacity(descs.length);
- for (SoundTriggerModuleDescriptor desc : descs) {
- modules.add(ConversionUtil.aidl2apiModuleDescriptor(desc));
- }
+ SoundTriggerModuleDescriptor[] descs = getService().listModulesAsOriginator(
+ originatorIdentity);
+ convertDescriptorsToModuleProperties(descs, modules);
+ return STATUS_OK;
+ } catch (Exception e) {
+ return handleException(e);
+ }
+ }
+
+ /**
+ * Returns a list of descriptors for all hardware modules loaded.
+ * This variant is intended for use when the caller is acting on behalf of a different identity
+ * for permission purposes.
+ * @param modules A ModuleProperties array where the list will be returned.
+ * @param middlemanIdentity The identity of the caller, acting as middleman.
+ * @param originatorIdentity The identity of the originator, which will be used for permission
+ * purposes.
+ * @return - {@link #STATUS_OK} in case of success
+ * - {@link #STATUS_ERROR} in case of unspecified error
+ * - {@link #STATUS_PERMISSION_DENIED} if the caller does not have system permission
+ * - {@link #STATUS_NO_INIT} if the native service cannot be reached
+ * - {@link #STATUS_BAD_VALUE} if modules is null
+ * - {@link #STATUS_DEAD_OBJECT} if the binder transaction to the native service fails
+ *
+ * @hide
+ */
+ @RequiresPermission(SOUNDTRIGGER_DELEGATE_IDENTITY)
+ public static int listModulesAsMiddleman(@NonNull ArrayList<ModuleProperties> modules,
+ @NonNull Identity middlemanIdentity,
+ @NonNull Identity originatorIdentity) {
+ try {
+ SoundTriggerModuleDescriptor[] descs = getService().listModulesAsMiddleman(
+ middlemanIdentity, originatorIdentity);
+ convertDescriptorsToModuleProperties(descs, modules);
return STATUS_OK;
} catch (Exception e) {
return handleException(e);
@@ -2023,6 +2085,22 @@ public class SoundTrigger {
}
/**
+ * Converts an array of SoundTriggerModuleDescriptor into an (existing) ArrayList of
+ * ModuleProperties.
+ * @param descsIn The input descriptors.
+ * @param modulesOut The output list.
+ */
+ private static void convertDescriptorsToModuleProperties(
+ @NonNull SoundTriggerModuleDescriptor[] descsIn,
+ @NonNull ArrayList<ModuleProperties> modulesOut) {
+ modulesOut.clear();
+ modulesOut.ensureCapacity(descsIn.length);
+ for (SoundTriggerModuleDescriptor desc : descsIn) {
+ modulesOut.add(ConversionUtil.aidl2apiModuleDescriptor(desc));
+ }
+ }
+
+ /**
* Get an interface on a hardware module to control sound models and recognition on
* this module.
* @param moduleId Sound module system identifier {@link ModuleProperties#mId}. mandatory.
@@ -2031,15 +2109,85 @@ public class SoundTrigger {
* is OK.
* @return a valid sound module in case of success or null in case of error.
*
+ * @deprecated Please use
+ * {@link #attachModuleAsOriginator(int, StatusListener, Handler, Identity)} or
+ * {@link #attachModuleAsMiddleman(int, StatusListener, Handler, Identity, Identity)}, based
+ * on whether the client is acting on behalf of its own identity or a separate identity.
* @hide
*/
@UnsupportedAppUsage
- public static @NonNull SoundTriggerModule attachModule(int moduleId,
+ private static SoundTriggerModule attachModule(int moduleId,
@NonNull StatusListener listener,
@Nullable Handler handler) {
+ // TODO(ytai): This is a temporary hack to retain prior behavior, which makes
+ // assumptions about process affinity and Binder context, namely that the binder calling ID
+ // reliably reflects the originator identity.
+ Identity middlemanIdentity = new Identity();
+ middlemanIdentity.packageName = ActivityThread.currentOpPackageName();
+
+ Identity originatorIdentity = new Identity();
+ originatorIdentity.pid = Binder.getCallingPid();
+ originatorIdentity.uid = Binder.getCallingUid();
+
+ try (SafeCloseable ignored = ClearCallingIdentityContext.create()) {
+ return attachModuleAsMiddleman(moduleId, listener, handler, middlemanIdentity,
+ originatorIdentity);
+ }
+ }
+
+ /**
+ * Get an interface on a hardware module to control sound models and recognition on
+ * this module.
+ * This variant is intended for use when the caller is acting on behalf of a different identity
+ * for permission purposes.
+ * @param moduleId Sound module system identifier {@link ModuleProperties#mId}. mandatory.
+ * @param listener {@link StatusListener} interface. Mandatory.
+ * @param handler the Handler that will receive the callabcks. Can be null if default handler
+ * is OK.
+ * @param middlemanIdentity The identity of the caller, acting as middleman.
+ * @param originatorIdentity The identity of the originator, which will be used for permission
+ * purposes.
+ * @return a valid sound module in case of success or null in case of error.
+ *
+ * @hide
+ */
+ @RequiresPermission(SOUNDTRIGGER_DELEGATE_IDENTITY)
+ public static SoundTriggerModule attachModuleAsMiddleman(int moduleId,
+ @NonNull SoundTrigger.StatusListener listener,
+ @Nullable Handler handler, Identity middlemanIdentity,
+ Identity originatorIdentity) {
+ Looper looper = handler != null ? handler.getLooper() : Looper.getMainLooper();
+ try {
+ return new SoundTriggerModule(getService(), moduleId, listener, looper,
+ middlemanIdentity, originatorIdentity);
+ } catch (Exception e) {
+ Log.e(TAG, "", e);
+ return null;
+ }
+ }
+
+ /**
+ * Get an interface on a hardware module to control sound models and recognition on
+ * this module.
+ * This variant is intended for use when the caller itself is the originator of the operation.
+ * @param moduleId Sound module system identifier {@link ModuleProperties#mId}. mandatory.
+ * @param listener {@link StatusListener} interface. Mandatory.
+ * @param handler the Handler that will receive the callabcks. Can be null if default handler
+ * is OK.
+ * @param originatorIdentity The identity of the originator, which will be used for permission
+ * purposes.
+ * @return a valid sound module in case of success or null in case of error.
+ *
+ * @hide
+ */
+ @RequiresPermission(allOf = {RECORD_AUDIO, CAPTURE_AUDIO_HOTWORD})
+ public static SoundTriggerModule attachModuleAsOriginator(int moduleId,
+ @NonNull SoundTrigger.StatusListener listener,
+ @Nullable Handler handler, @NonNull Identity originatorIdentity) {
Looper looper = handler != null ? handler.getLooper() : Looper.getMainLooper();
try {
- return new SoundTriggerModule(getService(), moduleId, listener, looper);
+ return new SoundTriggerModule(getService(), moduleId, listener, looper,
+ originatorIdentity);
} catch (Exception e) {
Log.e(TAG, "", e);
return null;
diff --git a/core/java/android/hardware/soundtrigger/SoundTriggerModule.java b/core/java/android/hardware/soundtrigger/SoundTriggerModule.java
index a2a15b30d578..05823bf14b63 100644
--- a/core/java/android/hardware/soundtrigger/SoundTriggerModule.java
+++ b/core/java/android/hardware/soundtrigger/SoundTriggerModule.java
@@ -19,6 +19,9 @@ package android.hardware.soundtrigger;
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.compat.annotation.UnsupportedAppUsage;
+import android.media.permission.ClearCallingIdentityContext;
+import android.media.permission.Identity;
+import android.media.permission.SafeCloseable;
import android.media.soundtrigger_middleware.ISoundTriggerCallback;
import android.media.soundtrigger_middleware.ISoundTriggerMiddlewareService;
import android.media.soundtrigger_middleware.ISoundTriggerModule;
@@ -50,12 +53,39 @@ public class SoundTriggerModule {
private EventHandlerDelegate mEventHandlerDelegate;
private ISoundTriggerModule mService;
+ /**
+ * This variant is intended for use when the caller is acting an originator, rather than on
+ * behalf of a different entity, as far as authorization goes.
+ */
SoundTriggerModule(@NonNull ISoundTriggerMiddlewareService service,
- int moduleId, @NonNull SoundTrigger.StatusListener listener, @NonNull Looper looper)
+ int moduleId, @NonNull SoundTrigger.StatusListener listener, @NonNull Looper looper,
+ @NonNull Identity originatorIdentity)
throws RemoteException {
mId = moduleId;
mEventHandlerDelegate = new EventHandlerDelegate(listener, looper);
- mService = service.attach(moduleId, mEventHandlerDelegate);
+
+ try (SafeCloseable ignored = ClearCallingIdentityContext.create()) {
+ mService = service.attachAsOriginator(moduleId, originatorIdentity,
+ mEventHandlerDelegate);
+ }
+ mService.asBinder().linkToDeath(mEventHandlerDelegate, 0);
+ }
+
+ /**
+ * This variant is intended for use when the caller is acting as a middleman, i.e. on behalf of
+ * a different entity, as far as authorization goes.
+ */
+ SoundTriggerModule(@NonNull ISoundTriggerMiddlewareService service,
+ int moduleId, @NonNull SoundTrigger.StatusListener listener, @NonNull Looper looper,
+ @NonNull Identity middlemanIdentity, @NonNull Identity originatorIdentity)
+ throws RemoteException {
+ mId = moduleId;
+ mEventHandlerDelegate = new EventHandlerDelegate(listener, looper);
+
+ try (SafeCloseable ignored = ClearCallingIdentityContext.create()) {
+ mService = service.attachAsMiddleman(moduleId, middlemanIdentity, originatorIdentity,
+ mEventHandlerDelegate);
+ }
mService.asBinder().linkToDeath(mEventHandlerDelegate, 0);
}
diff --git a/core/java/android/inputmethodservice/InputMethodService.java b/core/java/android/inputmethodservice/InputMethodService.java
index 27bb9e20f968..d62d1e1e5775 100644
--- a/core/java/android/inputmethodservice/InputMethodService.java
+++ b/core/java/android/inputmethodservice/InputMethodService.java
@@ -16,11 +16,10 @@
package android.inputmethodservice;
+import static android.graphics.Color.TRANSPARENT;
import static android.view.ViewGroup.LayoutParams.MATCH_PARENT;
import static android.view.ViewGroup.LayoutParams.WRAP_CONTENT;
-import static android.view.ViewRootImpl.NEW_INSETS_MODE_NONE;
import static android.view.WindowInsets.Type.navigationBars;
-import static android.view.WindowInsets.Type.statusBars;
import static android.view.WindowManager.LayoutParams.FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS;
import static java.lang.annotation.RetentionPolicy.SOURCE;
@@ -70,7 +69,6 @@ import android.view.ViewGroup;
import android.view.ViewRootImpl;
import android.view.ViewTreeObserver;
import android.view.Window;
-import android.view.WindowInsets;
import android.view.WindowInsets.Side;
import android.view.WindowManager;
import android.view.animation.AnimationUtils;
@@ -1205,25 +1203,22 @@ public class InputMethodService extends AbstractInputMethodService {
Context.LAYOUT_INFLATER_SERVICE);
mWindow = new SoftInputWindow(this, "InputMethod", mTheme, null, null, mDispatcherState,
WindowManager.LayoutParams.TYPE_INPUT_METHOD, Gravity.BOTTOM, false);
- mWindow.getWindow().getAttributes().setFitInsetsTypes(statusBars() | navigationBars());
+ mWindow.getWindow().getAttributes().setFitInsetsTypes(navigationBars());
mWindow.getWindow().getAttributes().setFitInsetsSides(Side.all() & ~Side.BOTTOM);
mWindow.getWindow().getAttributes().setFitInsetsIgnoringVisibility(true);
- // IME layout should always be inset by navigation bar, no matter its current visibility,
- // unless automotive requests it. Automotive devices may request the navigation bar to be
- // hidden when the IME shows up (controlled via config_automotiveHideNavBarForKeyboard)
- // in order to maximize the visible screen real estate. When this happens, the IME window
- // should animate from the bottom of the screen to reduce the jank that happens from the
- // lack of synchronization between the bottom system window and the IME window.
+ // Our window will extend into the status bar area no matter the bar is visible or not.
+ // We don't want the ColorView to be visible when status bar is shown.
+ mWindow.getWindow().setStatusBarColor(TRANSPARENT);
+
+ // Automotive devices may request the navigation bar to be hidden when the IME shows up
+ // (controlled via config_automotiveHideNavBarForKeyboard) in order to maximize the visible
+ // screen real estate. When this happens, the IME window should animate from the bottom of
+ // the screen to reduce the jank that happens from the lack of synchronization between the
+ // bottom system window and the IME window.
if (mIsAutomotive && mAutomotiveHideNavBarForKeyboard) {
mWindow.getWindow().setDecorFitsSystemWindows(false);
}
- mWindow.getWindow().getDecorView().setOnApplyWindowInsetsListener(
- (v, insets) -> v.onApplyWindowInsets(
- new WindowInsets.Builder(insets).setInsets(
- navigationBars(),
- insets.getInsetsIgnoringVisibility(navigationBars()))
- .build()));
// For ColorView in DecorView to work, FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS needs to be set
// by default (but IME developers can opt this out later if they want a new behavior).
@@ -2201,22 +2196,15 @@ public class InputMethodService extends AbstractInputMethodService {
}
/**
- * Apply the IME visibility in {@link android.view.ImeInsetsSourceConsumer} when
- * {@link ViewRootImpl.sNewInsetsMode} is enabled.
+ * Applies the IME visibility in {@link android.view.ImeInsetsSourceConsumer}.
+ *
* @param setVisible {@code true} to make it visible, false to hide it.
*/
private void applyVisibilityInInsetsConsumerIfNecessary(boolean setVisible) {
- if (!isVisibilityAppliedUsingInsetsConsumer()) {
- return;
- }
mPrivOps.applyImeVisibility(setVisible
? mCurShowInputToken : mCurHideInputToken, setVisible);
}
- private boolean isVisibilityAppliedUsingInsetsConsumer() {
- return ViewRootImpl.sNewInsetsMode > NEW_INSETS_MODE_NONE;
- }
-
private void finishViews(boolean finishingInput) {
if (mInputViewStarted) {
if (DEBUG) Log.v(TAG, "CALL: onFinishInputView");
@@ -2241,14 +2229,9 @@ public class InputMethodService extends AbstractInputMethodService {
mWindowVisible = false;
finishViews(false /* finishingInput */);
if (mDecorViewVisible) {
- // When insets API is enabled, it is responsible for client and server side
- // visibility of IME window.
- if (isVisibilityAppliedUsingInsetsConsumer()) {
- if (mInputView != null) {
- mInputView.dispatchWindowVisibilityChanged(View.GONE);
- }
- } else {
- mWindow.hide();
+ // It is responsible for client and server side visibility of IME window.
+ if (mInputView != null) {
+ mInputView.dispatchWindowVisibilityChanged(View.GONE);
}
mDecorViewVisible = false;
onWindowHidden();
diff --git a/core/java/android/os/Debug.java b/core/java/android/os/Debug.java
index 4b2cfe222dd6..a2e53e29193c 100644
--- a/core/java/android/os/Debug.java
+++ b/core/java/android/os/Debug.java
@@ -2551,14 +2551,16 @@ public final class Debug
public static native long getZramFreeKb();
/**
- * Return memory size in kilobytes allocated for ION heaps.
+ * Return memory size in kilobytes allocated for ION heaps or -1 if
+ * /sys/kernel/ion/total_heaps_kb could not be read.
*
* @hide
*/
public static native long getIonHeapsSizeKb();
/**
- * Return memory size in kilobytes allocated for ION pools.
+ * Return memory size in kilobytes allocated for ION pools or -1 if
+ * /sys/kernel/ion/total_pools_kb could not be read.
*
* @hide
*/
diff --git a/core/java/android/os/incremental/IIncrementalService.aidl b/core/java/android/os/incremental/IIncrementalService.aidl
index ad9a16231f78..52475e9cd89d 100644
--- a/core/java/android/os/incremental/IIncrementalService.aidl
+++ b/core/java/android/os/incremental/IIncrementalService.aidl
@@ -69,7 +69,7 @@ interface IIncrementalService {
/**
* Creates a file under a storage.
*/
- int makeFile(int storageId, in @utf8InCpp String path, in IncrementalNewFileParams params);
+ int makeFile(int storageId, in @utf8InCpp String path, in IncrementalNewFileParams params, in @nullable byte[] content);
/**
* Creates a file under a storage. Content of the file is from a range inside another file.
diff --git a/core/java/android/os/incremental/IncrementalFileStorages.java b/core/java/android/os/incremental/IncrementalFileStorages.java
index dcbbd712914a..284c2df2ee7b 100644
--- a/core/java/android/os/incremental/IncrementalFileStorages.java
+++ b/core/java/android/os/incremental/IncrementalFileStorages.java
@@ -42,6 +42,7 @@ import android.text.TextUtils;
import java.io.File;
import java.io.IOException;
import java.util.List;
+import java.util.UUID;
/**
* This class manages storage instances used during a package installation session.
@@ -139,7 +140,7 @@ public final class IncrementalFileStorages {
final String apkName = apk.name;
final File targetFile = new File(mStageDir, apkName);
if (!targetFile.exists()) {
- mDefaultStorage.makeFile(apkName, apk.size, null, apk.metadata, apk.signature);
+ mDefaultStorage.makeFile(apkName, apk.size, null, apk.metadata, apk.signature, null);
}
}
@@ -153,6 +154,13 @@ public final class IncrementalFileStorages {
}
/**
+ * Creates file in default storage and sets its content.
+ */
+ public void makeFile(@NonNull String name, @NonNull byte[] content) throws IOException {
+ mDefaultStorage.makeFile(name, content.length, UUID.randomUUID(), null, null, content);
+ }
+
+ /**
* Permanently disables readlogs.
*/
public void disableReadLogs() {
diff --git a/core/java/android/os/incremental/IncrementalStorage.java b/core/java/android/os/incremental/IncrementalStorage.java
index f83541293330..a1c3cc697e02 100644
--- a/core/java/android/os/incremental/IncrementalStorage.java
+++ b/core/java/android/os/incremental/IncrementalStorage.java
@@ -170,9 +170,10 @@ public final class IncrementalStorage {
* @param size Size of the new file in bytes.
* @param metadata Metadata bytes.
* @param v4signatureBytes Serialized V4SignatureProto.
+ * @param content Optionally set file content.
*/
public void makeFile(@NonNull String path, long size, @Nullable UUID id,
- @Nullable byte[] metadata, @Nullable byte[] v4signatureBytes)
+ @Nullable byte[] metadata, @Nullable byte[] v4signatureBytes, @Nullable byte[] content)
throws IOException {
try {
if (id == null && metadata == null) {
@@ -184,7 +185,7 @@ public final class IncrementalStorage {
params.metadata = (metadata == null ? new byte[0] : metadata);
params.fileId = idToBytes(id);
params.signature = v4signatureBytes;
- int res = mService.makeFile(mId, path, params);
+ int res = mService.makeFile(mId, path, params, content);
if (res != 0) {
throw new IOException("makeFile() failed with errno " + -res);
}
diff --git a/core/java/android/preference/SeekBarVolumizer.java b/core/java/android/preference/SeekBarVolumizer.java
index 0cdad9f8de93..400b312bb913 100644
--- a/core/java/android/preference/SeekBarVolumizer.java
+++ b/core/java/android/preference/SeekBarVolumizer.java
@@ -46,6 +46,8 @@ import android.widget.SeekBar.OnSeekBarChangeListener;
import com.android.internal.annotations.GuardedBy;
import com.android.internal.os.SomeArgs;
+import java.util.concurrent.TimeUnit;
+
/**
* Turns a {@link SeekBar} into a volume control.
* @hide
@@ -64,9 +66,15 @@ public class SeekBarVolumizer implements OnSeekBarChangeListener, Handler.Callba
void onSampleStarting(SeekBarVolumizer sbv);
void onProgressChanged(SeekBar seekBar, int progress, boolean fromTouch);
void onMuted(boolean muted, boolean zenMuted);
+ /**
+ * Callback reporting that the seek bar is start tracking.
+ * @param sbv - The seek bar that start tracking
+ */
+ void onStartTrackingTouch(SeekBarVolumizer sbv);
}
private static final int MSG_GROUP_VOLUME_CHANGED = 1;
+ private static long sStopVolumeTime = 0L;
private final Handler mVolumeHandler = new VolumeHandler();
private AudioAttributes mAttributes;
private int mVolumeGroupId;
@@ -126,6 +134,9 @@ public class SeekBarVolumizer implements OnSeekBarChangeListener, Handler.Callba
private static final int MSG_STOP_SAMPLE = 2;
private static final int MSG_INIT_SAMPLE = 3;
private static final int CHECK_RINGTONE_PLAYBACK_DELAY_MS = 1000;
+ private static final long SET_STREAM_VOLUME_DELAY_MS = TimeUnit.MILLISECONDS.toMillis(500);
+ private static final long START_SAMPLE_DELAY_MS = TimeUnit.MILLISECONDS.toMillis(500);
+ private static final long DURATION_TO_START_DELAYING = TimeUnit.MILLISECONDS.toMillis(2000);
private NotificationManager.Policy mNotificationPolicy;
private boolean mAllowAlarms;
@@ -314,7 +325,29 @@ public class SeekBarVolumizer implements OnSeekBarChangeListener, Handler.Callba
if (mHandler == null) return;
mHandler.removeMessages(MSG_START_SAMPLE);
mHandler.sendMessageDelayed(mHandler.obtainMessage(MSG_START_SAMPLE),
- isSamplePlaying() ? CHECK_RINGTONE_PLAYBACK_DELAY_MS : 0);
+ isSamplePlaying() ? CHECK_RINGTONE_PLAYBACK_DELAY_MS
+ : isDelay() ? START_SAMPLE_DELAY_MS : 0);
+ }
+
+ // After stop volume it needs to add a small delay when playing volume or set stream.
+ // It is because the call volume is from the earpiece and the alarm/ring/media
+ // is from the speaker. If play the alarm volume or set alarm stream right after stop
+ // call volume, the alarm volume on earpiece is returned then cause the volume value incorrect.
+ // It needs a small delay after stop call volume to get alarm volume on speaker.
+ // e.g. : If the ring volume has adjusted right after call volume stopped in 2 second
+ // then delay 0.5 second to set stream or play volume ringtone.
+ private boolean isDelay() {
+ final long durationTime = java.lang.System.currentTimeMillis() - sStopVolumeTime;
+ return durationTime >= 0 && durationTime < DURATION_TO_START_DELAYING;
+ }
+
+ private void setStopVolumeTime() {
+ // set the time of stop volume
+ if ((mStreamType == AudioManager.STREAM_VOICE_CALL
+ || mStreamType == AudioManager.STREAM_RING
+ || mStreamType == AudioManager.STREAM_ALARM)) {
+ sStopVolumeTime = java.lang.System.currentTimeMillis();
+ }
}
private void onStartSample() {
@@ -341,6 +374,7 @@ public class SeekBarVolumizer implements OnSeekBarChangeListener, Handler.Callba
private void postStopSample() {
if (mHandler == null) return;
+ setStopVolumeTime();
// remove pending delayed start messages
mHandler.removeMessages(MSG_START_SAMPLE);
mHandler.removeMessages(MSG_STOP_SAMPLE);
@@ -404,10 +438,15 @@ public class SeekBarVolumizer implements OnSeekBarChangeListener, Handler.Callba
// Do the volume changing separately to give responsive UI
mLastProgress = progress;
mHandler.removeMessages(MSG_SET_STREAM_VOLUME);
- mHandler.sendMessage(mHandler.obtainMessage(MSG_SET_STREAM_VOLUME));
+ mHandler.removeMessages(MSG_START_SAMPLE);
+ mHandler.sendMessageDelayed(mHandler.obtainMessage(MSG_SET_STREAM_VOLUME),
+ isDelay() ? SET_STREAM_VOLUME_DELAY_MS : 0);
}
public void onStartTrackingTouch(SeekBar seekBar) {
+ if (mCallback != null) {
+ mCallback.onStartTrackingTouch(this);
+ }
}
public void onStopTrackingTouch(SeekBar seekBar) {
@@ -539,7 +578,7 @@ public class SeekBarVolumizer implements OnSeekBarChangeListener, Handler.Callba
if (AudioManager.VOLUME_CHANGED_ACTION.equals(action)) {
int streamType = intent.getIntExtra(AudioManager.EXTRA_VOLUME_STREAM_TYPE, -1);
int streamValue = intent.getIntExtra(AudioManager.EXTRA_VOLUME_STREAM_VALUE, -1);
- if (hasAudioProductStrategies()) {
+ if (hasAudioProductStrategies() && !isDelay()) {
updateVolumeSlider(streamType, streamValue);
}
} else if (AudioManager.INTERNAL_RINGER_MODE_CHANGED_ACTION.equals(action)) {
@@ -551,7 +590,7 @@ public class SeekBarVolumizer implements OnSeekBarChangeListener, Handler.Callba
}
} else if (AudioManager.STREAM_DEVICES_CHANGED_ACTION.equals(action)) {
int streamType = intent.getIntExtra(AudioManager.EXTRA_VOLUME_STREAM_TYPE, -1);
- if (hasAudioProductStrategies()) {
+ if (hasAudioProductStrategies() && !isDelay()) {
int streamVolume = mAudioManager.getStreamVolume(streamType);
updateVolumeSlider(streamType, streamVolume);
} else {
@@ -559,7 +598,9 @@ public class SeekBarVolumizer implements OnSeekBarChangeListener, Handler.Callba
if (volumeGroup != AudioVolumeGroup.DEFAULT_VOLUME_GROUP
&& volumeGroup == mVolumeGroupId) {
int streamVolume = mAudioManager.getStreamVolume(streamType);
- updateVolumeSlider(streamType, streamVolume);
+ if (!isDelay()) {
+ updateVolumeSlider(streamType, streamVolume);
+ }
}
}
} else if (NotificationManager.ACTION_INTERRUPTION_FILTER_CHANGED.equals(action)) {
diff --git a/core/java/android/preference/VolumePreference.java b/core/java/android/preference/VolumePreference.java
index 6eb524a3886c..563bc463e5a3 100644
--- a/core/java/android/preference/VolumePreference.java
+++ b/core/java/android/preference/VolumePreference.java
@@ -176,6 +176,11 @@ public class VolumePreference extends SeekBarDialogPreference implements
}
@Override
+ public void onStartTrackingTouch(SeekBarVolumizer sbv) {
+ // noop
+ }
+
+ @Override
protected Parcelable onSaveInstanceState() {
final Parcelable superState = super.onSaveInstanceState();
if (isPersistent()) {
diff --git a/core/java/android/provider/DeviceConfig.java b/core/java/android/provider/DeviceConfig.java
index 7a03953f2e9b..99ffee3e2ad9 100644
--- a/core/java/android/provider/DeviceConfig.java
+++ b/core/java/android/provider/DeviceConfig.java
@@ -84,6 +84,15 @@ public final class DeviceConfig {
"activity_manager_native_boot";
/**
+ * Namespace for AlarmManager configurations.
+ *
+ * @hide
+ */
+ @SystemApi(client = SystemApi.Client.MODULE_LIBRARIES)
+ @TestApi
+ public static final String NAMESPACE_ALARM_MANAGER = "alarm_manager";
+
+ /**
* Namespace for all app compat related features. These features will be applied
* immediately upon change.
*
diff --git a/core/java/android/provider/Settings.java b/core/java/android/provider/Settings.java
index 18337b661844..a7055ec0f050 100644
--- a/core/java/android/provider/Settings.java
+++ b/core/java/android/provider/Settings.java
@@ -7954,6 +7954,12 @@ public final class Settings {
public static final String UI_NIGHT_MODE_OVERRIDE_ON = "ui_night_mode_override_on";
/**
+ * The last computed night mode bool the last time the phone was on
+ * @hide
+ */
+ public static final String UI_NIGHT_MODE_LAST_COMPUTED = "ui_night_mode_last_computed";
+
+ /**
* The current night mode that has been overridden to turn off by the system. Owned
* and controlled by UiModeManagerService. Constants are as per
* UiModeManager.
@@ -9078,6 +9084,22 @@ public final class Settings {
};
/**
+ * How long Assistant handles have enabled in milliseconds.
+ *
+ * @hide
+ */
+ public static final String ASSIST_HANDLES_LEARNING_TIME_ELAPSED_MILLIS =
+ "reminder_exp_learning_time_elapsed";
+
+ /**
+ * How many times the Assistant has been triggered using the touch gesture.
+ *
+ * @hide
+ */
+ public static final String ASSIST_HANDLES_LEARNING_EVENT_COUNT =
+ "reminder_exp_learning_event_count";
+
+ /**
* These entries are considered common between the personal and the managed profile,
* since the managed profile doesn't get to change them.
*/
@@ -11808,29 +11830,6 @@ public final class Settings {
public static final String POWER_MANAGER_CONSTANTS = "power_manager_constants";
/**
- * Alarm manager specific settings.
- * This is encoded as a key=value list, separated by commas. Ex:
- *
- * "min_futurity=5000,allow_while_idle_short_time=4500"
- *
- * The following keys are supported:
- *
- * <pre>
- * min_futurity (long)
- * min_interval (long)
- * allow_while_idle_short_time (long)
- * allow_while_idle_long_time (long)
- * allow_while_idle_whitelist_duration (long)
- * </pre>
- *
- * <p>
- * Type: string
- * @hide
- * @see com.android.server.AlarmManagerService.Constants
- */
- public static final String ALARM_MANAGER_CONSTANTS = "alarm_manager_constants";
-
- /**
* Job scheduler QuotaController specific settings.
* This is encoded as a key=value list, separated by commas. Ex:
*
diff --git a/core/java/android/provider/Telephony.java b/core/java/android/provider/Telephony.java
index 9f52142a5109..79d6bb4a062a 100644
--- a/core/java/android/provider/Telephony.java
+++ b/core/java/android/provider/Telephony.java
@@ -3956,9 +3956,7 @@ public final class Telephony {
public static final String APN_SET_ID = "apn_set_id";
/**
- * Possible value for the {@link #APN_SET_ID} field. By default APNs will not belong to a
- * set. If the user manually selects an APN without apn set id, there is no need to
- * prioritize any specific APN set ids.
+ * Possible value for the {@link #APN_SET_ID} field. By default APNs are added to set 0.
* <p>Type: INTEGER</p>
* @hide
*/
@@ -3966,6 +3964,16 @@ public final class Telephony {
public static final int NO_APN_SET_ID = 0;
/**
+ * Possible value for the {@link #APN_SET_ID} field.
+ * APNs with MATCH_ALL_APN_SET_ID will be used regardless of any set ids of
+ * the selected APN.
+ * <p>Type: INTEGER</p>
+ * @hide
+ */
+ @SystemApi
+ public static final int MATCH_ALL_APN_SET_ID = -1;
+
+ /**
* A unique carrier id associated with this APN
* {@see TelephonyManager#getSimCarrierId()}
* <p>Type: STRING</p>
diff --git a/core/java/android/service/notification/ZenModeConfig.java b/core/java/android/service/notification/ZenModeConfig.java
index 0827fef60252..49bc65b3c8e5 100644
--- a/core/java/android/service/notification/ZenModeConfig.java
+++ b/core/java/android/service/notification/ZenModeConfig.java
@@ -1326,7 +1326,8 @@ public class ZenModeConfig implements Parcelable {
line2 = res.getString(R.string.zen_mode_until, formattedTime);
} else {
// display as day/time
- summary = line1 = line2 = res.getString(R.string.zen_mode_until, formattedTime);
+ summary = line1 = line2 = res.getString(R.string.zen_mode_until_next_day,
+ formattedTime);
}
final Uri id = toCountdownConditionId(time, false);
return new Condition(id, summary, line1, line2, 0, Condition.STATE_TRUE,
diff --git a/core/java/android/service/voice/AlwaysOnHotwordDetector.java b/core/java/android/service/voice/AlwaysOnHotwordDetector.java
index 6f941121771e..8f8e6cc3d84a 100644
--- a/core/java/android/service/voice/AlwaysOnHotwordDetector.java
+++ b/core/java/android/service/voice/AlwaysOnHotwordDetector.java
@@ -16,9 +16,15 @@
package android.service.voice;
+import static android.Manifest.permission.CAPTURE_AUDIO_HOTWORD;
+import static android.Manifest.permission.RECORD_AUDIO;
+
import android.annotation.IntDef;
import android.annotation.NonNull;
import android.annotation.Nullable;
+import android.app.ActivityThread;
+import android.annotation.RequiresPermission;
+import android.annotation.SystemApi;
import android.compat.annotation.UnsupportedAppUsage;
import android.content.Context;
import android.content.Intent;
@@ -32,6 +38,7 @@ import android.hardware.soundtrigger.SoundTrigger.KeyphraseRecognitionExtra;
import android.hardware.soundtrigger.SoundTrigger.ModuleProperties;
import android.hardware.soundtrigger.SoundTrigger.RecognitionConfig;
import android.media.AudioFormat;
+import android.media.permission.Identity;
import android.os.AsyncTask;
import android.os.Handler;
import android.os.Message;
@@ -39,6 +46,7 @@ import android.os.RemoteException;
import android.util.Slog;
import com.android.internal.app.IVoiceInteractionManagerService;
+import com.android.internal.app.IVoiceInteractionSoundTriggerSession;
import java.io.PrintWriter;
import java.lang.annotation.Retention;
@@ -48,7 +56,12 @@ import java.util.Locale;
/**
* A class that lets a VoiceInteractionService implementation interact with
* always-on keyphrase detection APIs.
+ *
+ * @hide
+ * TODO(b/168605867): Once Metalava supports expressing a removed public, but current system API,
+ * mark and track it as such.
*/
+@SystemApi
public class AlwaysOnHotwordDetector {
//---- States of Keyphrase availability. Return codes for onAvailabilityChanged() ----//
/**
@@ -228,6 +241,7 @@ public class AlwaysOnHotwordDetector {
private KeyphraseMetadata mKeyphraseMetadata;
private final KeyphraseEnrollmentInfo mKeyphraseEnrollmentInfo;
private final IVoiceInteractionManagerService mModelManagementService;
+ private final IVoiceInteractionSoundTriggerSession mSoundTriggerSession;
private final SoundTriggerListener mInternalCallback;
private final Callback mExternalCallback;
private final Object mLock = new Object();
@@ -425,6 +439,14 @@ public class AlwaysOnHotwordDetector {
mHandler = new MyHandler();
mInternalCallback = new SoundTriggerListener(mHandler);
mModelManagementService = modelManagementService;
+ try {
+ Identity identity = new Identity();
+ identity.packageName = ActivityThread.currentOpPackageName();
+ mSoundTriggerSession = mModelManagementService.createSoundTriggerSessionAsOriginator(
+ identity);
+ } catch (RemoteException e) {
+ throw e.rethrowAsRuntimeException();
+ }
new RefreshAvailabiltyTask().execute();
}
@@ -485,7 +507,7 @@ public class AlwaysOnHotwordDetector {
private int getSupportedAudioCapabilitiesLocked() {
try {
ModuleProperties properties =
- mModelManagementService.getDspModuleProperties();
+ mSoundTriggerSession.getDspModuleProperties();
if (properties != null) {
return properties.getAudioCapabilities();
}
@@ -513,6 +535,7 @@ public class AlwaysOnHotwordDetector {
* This may happen if another detector has been instantiated or the
* {@link VoiceInteractionService} hosting this detector has been shut down.
*/
+ @RequiresPermission(allOf = {RECORD_AUDIO, CAPTURE_AUDIO_HOTWORD})
public boolean startRecognition(@RecognitionFlags int recognitionFlags) {
if (DBG) Slog.d(TAG, "startRecognition(" + recognitionFlags + ")");
synchronized (mLock) {
@@ -543,6 +566,7 @@ public class AlwaysOnHotwordDetector {
* This may happen if another detector has been instantiated or the
* {@link VoiceInteractionService} hosting this detector has been shut down.
*/
+ @RequiresPermission(allOf = {RECORD_AUDIO, CAPTURE_AUDIO_HOTWORD})
public boolean stopRecognition() {
if (DBG) Slog.d(TAG, "stopRecognition()");
synchronized (mLock) {
@@ -577,6 +601,7 @@ public class AlwaysOnHotwordDetector {
* - {@link SoundTrigger#STATUS_INVALID_OPERATION} if the call is out of sequence or
* if API is not supported by HAL
*/
+ @RequiresPermission(allOf = {RECORD_AUDIO, CAPTURE_AUDIO_HOTWORD})
public int setParameter(@ModelParams int modelParam, int value) {
if (DBG) {
Slog.d(TAG, "setParameter(" + modelParam + ", " + value + ")");
@@ -604,6 +629,7 @@ public class AlwaysOnHotwordDetector {
* @param modelParam {@link ModelParams}
* @return value of parameter
*/
+ @RequiresPermission(allOf = {RECORD_AUDIO, CAPTURE_AUDIO_HOTWORD})
public int getParameter(@ModelParams int modelParam) {
if (DBG) {
Slog.d(TAG, "getParameter(" + modelParam + ")");
@@ -628,6 +654,7 @@ public class AlwaysOnHotwordDetector {
* @param modelParam {@link ModelParams}
* @return supported range of parameter, null if not supported
*/
+ @RequiresPermission(allOf = {RECORD_AUDIO, CAPTURE_AUDIO_HOTWORD})
@Nullable
public ModelParamRange queryParameter(@ModelParams int modelParam) {
if (DBG) {
@@ -658,6 +685,7 @@ public class AlwaysOnHotwordDetector {
* This may happen if another detector has been instantiated or the
* {@link VoiceInteractionService} hosting this detector has been shut down.
*/
+ @Nullable
public Intent createEnrollIntent() {
if (DBG) Slog.d(TAG, "createEnrollIntent");
synchronized (mLock) {
@@ -679,6 +707,7 @@ public class AlwaysOnHotwordDetector {
* This may happen if another detector has been instantiated or the
* {@link VoiceInteractionService} hosting this detector has been shut down.
*/
+ @Nullable
public Intent createUnEnrollIntent() {
if (DBG) Slog.d(TAG, "createUnEnrollIntent");
synchronized (mLock) {
@@ -700,6 +729,7 @@ public class AlwaysOnHotwordDetector {
* This may happen if another detector has been instantiated or the
* {@link VoiceInteractionService} hosting this detector has been shut down.
*/
+ @Nullable
public Intent createReEnrollIntent() {
if (DBG) Slog.d(TAG, "createReEnrollIntent");
synchronized (mLock) {
@@ -782,7 +812,7 @@ public class AlwaysOnHotwordDetector {
int code;
try {
- code = mModelManagementService.startRecognition(
+ code = mSoundTriggerSession.startRecognition(
mKeyphraseMetadata.getId(), mLocale.toLanguageTag(), mInternalCallback,
new RecognitionConfig(captureTriggerAudio, allowMultipleTriggers,
recognitionExtra, null /* additional data */, audioCapabilities));
@@ -799,7 +829,7 @@ public class AlwaysOnHotwordDetector {
private int stopRecognitionLocked() {
int code;
try {
- code = mModelManagementService.stopRecognition(mKeyphraseMetadata.getId(),
+ code = mSoundTriggerSession.stopRecognition(mKeyphraseMetadata.getId(),
mInternalCallback);
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
@@ -813,7 +843,7 @@ public class AlwaysOnHotwordDetector {
private int setParameterLocked(@ModelParams int modelParam, int value) {
try {
- int code = mModelManagementService.setParameter(mKeyphraseMetadata.getId(), modelParam,
+ int code = mSoundTriggerSession.setParameter(mKeyphraseMetadata.getId(), modelParam,
value);
if (code != STATUS_OK) {
@@ -828,7 +858,7 @@ public class AlwaysOnHotwordDetector {
private int getParameterLocked(@ModelParams int modelParam) {
try {
- return mModelManagementService.getParameter(mKeyphraseMetadata.getId(), modelParam);
+ return mSoundTriggerSession.getParameter(mKeyphraseMetadata.getId(), modelParam);
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
@@ -838,7 +868,7 @@ public class AlwaysOnHotwordDetector {
private ModelParamRange queryParameterLocked(@ModelParams int modelParam) {
try {
SoundTrigger.ModelParamRange modelParamRange =
- mModelManagementService.queryParameter(mKeyphraseMetadata.getId(), modelParam);
+ mSoundTriggerSession.queryParameter(mKeyphraseMetadata.getId(), modelParam);
if (modelParamRange == null) {
return null;
@@ -972,7 +1002,7 @@ public class AlwaysOnHotwordDetector {
ModuleProperties dspModuleProperties;
try {
dspModuleProperties =
- mModelManagementService.getDspModuleProperties();
+ mSoundTriggerSession.getDspModuleProperties();
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
diff --git a/core/java/android/service/voice/VoiceInteractionService.java b/core/java/android/service/voice/VoiceInteractionService.java
index 45d3465fdae8..fb03ed45113e 100644
--- a/core/java/android/service/voice/VoiceInteractionService.java
+++ b/core/java/android/service/voice/VoiceInteractionService.java
@@ -20,6 +20,7 @@ import android.Manifest;
import android.annotation.NonNull;
import android.annotation.RequiresPermission;
import android.annotation.SdkConstant;
+import android.annotation.SuppressLint;
import android.annotation.SystemApi;
import android.app.Service;
import android.compat.annotation.UnsupportedAppUsage;
@@ -237,9 +238,8 @@ public class VoiceInteractionService extends Service {
/**
* Called during service initialization to tell you when the system is ready
* to receive interaction from it. You should generally do initialization here
- * rather than in {@link #onCreate}. Methods such as {@link #showSession} and
- * {@link #createAlwaysOnHotwordDetector}
- * will not be operational until this point.
+ * rather than in {@link #onCreate}. Methods such as {@link #showSession} will
+ * not be operational until this point.
*/
public void onReady() {
mSystemService = IVoiceInteractionManagerService.Stub.asInterface(
@@ -309,9 +309,15 @@ public class VoiceInteractionService extends Service {
* @param locale The locale for which the enrollment needs to be performed.
* @param callback The callback to notify of detection events.
* @return An always-on hotword detector for the given keyphrase and locale.
+ *
+ * @hide
*/
+ @SystemApi
+ @NonNull
public final AlwaysOnHotwordDetector createAlwaysOnHotwordDetector(
- String keyphrase, Locale locale, AlwaysOnHotwordDetector.Callback callback) {
+ @SuppressLint("MissingNullability") String keyphrase, // TODO: annotate nullability properly
+ @SuppressLint({"MissingNullability", "UseIcu"}) Locale locale,
+ @SuppressLint("MissingNullability") AlwaysOnHotwordDetector.Callback callback) {
if (mSystemService == null) {
throw new IllegalStateException("Not available until onReady() is called");
}
diff --git a/core/java/android/telephony/PhoneStateListener.java b/core/java/android/telephony/PhoneStateListener.java
index 8f31d77ac180..16826865987e 100644
--- a/core/java/android/telephony/PhoneStateListener.java
+++ b/core/java/android/telephony/PhoneStateListener.java
@@ -1001,16 +1001,43 @@ public class PhoneStateListener {
/**
* Callback invoked when an outgoing SMS is placed to an emergency number.
*
+ * This method will be called when an emergency sms is sent on any subscription.
* @param sentEmergencyNumber the emergency number {@link EmergencyNumber} the SMS is sent to.
+ *
+ * @deprecated Use {@link #onOutgoingEmergencySms(EmergencyNumber, int)}.
* @hide
*/
@SystemApi
@TestApi
+ @Deprecated
public void onOutgoingEmergencySms(@NonNull EmergencyNumber sentEmergencyNumber) {
// default implementation empty
}
/**
+ * Smsback invoked when an outgoing sms is sent to an emergency number.
+ *
+ * This method will be called when an emergency sms is sent on any subscription,
+ * regardless of which subscription this listener was registered on.
+ *
+ * The default implementation of this method calls
+ * {@link #onOutgoingEmergencySms(EmergencyNumber)} for backwards compatibility purposes. Do
+ * not call {@code super(...)} from within your implementation unless you want
+ * {@link #onOutgoingEmergencySms(EmergencyNumber)} to be called as well.
+ *
+ * @param sentEmergencyNumber The {@link EmergencyNumber} the emergency sms was sent to.
+ * @param subscriptionId The subscription ID used to send the emergency sms.
+ * @hide
+ */
+ @SystemApi
+ @TestApi
+ public void onOutgoingEmergencySms(@NonNull EmergencyNumber sentEmergencyNumber,
+ int subscriptionId) {
+ // Default implementation for backwards compatibility
+ onOutgoingEmergencySms(sentEmergencyNumber);
+ }
+
+ /**
* Callback invoked when OEM hook raw event is received on the registered subscription.
* Note, the registration subId comes from {@link TelephonyManager} object which registers
* PhoneStateListener by {@link TelephonyManager#listen(PhoneStateListener, int)}.
@@ -1399,13 +1426,14 @@ public class PhoneStateListener {
subscriptionId)));
}
- public void onOutgoingEmergencySms(@NonNull EmergencyNumber sentEmergencyNumber) {
+ public void onOutgoingEmergencySms(@NonNull EmergencyNumber sentEmergencyNumber,
+ int subscriptionId) {
PhoneStateListener psl = mPhoneStateListenerWeakRef.get();
if (psl == null) return;
Binder.withCleanCallingIdentity(
() -> mExecutor.execute(
- () -> psl.onOutgoingEmergencySms(sentEmergencyNumber)));
+ () -> psl.onOutgoingEmergencySms(sentEmergencyNumber, subscriptionId)));
}
public void onPhoneCapabilityChanged(PhoneCapability capability) {
diff --git a/core/java/android/telephony/TelephonyRegistryManager.java b/core/java/android/telephony/TelephonyRegistryManager.java
index 6d48dc36ceb4..24ed29a66654 100644
--- a/core/java/android/telephony/TelephonyRegistryManager.java
+++ b/core/java/android/telephony/TelephonyRegistryManager.java
@@ -101,6 +101,10 @@ public class TelephonyRegistryManager {
public void addOnSubscriptionsChangedListener(
@NonNull SubscriptionManager.OnSubscriptionsChangedListener listener,
@NonNull Executor executor) {
+ if (mSubscriptionChangedListenerMap.get(listener) != null) {
+ Log.d(TAG, "addOnSubscriptionsChangedListener listener already present");
+ return;
+ }
IOnSubscriptionsChangedListener callback = new IOnSubscriptionsChangedListener.Stub() {
@Override
public void onSubscriptionsChanged () {
@@ -150,6 +154,10 @@ public class TelephonyRegistryManager {
public void addOnOpportunisticSubscriptionsChangedListener(
@NonNull SubscriptionManager.OnOpportunisticSubscriptionsChangedListener listener,
@NonNull Executor executor) {
+ if (mOpportunisticSubscriptionChangedListenerMap.get(listener) != null) {
+ Log.d(TAG, "addOnOpportunisticSubscriptionsChangedListener listener already present");
+ return;
+ }
/**
* The callback methods need to be called on the executor thread where
* this object was created. If the binder did that for us it'd be nice.
@@ -185,6 +193,9 @@ public class TelephonyRegistryManager {
*/
public void removeOnOpportunisticSubscriptionsChangedListener(
@NonNull SubscriptionManager.OnOpportunisticSubscriptionsChangedListener listener) {
+ if (mOpportunisticSubscriptionChangedListenerMap.get(listener) == null) {
+ return;
+ }
try {
sRegistry.removeOnSubscriptionsChangedListener(mContext.getOpPackageName(),
mOpportunisticSubscriptionChangedListenerMap.get(listener));
diff --git a/core/java/android/util/FeatureFlagUtils.java b/core/java/android/util/FeatureFlagUtils.java
index 9cc6b9f83ede..9244647432c7 100644
--- a/core/java/android/util/FeatureFlagUtils.java
+++ b/core/java/android/util/FeatureFlagUtils.java
@@ -44,6 +44,8 @@ public class FeatureFlagUtils {
/** @hide */
public static final String SETTINGS_DO_NOT_RESTORE_PRESERVED =
"settings_do_not_restore_preserved";
+ /** @hide */
+ public static final String SETTINGS_PROVIDER_MODEL = "settings_provider_model";
private static final Map<String, String> DEFAULT_FLAGS;
@@ -67,6 +69,7 @@ public class FeatureFlagUtils {
DEFAULT_FLAGS.put("settings_tether_all_in_one", "false");
DEFAULT_FLAGS.put("settings_silky_home", "false");
DEFAULT_FLAGS.put("settings_contextual_home", "false");
+ DEFAULT_FLAGS.put(SETTINGS_PROVIDER_MODEL, "false");
}
/**
diff --git a/core/java/android/util/IntArray.java b/core/java/android/util/IntArray.java
index 5a74ec0e52c0..b77265b0ebf6 100644
--- a/core/java/android/util/IntArray.java
+++ b/core/java/android/util/IntArray.java
@@ -144,6 +144,17 @@ public class IntArray implements Cloneable {
}
/**
+ * Adds the values in the specified array to this array.
+ */
+ public void addAll(int[] values) {
+ final int count = values.length;
+ ensureCapacity(count);
+
+ System.arraycopy(values, 0, mValues, mSize, count);
+ mSize += count;
+ }
+
+ /**
* Ensures capacity to append at least <code>count</code> values.
*/
private void ensureCapacity(int count) {
diff --git a/core/java/android/view/Choreographer.java b/core/java/android/view/Choreographer.java
index 987edf7761d2..042808a4cad2 100644
--- a/core/java/android/view/Choreographer.java
+++ b/core/java/android/view/Choreographer.java
@@ -660,8 +660,7 @@ public final class Choreographer {
ThreadedRenderer.setFPSDivisor(divisor);
}
- @UnsupportedAppUsage
- void doFrame(long frameTimeNanos, int frame) {
+ void doFrame(long frameTimeNanos, int frame, long frameTimelineVsyncId) {
final long startNanos;
synchronized (mLock) {
if (!mFrameScheduled) {
@@ -711,7 +710,7 @@ public final class Choreographer {
}
}
- mFrameInfo.setVsync(intendedFrameTimeNanos, frameTimeNanos);
+ mFrameInfo.setVsync(intendedFrameTimeNanos, frameTimeNanos, frameTimelineVsyncId);
mFrameScheduled = false;
mLastFrameTimeNanos = frameTimeNanos;
}
@@ -897,7 +896,7 @@ public final class Choreographer {
public void handleMessage(Message msg) {
switch (msg.what) {
case MSG_DO_FRAME:
- doFrame(System.nanoTime(), 0);
+ doFrame(System.nanoTime(), 0, FrameInfo.INVALID_VSYNC_ID);
break;
case MSG_DO_SCHEDULE_VSYNC:
doScheduleVsync();
@@ -914,6 +913,7 @@ public final class Choreographer {
private boolean mHavePendingVsync;
private long mTimestampNanos;
private int mFrame;
+ private long mFrameTimelineVsyncId;
public FrameDisplayEventReceiver(Looper looper, int vsyncSource) {
super(looper, vsyncSource, CONFIG_CHANGED_EVENT_SUPPRESS);
@@ -923,7 +923,8 @@ public final class Choreographer {
// the internal display and DisplayEventReceiver#scheduleVsync only allows requesting VSYNC
// for the internal display implicitly.
@Override
- public void onVsync(long timestampNanos, long physicalDisplayId, int frame) {
+ public void onVsync(long timestampNanos, long physicalDisplayId, int frame,
+ long frameTimelineVsyncId) {
// Post the vsync event to the Handler.
// The idea is to prevent incoming vsync events from completely starving
// the message queue. If there are no messages in the queue with timestamps
@@ -946,6 +947,7 @@ public final class Choreographer {
mTimestampNanos = timestampNanos;
mFrame = frame;
+ mFrameTimelineVsyncId = frameTimelineVsyncId;
Message msg = Message.obtain(mHandler, this);
msg.setAsynchronous(true);
mHandler.sendMessageAtTime(msg, timestampNanos / TimeUtils.NANOS_PER_MS);
@@ -954,7 +956,7 @@ public final class Choreographer {
@Override
public void run() {
mHavePendingVsync = false;
- doFrame(mTimestampNanos, mFrame);
+ doFrame(mTimestampNanos, mFrame, mFrameTimelineVsyncId);
}
}
diff --git a/core/java/android/view/DisplayEventReceiver.java b/core/java/android/view/DisplayEventReceiver.java
index eaf297cc05d8..51474d3c39c8 100644
--- a/core/java/android/view/DisplayEventReceiver.java
+++ b/core/java/android/view/DisplayEventReceiver.java
@@ -154,9 +154,11 @@ public abstract class DisplayEventReceiver {
* timebase.
* @param physicalDisplayId Stable display ID that uniquely describes a (display, port) pair.
* @param frame The frame number. Increases by one for each vertical sync interval.
+ * @param frameTimelineVsyncId The frame timeline vsync id, used to correlate a frame
+ * produced by HWUI with the timeline data stored in Surface Flinger.
*/
- @UnsupportedAppUsage
- public void onVsync(long timestampNanos, long physicalDisplayId, int frame) {
+ public void onVsync(long timestampNanos, long physicalDisplayId, int frame,
+ long frameTimelineVsyncId) {
}
/**
@@ -198,9 +200,9 @@ public abstract class DisplayEventReceiver {
// Called from native code.
@SuppressWarnings("unused")
- @UnsupportedAppUsage
- private void dispatchVsync(long timestampNanos, long physicalDisplayId, int frame) {
- onVsync(timestampNanos, physicalDisplayId, frame);
+ private void dispatchVsync(long timestampNanos, long physicalDisplayId, int frame,
+ long frameTimelineVsyncId) {
+ onVsync(timestampNanos, physicalDisplayId, frame, frameTimelineVsyncId);
}
// Called from native code.
diff --git a/core/java/android/view/FrameMetrics.java b/core/java/android/view/FrameMetrics.java
index 32cc30be8de4..280a1c0d95d2 100644
--- a/core/java/android/view/FrameMetrics.java
+++ b/core/java/android/view/FrameMetrics.java
@@ -189,6 +189,7 @@ public final class FrameMetrics {
*/
@IntDef ({
Index.FLAGS,
+ Index.FRAME_TIMELINE_VSYNC_ID,
Index.INTENDED_VSYNC,
Index.VSYNC,
Index.OLDEST_INPUT_EVENT,
@@ -206,21 +207,22 @@ public final class FrameMetrics {
@Retention(RetentionPolicy.SOURCE)
private @interface Index {
int FLAGS = 0;
- int INTENDED_VSYNC = 1;
- int VSYNC = 2;
- int OLDEST_INPUT_EVENT = 3;
- int NEWEST_INPUT_EVENT = 4;
- int HANDLE_INPUT_START = 5;
- int ANIMATION_START = 6;
- int PERFORM_TRAVERSALS_START = 7;
- int DRAW_START = 8;
- int SYNC_QUEUED = 9;
- int SYNC_START = 10;
- int ISSUE_DRAW_COMMANDS_START = 11;
- int SWAP_BUFFERS = 12;
- int FRAME_COMPLETED = 13;
+ int FRAME_TIMELINE_VSYNC_ID = 1;
+ int INTENDED_VSYNC = 2;
+ int VSYNC = 3;
+ int OLDEST_INPUT_EVENT = 4;
+ int NEWEST_INPUT_EVENT = 5;
+ int HANDLE_INPUT_START = 6;
+ int ANIMATION_START = 7;
+ int PERFORM_TRAVERSALS_START = 8;
+ int DRAW_START = 9;
+ int SYNC_QUEUED = 10;
+ int SYNC_START = 11;
+ int ISSUE_DRAW_COMMANDS_START = 12;
+ int SWAP_BUFFERS = 13;
+ int FRAME_COMPLETED = 14;
- int FRAME_STATS_COUNT = 17; // must always be last
+ int FRAME_STATS_COUNT = 18; // must always be last
}
/*
diff --git a/core/java/android/view/IWindowSession.aidl b/core/java/android/view/IWindowSession.aidl
index 609f80253ce6..18c87c092569 100644
--- a/core/java/android/view/IWindowSession.aidl
+++ b/core/java/android/view/IWindowSession.aidl
@@ -121,7 +121,7 @@ interface IWindowSession {
* @param childrenOnly Whether to only prepare child windows for replacement
* (for example when main windows are being reused via preservation).
*/
- void prepareToReplaceWindows(IBinder appToken, boolean childrenOnly);
+ oneway void prepareToReplaceWindows(IBinder appToken, boolean childrenOnly);
/**
* Called by a client to report that it ran out of graphics memory.
@@ -134,7 +134,7 @@ interface IWindowSession {
* to optimize compositing of this part of the window.
*/
@UnsupportedAppUsage
- void setTransparentRegion(IWindow window, in Region region);
+ oneway void setTransparentRegion(IWindow window, in Region region);
/**
* Tell the window manager about the content and visible insets of the
@@ -146,7 +146,7 @@ interface IWindowSession {
* frame can receive pointer events, as defined by
* {@link android.view.ViewTreeObserver.InternalInsetsInfo}.
*/
- void setInsets(IWindow window, int touchableInsets, in Rect contentInsets,
+ oneway void setInsets(IWindow window, int touchableInsets, in Rect contentInsets,
in Rect visibleInsets, in Region touchableRegion);
/**
@@ -157,10 +157,10 @@ interface IWindowSession {
* is null if there is no sync required.
*/
@UnsupportedAppUsage
- void finishDrawing(IWindow window, in SurfaceControl.Transaction postDrawTransaction);
+ oneway void finishDrawing(IWindow window, in SurfaceControl.Transaction postDrawTransaction);
@UnsupportedAppUsage
- void setInTouchMode(boolean showFocus);
+ oneway void setInTouchMode(boolean showFocus);
@UnsupportedAppUsage
boolean getInTouchMode();
@@ -192,24 +192,24 @@ interface IWindowSession {
* consumed is 'true' when the drop was accepted by a valid recipient,
* 'false' otherwise.
*/
- void reportDropResult(IWindow window, boolean consumed);
+ oneway void reportDropResult(IWindow window, boolean consumed);
/**
* Cancel the current drag operation.
* skipAnimation is 'true' when it should skip the drag cancel animation which brings the drag
* shadow image back to the drag start position.
*/
- void cancelDragAndDrop(IBinder dragToken, boolean skipAnimation);
+ oneway void cancelDragAndDrop(IBinder dragToken, boolean skipAnimation);
/**
* Tell the OS that we've just dragged into a View that is willing to accept the drop
*/
- void dragRecipientEntered(IWindow window);
+ oneway void dragRecipientEntered(IWindow window);
/**
* Tell the OS that we've just dragged *off* of a View that was willing to accept the drop
*/
- void dragRecipientExited(IWindow window);
+ oneway void dragRecipientExited(IWindow window);
/**
* For windows with the wallpaper behind them, and the wallpaper is
@@ -230,26 +230,26 @@ interface IWindowSession {
* scaled when setWallpaperZoomOut is called. If set to false, the WallpaperService will
* receive the zoom out value but the surface won't be scaled.
*/
- void setShouldZoomOutWallpaper(IBinder windowToken, boolean shouldZoom);
+ oneway void setShouldZoomOutWallpaper(IBinder windowToken, boolean shouldZoom);
@UnsupportedAppUsage
- void wallpaperOffsetsComplete(IBinder window);
+ oneway void wallpaperOffsetsComplete(IBinder window);
/**
* Apply a raw offset to the wallpaper service when shown behind this window.
*/
- void setWallpaperDisplayOffset(IBinder windowToken, int x, int y);
+ oneway void setWallpaperDisplayOffset(IBinder windowToken, int x, int y);
Bundle sendWallpaperCommand(IBinder window, String action, int x, int y,
int z, in Bundle extras, boolean sync);
@UnsupportedAppUsage
- void wallpaperCommandComplete(IBinder window, in Bundle result);
+ oneway void wallpaperCommandComplete(IBinder window, in Bundle result);
/**
* Notifies that a rectangle on the screen has been requested.
*/
- void onRectangleOnScreenRequested(IBinder token, in Rect rectangle);
+ oneway void onRectangleOnScreenRequested(IBinder token, in Rect rectangle);
IWindowId getWindowId(IBinder window);
@@ -276,9 +276,9 @@ interface IWindowSession {
*/
boolean startMovingTask(IWindow window, float startX, float startY);
- void finishMovingTask(IWindow window);
+ oneway void finishMovingTask(IWindow window);
- void updatePointerIcon(IWindow window);
+ oneway void updatePointerIcon(IWindow window);
/**
* Reparent the top layers for a display to the requested SurfaceControl. The display that is
@@ -292,7 +292,7 @@ interface IWindowSession {
* to.
* @param displayId The id of the display to be re-parented.
*/
- void reparentDisplayContent(IWindow window, in SurfaceControl sc, int displayId);
+ oneway void reparentDisplayContent(IWindow window, in SurfaceControl sc, int displayId);
/**
* Update the location of a child display in its parent window. This enables windows in the
@@ -303,14 +303,14 @@ interface IWindowSession {
* @param y The y coordinate in the parent window.
* @param displayId The id of the display to be notified.
*/
- void updateDisplayContentLocation(IWindow window, int x, int y, int displayId);
+ oneway void updateDisplayContentLocation(IWindow window, int x, int y, int displayId);
/**
* Update a tap exclude region identified by provided id in the window. Touches on this region
* will neither be dispatched to this window nor change the focus to this window. Passing an
* invalid region will remove the area from the exclude region of this window.
*/
- void updateTapExcludeRegion(IWindow window, in Region region);
+ oneway void updateTapExcludeRegion(IWindow window, in Region region);
/**
* Called when the client has changed the local insets state, and now the server should reflect
@@ -334,8 +334,8 @@ interface IWindowSession {
/**
* Update the flags on an input channel associated with a particular surface.
*/
- void updateInputChannel(in IBinder channelToken, int displayId, in SurfaceControl surface,
- int flags, int privateFlags, in Region region);
+ oneway void updateInputChannel(in IBinder channelToken, int displayId,
+ in SurfaceControl surface, int flags, int privateFlags, in Region region);
/**
* Transfer window focus to an embedded window if the calling window has focus.
diff --git a/core/java/android/view/InputChannel.java b/core/java/android/view/InputChannel.java
index e1b042160062..f2d3f5ad08bf 100644
--- a/core/java/android/view/InputChannel.java
+++ b/core/java/android/view/InputChannel.java
@@ -83,7 +83,7 @@ public final class InputChannel implements Parcelable {
*
* @hide
*/
- public void setNativeInputChannel(long nativeChannel) {
+ private void setNativeInputChannel(long nativeChannel) {
if (nativeChannel == 0) {
throw new IllegalArgumentException("Attempting to set native input channel to null.");
}
@@ -148,12 +148,11 @@ public final class InputChannel implements Parcelable {
}
/**
- * Transfers ownership of the internal state of the input channel to another
- * instance and invalidates this instance. This is used to pass an input channel
+ * Creates a copy of this instance to the outParameter. This is used to pass an input channel
* as an out parameter in a binder call.
* @param other The other input channel instance.
*/
- public void transferTo(InputChannel outParameter) {
+ public void copyTo(InputChannel outParameter) {
if (outParameter == null) {
throw new IllegalArgumentException("outParameter must not be null");
}
diff --git a/core/java/android/view/InsetsState.java b/core/java/android/view/InsetsState.java
index 373a0963dc2f..f462a990e958 100644
--- a/core/java/android/view/InsetsState.java
+++ b/core/java/android/view/InsetsState.java
@@ -17,10 +17,6 @@
package android.view;
import static android.view.View.SYSTEM_UI_FLAG_LAYOUT_STABLE;
-import static android.view.ViewRootImpl.NEW_INSETS_MODE_FULL;
-import static android.view.ViewRootImpl.NEW_INSETS_MODE_IME;
-import static android.view.ViewRootImpl.NEW_INSETS_MODE_NONE;
-import static android.view.ViewRootImpl.sNewInsetsMode;
import static android.view.WindowInsets.Type.MANDATORY_SYSTEM_GESTURES;
import static android.view.WindowInsets.Type.SYSTEM_GESTURES;
import static android.view.WindowInsets.Type.displayCutout;
@@ -195,18 +191,6 @@ public class InsetsState implements Parcelable {
continue;
}
- boolean skipNonImeInImeMode = ViewRootImpl.sNewInsetsMode == NEW_INSETS_MODE_IME
- && source.getType() != ITYPE_IME;
- boolean skipSystemBars = ViewRootImpl.sNewInsetsMode != NEW_INSETS_MODE_FULL
- && (type == ITYPE_STATUS_BAR || type == ITYPE_NAVIGATION_BAR);
- boolean skipLegacyTypes = ViewRootImpl.sNewInsetsMode == NEW_INSETS_MODE_NONE
- && (type == ITYPE_STATUS_BAR || type == ITYPE_NAVIGATION_BAR
- || type == ITYPE_IME);
- if (skipSystemBars || skipLegacyTypes || skipNonImeInImeMode) {
- typeVisibilityMap[indexOf(toPublicType(type))] = source.isVisible();
- continue;
- }
-
processSource(source, relativeFrame, false /* ignoreVisibility */, typeInsetsMap,
typeSideMap, typeVisibilityMap);
@@ -239,8 +223,7 @@ public class InsetsState implements Parcelable {
return new WindowInsets(typeInsetsMap, typeMaxInsetsMap, typeVisibilityMap, isScreenRound,
alwaysConsumeSystemBars, cutout, compatInsetsTypes,
- sNewInsetsMode == NEW_INSETS_MODE_FULL
- && (legacySystemUiFlags & SYSTEM_UI_FLAG_LAYOUT_STABLE) != 0);
+ (legacySystemUiFlags & SYSTEM_UI_FLAG_LAYOUT_STABLE) != 0);
}
public Rect calculateVisibleInsets(Rect frame, @SoftInputModeFlags int softInputMode) {
@@ -250,9 +233,6 @@ public class InsetsState implements Parcelable {
if (source == null) {
continue;
}
- if (sNewInsetsMode != NEW_INSETS_MODE_FULL && type != ITYPE_IME) {
- continue;
- }
// Ignore everything that's not a system bar or IME.
int publicType = InsetsState.toPublicType(type);
diff --git a/core/java/android/view/SurfaceControl.java b/core/java/android/view/SurfaceControl.java
index d55c25fc1a4f..c698b926e233 100644
--- a/core/java/android/view/SurfaceControl.java
+++ b/core/java/android/view/SurfaceControl.java
@@ -316,11 +316,19 @@ public final class SurfaceControl implements Parcelable {
public static final int HIDDEN = 0x00000004;
/**
- * Surface creation flag: The surface contains secure content, special
- * measures will be taken to disallow the surface's content to be copied
- * from another process. In particular, screenshots and VNC servers will
- * be disabled, but other measures can take place, for instance the
- * surface might not be hardware accelerated.
+ * Surface creation flag: Skip this layer and its children when taking a screenshot. This
+ * also includes mirroring and screen recording, so the layers with flag SKIP_SCREENSHOT
+ * will not be included on non primary displays.
+ * @hide
+ */
+ public static final int SKIP_SCREENSHOT = 0x00000040;
+
+ /**
+ * Surface creation flag: Special measures will be taken to disallow the surface's content to
+ * be copied. In particular, screenshots and secondary, non-secure displays will render black
+ * content instead of the surface content.
+ *
+ * @see #createDisplay(String, boolean)
* @hide
*/
public static final int SECURE = 0x00000080;
@@ -482,15 +490,6 @@ public final class SurfaceControl implements Parcelable {
public static final int POWER_MODE_ON_SUSPEND = 4;
/**
- * A value for windowType used to indicate that the window should be omitted from screenshots
- * and display mirroring. A temporary workaround until we express such things with
- * the hierarchy.
- * TODO: b/64227542
- * @hide
- */
- public static final int WINDOW_TYPE_DONT_SCREENSHOT = 441731;
-
- /**
* internal representation of how to interpret pixel value, used only to convert to ColorSpace.
*/
private static final int INTERNAL_DATASPACE_SRGB = 142671872;
@@ -3287,6 +3286,22 @@ public final class SurfaceControl implements Parcelable {
return this;
}
+ /**
+ * Adds or removes the flag SKIP_SCREENSHOT of the surface. Setting the flag is equivalent
+ * to creating the Surface with the {@link #SKIP_SCREENSHOT} flag.
+ *
+ * @hide
+ */
+ public Transaction setSkipScreenshot(SurfaceControl sc, boolean skipScrenshot) {
+ checkPreconditions(sc);
+ if (skipScrenshot) {
+ nativeSetFlags(mNativeObject, sc.mNativeObject, SKIP_SCREENSHOT, SKIP_SCREENSHOT);
+ } else {
+ nativeSetFlags(mNativeObject, sc.mNativeObject, 0, SKIP_SCREENSHOT);
+ }
+ return this;
+ }
+
/**
* Merge the other transaction into this transaction, clearing the
* other transaction as if it had been applied.
diff --git a/core/java/android/view/SurfaceView.java b/core/java/android/view/SurfaceView.java
index b644e9efa666..abda698fdf73 100644
--- a/core/java/android/view/SurfaceView.java
+++ b/core/java/android/view/SurfaceView.java
@@ -420,8 +420,6 @@ public class SurfaceView extends View implements ViewRootImpl.SurfaceChangedCall
final boolean useBLAST = viewRoot.useBLAST();
viewRoot.registerRtFrameCallback(frame -> {
try {
- final SurfaceControl.Transaction t = useBLAST ?
- viewRoot.getBLASTSyncTransaction() : new SurfaceControl.Transaction();
synchronized (mSurfaceControlLock) {
if (!parent.isValid()) {
if (DEBUG) {
@@ -443,16 +441,22 @@ public class SurfaceView extends View implements ViewRootImpl.SurfaceChangedCall
Log.d(TAG, System.identityHashCode(this)
+ " updateSurfaceAlpha RT: set alpha=" + alpha);
}
- t.setAlpha(mSurfaceControl, alpha);
- if (!useBLAST) {
+ if (useBLAST) {
+ synchronized (viewRoot.getBlastTransactionLock()) {
+ viewRoot.getBLASTSyncTransaction()
+ .setAlpha(mSurfaceControl, alpha);
+ }
+ } else {
+ Transaction t = new SurfaceControl.Transaction();
+ t.setAlpha(mSurfaceControl, alpha);
t.deferTransactionUntil(mSurfaceControl,
viewRoot.getRenderSurfaceControl(), frame);
+ t.apply();
}
}
// It's possible that mSurfaceControl is released in the UI thread before
// the transaction completes. If that happens, an exception is thrown, which
// must be caught immediately.
- t.apply();
} catch (Exception e) {
Log.e(TAG, System.identityHashCode(this)
+ "updateSurfaceAlpha RT: Exception during surface transaction", e);
@@ -793,23 +797,26 @@ public class SurfaceView extends View implements ViewRootImpl.SurfaceChangedCall
final boolean useBLAST = viewRoot.useBLAST();
viewRoot.registerRtFrameCallback(frame -> {
try {
- final SurfaceControl.Transaction t = useBLAST
- ? viewRoot.getBLASTSyncTransaction()
- : new SurfaceControl.Transaction();
synchronized (mSurfaceControlLock) {
if (!parent.isValid() || mSurfaceControl == null) {
return;
}
- updateRelativeZ(t);
- if (!useBLAST) {
+
+ if (useBLAST) {
+ synchronized (viewRoot.getBlastTransactionLock()) {
+ updateRelativeZ(viewRoot.getBLASTSyncTransaction());
+ }
+ } else {
+ final SurfaceControl.Transaction t = new SurfaceControl.Transaction();
+ updateRelativeZ(t);
t.deferTransactionUntil(mSurfaceControl,
viewRoot.getRenderSurfaceControl(), frame);
+ t.apply();
}
}
// It's possible that mSurfaceControl is released in the UI thread before
// the transaction completes. If that happens, an exception is thrown, which
// must be caught immediately.
- t.apply();
} catch (Exception e) {
Log.e(TAG, System.identityHashCode(this)
+ "setZOrderOnTop RT: Exception during surface transaction", e);
@@ -1252,7 +1259,7 @@ public class SurfaceView extends View implements ViewRootImpl.SurfaceChangedCall
private void applySurfaceTransforms(SurfaceControl surface, SurfaceControl.Transaction t,
Rect position, long frameNumber) {
final ViewRootImpl viewRoot = getViewRootImpl();
- if (frameNumber > 0 && viewRoot != null && !viewRoot.isDrawingToBLASTTransaction()) {
+ if (frameNumber > 0 && viewRoot != null && !viewRoot.useBLAST()) {
t.deferTransactionUntil(surface, viewRoot.getRenderSurfaceControl(),
frameNumber);
}
@@ -1277,19 +1284,23 @@ public class SurfaceView extends View implements ViewRootImpl.SurfaceChangedCall
return mRTLastReportedPosition;
}
- private void setParentSpaceRectangle(Rect position, long frameNumber) {
+ private void setParentSpaceRectangle(Transaction t, Rect position, long frameNumber) {
final ViewRootImpl viewRoot = getViewRootImpl();
- final boolean useBLAST = viewRoot.isDrawingToBLASTTransaction();
- final SurfaceControl.Transaction t = useBLAST ? viewRoot.getBLASTSyncTransaction() :
- mRtTransaction;
-
applySurfaceTransforms(mSurfaceControl, t, position, frameNumber);
+ applyChildSurfaceTransaction_renderWorker(t, viewRoot.mSurface, frameNumber);
+ }
- applyChildSurfaceTransaction_renderWorker(t, viewRoot.mSurface,
- frameNumber);
+ private void setParentSpaceRectangle(Rect position, long frameNumber) {
+ final ViewRootImpl viewRoot = getViewRootImpl();
+ final boolean useBLAST = viewRoot.useBLAST();
- if (!useBLAST) {
- t.apply();
+ if (useBLAST) {
+ synchronized (viewRoot.getBlastTransactionLock()) {
+ setParentSpaceRectangle(viewRoot.getBLASTSyncTransaction(), position, frameNumber);
+ }
+ } else {
+ setParentSpaceRectangle(mRtTransaction, position, frameNumber);
+ mRtTransaction.apply();
}
}
@@ -1337,10 +1348,20 @@ public class SurfaceView extends View implements ViewRootImpl.SurfaceChangedCall
}
}
+ private void releaseSurfaces(Transaction t) {
+ if (mRtReleaseSurfaces) {
+ mRtReleaseSurfaces = false;
+ t.remove(mSurfaceControl);
+ t.remove(mBackgroundControl);
+ mSurfaceControl = null;
+ mBackgroundControl = null;
+ }
+ }
+
@Override
public void positionLost(long frameNumber) {
final ViewRootImpl viewRoot = getViewRootImpl();
- boolean useBLAST = viewRoot != null && viewRoot.isDrawingToBLASTTransaction();
+ boolean useBLAST = viewRoot != null && viewRoot.useBLAST();
if (DEBUG) {
Log.d(TAG, String.format("%d windowPositionLost, frameNr = %d",
System.identityHashCode(this), frameNumber));
@@ -1351,38 +1372,31 @@ public class SurfaceView extends View implements ViewRootImpl.SurfaceChangedCall
return;
}
- final SurfaceControl.Transaction t = useBLAST ?
- (viewRoot != null ? viewRoot.getBLASTSyncTransaction() : mRtTransaction) :
- mRtTransaction;
-
/**
* positionLost can be called while UI thread is un-paused so we
* need to hold the lock here.
*/
synchronized (mSurfaceControlLock) {
- if (frameNumber > 0 && viewRoot != null && !useBLAST) {
- if (viewRoot.mSurface.isValid()) {
+ if (useBLAST) {
+ synchronized (viewRoot.getBlastTransactionLock()) {
+ viewRoot.getBLASTSyncTransaction().hide(mSurfaceControl);
+ releaseSurfaces(viewRoot.getBLASTSyncTransaction());
+ }
+ } else {
+ if (frameNumber > 0 && viewRoot != null && viewRoot.mSurface.isValid()) {
mRtTransaction.deferTransactionUntil(mSurfaceControl,
viewRoot.getRenderSurfaceControl(), frameNumber);
}
- }
- t.hide(mSurfaceControl);
-
- if (mRtReleaseSurfaces) {
- mRtReleaseSurfaces = false;
- mRtTransaction.remove(mSurfaceControl);
- mRtTransaction.remove(mBackgroundControl);
- mSurfaceControl = null;
- mBackgroundControl = null;
+ mRtTransaction.hide(mSurfaceControl);
+ releaseSurfaces(mRtTransaction);
+ // If we aren't using BLAST, we apply the transaction locally, otherwise we let
+ // the ViewRoot apply it for us.
+ // If the ViewRoot is null, we behave as if we aren't using BLAST so we need to
+ // apply the transaction.
+ mRtTransaction.apply();
}
mRtHandlingPositionUpdates = false;
}
-
- // If we aren't using BLAST, we apply the transaction locally, otherise we let the ViewRoot apply it for us.
- // If the ViewRoot is null, we behave as if we aren't using BLAST so we need to apply the transaction.
- if (!useBLAST || viewRoot == null) {
- mRtTransaction.apply();
- }
}
};
diff --git a/core/java/android/view/View.java b/core/java/android/view/View.java
index 92a0f633aba0..e7e28ac98234 100644
--- a/core/java/android/view/View.java
+++ b/core/java/android/view/View.java
@@ -17,7 +17,6 @@
package android.view;
import static android.content.res.Resources.ID_NULL;
-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;
@@ -5435,8 +5434,7 @@ public class View implements Drawable.Callback, KeyEvent.Callback,
sAcceptZeroSizeDragShadow = targetSdkVersion < Build.VERSION_CODES.P;
- sBrokenInsetsDispatch = ViewRootImpl.sNewInsetsMode != NEW_INSETS_MODE_FULL
- || targetSdkVersion < Build.VERSION_CODES.R;
+ sBrokenInsetsDispatch = targetSdkVersion < Build.VERSION_CODES.R;
sBrokenWindowBackground = targetSdkVersion < Build.VERSION_CODES.Q;
@@ -26263,6 +26261,14 @@ public class View implements Drawable.Callback, KeyEvent.Callback,
}
/**
+ * This needs to be a better API before it is exposed. For now, only the root view will get
+ * notified.
+ * @hide
+ */
+ public void onSystemBarAppearanceChanged(@WindowInsetsController.Appearance int appearance) {
+ }
+
+ /**
* Creates an image that the system displays during the drag and drop
* operation. This is called a &quot;drag shadow&quot;. The default implementation
* for a DragShadowBuilder based on a View returns an image that has exactly the same
diff --git a/core/java/android/view/ViewRootImpl.java b/core/java/android/view/ViewRootImpl.java
index e6f89a761e6b..7ce7a12d77aa 100644
--- a/core/java/android/view/ViewRootImpl.java
+++ b/core/java/android/view/ViewRootImpl.java
@@ -221,45 +221,6 @@ public final class ViewRootImpl implements ViewParent,
private static final boolean MT_RENDERER_AVAILABLE = true;
/**
- * If set to 2, the view system will switch from using rectangles retrieved from window to
- * dispatch to the view hierarchy to using {@link InsetsController}, that derives the insets
- * directly from the full configuration, enabling richer information about the insets state, as
- * well as new APIs to control it frame-by-frame, and synchronize animations with it.
- * <p>
- * Only set this to 2 once the new insets system is productionized and the old APIs are
- * fully migrated over.
- * <p>
- * If set to 1, this will switch to a mode where we only use the new approach for IME, but not
- * for the status/navigation bar.
- */
- private static final String USE_NEW_INSETS_PROPERTY = "persist.debug.new_insets";
-
- /**
- * @see #USE_NEW_INSETS_PROPERTY
- * @hide
- */
- public static final int NEW_INSETS_MODE_NONE = 0;
-
- /**
- * @see #USE_NEW_INSETS_PROPERTY
- * @hide
- */
- public static final int NEW_INSETS_MODE_IME = 1;
-
- /**
- * @see #USE_NEW_INSETS_PROPERTY
- * @hide
- */
- public static final int NEW_INSETS_MODE_FULL = 2;
-
- /**
- * @see #USE_NEW_INSETS_PROPERTY
- * @hide
- */
- public static int sNewInsetsMode =
- SystemProperties.getInt(USE_NEW_INSETS_PROPERTY, NEW_INSETS_MODE_FULL);
-
- /**
* Set this system property to true to force the view hierarchy to render
* at 60 Hz. This can be used to measure the potential framerate.
*/
@@ -454,8 +415,8 @@ public final class ViewRootImpl implements ViewParent,
@UnsupportedAppUsage
final View.AttachInfo mAttachInfo;
final SystemUiVisibilityInfo mCompatibleVisibilityInfo;
- int mDispatchedSystemUiVisibility =
- ViewRootImpl.sNewInsetsMode == NEW_INSETS_MODE_FULL ? 0 : -1;
+ int mDispatchedSystemUiVisibility;
+ int mDispatchedSystemBarAppearance;
InputQueue.Callback mInputQueueCallback;
InputQueue mInputQueue;
@UnsupportedAppUsage
@@ -693,26 +654,25 @@ public final class ViewRootImpl implements ViewParent,
int localChanges;
}
- // If set, ViewRootImpl will call BLASTBufferQueue::setNextTransaction with
- // mRtBLASTSyncTransaction, prior to invoking draw. This provides a way
- // to redirect the buffers in to transactions.
+ // If set, ViewRootImpl will request a callback from HWRender when it's ready to render the next
+ // frame. This will allow VRI to call BLASTBufferQueue::setNextTransaction with
+ // mRtBLASTSyncTransaction, so the next frame submitted will be added to the
+ // mRtBLASTSyncTransaction instead of getting applied.
private boolean mNextDrawUseBLASTSyncTransaction;
- // Set when calling setNextTransaction, we can't just reuse mNextDrawUseBLASTSyncTransaction
- // because, imagine this scenario:
- // 1. First draw is using BLAST, mNextDrawUseBLAST = true
- // 2. We call perform draw and are waiting on the callback
- // 3. After the first perform draw but before the first callback and the
- // second perform draw, a second draw sets mNextDrawUseBLAST = true (it already was)
- // 4. At this point the callback fires and we set mNextDrawUseBLAST = false;
- // 5. We get to performDraw and fail to sync as we intended because mNextDrawUseBLAST
- // is now false.
- // This is why we use a two-step latch with the two booleans, one consumed from
- // performDraw and one consumed from finishBLASTSync()
- private boolean mNextReportConsumeBLAST;
- // Be very careful with the threading here. This is used from the render thread while
- // the UI thread is paused and then applied and cleared from the UI thread right after
- // draw returns.
- private SurfaceControl.Transaction mRtBLASTSyncTransaction = new SurfaceControl.Transaction();
+
+ // This is used to signal if the mRtBLASTSyncTransaction should be applied/merged. When true,
+ // it indicates mRtBLASTSyncTransaction was sent to BLASTBufferQueue::setNextTransaction.
+ // Therefore, in onFrameComplete, if mRtNextFrameReportConsumeWithBlast is true, that means
+ // mRtBLASTSyncTransaction now contains the next buffer frame to be applied.
+ private boolean mRtNextFrameReportedConsumeWithBlast;
+
+ // Be very careful with the threading here. This is used from a thread pool generated by the
+ // render thread. Therefore, it needs to be locked when updating from the thread pool since
+ // multiple threads can be accessing it. It does not need to be locked when applied or merged
+ // since that can only happen from the frame complete callback after the other callbacks have
+ // been invoked.
+ private final SurfaceControl.Transaction mRtBLASTSyncTransaction =
+ new SurfaceControl.Transaction();
// Keeps track of whether the WM requested us to use BLAST Sync when calling relayout.
// We use this to make sure we don't send the WM transactions from an internal BLAST sync
@@ -1409,6 +1369,10 @@ public final class ViewRootImpl implements ViewParent,
attrs.systemUiVisibility = mWindowAttributes.systemUiVisibility;
attrs.subtreeSystemUiVisibility = mWindowAttributes.subtreeSystemUiVisibility;
+ // Transfer over appearance and behavior values as they carry current state.
+ attrs.insetsFlags.appearance = mWindowAttributes.insetsFlags.appearance;
+ attrs.insetsFlags.behavior = mWindowAttributes.insetsFlags.behavior;
+
final int changes = mWindowAttributes.copyFrom(attrs);
if ((changes & WindowManager.LayoutParams.TRANSLUCENT_FLAGS_CHANGED) != 0) {
// Recompute system ui visibility.
@@ -1627,9 +1591,6 @@ public final class ViewRootImpl implements ViewParent,
}
void notifyInsetsChanged() {
- if (sNewInsetsMode == NEW_INSETS_MODE_NONE) {
- return;
- }
mApplyInsetsRequested = true;
requestLayout();
@@ -2063,8 +2024,7 @@ public final class ViewRootImpl implements ViewParent,
*/
void updateCompatSysUiVisibility(@InternalInsetsType int type, boolean visible,
boolean hasControl) {
- if ((type != ITYPE_STATUS_BAR && type != ITYPE_NAVIGATION_BAR)
- || ViewRootImpl.sNewInsetsMode != ViewRootImpl.NEW_INSETS_MODE_FULL) {
+ if (type != ITYPE_STATUS_BAR && type != ITYPE_NAVIGATION_BAR) {
return;
}
final SystemUiVisibilityInfo info = mCompatibleVisibilityInfo;
@@ -2089,14 +2049,6 @@ public final class ViewRootImpl implements ViewParent,
}
private void handleDispatchSystemUiVisibilityChanged(SystemUiVisibilityInfo args) {
- if (mSeq != args.seq && sNewInsetsMode != NEW_INSETS_MODE_FULL) {
- // The sequence has changed, so we need to update our value and make
- // sure to do a traversal afterward so the window manager is given our
- // most recent data.
- mSeq = args.seq;
- mAttachInfo.mForceReportNewAttributes = true;
- scheduleTraversals();
- }
if (mView == null) return;
if (args.localChanges != 0) {
mView.updateLocalSystemUiVisibility(args.localValue, args.localChanges);
@@ -2112,9 +2064,6 @@ public final class ViewRootImpl implements ViewParent,
@VisibleForTesting
public static void adjustLayoutParamsForCompatibility(WindowManager.LayoutParams inOutParams) {
- if (sNewInsetsMode != NEW_INSETS_MODE_FULL) {
- return;
- }
final int sysUiVis = inOutParams.systemUiVisibility | inOutParams.subtreeSystemUiVisibility;
final int flags = inOutParams.flags;
final int type = inOutParams.type;
@@ -2179,9 +2128,6 @@ public final class ViewRootImpl implements ViewParent,
}
private void controlInsetsForCompatibility(WindowManager.LayoutParams params) {
- if (sNewInsetsMode != NEW_INSETS_MODE_FULL) {
- return;
- }
final int sysUiVis = params.systemUiVisibility | params.subtreeSystemUiVisibility;
final int flags = params.flags;
final boolean matchParent = params.width == MATCH_PARENT && params.height == MATCH_PARENT;
@@ -2629,6 +2575,10 @@ public final class ViewRootImpl implements ViewParent,
}
adjustLayoutParamsForCompatibility(params);
controlInsetsForCompatibility(params);
+ if (mDispatchedSystemBarAppearance != params.insetsFlags.appearance) {
+ mDispatchedSystemBarAppearance = params.insetsFlags.appearance;
+ mView.onSystemBarAppearanceChanged(mDispatchedSystemBarAppearance);
+ }
}
if (mFirst || windowShouldResize || viewVisibilityChanged || cutoutChanged || params != null
@@ -3239,14 +3189,12 @@ public final class ViewRootImpl implements ViewParent,
hasWindowFocus = mUpcomingWindowFocus;
inTouchMode = mUpcomingInTouchMode;
}
- if (sNewInsetsMode != NEW_INSETS_MODE_NONE) {
- // TODO (b/131181940): Make sure this doesn't leak Activity with mActivityConfigCallback
- // config changes.
- if (hasWindowFocus) {
- mInsetsController.onWindowFocusGained();
- } else {
- mInsetsController.onWindowFocusLost();
- }
+ // TODO (b/131181940): Make sure this doesn't leak Activity with mActivityConfigCallback
+ // config changes.
+ if (hasWindowFocus) {
+ mInsetsController.onWindowFocusGained();
+ } else {
+ mInsetsController.onWindowFocusLost();
}
if (mAdded) {
@@ -3834,26 +3782,29 @@ public final class ViewRootImpl implements ViewParent,
commitCallbacks.get(i).run();
}
}
- });});
+ });
+ });
}
}
try {
if (mNextDrawUseBLASTSyncTransaction) {
- // TODO(b/149747443)
- // We aren't prepared to handle overlapping use of mRtBLASTSyncTransaction
- // so if we are BLAST syncing we make sure the previous draw has
- // totally finished.
- if (mAttachInfo.mThreadedRenderer != null) {
- mAttachInfo.mThreadedRenderer.pause();
- }
-
- mNextReportConsumeBLAST = true;
+ // Frame callbacks will always occur after submitting draw requests and before
+ // the draw actually occurs. This will ensure that we set the next transaction
+ // for the frame that's about to get drawn and not on a previous frame that.
+ //
+ // This is thread safe since mRtNextFrameReportConsumeWithBlast will only be
+ // modified in onFrameDraw and then again in onFrameComplete. This is to ensure the
+ // next frame completed should be reported with the blast sync transaction.
+ registerRtFrameCallback(frame -> {
+ mRtNextFrameReportedConsumeWithBlast = true;
+ if (mBlastBufferQueue != null) {
+ // We don't need to synchronize mRtBLASTSyncTransaction here since it's not
+ // being modified and only sent to BlastBufferQueue.
+ mBlastBufferQueue.setNextTransaction(mRtBLASTSyncTransaction);
+ }
+ });
mNextDrawUseBLASTSyncTransaction = false;
-
- if (mBlastBufferQueue != null) {
- mBlastBufferQueue.setNextTransaction(mRtBLASTSyncTransaction);
- }
}
boolean canUseAsync = draw(fullRedrawNeeded);
if (usingAsyncReport && !canUseAsync) {
@@ -9808,9 +9759,12 @@ public final class ViewRootImpl implements ViewParent,
private void finishBLASTSync(boolean apply) {
mSendNextFrameToWm = false;
- if (mNextReportConsumeBLAST) {
- mNextReportConsumeBLAST = false;
+ if (mRtNextFrameReportedConsumeWithBlast) {
+ mRtNextFrameReportedConsumeWithBlast = false;
+ // We don't need to synchronize mRtBLASTSyncTransaction here we're guaranteed that this
+ // is called after all onFrameDraw and after callbacks to PositionUpdateListener.
+ // Therefore, no one should be modifying this object until the next vsync.
if (apply) {
mRtBLASTSyncTransaction.apply();
} else {
@@ -9823,6 +9777,10 @@ public final class ViewRootImpl implements ViewParent,
return mRtBLASTSyncTransaction;
}
+ Object getBlastTransactionLock() {
+ return mRtBLASTSyncTransaction;
+ }
+
/**
* @hide
*/
@@ -9850,12 +9808,4 @@ public final class ViewRootImpl implements ViewParent,
boolean useBLAST() {
return mUseBLASTAdapter && !mForceDisableBLAST;
}
-
- /**
- * Returns true if we are about to or currently processing a draw directed
- * in to a BLAST transaction.
- */
- boolean isDrawingToBLASTTransaction() {
- return mNextReportConsumeBLAST;
- }
}
diff --git a/core/java/android/view/WindowManagerImpl.java b/core/java/android/view/WindowManagerImpl.java
index 5d53ad7ef186..f57ee65948c0 100644
--- a/core/java/android/view/WindowManagerImpl.java
+++ b/core/java/android/view/WindowManagerImpl.java
@@ -19,7 +19,6 @@ package android.view;
import static android.view.View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN;
import static android.view.View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION;
import static android.view.View.SYSTEM_UI_FLAG_VISIBLE;
-import static android.view.ViewRootImpl.NEW_INSETS_MODE_FULL;
import static android.view.WindowManager.LayoutParams.FLAG_LAYOUT_INSET_DECOR;
import static android.view.WindowManager.LayoutParams.FLAG_LAYOUT_IN_SCREEN;
import static android.view.WindowManager.LayoutParams.SOFT_INPUT_ADJUST_NOTHING;
@@ -30,7 +29,6 @@ import android.app.ResourcesManager;
import android.compat.annotation.UnsupportedAppUsage;
import android.content.Context;
import android.content.res.Configuration;
-import android.graphics.Insets;
import android.graphics.Rect;
import android.graphics.Region;
import android.os.Bundle;
@@ -275,19 +273,10 @@ public final class WindowManagerImpl implements WindowManager {
final Configuration config = mContext.getResources().getConfiguration();
final boolean isScreenRound = config.isScreenRound();
final int windowingMode = config.windowConfiguration.getWindowingMode();
- if (ViewRootImpl.sNewInsetsMode == NEW_INSETS_MODE_FULL) {
- return insetsState.calculateInsets(bounds, null /* ignoringVisibilityState*/,
- isScreenRound, alwaysConsumeSystemBars, displayCutout.get(),
- SOFT_INPUT_ADJUST_NOTHING, attrs.flags, SYSTEM_UI_FLAG_VISIBLE, attrs.type,
- windowingMode, null /* typeSideMap */);
- } else {
- return new WindowInsets.Builder()
- .setAlwaysConsumeSystemBars(alwaysConsumeSystemBars)
- .setRound(isScreenRound)
- .setSystemWindowInsets(Insets.of(systemWindowInsets))
- .setStableInsets(Insets.of(stableInsets))
- .setDisplayCutout(displayCutout.get()).build();
- }
+ return insetsState.calculateInsets(bounds, null /* ignoringVisibilityState*/,
+ isScreenRound, alwaysConsumeSystemBars, displayCutout.get(),
+ SOFT_INPUT_ADJUST_NOTHING, attrs.flags, SYSTEM_UI_FLAG_VISIBLE, attrs.type,
+ windowingMode, null /* typeSideMap */);
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
diff --git a/core/java/android/view/inputmethod/EditorInfo.java b/core/java/android/view/inputmethod/EditorInfo.java
index c5f2299e4f83..104bc4347c29 100644
--- a/core/java/android/view/inputmethod/EditorInfo.java
+++ b/core/java/android/view/inputmethod/EditorInfo.java
@@ -22,13 +22,14 @@ import android.annotation.IntDef;
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.annotation.RequiresPermission;
+import android.os.Build.VERSION_CODES;
import android.os.Bundle;
import android.os.LocaleList;
import android.os.Parcel;
import android.os.Parcelable;
import android.os.UserHandle;
-import android.text.Editable;
import android.text.InputType;
+import android.text.ParcelableSpan;
import android.text.TextUtils;
import android.util.Printer;
import android.view.View;
@@ -543,6 +544,9 @@ public class EditorInfo implements InputType, Parcelable {
* {@code #getInitialSelectedText}, and {@code #getInitialTextBeforeCursor}. System is allowed
* to trim {@code sourceText} for various reasons while keeping the most valuable data to IMEs.
*
+ * Starting from {@link VERSION_CODES#S}, spans that do not implement {@link Parcelable} will
+ * be automatically dropped.
+ *
* <p><strong>Editor authors: </strong>Providing the initial input text helps reducing IPC calls
* for IMEs to provide many modern features right after the connection setup. We recommend
* calling this method in your implementation.
@@ -562,14 +566,16 @@ public class EditorInfo implements InputType, Parcelable {
* try to include the selected text within {@code subText} to give the system best flexibility
* to choose where and how to trim {@code subText} when necessary.
*
+ * Starting from {@link VERSION_CODES#S}, spans that do not implement {@link Parcelable} will
+ * be automatically dropped.
+ *
* @param subText The input text. When it was trimmed, {@code subTextStart} must be provided
* correctly.
* @param subTextStart The position that the input text got trimmed. For example, when the
* editor wants to trim out the first 10 chars, subTextStart should be 10.
*/
public void setInitialSurroundingSubText(@NonNull CharSequence subText, int subTextStart) {
- CharSequence newSubText = Editable.Factory.getInstance().newEditable(subText);
- Objects.requireNonNull(newSubText);
+ Objects.requireNonNull(subText);
// Swap selection start and end if necessary.
final int subTextSelStart = initialSelStart > initialSelEnd
@@ -577,7 +583,7 @@ public class EditorInfo implements InputType, Parcelable {
final int subTextSelEnd = initialSelStart > initialSelEnd
? initialSelStart - subTextStart : initialSelEnd - subTextStart;
- final int subTextLength = newSubText.length();
+ final int subTextLength = subText.length();
// Unknown or invalid selection.
if (subTextStart < 0 || subTextSelStart < 0 || subTextSelEnd > subTextLength) {
mInitialSurroundingText = new InitialSurroundingText();
@@ -591,12 +597,12 @@ public class EditorInfo implements InputType, Parcelable {
}
if (subTextLength <= MEMORY_EFFICIENT_TEXT_LENGTH) {
- mInitialSurroundingText = new InitialSurroundingText(newSubText, subTextSelStart,
+ mInitialSurroundingText = new InitialSurroundingText(subText, subTextSelStart,
subTextSelEnd);
return;
}
- trimLongSurroundingText(newSubText, subTextSelStart, subTextSelEnd);
+ trimLongSurroundingText(subText, subTextSelStart, subTextSelEnd);
}
/**
@@ -901,7 +907,9 @@ public class EditorInfo implements InputType, Parcelable {
InitialSurroundingText(@Nullable CharSequence surroundingText, int selectionHead,
int selectionEnd) {
- mSurroundingText = surroundingText;
+ // Copy the original text (without NoCopySpan) in case the original text is updated
+ // later.
+ mSurroundingText = copyWithParcelableSpans(surroundingText);
mSelectionHead = selectionHead;
mSelectionEnd = selectionEnd;
}
@@ -975,5 +983,30 @@ public class EditorInfo implements InputType, Parcelable {
return new InitialSurroundingText[size];
}
};
+
+ /**
+ * Create a copy of the given {@link CharSequence} object, with completely copy
+ * {@link ParcelableSpan} instances.
+ *
+ * @param source the original {@link CharSequence} to be copied.
+ * @return the copied {@link CharSequence}. {@code null} if {@code source} is {@code null}.
+ */
+ @Nullable
+ private static CharSequence copyWithParcelableSpans(@Nullable CharSequence source) {
+ if (source == null) {
+ return null;
+ }
+ Parcel parcel = null;
+ try {
+ parcel = Parcel.obtain();
+ TextUtils.writeToParcel(source, parcel, /* parcelableFlags= */ 0);
+ parcel.setDataPosition(0);
+ return TextUtils.CHAR_SEQUENCE_CREATOR.createFromParcel(parcel);
+ } finally {
+ if (parcel != null) {
+ parcel.recycle();
+ }
+ }
+ }
}
}
diff --git a/core/java/android/view/inputmethod/InputMethodManager.java b/core/java/android/view/inputmethod/InputMethodManager.java
index f6671d86cf7b..a3c95a916669 100644
--- a/core/java/android/view/inputmethod/InputMethodManager.java
+++ b/core/java/android/view/inputmethod/InputMethodManager.java
@@ -457,9 +457,7 @@ public final class InputMethodManager {
private int mRequestUpdateCursorAnchorInfoMonitorMode = REQUEST_UPDATE_CURSOR_ANCHOR_INFO_NONE;
/**
- * When {@link ViewRootImpl#sNewInsetsMode} is set to
- * >= {@link ViewRootImpl#NEW_INSETS_MODE_IME}, {@link ImeInsetsSourceConsumer} applies the
- * IME visibility and listens for other state changes.
+ * Applies the IME visibility and listens for other state changes.
*/
private ImeInsetsSourceConsumer mImeInsetsConsumer;
diff --git a/core/java/android/window/WindowContainerTransaction.java b/core/java/android/window/WindowContainerTransaction.java
index 8be37e9e492d..ba901549f2b5 100644
--- a/core/java/android/window/WindowContainerTransaction.java
+++ b/core/java/android/window/WindowContainerTransaction.java
@@ -238,6 +238,22 @@ public final class WindowContainerTransaction implements Parcelable {
}
/**
+ * Sets whether a container should ignore the orientation request from apps below it. It
+ * currently only applies to {@link com.android.server.wm.TaskDisplayArea}. When {@code false},
+ * it may rotate based on the orientation request; When {@code true}, it can never specify
+ * orientation, but shows the fixed-orientation apps in the letterbox.
+ * @hide
+ */
+ @NonNull
+ public WindowContainerTransaction setIgnoreOrientationRequest(
+ @NonNull WindowContainerToken container, boolean ignoreOrientationRequest) {
+ Change chg = getOrCreateChange(container.asBinder());
+ chg.mIgnoreOrientationRequest = ignoreOrientationRequest;
+ chg.mChangeMask |= Change.CHANGE_IGNORE_ORIENTATION_REQUEST;
+ return this;
+ }
+
+ /**
* Reparents a container into another one. The effect of a {@code null} parent can vary. For
* example, reparenting a stack to {@code null} will reparent it to its display.
*
@@ -341,10 +357,12 @@ public final class WindowContainerTransaction implements Parcelable {
public static final int CHANGE_PIP_CALLBACK = 1 << 2;
public static final int CHANGE_HIDDEN = 1 << 3;
public static final int CHANGE_BOUNDS_TRANSACTION_RECT = 1 << 4;
+ public static final int CHANGE_IGNORE_ORIENTATION_REQUEST = 1 << 5;
private final Configuration mConfiguration = new Configuration();
private boolean mFocusable = true;
private boolean mHidden = false;
+ private boolean mIgnoreOrientationRequest = false;
private int mChangeMask = 0;
private @ActivityInfo.Config int mConfigSetMask = 0;
private @WindowConfiguration.WindowConfig int mWindowSetMask = 0;
@@ -362,6 +380,7 @@ public final class WindowContainerTransaction implements Parcelable {
mConfiguration.readFromParcel(in);
mFocusable = in.readBoolean();
mHidden = in.readBoolean();
+ mIgnoreOrientationRequest = in.readBoolean();
mChangeMask = in.readInt();
mConfigSetMask = in.readInt();
mWindowSetMask = in.readInt();
@@ -404,6 +423,9 @@ public final class WindowContainerTransaction implements Parcelable {
if ((other.mChangeMask & CHANGE_HIDDEN) != 0) {
mHidden = other.mHidden;
}
+ if ((other.mChangeMask & CHANGE_IGNORE_ORIENTATION_REQUEST) != 0) {
+ mIgnoreOrientationRequest = other.mIgnoreOrientationRequest;
+ }
mChangeMask |= other.mChangeMask;
if (other.mActivityWindowingMode >= 0) {
mActivityWindowingMode = other.mActivityWindowingMode;
@@ -445,6 +467,15 @@ public final class WindowContainerTransaction implements Parcelable {
return mHidden;
}
+ /** Gets the requested state of whether to ignore orientation request. */
+ public boolean getIgnoreOrientationRequest() {
+ if ((mChangeMask & CHANGE_IGNORE_ORIENTATION_REQUEST) == 0) {
+ throw new RuntimeException("IgnoreOrientationRequest not set. "
+ + "Check CHANGE_IGNORE_ORIENTATION_REQUEST first");
+ }
+ return mIgnoreOrientationRequest;
+ }
+
public int getChangeMask() {
return mChangeMask;
}
@@ -509,6 +540,9 @@ public final class WindowContainerTransaction implements Parcelable {
if (mBoundsChangeTransaction != null) {
sb.append("hasBoundsTransaction,");
}
+ if ((mChangeMask & CHANGE_IGNORE_ORIENTATION_REQUEST) != 0) {
+ sb.append("ignoreOrientationRequest:" + mIgnoreOrientationRequest + ",");
+ }
sb.append("}");
return sb.toString();
}
@@ -518,6 +552,7 @@ public final class WindowContainerTransaction implements Parcelable {
mConfiguration.writeToParcel(dest, flags);
dest.writeBoolean(mFocusable);
dest.writeBoolean(mHidden);
+ dest.writeBoolean(mIgnoreOrientationRequest);
dest.writeInt(mChangeMask);
dest.writeInt(mConfigSetMask);
dest.writeInt(mWindowSetMask);
diff --git a/core/java/android/window/WindowMetricsHelper.java b/core/java/android/window/WindowMetricsHelper.java
index fb8b27e52be1..170ff6fb1a10 100644
--- a/core/java/android/window/WindowMetricsHelper.java
+++ b/core/java/android/window/WindowMetricsHelper.java
@@ -16,17 +16,13 @@
package android.window;
-import static android.view.ViewRootImpl.NEW_INSETS_MODE_FULL;
import static android.view.WindowInsets.Type.displayCutout;
import static android.view.WindowInsets.Type.navigationBars;
import android.annotation.NonNull;
-import android.graphics.Insets;
import android.graphics.Point;
import android.graphics.Rect;
import android.view.Display;
-import android.view.DisplayCutout;
-import android.view.ViewRootImpl;
import android.view.WindowInsets;
import android.view.WindowMetrics;
@@ -46,19 +42,8 @@ public final class WindowMetricsHelper {
public static Rect getBoundsExcludingNavigationBarAndCutout(
@NonNull WindowMetrics windowMetrics) {
final WindowInsets windowInsets = windowMetrics.getWindowInsets();
- Insets insets;
- if (ViewRootImpl.sNewInsetsMode == NEW_INSETS_MODE_FULL) {
- insets = windowInsets.getInsetsIgnoringVisibility(navigationBars() | displayCutout());
- } else {
- final Insets stableInsets = windowInsets.getStableInsets();
- insets = Insets.of(stableInsets.left, 0 /* top */, stableInsets.right,
- stableInsets.bottom);
- final DisplayCutout cutout = windowInsets.getDisplayCutout();
- insets = (cutout != null) ? Insets.max(insets, Insets.of(cutout.getSafeInsets()))
- : insets;
- }
final Rect result = new Rect(windowMetrics.getBounds());
- result.inset(insets);
+ result.inset(windowInsets.getInsetsIgnoringVisibility(navigationBars() | displayCutout()));
return result;
}
}
diff --git a/core/java/com/android/internal/BrightnessSynchronizer.java b/core/java/com/android/internal/BrightnessSynchronizer.java
index f08d0ef8c052..6b8cf6361e91 100644
--- a/core/java/com/android/internal/BrightnessSynchronizer.java
+++ b/core/java/com/android/internal/BrightnessSynchronizer.java
@@ -36,7 +36,7 @@ import java.util.Queue;
* (new) system for storing the brightness. It has methods to convert between the two and also
* observes for when one of the settings is changed and syncs this with the other.
*/
-public class BrightnessSynchronizer{
+public class BrightnessSynchronizer {
private static final int MSG_UPDATE_FLOAT = 1;
private static final int MSG_UPDATE_INT = 2;
@@ -78,6 +78,26 @@ public class BrightnessSynchronizer{
mContext = context;
mBrightnessSyncObserver = new BrightnessSyncObserver(mHandler);
mBrightnessSyncObserver.startObserving();
+
+ // It is possible for the system to start up with the int and float values not
+ // synchronized. So we force an update to the int value, since float is the source
+ // of truth. Fallback to int value, if float is invalid. If both are invalid, use default
+ // float value from config.
+ final float currentFloatBrightness = getScreenBrightnessFloat(context);
+ final int currentIntBrightness = getScreenBrightnessInt(context);
+
+ if (!Float.isNaN(currentFloatBrightness)) {
+ updateBrightnessIntFromFloat(currentFloatBrightness);
+ } else if (currentIntBrightness != -1) {
+ updateBrightnessFloatFromInt(currentIntBrightness);
+ } else {
+ final float defaultBrightness = mContext.getResources().getFloat(
+ com.android.internal.R.dimen.config_screenBrightnessSettingDefaultFloat);
+ Settings.System.putFloatForUser(mContext.getContentResolver(),
+ Settings.System.SCREEN_BRIGHTNESS_FLOAT, defaultBrightness,
+ UserHandle.USER_CURRENT);
+
+ }
}
/**
@@ -132,7 +152,8 @@ public class BrightnessSynchronizer{
private static int getScreenBrightnessInt(Context context) {
return Settings.System.getIntForUser(context.getContentResolver(),
- Settings.System.SCREEN_BRIGHTNESS, 0, UserHandle.USER_CURRENT);
+ Settings.System.SCREEN_BRIGHTNESS, PowerManager.BRIGHTNESS_INVALID,
+ UserHandle.USER_CURRENT);
}
private float mPreferredSettingValue;
diff --git a/core/java/com/android/internal/app/ISoundTriggerService.aidl b/core/java/com/android/internal/app/ISoundTriggerService.aidl
index 74bfb963c6b0..f8aac42b8018 100644
--- a/core/java/com/android/internal/app/ISoundTriggerService.aidl
+++ b/core/java/com/android/internal/app/ISoundTriggerService.aidl
@@ -16,53 +16,45 @@
package com.android.internal.app;
-import android.app.PendingIntent;
-import android.content.ComponentName;
-import android.hardware.soundtrigger.IRecognitionStatusCallback;
-import android.hardware.soundtrigger.SoundTrigger;
-import android.hardware.soundtrigger.ModelParams;
-import android.os.Bundle;
-import android.os.ParcelUuid;
+import android.media.permission.Identity;
+import com.android.internal.app.ISoundTriggerSession;
/**
* Service interface for a generic sound recognition model.
+ *
+ * This interface serves as an entry point to establish a session, associated with a client
+ * identity, which exposes the actual functionality.
+ *
* @hide
*/
interface ISoundTriggerService {
-
- SoundTrigger.GenericSoundModel getSoundModel(in ParcelUuid soundModelId);
-
- void updateSoundModel(in SoundTrigger.GenericSoundModel soundModel);
-
- void deleteSoundModel(in ParcelUuid soundModelId);
-
- int startRecognition(in ParcelUuid soundModelId, in IRecognitionStatusCallback callback,
- in SoundTrigger.RecognitionConfig config);
-
- int stopRecognition(in ParcelUuid soundModelId, in IRecognitionStatusCallback callback);
-
- int loadGenericSoundModel(in SoundTrigger.GenericSoundModel soundModel);
- int loadKeyphraseSoundModel(in SoundTrigger.KeyphraseSoundModel soundModel);
-
- int startRecognitionForService(in ParcelUuid soundModelId, in Bundle params,
- in ComponentName callbackIntent,in SoundTrigger.RecognitionConfig config);
-
- int stopRecognitionForService(in ParcelUuid soundModelId);
-
- int unloadSoundModel(in ParcelUuid soundModelId);
-
- /** For both ...Intent and ...Service based usage */
- boolean isRecognitionActive(in ParcelUuid parcelUuid);
-
- int getModelState(in ParcelUuid soundModelId);
-
- @nullable SoundTrigger.ModuleProperties getModuleProperties();
-
- int setParameter(in ParcelUuid soundModelId, in ModelParams modelParam,
- int value);
-
- int getParameter(in ParcelUuid soundModelId, in ModelParams modelParam);
-
- @nullable SoundTrigger.ModelParamRange queryParameter(in ParcelUuid soundModelId,
- in ModelParams modelParam);
+ /**
+ * Creates a new session.
+ *
+ * This version is intended to be used when the caller itself is the originator of the
+ * operations, for authorization purposes.
+ *
+ * The pid/uid fields are ignored and will be replaced by those provided by binder.
+ *
+ * It is good practice to clear the binder calling identity prior to calling this, in case the
+ * caller is ever in the same process as the callee.
+ */
+ ISoundTriggerSession attachAsOriginator(in Identity originatorIdentity);
+
+ /**
+ * Creates a new session.
+ *
+ * This version is intended to be used when the caller is acting on behalf of a separate entity
+ * (the originator) and the sessions operations are to be accounted against that originator for
+ * authorization purposes.
+ *
+ * The caller must hold the SOUNDTRIGGER_DELEGATE_IDENTITY permission in order to be trusted to
+ * provide a reliable originator identity. It should follow the best practices for reliably and
+ * securely verifying the identity of the originator.
+ *
+ * It is good practice to clear the binder calling identity prior to calling this, in case the
+ * caller is ever in the same process as the callee.
+ */
+ ISoundTriggerSession attachAsMiddleman(in Identity middlemanIdentity,
+ in Identity originatorIdentity);
}
diff --git a/core/java/com/android/internal/app/ISoundTriggerSession.aidl b/core/java/com/android/internal/app/ISoundTriggerSession.aidl
new file mode 100644
index 000000000000..ec7d282522c8
--- /dev/null
+++ b/core/java/com/android/internal/app/ISoundTriggerSession.aidl
@@ -0,0 +1,68 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.internal.app;
+
+import android.app.PendingIntent;
+import android.content.ComponentName;
+import android.hardware.soundtrigger.IRecognitionStatusCallback;
+import android.hardware.soundtrigger.SoundTrigger;
+import android.hardware.soundtrigger.ModelParams;
+import android.os.Bundle;
+import android.os.ParcelUuid;
+
+/**
+ * Service interface for a generic sound recognition model.
+ * @hide
+ */
+interface ISoundTriggerSession {
+
+ SoundTrigger.GenericSoundModel getSoundModel(in ParcelUuid soundModelId);
+
+ void updateSoundModel(in SoundTrigger.GenericSoundModel soundModel);
+
+ void deleteSoundModel(in ParcelUuid soundModelId);
+
+ int startRecognition(in ParcelUuid soundModelId, in IRecognitionStatusCallback callback,
+ in SoundTrigger.RecognitionConfig config);
+
+ int stopRecognition(in ParcelUuid soundModelId, in IRecognitionStatusCallback callback);
+
+ int loadGenericSoundModel(in SoundTrigger.GenericSoundModel soundModel);
+ int loadKeyphraseSoundModel(in SoundTrigger.KeyphraseSoundModel soundModel);
+
+ int startRecognitionForService(in ParcelUuid soundModelId, in Bundle params,
+ in ComponentName callbackIntent,in SoundTrigger.RecognitionConfig config);
+
+ int stopRecognitionForService(in ParcelUuid soundModelId);
+
+ int unloadSoundModel(in ParcelUuid soundModelId);
+
+ /** For both ...Intent and ...Service based usage */
+ boolean isRecognitionActive(in ParcelUuid parcelUuid);
+
+ int getModelState(in ParcelUuid soundModelId);
+
+ @nullable SoundTrigger.ModuleProperties getModuleProperties();
+
+ int setParameter(in ParcelUuid soundModelId, in ModelParams modelParam,
+ int value);
+
+ int getParameter(in ParcelUuid soundModelId, in ModelParams modelParam);
+
+ @nullable SoundTrigger.ModelParamRange queryParameter(in ParcelUuid soundModelId,
+ in ModelParams modelParam);
+}
diff --git a/core/java/com/android/internal/app/IVoiceInteractionManagerService.aidl b/core/java/com/android/internal/app/IVoiceInteractionManagerService.aidl
index 15ba8e8c11f7..49b4cd1e6a73 100644
--- a/core/java/com/android/internal/app/IVoiceInteractionManagerService.aidl
+++ b/core/java/com/android/internal/app/IVoiceInteractionManagerService.aidl
@@ -18,6 +18,7 @@ package com.android.internal.app;
import android.content.ComponentName;
import android.content.Intent;
+import android.media.permission.Identity;
import android.os.Bundle;
import android.os.RemoteCallback;
@@ -25,9 +26,8 @@ import com.android.internal.app.IVoiceActionCheckCallback;
import com.android.internal.app.IVoiceInteractionSessionShowCallback;
import com.android.internal.app.IVoiceInteractor;
import com.android.internal.app.IVoiceInteractionSessionListener;
-import android.hardware.soundtrigger.IRecognitionStatusCallback;
+import com.android.internal.app.IVoiceInteractionSoundTriggerSession;
import android.hardware.soundtrigger.KeyphraseMetadata;
-import android.hardware.soundtrigger.ModelParams;
import android.hardware.soundtrigger.SoundTrigger;
import android.service.voice.IVoiceInteractionService;
import android.service.voice.IVoiceInteractionSession;
@@ -86,13 +86,6 @@ interface IVoiceInteractionManagerService {
* @RequiresPermission Manifest.permission.MANAGE_VOICE_KEYPHRASES
*/
int deleteKeyphraseSoundModel(int keyphraseId, in String bcp47Locale);
-
- /**
- * Gets the properties of the DSP hardware on this device, null if not present.
- * Caller must be the active voice interaction service via
- * {@link Settings.Secure.VOICE_INTERACTION_SERVICE}.
- */
- SoundTrigger.ModuleProperties getDspModuleProperties();
/**
* Indicates if there's a keyphrase sound model available for the given keyphrase ID and the
* user ID of the caller.
@@ -116,65 +109,6 @@ interface IVoiceInteractionManagerService {
*/
KeyphraseMetadata getEnrolledKeyphraseMetadata(String keyphrase, String bcp47Locale);
/**
- * Starts a recognition for the given keyphrase.
- * Caller must be the active voice interaction service via
- * {@link Settings.Secure.VOICE_INTERACTION_SERVICE}.
- */
- int startRecognition(int keyphraseId, in String bcp47Locale,
- in IRecognitionStatusCallback callback,
- in SoundTrigger.RecognitionConfig recognitionConfig);
- /**
- * Stops a recognition for the given keyphrase.
- * Caller must be the active voice interaction service via
- * {@link Settings.Secure.VOICE_INTERACTION_SERVICE}.
- */
- int stopRecognition(int keyphraseId, in IRecognitionStatusCallback callback);
- /**
- * Set a model specific ModelParams with the given value. This
- * parameter will keep its value for the duration the model is loaded regardless of starting and
- * stopping recognition. Once the model is unloaded, the value will be lost.
- * queryParameter should be checked first before calling this method.
- * Caller must be the active voice interaction service via
- * {@link Settings.Secure.VOICE_INTERACTION_SERVICE}.
- *
- * @param keyphraseId The unique identifier for the keyphrase.
- * @param modelParam ModelParams
- * @param value Value to set
- * @return - {@link SoundTrigger#STATUS_OK} in case of success
- * - {@link SoundTrigger#STATUS_NO_INIT} if the native service cannot be reached
- * - {@link SoundTrigger#STATUS_BAD_VALUE} invalid input parameter
- * - {@link SoundTrigger#STATUS_INVALID_OPERATION} if the call is out of sequence or
- * if API is not supported by HAL
- */
- int setParameter(int keyphraseId, in ModelParams modelParam, int value);
- /**
- * Get a model specific ModelParams. This parameter will keep its value
- * for the duration the model is loaded regardless of starting and stopping recognition.
- * Once the model is unloaded, the value will be lost. If the value is not set, a default
- * value is returned. See ModelParams for parameter default values.
- * queryParameter should be checked first before calling this method.
- * Caller must be the active voice interaction service via
- * {@link Settings.Secure.VOICE_INTERACTION_SERVICE}.
- *
- * @param keyphraseId The unique identifier for the keyphrase.
- * @param modelParam ModelParams
- * @return value of parameter
- */
- int getParameter(int keyphraseId, in ModelParams modelParam);
- /**
- * Determine if parameter control is supported for the given model handle.
- * This method should be checked prior to calling setParameter or getParameter.
- * Caller must be the active voice interaction service via
- * {@link Settings.Secure.VOICE_INTERACTION_SERVICE}.
- *
- * @param keyphraseId The unique identifier for the keyphrase.
- * @param modelParam ModelParams
- * @return supported range of parameter, null if not supported
- */
- @nullable SoundTrigger.ModelParamRange queryParameter(int keyphraseId,
- in ModelParams modelParam);
-
- /**
* @return the component name for the currently active voice interaction service
* @RequiresPermission Manifest.permission.ACCESS_VOICE_INTERACTION_SERVICE
*/
@@ -277,4 +211,12 @@ interface IVoiceInteractionManagerService {
*/
void setDisabled(boolean disabled);
+ /**
+ * Creates a session, allowing controlling running sound models on detection hardware.
+ * Caller must provide an identity, used for permission tracking purposes.
+ * The uid/pid elements of the identity will be ignored by the server and replaced with the ones
+ * provided by binder.
+ */
+ IVoiceInteractionSoundTriggerSession createSoundTriggerSessionAsOriginator(
+ in Identity originatorIdentity);
}
diff --git a/core/java/com/android/internal/app/IVoiceInteractionSoundTriggerSession.aidl b/core/java/com/android/internal/app/IVoiceInteractionSoundTriggerSession.aidl
new file mode 100644
index 000000000000..33aab4132150
--- /dev/null
+++ b/core/java/com/android/internal/app/IVoiceInteractionSoundTriggerSession.aidl
@@ -0,0 +1,95 @@
+/*
+ * 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.internal.app;
+
+import android.hardware.soundtrigger.IRecognitionStatusCallback;
+import android.hardware.soundtrigger.ModelParams;
+import android.hardware.soundtrigger.SoundTrigger;
+
+/**
+ * This interface allows performing sound-trigger related operations with the actual sound trigger
+ * hardware.
+ *
+ * Every instance of this interface is associated with a client identity ("originator"), established
+ * upon the creation of the instance and used for permission accounting.
+ */
+interface IVoiceInteractionSoundTriggerSession {
+ /**
+ * Gets the properties of the DSP hardware on this device, null if not present.
+ * Caller must be the active voice interaction service via
+ * {@link Settings.Secure.VOICE_INTERACTION_SERVICE}.
+ */
+ SoundTrigger.ModuleProperties getDspModuleProperties();
+ /**
+ * Starts a recognition for the given keyphrase.
+ * Caller must be the active voice interaction service via
+ * {@link Settings.Secure.VOICE_INTERACTION_SERVICE}.
+ */
+ int startRecognition(int keyphraseId, in String bcp47Locale,
+ in IRecognitionStatusCallback callback,
+ in SoundTrigger.RecognitionConfig recognitionConfig);
+ /**
+ * Stops a recognition for the given keyphrase.
+ * Caller must be the active voice interaction service via
+ * {@link Settings.Secure.VOICE_INTERACTION_SERVICE}.
+ */
+ int stopRecognition(int keyphraseId, in IRecognitionStatusCallback callback);
+ /**
+ * Set a model specific ModelParams with the given value. This
+ * parameter will keep its value for the duration the model is loaded regardless of starting and
+ * stopping recognition. Once the model is unloaded, the value will be lost.
+ * queryParameter should be checked first before calling this method.
+ * Caller must be the active voice interaction service via
+ * {@link Settings.Secure.VOICE_INTERACTION_SERVICE}.
+ *
+ * @param keyphraseId The unique identifier for the keyphrase.
+ * @param modelParam ModelParams
+ * @param value Value to set
+ * @return - {@link SoundTrigger#STATUS_OK} in case of success
+ * - {@link SoundTrigger#STATUS_NO_INIT} if the native service cannot be reached
+ * - {@link SoundTrigger#STATUS_BAD_VALUE} invalid input parameter
+ * - {@link SoundTrigger#STATUS_INVALID_OPERATION} if the call is out of sequence or
+ * if API is not supported by HAL
+ */
+ int setParameter(int keyphraseId, in ModelParams modelParam, int value);
+ /**
+ * Get a model specific ModelParams. This parameter will keep its value
+ * for the duration the model is loaded regardless of starting and stopping recognition.
+ * Once the model is unloaded, the value will be lost. If the value is not set, a default
+ * value is returned. See ModelParams for parameter default values.
+ * queryParameter should be checked first before calling this method.
+ * Caller must be the active voice interaction service via
+ * {@link Settings.Secure.VOICE_INTERACTION_SERVICE}.
+ *
+ * @param keyphraseId The unique identifier for the keyphrase.
+ * @param modelParam ModelParams
+ * @return value of parameter
+ */
+ int getParameter(int keyphraseId, in ModelParams modelParam);
+ /**
+ * Determine if parameter control is supported for the given model handle.
+ * This method should be checked prior to calling setParameter or getParameter.
+ * Caller must be the active voice interaction service via
+ * {@link Settings.Secure.VOICE_INTERACTION_SERVICE}.
+ *
+ * @param keyphraseId The unique identifier for the keyphrase.
+ * @param modelParam ModelParams
+ * @return supported range of parameter, null if not supported
+ */
+ @nullable SoundTrigger.ModelParamRange queryParameter(int keyphraseId,
+ in ModelParams modelParam);
+}
diff --git a/core/java/com/android/internal/config/sysui/SystemUiDeviceConfigFlags.java b/core/java/com/android/internal/config/sysui/SystemUiDeviceConfigFlags.java
index 6314fcbde333..1ce246ad4274 100644
--- a/core/java/com/android/internal/config/sysui/SystemUiDeviceConfigFlags.java
+++ b/core/java/com/android/internal/config/sysui/SystemUiDeviceConfigFlags.java
@@ -419,6 +419,17 @@ public final class SystemUiDeviceConfigFlags {
*/
public static final String SCREENSHOT_KEYCHORD_DELAY = "screenshot_keychord_delay";
+ /**
+ * (boolean) Whether to use an ML model for the Back Gesture.
+ */
+ public static final String USE_BACK_GESTURE_ML_MODEL = "use_back_gesture_ml_model";
+
+ /**
+ * (float) Threshold for Back Gesture ML model prediction.
+ */
+ public static final String BACK_GESTURE_ML_MODEL_THRESHOLD = "back_gesture_ml_model_threshold";
+
+
private SystemUiDeviceConfigFlags() {
}
}
diff --git a/core/java/com/android/internal/infra/AbstractRemoteService.java b/core/java/com/android/internal/infra/AbstractRemoteService.java
index b2852eaeef2a..722e5c102fcf 100644
--- a/core/java/com/android/internal/infra/AbstractRemoteService.java
+++ b/core/java/com/android/internal/infra/AbstractRemoteService.java
@@ -225,6 +225,7 @@ public abstract class AbstractRemoteService<S extends AbstractRemoteService<S, I
if (mService != null) {
mService.asBinder().unlinkToDeath(this, 0);
}
+ mBinding = false;
mService = null;
mServiceDied = true;
cancelScheduledUnbind();
diff --git a/core/java/com/android/internal/jank/FrameTracker.java b/core/java/com/android/internal/jank/FrameTracker.java
index dd1978ebb026..85dc2aea4b1c 100644
--- a/core/java/com/android/internal/jank/FrameTracker.java
+++ b/core/java/com/android/internal/jank/FrameTracker.java
@@ -24,11 +24,11 @@ import android.util.Log;
import android.view.FrameMetrics;
import android.view.ThreadedRenderer;
-import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.jank.InteractionJankMonitor.Session;
import com.android.internal.util.FrameworkStatsLog;
/**
+ * A class that allows the app to get the frame metrics from HardwareRendererObserver.
* @hide
*/
public class FrameTracker implements HardwareRendererObserver.OnFrameMetricsAvailableListener {
@@ -45,28 +45,21 @@ public class FrameTracker implements HardwareRendererObserver.OnFrameMetricsAvai
private long mBeginTime = UNKNOWN_TIMESTAMP;
private long mEndTime = UNKNOWN_TIMESTAMP;
private boolean mShouldTriggerTrace;
+ private boolean mSessionEnd;
private int mTotalFramesCount = 0;
private int mMissedFramesCount = 0;
private long mMaxFrameTimeNanos = 0;
private Session mSession;
- public FrameTracker(@NonNull Session session,
- @NonNull Handler handler, @NonNull ThreadedRenderer renderer) {
- mSession = session;
- mRendererWrapper = new ThreadedRendererWrapper(renderer);
- mMetricsWrapper = new FrameMetricsWrapper();
- mObserver = new HardwareRendererObserver(this, mMetricsWrapper.getTiming(), handler);
- }
-
/**
- * This constructor is only for unit tests.
+ * Constructor of FrameTracker.
* @param session a trace session.
- * @param renderer a test double for ThreadedRenderer
- * @param metrics a test double for FrameMetrics
+ * @param handler a handler for handling callbacks.
+ * @param renderer a ThreadedRendererWrapper instance.
+ * @param metrics a FrameMetricsWrapper instance.
*/
- @VisibleForTesting
- public FrameTracker(@NonNull Session session, Handler handler,
+ public FrameTracker(@NonNull Session session, @NonNull Handler handler,
@NonNull ThreadedRendererWrapper renderer, @NonNull FrameMetricsWrapper metrics) {
mSession = session;
mRendererWrapper = renderer;
@@ -77,15 +70,11 @@ public class FrameTracker implements HardwareRendererObserver.OnFrameMetricsAvai
/**
* Begin a trace session of the CUJ.
*/
- public void begin() {
+ public synchronized void begin() {
long timestamp = System.nanoTime();
if (DEBUG) {
Log.d(TAG, "begin: time(ns)=" + timestamp + ", begin(ns)=" + mBeginTime
- + ", end(ns)=" + mEndTime + ", session=" + mSession);
- }
- if (mBeginTime != UNKNOWN_TIMESTAMP && mEndTime == UNKNOWN_TIMESTAMP) {
- // We have an ongoing tracing already, skip subsequent calls.
- return;
+ + ", end(ns)=" + mEndTime + ", session=" + mSession.getName());
}
mBeginTime = timestamp;
mEndTime = UNKNOWN_TIMESTAMP;
@@ -96,32 +85,48 @@ public class FrameTracker implements HardwareRendererObserver.OnFrameMetricsAvai
/**
* End the trace session of the CUJ.
*/
- public void end() {
+ public synchronized void end() {
long timestamp = System.nanoTime();
if (DEBUG) {
Log.d(TAG, "end: time(ns)=" + timestamp + ", begin(ns)=" + mBeginTime
- + ", end(ns)=" + mEndTime + ", session=" + mSession);
- }
- if (mBeginTime == UNKNOWN_TIMESTAMP || mEndTime != UNKNOWN_TIMESTAMP) {
- // We haven't started a trace yet.
- return;
+ + ", end(ns)=" + mEndTime + ", session=" + mSession.getName());
}
mEndTime = timestamp;
Trace.endAsyncSection(mSession.getName(), (int) mBeginTime);
+ // We don't remove observer here,
+ // will remove it when all the frame metrics in this duration are called back.
+ // See onFrameMetricsAvailable for the logic of removing the observer.
+ }
+
+ /**
+ * Cancel the trace session of the CUJ.
+ */
+ public synchronized void cancel() {
+ if (mBeginTime == UNKNOWN_TIMESTAMP || mEndTime != UNKNOWN_TIMESTAMP) return;
+ if (DEBUG) {
+ Log.d(TAG, "cancel: time(ns)=" + System.nanoTime() + ", begin(ns)=" + mBeginTime
+ + ", end(ns)=" + mEndTime + ", session=" + mSession.getName());
+ }
+ Trace.endAsyncSection(mSession.getName(), (int) mBeginTime);
+ mRendererWrapper.removeObserver(mObserver);
+ mBeginTime = UNKNOWN_TIMESTAMP;
+ mEndTime = UNKNOWN_TIMESTAMP;
+ mShouldTriggerTrace = false;
}
@Override
- public void onFrameMetricsAvailable(int dropCountSinceLastInvocation) {
+ public synchronized void onFrameMetricsAvailable(int dropCountSinceLastInvocation) {
// Since this callback might come a little bit late after the end() call.
// We should keep tracking the begin / end timestamp.
// Then compare with vsync timestamp to check if the frame is in the duration of the CUJ.
- if (mBeginTime == UNKNOWN_TIMESTAMP) return; // We haven't started tracing yet.
long vsyncTimestamp = mMetricsWrapper.getMetric(FrameMetrics.VSYNC_TIMESTAMP);
- if (vsyncTimestamp < mBeginTime) return; // The tracing has been started.
+ // Discard the frame metrics which is not in the trace session.
+ if (vsyncTimestamp < mBeginTime) return;
- // If the end time has not been set, we are still in the tracing.
- if (mEndTime != UNKNOWN_TIMESTAMP && vsyncTimestamp > mEndTime) {
+ // We stop getting callback when the vsync is later than the end calls.
+ if (mEndTime != UNKNOWN_TIMESTAMP && vsyncTimestamp > mEndTime && !mSessionEnd) {
+ mSessionEnd = true;
// The tracing has been ended, remove the observer, see if need to trigger perfetto.
mRendererWrapper.removeObserver(mObserver);
@@ -170,9 +175,8 @@ public class FrameTracker implements HardwareRendererObserver.OnFrameMetricsAvai
/**
* Trigger the prefetto daemon.
*/
- @VisibleForTesting
public void triggerPerfetto() {
- InteractionJankMonitor.trigger();
+ InteractionJankMonitor.getInstance().trigger();
}
/**
diff --git a/core/java/com/android/internal/jank/InteractionJankMonitor.java b/core/java/com/android/internal/jank/InteractionJankMonitor.java
index 5a0cbf9e93e4..19dc2ed6daea 100644
--- a/core/java/com/android/internal/jank/InteractionJankMonitor.java
+++ b/core/java/com/android/internal/jank/InteractionJankMonitor.java
@@ -21,15 +21,17 @@ import static com.android.internal.util.FrameworkStatsLog.UIINTERACTION_FRAME_IN
import android.annotation.IntDef;
import android.annotation.NonNull;
import android.os.HandlerThread;
-import android.view.ThreadedRenderer;
+import android.util.Log;
+import android.util.SparseArray;
import android.view.View;
import com.android.internal.annotations.VisibleForTesting;
+import com.android.internal.jank.FrameTracker.FrameMetricsWrapper;
+import com.android.internal.jank.FrameTracker.ThreadedRendererWrapper;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
-import java.util.HashMap;
-import java.util.Map;
+import java.util.concurrent.TimeUnit;
/**
* This class let users to begin and end the always on tracing mechanism.
@@ -38,11 +40,17 @@ import java.util.Map;
public class InteractionJankMonitor {
private static final String TAG = InteractionJankMonitor.class.getSimpleName();
private static final boolean DEBUG = false;
- private static final Object LOCK = new Object();
+ private static final String DEFAULT_WORKER_NAME = TAG + "-Worker";
+ private static final long DEFAULT_TIMEOUT_MS = TimeUnit.SECONDS.toMillis(5L);
// Every value must have a corresponding entry in CUJ_STATSD_INTERACTION_TYPE.
- public static final int CUJ_NOTIFICATION_SHADE_MOTION = 0;
- public static final int CUJ_NOTIFICATION_SHADE_GESTURE = 1;
+ public static final int CUJ_NOTIFICATION_SHADE_EXPAND_COLLAPSE = 1;
+ public static final int CUJ_NOTIFICATION_SHADE_EXPAND_COLLAPSE_LOCK = 0;
+ public static final int CUJ_NOTIFICATION_SHADE_SCROLL_FLING = 0;
+ public static final int CUJ_NOTIFICATION_SHADE_ROW_EXPAND = 0;
+ public static final int CUJ_NOTIFICATION_SHADE_ROW_SWIPE = 0;
+ public static final int CUJ_NOTIFICATION_SHADE_QS_EXPAND_COLLAPSE = 0;
+ public static final int CUJ_NOTIFICATION_SHADE_QS_SCROLL_SWIPE = 0;
private static final int NO_STATSD_LOGGING = -1;
@@ -53,141 +61,255 @@ public class InteractionJankMonitor {
UIINTERACTION_FRAME_INFO_REPORTED__INTERACTION_TYPE__NOTIFICATION_SHADE_SWIPE,
};
- private static ThreadedRenderer sRenderer;
- private static Map<String, FrameTracker> sRunningTracker;
- private static HandlerThread sWorker;
- private static boolean sInitialized;
+ private static volatile InteractionJankMonitor sInstance;
+
+ private ThreadedRendererWrapper mRenderer;
+ private FrameMetricsWrapper mMetrics;
+ private SparseArray<FrameTracker> mRunningTrackers;
+ private SparseArray<Runnable> mTimeoutActions;
+ private HandlerThread mWorker;
+ private boolean mInitialized;
/** @hide */
@IntDef({
- CUJ_NOTIFICATION_SHADE_MOTION,
- CUJ_NOTIFICATION_SHADE_GESTURE
+ CUJ_NOTIFICATION_SHADE_EXPAND_COLLAPSE,
+ CUJ_NOTIFICATION_SHADE_EXPAND_COLLAPSE_LOCK,
+ CUJ_NOTIFICATION_SHADE_SCROLL_FLING,
+ CUJ_NOTIFICATION_SHADE_ROW_EXPAND,
+ CUJ_NOTIFICATION_SHADE_ROW_SWIPE,
+ CUJ_NOTIFICATION_SHADE_QS_EXPAND_COLLAPSE,
+ CUJ_NOTIFICATION_SHADE_QS_SCROLL_SWIPE
})
@Retention(RetentionPolicy.SOURCE)
public @interface CujType {}
/**
- * @param view Any view in the view tree to get context and ThreadedRenderer.
+ * Get the singleton of InteractionJankMonitor.
+ * @return instance of InteractionJankMonitor
*/
- public static void init(@NonNull View view) {
- init(view, null, null, null);
+ public static InteractionJankMonitor getInstance() {
+ // Use DCL here since this method might be invoked very often.
+ if (sInstance == null) {
+ synchronized (InteractionJankMonitor.class) {
+ if (sInstance == null) {
+ sInstance = new InteractionJankMonitor(new HandlerThread(DEFAULT_WORKER_NAME));
+ }
+ }
+ }
+ return sInstance;
}
/**
- * Should be only invoked internally or from unit tests.
+ * This constructor should be only public to tests.
+ * @param worker the worker thread for the callbacks
*/
@VisibleForTesting
- public static void init(@NonNull View view, @NonNull ThreadedRenderer renderer,
- @NonNull Map<String, FrameTracker> map, @NonNull HandlerThread worker) {
+ public InteractionJankMonitor(@NonNull HandlerThread worker) {
+ mRunningTrackers = new SparseArray<>();
+ mTimeoutActions = new SparseArray<>();
+ mWorker = worker;
+ }
+
+ /**
+ * Init InteractionJankMonitor for later instrumentation.
+ * @param view Any view in the view tree to get context and ThreadedRenderer.
+ * @return boolean true if the instance has been initialized successfully.
+ */
+ public boolean init(@NonNull View view) {
//TODO (163505250): This should be no-op if not in droid food rom.
- synchronized (LOCK) {
- if (!sInitialized) {
- if (!view.isAttachedToWindow()) {
- throw new IllegalStateException("View is not attached!");
+ if (!mInitialized) {
+ synchronized (this) {
+ if (!mInitialized) {
+ if (!view.isAttachedToWindow()) {
+ Log.d(TAG, "Expect an attached view!", new Throwable());
+ return false;
+ }
+ mRenderer = new ThreadedRendererWrapper(view.getThreadedRenderer());
+ mMetrics = new FrameMetricsWrapper();
+ mWorker.start();
+ mInitialized = true;
}
- sRenderer = renderer == null ? view.getThreadedRenderer() : renderer;
- sRunningTracker = map == null ? new HashMap<>() : map;
- sWorker = worker == null ? new HandlerThread("Aot-Worker") : worker;
- sWorker.start();
- sInitialized = true;
}
}
+ return true;
}
/**
- * Must invoke init() before invoking this method.
+ * Create a {@link FrameTracker} instance.
+ * @param session the session associates with this tracker
+ * @return instance of the FrameTracker
*/
- public static void begin(@NonNull @CujType int cujType) {
- begin(cujType, null);
+ @VisibleForTesting
+ public FrameTracker createFrameTracker(Session session) {
+ synchronized (this) {
+ if (!mInitialized) return null;
+ return new FrameTracker(session, mWorker.getThreadHandler(), mRenderer, mMetrics);
+ }
}
/**
- * Should be only invoked internally or from unit tests.
+ * Begin a trace session, must invoke {@link #init(View)} before invoking this method.
+ * @param cujType the specific {@link InteractionJankMonitor.CujType}.
+ * @return boolean true if the tracker is started successfully, false otherwise.
*/
- @VisibleForTesting
- public static void begin(@NonNull @CujType int cujType, FrameTracker tracker) {
+ public boolean begin(@CujType int cujType) {
//TODO (163505250): This should be no-op if not in droid food rom.
- //TODO (163510843): Remove synchronized, add @UiThread if only invoked from ui threads.
- synchronized (LOCK) {
- checkInitStateLocked();
- Session session = new Session(cujType);
- FrameTracker currentTracker = getTracker(session.getName());
- if (currentTracker != null) return;
- if (tracker == null) {
- tracker = new FrameTracker(session, sWorker.getThreadHandler(), sRenderer);
+ synchronized (this) {
+ return begin(cujType, 0L /* timeout */);
+ }
+ }
+
+ /**
+ * Begin a trace session, must invoke {@link #init(View)} before invoking this method.
+ * @param cujType the specific {@link InteractionJankMonitor.CujType}.
+ * @param timeout the elapsed time in ms until firing the timeout action.
+ * @return boolean true if the tracker is started successfully, false otherwise.
+ */
+ public boolean begin(@CujType int cujType, long timeout) {
+ //TODO (163505250): This should be no-op if not in droid food rom.
+ synchronized (this) {
+ if (!mInitialized) {
+ Log.d(TAG, "Not initialized!", new Throwable());
+ return false;
}
- sRunningTracker.put(session.getName(), tracker);
+ Session session = new Session(cujType);
+ FrameTracker tracker = getTracker(session);
+ // Skip subsequent calls if we already have an ongoing tracing.
+ if (tracker != null) return false;
+
+ // begin a new trace session.
+ tracker = createFrameTracker(session);
+ mRunningTrackers.put(cujType, tracker);
tracker.begin();
+
+ // Cancel the trace if we don't get an end() call in specified duration.
+ timeout = timeout > 0L ? timeout : DEFAULT_TIMEOUT_MS;
+ Runnable timeoutAction = () -> cancel(cujType);
+ mTimeoutActions.put(cujType, timeoutAction);
+ mWorker.getThreadHandler().postDelayed(timeoutAction, timeout);
+ return true;
}
}
/**
- * Must invoke init() before invoking this method.
+ * End a trace session, must invoke {@link #init(View)} before invoking this method.
+ * @param cujType the specific {@link InteractionJankMonitor.CujType}.
+ * @return boolean true if the tracker is ended successfully, false otherwise.
*/
- public static void end(@NonNull @CujType int cujType) {
+ public boolean end(@CujType int cujType) {
//TODO (163505250): This should be no-op if not in droid food rom.
- //TODO (163510843): Remove synchronized, add @UiThread if only invoked from ui threads.
- synchronized (LOCK) {
- checkInitStateLocked();
+ synchronized (this) {
+ if (!mInitialized) {
+ Log.d(TAG, "Not initialized!", new Throwable());
+ return false;
+ }
+ // remove the timeout action first.
+ Runnable timeout = mTimeoutActions.get(cujType);
+ if (timeout != null) {
+ mWorker.getThreadHandler().removeCallbacks(timeout);
+ mTimeoutActions.remove(cujType);
+ }
+
Session session = new Session(cujType);
- FrameTracker tracker = getTracker(session.getName());
- if (tracker != null) {
- tracker.end();
- sRunningTracker.remove(session.getName());
+ FrameTracker tracker = getTracker(session);
+ // Skip this call since we haven't started a trace yet.
+ if (tracker == null) return false;
+ tracker.end();
+ mRunningTrackers.remove(session.getCuj());
+ return true;
+ }
+ }
+
+ /**
+ * Cancel the trace session, must invoke {@link #init(View)} before invoking this method.
+ * @return boolean true if the tracker is cancelled successfully, false otherwise.
+ */
+ public boolean cancel(@CujType int cujType) {
+ //TODO (163505250): This should be no-op if not in droid food rom.
+ synchronized (this) {
+ if (!mInitialized) {
+ Log.d(TAG, "Not initialized!", new Throwable());
+ return false;
}
+ // remove the timeout action first.
+ Runnable timeout = mTimeoutActions.get(cujType);
+ if (timeout != null) {
+ mWorker.getThreadHandler().removeCallbacks(timeout);
+ mTimeoutActions.remove(cujType);
+ }
+
+ Session session = new Session(cujType);
+ FrameTracker tracker = getTracker(session);
+ // Skip this call since we haven't started a trace yet.
+ if (tracker == null) return false;
+ tracker.cancel();
+ mRunningTrackers.remove(session.getCuj());
+ return true;
}
}
- private static void checkInitStateLocked() {
- if (!sInitialized) {
- throw new IllegalStateException("InteractionJankMonitor not initialized!");
+ private void destroy() {
+ synchronized (this) {
+ int trackers = mRunningTrackers.size();
+ for (int i = 0; i < trackers; i++) {
+ mRunningTrackers.valueAt(i).cancel();
+ }
+ mRunningTrackers = null;
+ mTimeoutActions.clear();
+ mTimeoutActions = null;
+ mWorker.quit();
+ mWorker = null;
}
}
/**
- * Should be only invoked from unit tests.
+ * Abandon current instance.
*/
@VisibleForTesting
- public static void reset() {
- sInitialized = false;
- sRenderer = null;
- sRunningTracker = null;
- if (sWorker != null) {
- sWorker.quit();
- sWorker = null;
+ public static void abandon() {
+ if (sInstance == null) return;
+ synchronized (InteractionJankMonitor.class) {
+ if (sInstance == null) return;
+ sInstance.destroy();
+ sInstance = null;
}
}
- private static FrameTracker getTracker(String sessionName) {
- synchronized (LOCK) {
- return sRunningTracker.get(sessionName);
+ private FrameTracker getTracker(Session session) {
+ synchronized (this) {
+ if (!mInitialized) return null;
+ return mRunningTrackers.get(session.getCuj());
}
}
/**
* Trigger the perfetto daemon to collect and upload data.
*/
- public static void trigger() {
- sWorker.getThreadHandler().post(
- () -> PerfettoTrigger.trigger(PerfettoTrigger.TRIGGER_TYPE_JANK));
+ @VisibleForTesting
+ public void trigger() {
+ synchronized (this) {
+ if (!mInitialized) return;
+ mWorker.getThreadHandler().post(
+ () -> PerfettoTrigger.trigger(PerfettoTrigger.TRIGGER_TYPE_JANK));
+ }
}
/**
* A class to represent a session.
*/
public static class Session {
- private @CujType int mId;
+ private @CujType int mCujType;
- public Session(@CujType int session) {
- mId = session;
+ public Session(@CujType int cujType) {
+ mCujType = cujType;
}
- public int getId() {
- return mId;
+ public int getCuj() {
+ return mCujType;
}
public int getStatsdInteractionType() {
- return CUJ_TO_STATSD_INTERACTION_TYPE[mId];
+ return CUJ_TO_STATSD_INTERACTION_TYPE[mCujType];
}
/** Describes whether the measurement from this session should be written to statsd. */
@@ -196,7 +318,7 @@ public class InteractionJankMonitor {
}
public String getName() {
- return "CujType<" + mId + ">";
+ return "Cuj<" + getCuj() + ">";
}
}
diff --git a/core/java/com/android/internal/listeners/ListenerExecutor.java b/core/java/com/android/internal/listeners/ListenerExecutor.java
index 9979e6056f50..7d9b9c96b3a0 100644
--- a/core/java/com/android/internal/listeners/ListenerExecutor.java
+++ b/core/java/com/android/internal/listeners/ListenerExecutor.java
@@ -38,71 +38,86 @@ public interface ListenerExecutor {
void operate(TListener listener) throws Exception;
/**
- * Called before this operation is to be run. Some operations may be canceled before they
- * are run, in which case this method may not be called. {@link #onPostExecute(boolean)}
- * will only be run if this method was run. This callback is invoked on the calling thread.
+ * Called before this operation is to be run. An operation may be canceled before it is run,
+ * in which case this method may not be invoked. {@link #onPostExecute(boolean)} will only
+ * be invoked if this method was previously invoked. This callback is invoked on the
+ * calling thread.
*/
default void onPreExecute() {}
/**
* Called if the operation fails while running. Will not be invoked in the event of a
- * RuntimeException, which will propagate normally. Implementations of
- * {@link ListenerExecutor} have the option to override
- * {@link ListenerExecutor#onOperationFailure(ListenerOperation, Exception)} instead to
- * intercept failures at the class level. This callback is invoked on the executor thread.
+ * unchecked exception, which will propagate normally. This callback is invoked on the
+ * executor thread.
*/
- default void onFailure(Exception e) {
- // implementations should handle any exceptions that may be thrown
- throw new AssertionError(e);
- }
+ default void onFailure(Exception e) {}
/**
- * Called after the operation is run. This method will always be called if
- * {@link #onPreExecute()} is called. Success implies that the operation was run to
- * completion with no failures. This callback may be invoked on the calling thread or
- * executor thread.
+ * Called after the operation may have been run. Will always be invoked for every operation
+ * which has previously had {@link #onPreExecute()} invoked. Success implies that the
+ * operation was run to completion with no failures. If {@code success} is true, this
+ * callback will always be invoked on the executor thread. If {@code success} is false, this
+ * callback may be invoked on the calling thread or executor thread.
*/
default void onPostExecute(boolean success) {}
/**
- * Called after this operation is complete (which does not imply that it was necessarily
- * run). Will always be called once per operation, no matter if the operation was run or
- * not. Success implies that the operation was run to completion with no failures. This
- * callback may be invoked on the calling thread or executor thread.
+ * Will always be called once for every operation submitted to
+ * {@link #executeSafely(Executor, Supplier, ListenerOperation)}, no matter if the operation
+ * was run or not. This method is always invoked last, after every other callback. Success
+ * implies that the operation was run to completion with no failures. If {@code success}
+ * is true, this callback will always be invoked on the executor thread. If {@code success}
+ * is false, this callback may be invoked on the calling thread or executor thread.
*/
default void onComplete(boolean success) {}
}
/**
- * May be override to handle operation failures at a class level. Will not be invoked in the
- * event of a RuntimeException, which will propagate normally. This callback is invoked on the
- * executor thread.
+ * An callback for listener operation failure.
+ *
+ * @param <TListenerOperation> listener operation type
+ */
+ interface FailureCallback<TListenerOperation extends ListenerOperation<?>> {
+
+ /**
+ * Called if a listener operation fails while running with a checked exception. This
+ * callback is invoked on the executor thread.
+ */
+ void onFailure(TListenerOperation operation, Exception exception);
+ }
+
+ /**
+ * See {@link #executeSafely(Executor, Supplier, ListenerOperation, FailureCallback)}.
*/
- default <TListener> void onOperationFailure(ListenerOperation<TListener> operation,
- Exception exception) {
- operation.onFailure(exception);
+ default <TListener> void executeSafely(Executor executor, Supplier<TListener> listenerSupplier,
+ @Nullable ListenerOperation<TListener> operation) {
+ executeSafely(executor, listenerSupplier, operation, null);
}
/**
* Executes the given listener operation on the given executor, using the provided listener
* supplier. If the supplier returns a null value, or a value during the operation that does not
* match the value prior to the operation, then the operation is considered canceled. If a null
- * operation is supplied, nothing happens.
+ * operation is supplied, nothing happens. If a failure callback is supplied, this will be
+ * invoked on the executor thread in the event a checked exception is thrown from the listener
+ * operation.
*/
- default <TListener> void executeSafely(Executor executor, Supplier<TListener> listenerSupplier,
- @Nullable ListenerOperation<TListener> operation) {
+ default <TListener, TListenerOperation extends ListenerOperation<TListener>> void executeSafely(
+ Executor executor, Supplier<TListener> listenerSupplier,
+ @Nullable TListenerOperation operation,
+ @Nullable FailureCallback<TListenerOperation> failureCallback) {
if (operation == null) {
return;
}
+ TListener listener = listenerSupplier.get();
+ if (listener == null) {
+ return;
+ }
+
boolean executing = false;
boolean preexecute = false;
try {
- TListener listener = listenerSupplier.get();
- if (listener == null) {
- return;
- }
-
operation.onPreExecute();
preexecute = true;
executor.execute(() -> {
@@ -116,7 +131,10 @@ public interface ListenerExecutor {
if (e instanceof RuntimeException) {
throw (RuntimeException) e;
} else {
- onOperationFailure(operation, e);
+ operation.onFailure(e);
+ if (failureCallback != null) {
+ failureCallback.onFailure(operation, e);
+ }
}
} finally {
operation.onPostExecute(success);
diff --git a/core/java/com/android/internal/logging/UiEventLoggerImpl.java b/core/java/com/android/internal/logging/UiEventLoggerImpl.java
index c9156c13aae3..c0f44a5eb39b 100644
--- a/core/java/com/android/internal/logging/UiEventLoggerImpl.java
+++ b/core/java/com/android/internal/logging/UiEventLoggerImpl.java
@@ -33,7 +33,11 @@ public class UiEventLoggerImpl implements UiEventLogger {
public void log(UiEventEnum event, int uid, String packageName) {
final int eventID = event.getId();
if (eventID > 0) {
- FrameworkStatsLog.write(FrameworkStatsLog.UI_EVENT_REPORTED, eventID, uid, packageName);
+ FrameworkStatsLog.write(FrameworkStatsLog.UI_EVENT_REPORTED,
+ /* event_id = 1 */ eventID,
+ /* uid = 2 */ uid,
+ /* package_name = 3 */ packageName,
+ /* instance_id = 4 */ 0);
}
}
@@ -42,8 +46,11 @@ public class UiEventLoggerImpl implements UiEventLogger {
InstanceId instance) {
final int eventID = event.getId();
if ((eventID > 0) && (instance != null)) {
- FrameworkStatsLog.write(FrameworkStatsLog.UI_EVENT_REPORTED, eventID, uid, packageName,
- instance.getId());
+ FrameworkStatsLog.write(FrameworkStatsLog.UI_EVENT_REPORTED,
+ /* event_id = 1 */ eventID,
+ /* uid = 2 */ uid,
+ /* package_name = 3 */ packageName,
+ /* instance_id = 4 */ instance.getId());
} else {
log(event, uid, packageName);
}
diff --git a/core/java/com/android/internal/os/BatteryStatsImpl.java b/core/java/com/android/internal/os/BatteryStatsImpl.java
index e58990eff2c8..4a0e26a5c7cb 100644
--- a/core/java/com/android/internal/os/BatteryStatsImpl.java
+++ b/core/java/com/android/internal/os/BatteryStatsImpl.java
@@ -156,7 +156,7 @@ public class BatteryStatsImpl extends BatteryStats {
private static final int MAGIC = 0xBA757475; // 'BATSTATS'
// Current on-disk Parcel version
- static final int VERSION = 187 + (USE_OLD_HISTORY ? 1000 : 0);
+ static final int VERSION = 188 + (USE_OLD_HISTORY ? 1000 : 0);
// The maximum number of names wakelocks we will keep track of
// per uid; once the limit is reached, we batch the remaining wakelocks
@@ -6613,22 +6613,29 @@ public class BatteryStatsImpl extends BatteryStats {
* the power consumption to the calling app.
*/
public void noteBinderCallStats(int workSourceUid, long incrementalCallCount,
- Collection<BinderCallsStats.CallStat> callStats, int[] binderThreadNativeTids) {
- noteBinderCallStats(workSourceUid, incrementalCallCount, callStats, binderThreadNativeTids,
+ Collection<BinderCallsStats.CallStat> callStats) {
+ noteBinderCallStats(workSourceUid, incrementalCallCount, callStats,
mClocks.elapsedRealtime(), mClocks.uptimeMillis());
}
public void noteBinderCallStats(int workSourceUid, long incrementalCallCount,
- Collection<BinderCallsStats.CallStat> callStats, int[] binderThreadNativeTids,
+ Collection<BinderCallsStats.CallStat> callStats,
long elapsedRealtimeMs, long uptimeMs) {
synchronized (this) {
getUidStatsLocked(workSourceUid, elapsedRealtimeMs, uptimeMs)
.noteBinderCallStatsLocked(incrementalCallCount, callStats);
- mSystemServerCpuThreadReader.setBinderThreadNativeTids(binderThreadNativeTids);
}
}
/**
+ * Takes note of native IDs of threads taking incoming binder calls. The CPU time
+ * of these threads is attributed to the apps making those binder calls.
+ */
+ public void noteBinderThreadNativeIds(int[] binderThreadNativeTids) {
+ mSystemServerCpuThreadReader.setBinderThreadNativeTids(binderThreadNativeTids);
+ }
+
+ /**
* Estimates the proportion of system server CPU activity handling incoming binder calls
* that can be attributed to each app
*/
@@ -14569,6 +14576,7 @@ public class BatteryStatsImpl extends BatteryStats {
mDailyStartTimeMs = in.readLong();
mNextMinDailyDeadlineMs = in.readLong();
mNextMaxDailyDeadlineMs = in.readLong();
+ mBatteryTimeToFullSeconds = in.readLong();
mStartCount++;
@@ -15061,6 +15069,7 @@ public class BatteryStatsImpl extends BatteryStats {
out.writeLong(mDailyStartTimeMs);
out.writeLong(mNextMinDailyDeadlineMs);
out.writeLong(mNextMaxDailyDeadlineMs);
+ out.writeLong(mBatteryTimeToFullSeconds);
mScreenOnTimer.writeSummaryFromParcelLocked(out, NOWREAL_SYS);
mScreenDozeTimer.writeSummaryFromParcelLocked(out, NOWREAL_SYS);
@@ -15644,6 +15653,7 @@ public class BatteryStatsImpl extends BatteryStats {
mDischargeLightDozeCounter = new LongSamplingCounter(mOnBatteryTimeBase, in);
mDischargeDeepDozeCounter = new LongSamplingCounter(mOnBatteryTimeBase, in);
mLastWriteTimeMs = in.readLong();
+ mBatteryTimeToFullSeconds = in.readLong();
mRpmStats.clear();
int NRPMS = in.readInt();
@@ -15843,6 +15853,7 @@ public class BatteryStatsImpl extends BatteryStats {
mDischargeLightDozeCounter.writeToParcel(out);
mDischargeDeepDozeCounter.writeToParcel(out);
out.writeLong(mLastWriteTimeMs);
+ out.writeLong(mBatteryTimeToFullSeconds);
out.writeInt(mRpmStats.size());
for (Map.Entry<String, SamplingTimer> ent : mRpmStats.entrySet()) {
diff --git a/core/java/com/android/internal/os/BinderCallsStats.java b/core/java/com/android/internal/os/BinderCallsStats.java
index f5bef0b006f5..70b1ad49d8b8 100644
--- a/core/java/com/android/internal/os/BinderCallsStats.java
+++ b/core/java/com/android/internal/os/BinderCallsStats.java
@@ -119,8 +119,8 @@ public class BinderCallsStats implements BinderInternal.Observer {
if (uidEntry != null) {
ArrayMap<CallStatKey, CallStat> callStats = uidEntry.mCallStats;
mCallStatsObserver.noteCallStats(uidEntry.workSourceUid,
- uidEntry.incrementalCallCount, callStats.values(),
- mNativeTids.toArray());
+ uidEntry.incrementalCallCount, callStats.values()
+ );
uidEntry.incrementalCallCount = 0;
for (int j = callStats.size() - 1; j >= 0; j--) {
callStats.valueAt(j).incrementalCallCount = 0;
@@ -168,6 +168,7 @@ public class BinderCallsStats implements BinderInternal.Observer {
public void setCallStatsObserver(
BinderInternal.CallStatsObserver callStatsObserver) {
mCallStatsObserver = callStatsObserver;
+ noteBinderThreadNativeIds();
noteCallsStatsDelayed();
}
@@ -182,13 +183,13 @@ public class BinderCallsStats implements BinderInternal.Observer {
@Override
@Nullable
public CallSession callStarted(Binder binder, int code, int workSourceUid) {
+ noteNativeThreadId();
+
if (!mRecordingAllTransactionsForUid
&& (mDeviceState == null || mDeviceState.isCharging())) {
return null;
}
- noteNativeThreadId();
-
final CallSession s = obtainCallSession();
s.binderClass = binder.getClass();
s.transactionCode = code;
@@ -359,6 +360,16 @@ public class BinderCallsStats implements BinderInternal.Observer {
mNativeTids = copyOnWriteArray;
}
}
+
+ noteBinderThreadNativeIds();
+ }
+
+ private void noteBinderThreadNativeIds() {
+ if (mCallStatsObserver == null) {
+ return;
+ }
+
+ mCallStatsObserver.noteBinderThreadNativeIds(getNativeTids());
}
/**
diff --git a/core/java/com/android/internal/os/BinderInternal.java b/core/java/com/android/internal/os/BinderInternal.java
index 2645b8e84cf1..c14d8d805d29 100644
--- a/core/java/com/android/internal/os/BinderInternal.java
+++ b/core/java/com/android/internal/os/BinderInternal.java
@@ -143,8 +143,12 @@ public class BinderInternal {
* Notes incoming binder call stats associated with this work source UID.
*/
void noteCallStats(int workSourceUid, long incrementalCallCount,
- Collection<BinderCallsStats.CallStat> callStats,
- int[] binderThreadNativeTids);
+ Collection<BinderCallsStats.CallStat> callStats);
+
+ /**
+ * Notes the native IDs of threads taking incoming binder calls.
+ */
+ void noteBinderThreadNativeIds(int[] binderThreadNativeTids);
}
/**
diff --git a/core/java/com/android/internal/os/SystemServerCpuThreadReader.java b/core/java/com/android/internal/os/SystemServerCpuThreadReader.java
index 1cdd42c7403e..3aa2390375ec 100644
--- a/core/java/com/android/internal/os/SystemServerCpuThreadReader.java
+++ b/core/java/com/android/internal/os/SystemServerCpuThreadReader.java
@@ -31,7 +31,7 @@ import java.util.Arrays;
*/
public class SystemServerCpuThreadReader {
private KernelCpuThreadReader mKernelCpuThreadReader;
- private int[] mBinderThreadNativeTids;
+ private int[] mBinderThreadNativeTids = new int[0]; // Sorted
private int[] mThreadCpuTimesUs;
private int[] mBinderThreadCpuTimesUs;
@@ -75,7 +75,8 @@ public class SystemServerCpuThreadReader {
}
public void setBinderThreadNativeTids(int[] nativeTids) {
- mBinderThreadNativeTids = nativeTids;
+ mBinderThreadNativeTids = nativeTids.clone();
+ Arrays.sort(mBinderThreadNativeTids);
}
/**
@@ -107,7 +108,8 @@ public class SystemServerCpuThreadReader {
int threadCpuUsagesSize = threadCpuUsages.size();
for (int j = 0; j < threadCpuUsagesSize; j++) {
KernelCpuThreadReader.ThreadCpuUsage tcu = threadCpuUsages.get(j);
- boolean isBinderThread = isBinderThread(tcu.threadId);
+ boolean isBinderThread =
+ Arrays.binarySearch(mBinderThreadNativeTids, tcu.threadId) >= 0;
final int len = Math.min(tcu.usageTimesMillis.length, mThreadCpuTimesUs.length);
for (int k = 0; k < len; k++) {
@@ -138,14 +140,4 @@ public class SystemServerCpuThreadReader {
return mDeltaCpuThreadTimes;
}
- private boolean isBinderThread(int threadId) {
- if (mBinderThreadNativeTids != null) {
- for (int i = 0; i < mBinderThreadNativeTids.length; i++) {
- if (threadId == mBinderThreadNativeTids[i]) {
- return true;
- }
- }
- }
- return false;
- }
}
diff --git a/core/java/com/android/internal/policy/DecorView.java b/core/java/com/android/internal/policy/DecorView.java
index a6c3dd1071c9..4512fbac38c1 100644
--- a/core/java/com/android/internal/policy/DecorView.java
+++ b/core/java/com/android/internal/policy/DecorView.java
@@ -32,6 +32,8 @@ import static android.view.ViewGroup.LayoutParams.MATCH_PARENT;
import static android.view.ViewGroup.LayoutParams.WRAP_CONTENT;
import static android.view.Window.DECOR_CAPTION_SHADE_DARK;
import static android.view.Window.DECOR_CAPTION_SHADE_LIGHT;
+import static android.view.WindowInsetsController.APPEARANCE_LIGHT_NAVIGATION_BARS;
+import static android.view.WindowInsetsController.APPEARANCE_LIGHT_STATUS_BARS;
import static android.view.WindowManager.LayoutParams.FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS;
import static android.view.WindowManager.LayoutParams.FLAG_FULLSCREEN;
import static android.view.WindowManager.LayoutParams.FLAG_LAYOUT_INSET_DECOR;
@@ -97,6 +99,7 @@ import android.view.Window;
import android.view.WindowCallbacks;
import android.view.WindowInsets;
import android.view.WindowInsetsController;
+import android.view.WindowInsetsController.Appearance;
import android.view.WindowManager;
import android.view.accessibility.AccessibilityEvent;
import android.view.accessibility.AccessibilityManager;
@@ -137,19 +140,16 @@ public class DecorView extends FrameLayout implements RootViewSurfaceTaker, Wind
private static final int SCRIM_LIGHT = 0xe6ffffff; // 90% white
public static final ColorViewAttributes STATUS_BAR_COLOR_VIEW_ATTRIBUTES =
- new ColorViewAttributes(SYSTEM_UI_FLAG_FULLSCREEN, FLAG_TRANSLUCENT_STATUS,
+ new ColorViewAttributes(FLAG_TRANSLUCENT_STATUS,
Gravity.TOP, Gravity.LEFT, Gravity.RIGHT,
Window.STATUS_BAR_BACKGROUND_TRANSITION_NAME,
- com.android.internal.R.id.statusBarBackground,
- FLAG_FULLSCREEN, ITYPE_STATUS_BAR);
+ com.android.internal.R.id.statusBarBackground, ITYPE_STATUS_BAR);
public static final ColorViewAttributes NAVIGATION_BAR_COLOR_VIEW_ATTRIBUTES =
- new ColorViewAttributes(
- SYSTEM_UI_FLAG_HIDE_NAVIGATION, FLAG_TRANSLUCENT_NAVIGATION,
+ new ColorViewAttributes(FLAG_TRANSLUCENT_NAVIGATION,
Gravity.BOTTOM, Gravity.RIGHT, Gravity.LEFT,
Window.NAVIGATION_BAR_BACKGROUND_TRANSITION_NAME,
- com.android.internal.R.id.navigationBarBackground,
- 0 /* hideWindowFlag */, ITYPE_NAVIGATION_BAR);
+ com.android.internal.R.id.navigationBarBackground, ITYPE_NAVIGATION_BAR);
// This is used to workaround an issue where the PiP shadow can be transparent if the window
// background is transparent
@@ -1016,6 +1016,11 @@ public class DecorView extends FrameLayout implements RootViewSurfaceTaker, Wind
}
@Override
+ public void onSystemBarAppearanceChanged(@WindowInsetsController.Appearance int appearance) {
+ updateColorViews(null /* insets */, true /* animate */);
+ }
+
+ @Override
public WindowInsets onApplyWindowInsets(WindowInsets insets) {
final WindowManager.LayoutParams attrs = mWindow.getAttributes();
mFloatingInsets.setEmpty();
@@ -1094,16 +1099,24 @@ public class DecorView extends FrameLayout implements RootViewSurfaceTaker, Wind
& FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS) != 0;
mLastWindowFlags = attrs.flags;
+ final ViewRootImpl viewRoot = getViewRootImpl();
+ final @Appearance int appearance = viewRoot != null
+ ? viewRoot.mWindowAttributes.insetsFlags.appearance
+ : controller.getSystemBarsAppearance();
+
if (insets != null) {
- final Insets systemBarInsets = insets.getInsets(WindowInsets.Type.systemBars());
- final Insets stableBarInsets = insets.getInsetsIgnoringVisibility(
- WindowInsets.Type.systemBars());
final boolean clearCompatInsets = clearCompatInsets(attrs.type, attrs.flags,
getResources().getConfiguration().windowConfiguration.getWindowingMode());
- mLastTopInset = clearCompatInsets ? 0 : systemBarInsets.top;
- mLastBottomInset = clearCompatInsets ? 0 : systemBarInsets.bottom;
- mLastRightInset = clearCompatInsets ? 0 : systemBarInsets.right;
- mLastLeftInset = clearCompatInsets ? 0 : systemBarInsets.left;
+ final Insets stableBarInsets = insets.getInsetsIgnoringVisibility(
+ WindowInsets.Type.systemBars());
+ final Insets systemInsets = clearCompatInsets
+ ? Insets.NONE
+ : Insets.min(insets.getInsets(WindowInsets.Type.systemBars()
+ | WindowInsets.Type.displayCutout()), stableBarInsets);
+ mLastTopInset = systemInsets.top;
+ mLastBottomInset = systemInsets.bottom;
+ mLastRightInset = systemInsets.right;
+ mLastLeftInset = systemInsets.left;
// Don't animate if the presence of stable insets has changed, because that
// indicates that the window was either just added and received them for the
@@ -1130,8 +1143,8 @@ public class DecorView extends FrameLayout implements RootViewSurfaceTaker, Wind
boolean navBarToRightEdge = isNavBarToRightEdge(mLastBottomInset, mLastRightInset);
boolean navBarToLeftEdge = isNavBarToLeftEdge(mLastBottomInset, mLastLeftInset);
int navBarSize = getNavBarSize(mLastBottomInset, mLastRightInset, mLastLeftInset);
- updateColorViewInt(mNavigationColorViewState, sysUiVisibility,
- calculateNavigationBarColor(), mWindow.mNavigationBarDividerColor, navBarSize,
+ updateColorViewInt(mNavigationColorViewState, calculateNavigationBarColor(appearance),
+ mWindow.mNavigationBarDividerColor, navBarSize,
navBarToRightEdge || navBarToLeftEdge, navBarToLeftEdge,
0 /* sideInset */, animate && !disallowAnimate,
mForceWindowDrawsBarBackgrounds, controller);
@@ -1139,9 +1152,8 @@ public class DecorView extends FrameLayout implements RootViewSurfaceTaker, Wind
mDrawLegacyNavigationBarBackground = mNavigationColorViewState.visible
&& (mWindow.getAttributes().flags & FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS) == 0;
if (oldDrawLegacy != mDrawLegacyNavigationBarBackground) {
- ViewRootImpl vri = getViewRootImpl();
- if (vri != null) {
- vri.requestInvalidateRootRenderNode();
+ if (viewRoot != null) {
+ viewRoot.requestInvalidateRootRenderNode();
}
}
@@ -1151,15 +1163,14 @@ public class DecorView extends FrameLayout implements RootViewSurfaceTaker, Wind
&& mNavigationColorViewState.present;
int statusBarSideInset = statusBarNeedsRightInset ? mLastRightInset
: statusBarNeedsLeftInset ? mLastLeftInset : 0;
- updateColorViewInt(mStatusColorViewState, sysUiVisibility,
- calculateStatusBarColor(), 0, mLastTopInset,
- false /* matchVertical */, statusBarNeedsLeftInset, statusBarSideInset,
- animate && !disallowAnimate,
+ int statusBarColor = calculateStatusBarColor(appearance);
+ updateColorViewInt(mStatusColorViewState, statusBarColor, 0,
+ mLastTopInset, false /* matchVertical */, statusBarNeedsLeftInset,
+ statusBarSideInset, animate && !disallowAnimate,
mForceWindowDrawsBarBackgrounds, controller);
if (mHasCaption) {
- final int captionColor = calculateStatusBarColor();
- mDecorCaptionView.getCaption().setBackgroundColor(captionColor);
+ mDecorCaptionView.getCaption().setBackgroundColor(statusBarColor);
updateDecorCaptionShade();
}
}
@@ -1286,29 +1297,30 @@ public class DecorView extends FrameLayout implements RootViewSurfaceTaker, Wind
return mOriginalBackgroundDrawable;
}
- private int calculateStatusBarColor() {
+ private int calculateStatusBarColor(@Appearance int appearance) {
return calculateBarColor(mWindow.getAttributes().flags, FLAG_TRANSLUCENT_STATUS,
mSemiTransparentBarColor, mWindow.mStatusBarColor,
- getWindowSystemUiVisibility(), SYSTEM_UI_FLAG_LIGHT_STATUS_BAR,
+ appearance, APPEARANCE_LIGHT_STATUS_BARS,
mWindow.mEnsureStatusBarContrastWhenTransparent);
}
- private int calculateNavigationBarColor() {
+ private int calculateNavigationBarColor(@Appearance int appearance) {
return calculateBarColor(mWindow.getAttributes().flags, FLAG_TRANSLUCENT_NAVIGATION,
mSemiTransparentBarColor, mWindow.mNavigationBarColor,
- getWindowSystemUiVisibility(), SYSTEM_UI_FLAG_LIGHT_NAVIGATION_BAR,
+ appearance, APPEARANCE_LIGHT_NAVIGATION_BARS,
mWindow.mEnsureNavigationBarContrastWhenTransparent
&& getContext().getResources().getBoolean(R.bool.config_navBarNeedsScrim));
}
public static int calculateBarColor(int flags, int translucentFlag, int semiTransparentBarColor,
- int barColor, int sysuiVis, int lightSysuiFlag, boolean scrimTransparent) {
+ int barColor, @Appearance int appearance, @Appearance int lightAppearanceFlag,
+ boolean scrimTransparent) {
if ((flags & translucentFlag) != 0) {
return semiTransparentBarColor;
} else if ((flags & FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS) == 0) {
return Color.BLACK;
} else if (scrimTransparent && Color.alpha(barColor) == 0) {
- boolean light = (sysuiVis & lightSysuiFlag) != 0;
+ boolean light = (appearance & lightAppearanceFlag) != 0;
return light ? SCRIM_LIGHT : semiTransparentBarColor;
} else {
return barColor;
@@ -1327,7 +1339,6 @@ public class DecorView extends FrameLayout implements RootViewSurfaceTaker, Wind
* Update a color view
*
* @param state the color view to update.
- * @param sysUiVis the current systemUiVisibility to apply.
* @param color the current color to apply.
* @param dividerColor the current divider color to apply.
* @param size the current size in the non-parent-matching dimension.
@@ -1336,12 +1347,10 @@ public class DecorView extends FrameLayout implements RootViewSurfaceTaker, Wind
* @param sideMargin sideMargin for the color view.
* @param animate if true, the change will be animated.
*/
- private void updateColorViewInt(final ColorViewState state, int sysUiVis, int color,
- int dividerColor, int size, boolean verticalBar, boolean seascape, int sideMargin,
- boolean animate, boolean force, WindowInsetsController controller) {
- state.present = ViewRootImpl.sNewInsetsMode != ViewRootImpl.NEW_INSETS_MODE_FULL
- ? state.attributes.isPresent(sysUiVis, mWindow.getAttributes().flags, force)
- : state.attributes.isPresent(
+ private void updateColorViewInt(final ColorViewState state, int color, int dividerColor,
+ int size, boolean verticalBar, boolean seascape, int sideMargin, boolean animate,
+ boolean force, WindowInsetsController controller) {
+ state.present = state.attributes.isPresent(
controller.isRequestedVisible(state.attributes.insetsType),
mWindow.getAttributes().flags, force);
boolean show = state.attributes.isVisible(state.present, color,
@@ -2583,37 +2592,25 @@ public class DecorView extends FrameLayout implements RootViewSurfaceTaker, Wind
public static class ColorViewAttributes {
final int id;
- final int systemUiHideFlag;
final int translucentFlag;
final int verticalGravity;
final int horizontalGravity;
final int seascapeGravity;
final String transitionName;
- final int hideWindowFlag;
final @InternalInsetsType int insetsType;
- private ColorViewAttributes(int systemUiHideFlag, int translucentFlag, int verticalGravity,
- int horizontalGravity, int seascapeGravity, String transitionName, int id,
- int hideWindowFlag, @InternalInsetsType int insetsType) {
+ private ColorViewAttributes(int translucentFlag, int verticalGravity, int horizontalGravity,
+ int seascapeGravity, String transitionName, int id,
+ @InternalInsetsType int insetsType) {
this.id = id;
- this.systemUiHideFlag = systemUiHideFlag;
this.translucentFlag = translucentFlag;
this.verticalGravity = verticalGravity;
this.horizontalGravity = horizontalGravity;
this.seascapeGravity = seascapeGravity;
this.transitionName = transitionName;
- this.hideWindowFlag = hideWindowFlag;
this.insetsType = insetsType;
}
- // TODO(b/118118435): remove after migration
- public boolean isPresent(int sysUiVis, int windowFlags, boolean force) {
- return (sysUiVis & systemUiHideFlag) == 0
- && (windowFlags & hideWindowFlag) == 0
- && ((windowFlags & FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS) != 0
- || force);
- }
-
public boolean isPresent(boolean requestedVisible, int windowFlags, boolean force) {
return requestedVisible
&& ((windowFlags & FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS) != 0 || force);
@@ -2625,12 +2622,6 @@ public class DecorView extends FrameLayout implements RootViewSurfaceTaker, Wind
&& ((windowFlags & translucentFlag) == 0 || force);
}
- // TODO(b/118118435): remove after migration
- public boolean isVisible(int sysUiVis, int color, int windowFlags, boolean force) {
- final boolean present = isPresent(sysUiVis, windowFlags, force);
- return isVisible(present, color, windowFlags, force);
- }
-
public boolean isVisible(InsetsState state, int color, int windowFlags, boolean force) {
final boolean present = isPresent(state.getSource(insetsType).isVisible(), windowFlags,
force);
diff --git a/core/java/com/android/internal/protolog/ProtoLogGroup.java b/core/java/com/android/internal/protolog/ProtoLogGroup.java
index 9f7436a13bdc..9874c6aabf04 100644
--- a/core/java/com/android/internal/protolog/ProtoLogGroup.java
+++ b/core/java/com/android/internal/protolog/ProtoLogGroup.java
@@ -72,7 +72,9 @@ public enum ProtoLogGroup implements IProtoLogGroup {
Consts.TAG_WM),
WM_DEBUG_WINDOW_ORGANIZER(Consts.ENABLE_DEBUG, Consts.ENABLE_LOG_TO_PROTO_DEBUG, false,
Consts.TAG_WM),
- TEST_GROUP(true, true, false, "WindowManagetProtoLogTest");
+ WM_DEBUG_SYNC_ENGINE(Consts.ENABLE_DEBUG, Consts.ENABLE_LOG_TO_PROTO_DEBUG, false,
+ Consts.TAG_WM),
+ TEST_GROUP(true, true, false, "WindowManagerProtoLogTest");
private final boolean mEnabled;
private volatile boolean mLogToProto;
diff --git a/core/java/com/android/internal/telephony/IPhoneStateListener.aidl b/core/java/com/android/internal/telephony/IPhoneStateListener.aidl
index 25d1ae699aa6..44dca9bae3da 100644
--- a/core/java/com/android/internal/telephony/IPhoneStateListener.aidl
+++ b/core/java/com/android/internal/telephony/IPhoneStateListener.aidl
@@ -63,7 +63,7 @@ oneway interface IPhoneStateListener {
void onCallAttributesChanged(in CallAttributes callAttributes);
void onEmergencyNumberListChanged(in Map emergencyNumberList);
void onOutgoingEmergencyCall(in EmergencyNumber placedEmergencyNumber, int subscriptionId);
- void onOutgoingEmergencySms(in EmergencyNumber sentEmergencyNumber);
+ void onOutgoingEmergencySms(in EmergencyNumber sentEmergencyNumber, int subscriptionId);
void onCallDisconnectCauseChanged(in int disconnectCause, in int preciseDisconnectCause);
void onImsCallDisconnectCauseChanged(in ImsReasonInfo imsReasonInfo);
void onRegistrationFailed(in CellIdentity cellIdentity,
diff --git a/core/jni/android_os_Debug.cpp b/core/jni/android_os_Debug.cpp
index ef0eeec2c7af..5a859aabce7b 100644
--- a/core/jni/android_os_Debug.cpp
+++ b/core/jni/android_os_Debug.cpp
@@ -792,7 +792,7 @@ static jlong android_os_Debug_getFreeZramKb(JNIEnv* env, jobject clazz) {
}
static jlong android_os_Debug_getIonHeapsSizeKb(JNIEnv* env, jobject clazz) {
- jlong heapsSizeKb = 0;
+ jlong heapsSizeKb = -1;
uint64_t size;
if (meminfo::ReadIonHeapsSizeKb(&size)) {
@@ -803,7 +803,7 @@ static jlong android_os_Debug_getIonHeapsSizeKb(JNIEnv* env, jobject clazz) {
}
static jlong android_os_Debug_getIonPoolsSizeKb(JNIEnv* env, jobject clazz) {
- jlong poolsSizeKb = 0;
+ jlong poolsSizeKb = -1;
uint64_t size;
if (meminfo::ReadIonPoolsSizeKb(&size)) {
diff --git a/core/jni/android_view_DisplayEventReceiver.cpp b/core/jni/android_view_DisplayEventReceiver.cpp
index 50a557bb53a1..3acd15a8f340 100644
--- a/core/jni/android_view_DisplayEventReceiver.cpp
+++ b/core/jni/android_view_DisplayEventReceiver.cpp
@@ -59,7 +59,8 @@ private:
jobject mReceiverWeakGlobal;
sp<MessageQueue> mMessageQueue;
- void dispatchVsync(nsecs_t timestamp, PhysicalDisplayId displayId, uint32_t count) override;
+ void dispatchVsync(nsecs_t timestamp, PhysicalDisplayId displayId, uint32_t count,
+ int64_t sharedTimelineFrameCount) override;
void dispatchHotplug(nsecs_t timestamp, PhysicalDisplayId displayId, bool connected) override;
void dispatchConfigChanged(nsecs_t timestamp, PhysicalDisplayId displayId,
int32_t configId, nsecs_t vsyncPeriod) override;
@@ -90,14 +91,14 @@ void NativeDisplayEventReceiver::dispose() {
}
void NativeDisplayEventReceiver::dispatchVsync(nsecs_t timestamp, PhysicalDisplayId displayId,
- uint32_t count) {
+ uint32_t count, int64_t frameTimelineVsyncId) {
JNIEnv* env = AndroidRuntime::getJNIEnv();
ScopedLocalRef<jobject> receiverObj(env, jniGetReferent(env, mReceiverWeakGlobal));
if (receiverObj.get()) {
ALOGV("receiver %p ~ Invoking vsync handler.", this);
env->CallVoidMethod(receiverObj.get(), gDisplayEventReceiverClassInfo.dispatchVsync,
- timestamp, displayId.value, count);
+ timestamp, displayId.value, count, frameTimelineVsyncId);
ALOGV("receiver %p ~ Returned from vsync handler.", this);
}
@@ -196,8 +197,8 @@ int register_android_view_DisplayEventReceiver(JNIEnv* env) {
jclass clazz = FindClassOrDie(env, "android/view/DisplayEventReceiver");
gDisplayEventReceiverClassInfo.clazz = MakeGlobalRefOrDie(env, clazz);
- gDisplayEventReceiverClassInfo.dispatchVsync = GetMethodIDOrDie(env,
- gDisplayEventReceiverClassInfo.clazz, "dispatchVsync", "(JJI)V");
+ gDisplayEventReceiverClassInfo.dispatchVsync =
+ GetMethodIDOrDie(env, gDisplayEventReceiverClassInfo.clazz, "dispatchVsync", "(JJIJ)V");
gDisplayEventReceiverClassInfo.dispatchHotplug = GetMethodIDOrDie(env,
gDisplayEventReceiverClassInfo.clazz, "dispatchHotplug", "(JJZ)V");
gDisplayEventReceiverClassInfo.dispatchConfigChanged = GetMethodIDOrDie(env,
diff --git a/core/jni/android_view_InputChannel.cpp b/core/jni/android_view_InputChannel.cpp
index 2436b23a45d0..d4a746290baa 100644
--- a/core/jni/android_view_InputChannel.cpp
+++ b/core/jni/android_view_InputChannel.cpp
@@ -36,6 +36,9 @@ namespace android {
static struct {
jclass clazz;
+ jmethodID mCtor;
+ jmethodID mSetNativeInputChannel;
+
jfieldID mPtr; // native object attached to the DVM InputChannel
} gInputChannelClassInfo;
@@ -43,7 +46,7 @@ static struct {
class NativeInputChannel {
public:
- explicit NativeInputChannel(const std::shared_ptr<InputChannel>& inputChannel);
+ explicit NativeInputChannel(std::unique_ptr<InputChannel> inputChannel);
~NativeInputChannel();
inline std::shared_ptr<InputChannel> getInputChannel() { return mInputChannel; }
@@ -59,8 +62,8 @@ private:
// ----------------------------------------------------------------------------
-NativeInputChannel::NativeInputChannel(const std::shared_ptr<InputChannel>& inputChannel)
- : mInputChannel(inputChannel), mDisposeCallback(nullptr) {}
+NativeInputChannel::NativeInputChannel(std::unique_ptr<InputChannel> inputChannel)
+ : mInputChannel(std::move(inputChannel)), mDisposeCallback(nullptr) {}
NativeInputChannel::~NativeInputChannel() {
}
@@ -110,13 +113,33 @@ void android_view_InputChannel_setDisposeCallback(JNIEnv* env, jobject inputChan
}
static jlong android_view_InputChannel_createInputChannel(
- JNIEnv* env, std::shared_ptr<InputChannel> inputChannel) {
+ JNIEnv* env, std::unique_ptr<InputChannel> inputChannel) {
std::unique_ptr<NativeInputChannel> nativeInputChannel =
- std::make_unique<NativeInputChannel>(inputChannel);
+ std::make_unique<NativeInputChannel>(std::move(inputChannel));
return reinterpret_cast<jlong>(nativeInputChannel.release());
}
+jobject android_view_InputChannel_createJavaObject(JNIEnv* env,
+ std::unique_ptr<InputChannel> inputChannel) {
+ std::string name = inputChannel->getName();
+ jlong ptr = android_view_InputChannel_createInputChannel(env, std::move(inputChannel));
+ jobject javaInputChannel =
+ env->NewObject(gInputChannelClassInfo.clazz, gInputChannelClassInfo.mCtor);
+ if (!javaInputChannel) {
+ ALOGE("Failed to create a Java InputChannel for channel %s.", name.c_str());
+ return nullptr;
+ }
+
+ env->CallVoidMethod(javaInputChannel, gInputChannelClassInfo.mSetNativeInputChannel, ptr);
+ if (env->ExceptionOccurred()) {
+ ALOGE("Failed to set native ptr to the Java InputChannel for channel %s.",
+ inputChannel->getName().c_str());
+ return nullptr;
+ }
+ return javaInputChannel;
+}
+
static jlongArray android_view_InputChannel_nativeOpenInputChannelPair(JNIEnv* env,
jclass clazz, jstring nameObj) {
ScopedUtfChars nameChars(env, nameObj);
@@ -180,9 +203,10 @@ static jlong android_view_InputChannel_nativeReadFromParcel(JNIEnv* env, jobject
if (parcel) {
bool isInitialized = parcel->readInt32();
if (isInitialized) {
- std::shared_ptr<InputChannel> inputChannel = std::make_shared<InputChannel>();
+ std::unique_ptr<InputChannel> inputChannel = std::make_unique<InputChannel>();
inputChannel->readFromParcel(parcel);
- NativeInputChannel* nativeInputChannel = new NativeInputChannel(inputChannel);
+ NativeInputChannel* nativeInputChannel =
+ new NativeInputChannel(std::move(inputChannel));
return reinterpret_cast<jlong>(nativeInputChannel);
}
}
@@ -233,13 +257,13 @@ static jlong android_view_InputChannel_nativeDup(JNIEnv* env, jobject obj, jlong
return 0;
}
- std::shared_ptr<InputChannel> dupInputChannel = inputChannel->dup();
+ std::unique_ptr<InputChannel> dupInputChannel = inputChannel->dup();
if (dupInputChannel == nullptr) {
std::string message = android::base::StringPrintf(
"Could not duplicate input channel %s", inputChannel->getName().c_str());
jniThrowRuntimeException(env, message.c_str());
}
- return reinterpret_cast<jlong>(new NativeInputChannel(dupInputChannel));
+ return reinterpret_cast<jlong>(new NativeInputChannel(std::move(dupInputChannel)));
}
static jobject android_view_InputChannel_nativeGetToken(JNIEnv* env, jobject obj, jlong channel) {
@@ -281,6 +305,11 @@ int register_android_view_InputChannel(JNIEnv* env) {
jclass clazz = FindClassOrDie(env, "android/view/InputChannel");
gInputChannelClassInfo.clazz = MakeGlobalRefOrDie(env, clazz);
+ gInputChannelClassInfo.mCtor =
+ GetMethodIDOrDie(env, gInputChannelClassInfo.clazz, "<init>", "()V");
+ gInputChannelClassInfo.mSetNativeInputChannel =
+ GetMethodIDOrDie(env, gInputChannelClassInfo.clazz, "setNativeInputChannel", "(J)V");
+
gInputChannelClassInfo.mPtr = GetFieldIDOrDie(env, gInputChannelClassInfo.clazz, "mPtr", "J");
return res;
diff --git a/core/jni/android_view_InputChannel.h b/core/jni/android_view_InputChannel.h
index 8030c96ab19f..50b9aed4b12c 100644
--- a/core/jni/android_view_InputChannel.h
+++ b/core/jni/android_view_InputChannel.h
@@ -36,6 +36,8 @@ extern std::shared_ptr<InputChannel> android_view_InputChannel_getInputChannel(
extern void android_view_InputChannel_setDisposeCallback(JNIEnv* env, jobject inputChannelObj,
InputChannelObjDisposeCallback callback, void* data = NULL);
+extern jobject android_view_InputChannel_createJavaObject(
+ JNIEnv* env, std::unique_ptr<InputChannel> inputChannel);
} // namespace android
#endif // _ANDROID_OS_INPUTCHANNEL_H
diff --git a/core/proto/android/content/configuration.proto b/core/proto/android/content/configuration.proto
index 7fa0ff64f8bf..d578414eb177 100644
--- a/core/proto/android/content/configuration.proto
+++ b/core/proto/android/content/configuration.proto
@@ -49,6 +49,7 @@ message ConfigurationProto {
optional uint32 density_dpi = 18;
optional .android.app.WindowConfigurationProto window_configuration = 19;
optional string locale_list = 20;
+ optional uint32 force_bold_text = 21;
}
/**
diff --git a/core/proto/android/providers/settings/config.proto b/core/proto/android/providers/settings/config.proto
index d433c0e1859d..f343c3a30927 100644
--- a/core/proto/android/providers/settings/config.proto
+++ b/core/proto/android/providers/settings/config.proto
@@ -29,6 +29,7 @@ message ConfigSettingsProto {
repeated NamespaceProto extra_namespaces = 2;
repeated SettingProto activity_manager_native_boot_settings = 3;
repeated SettingProto activity_manager_settings = 4;
+ repeated SettingProto alarm_manager_settings = 26;
repeated SettingProto app_compat_settings = 5;
repeated SettingProto autofill_settings = 6;
repeated SettingProto blobstore_settings = 23;
@@ -51,10 +52,10 @@ message ConfigSettingsProto {
repeated SettingProto telephony_settings = 21;
repeated SettingProto textclassifier_settings = 22;
- // Next tag: 26
+ // Next tag: 27
message NamespaceProto {
optional string namespace = 1;
repeated SettingProto settings = 2;
}
-} \ No newline at end of file
+}
diff --git a/core/proto/android/providers/settings/global.proto b/core/proto/android/providers/settings/global.proto
index 3ec14c01c363..50912702e3d6 100644
--- a/core/proto/android/providers/settings/global.proto
+++ b/core/proto/android/providers/settings/global.proto
@@ -47,7 +47,7 @@ message GlobalSettingsProto {
}
optional AirplaneMode airplane_mode = 5;
- optional SettingProto alarm_manager_constants = 6;
+ reserved 6; // alarm_manager_constants
optional SettingProto allow_user_switching_when_system_user_locked = 7 [ (android.privacy).dest = DEST_AUTOMATIC ];
// This is a key=value list, separated by commas.
optional SettingProto always_on_display_constants = 8;
diff --git a/core/proto/android/providers/settings/secure.proto b/core/proto/android/providers/settings/secure.proto
index 7f743ef037a2..e1a980caa635 100644
--- a/core/proto/android/providers/settings/secure.proto
+++ b/core/proto/android/providers/settings/secure.proto
@@ -120,6 +120,14 @@ message SecureSettingsProto {
}
optional Assist assist = 7;
+ message AssistHandles {
+ option (android.msg_privacy).dest = DEST_EXPLICIT;
+
+ optional SettingProto learning_time_elapsed_millis = 1 [ (android.privacy).dest = DEST_AUTOMATIC ];
+ optional SettingProto learning_event_count = 2 [ (android.privacy).dest = DEST_AUTOMATIC ];
+ }
+ optional AssistHandles assist_handles = 86;
+
message Autofill {
option (android.msg_privacy).dest = DEST_EXPLICIT;
@@ -518,11 +526,11 @@ message SecureSettingsProto {
}
optional Sounds sounds = 72;
+ optional SettingProto swipe_bottom_to_notification_enabled = 82 [ (android.privacy).dest = DEST_AUTOMATIC ];
// Defines whether managed profile ringtones should be synced from its
// parent profile.
optional SettingProto sync_parent_sounds = 55 [ (android.privacy).dest = DEST_AUTOMATIC ];
optional SettingProto system_navigation_keys_enabled = 56 [ (android.privacy).dest = DEST_AUTOMATIC ];
- optional SettingProto swipe_bottom_to_notification_enabled = 82 [ (android.privacy).dest = DEST_AUTOMATIC ];
optional SettingProto theme_customization_overlay_packages = 75 [ (android.privacy).dest = DEST_AUTOMATIC ];
optional SettingProto trust_agents_initialized = 57 [ (android.privacy).dest = DEST_AUTOMATIC ];
@@ -617,5 +625,5 @@ message SecureSettingsProto {
// Please insert fields in alphabetical order and group them into messages
// if possible (to avoid reaching the method limit).
- // Next tag = 86;
+ // Next tag = 87;
}
diff --git a/core/proto/android/stats/hdmi/enums.proto b/core/proto/android/stats/hdmi/enums.proto
new file mode 100644
index 000000000000..acb8899fbdd9
--- /dev/null
+++ b/core/proto/android/stats/hdmi/enums.proto
@@ -0,0 +1,122 @@
+/*
+ * 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 android.stats.hdmi;
+option java_multiple_files = true;
+option java_outer_classname = "HdmiStatsEnums";
+
+// HDMI CEC logical addresses.
+// Values correspond to "CEC Table 5 Logical Addresses" in the HDMI CEC 1.4b spec.
+enum LogicalAddress {
+ LOGICAL_ADDRESS_UNKNOWN = -1;
+ TV = 0;
+ RECORDING_DEVICE_1 = 1;
+ RECORDING_DEVICE_2 = 2;
+ TUNER_1 = 3;
+ PLAYBACK_DEVICE_1 = 4;
+ AUDIO_SYSTEM = 5;
+ TUNER_2 = 6;
+ TUNER_3 = 7;
+ PLAYBACK_DEVICE_2 = 8;
+ RECORDING_DEVICE_3 = 9;
+ TUNER_4 = 10;
+ PLAYBACK_DEVICE_3 = 11;
+ RESERVED_1 = 12;
+ RESERVED_2 = 13;
+ SPECIFIC_USE = 14;
+ UNREGISTERED_OR_BROADCAST = 15;
+}
+
+// The relationship between two paths.
+// Values correspond exactly to PathRelationship in com.android.server.hdmi.Constants.
+enum PathRelationship {
+ RELATIONSHIP_TO_ACTIVE_SOURCE_UNKNOWN = 0;
+ DIFFERENT_BRANCH = 1;
+ ANCESTOR = 2;
+ DESCENDANT = 3;
+ SIBLING = 4;
+ SAME = 5;
+}
+
+// The result of attempting to send a HDMI CEC message.
+// Values correspond to the constants in android.hardware.tv.cec.V1_0.SendMessageResult,
+// offset by 10.
+enum SendMessageResult {
+ SEND_MESSAGE_RESULT_UNKNOWN = 0;
+ SUCCESS = 10;
+ NACK = 11;
+ BUSY = 12;
+ FAIL = 13;
+}
+
+// Whether a HDMI CEC message is sent from this device, to this device, or neither.
+enum MessageDirection {
+ MESSAGE_DIRECTION_UNKNOWN = 0;
+ MESSAGE_DIRECTION_OTHER = 1; // None of the other options.
+ OUTGOING = 2; // Sent from this device.
+ INCOMING = 3; // Sent to this device.
+ TO_SELF = 4; // Sent from this device, to this device. Indicates a bug.
+}
+
+// User control commands. Each value can represent an individual command, or a set of commands.
+// Values correspond to "CEC Table 30 UI Command Codes" in the HDMI CEC 1.4b spec, offset by 0x100.
+enum UserControlPressedCommand {
+ USER_CONTROL_PRESSED_COMMAND_UNKNOWN = 0;
+
+ // Represents all codes that are not represented by another value.
+ USER_CONTROL_PRESSED_COMMAND_OTHER = 1;
+
+ // Represents all number codes (codes 0x1E through 0x29).
+ NUMBER = 2;
+
+ // Navigation
+ SELECT = 0x100;
+ UP = 0x101;
+ DOWN = 0x102;
+ LEFT = 0x103;
+ RIGHT = 0x104;
+ RIGHT_UP = 0x105;
+ RIGHT_DOWN = 0x106;
+ LEFT_UP = 0x107;
+ LEFT_DOWN = 0x108;
+ EXIT = 0x10D;
+
+ // Volume
+ VOLUME_UP = 0x141;
+ VOLUME_DOWN = 0x142;
+ VOLUME_MUTE = 0x143;
+
+ // Power
+ POWER = 0x140;
+ POWER_TOGGLE = 0x16B;
+ POWER_OFF = 0x16C;
+ POWER_ON = 0x16D;
+}
+
+// Reason parameter of the <Feature Abort> message.
+// Values correspond to "CEC Table 29 Operand Descriptions" in the HDMI CEC 1.4b spec,
+// offset by 10.
+enum FeatureAbortReason {
+ FEATURE_ABORT_REASON_UNKNOWN = 0;
+ UNRECOGNIZED_OPCODE = 10;
+ NOT_IN_CORRECT_MODE_TO_RESPOND = 11;
+ CANNOT_PROVIDE_SOURCE = 12;
+ INVALID_OPERAND = 13;
+ REFUSED = 14;
+ UNABLE_TO_DETERMINE = 15;
+} \ No newline at end of file
diff --git a/core/res/AndroidManifest.xml b/core/res/AndroidManifest.xml
index 68e2891657ac..34543f13c166 100644
--- a/core/res/AndroidManifest.xml
+++ b/core/res/AndroidManifest.xml
@@ -238,6 +238,8 @@
android:name="com.android.bluetooth.BluetoothMapContentObserver.action.MESSAGE_DELIVERY" />
<protected-broadcast
android:name="android.bluetooth.pan.profile.action.CONNECTION_STATE_CHANGED" />
+ <protected-broadcast
+ android:name="android.bluetooth.action.TETHERING_STATE_CHANGED" />
<protected-broadcast android:name="android.bluetooth.pbap.profile.action.CONNECTION_STATE_CHANGED" />
<protected-broadcast android:name="android.bluetooth.pbapclient.profile.action.CONNECTION_STATE_CHANGED" />
<protected-broadcast android:name="android.bluetooth.sap.profile.action.CONNECTION_STATE_CHANGED" />
@@ -2457,8 +2459,9 @@
android:protectionLevel="signature|privileged" />
<!-- @SystemApi @hide Allows an application to create, remove users and get the list of
- users on the device. Applications holding this permission can only create restricted,
- guest, managed, demo, and ephemeral users. For creating other kind of users,
+ users on the device. Applications holding this permission can create users (including
+ normal, restricted, guest, managed, and demo users) and can optionally endow them with the
+ ephemeral property. For creating users with other kinds of properties,
{@link android.Manifest.permission#MANAGE_USERS} is needed.
This permission is not available to third party applications. -->
<permission android:name="android.permission.CREATE_USERS"
@@ -3912,6 +3915,14 @@
<permission android:name="android.permission.CONTROL_DISPLAY_BRIGHTNESS"
android:protectionLevel="signature" />
+ <!-- Allows an application to override the display mode requests
+ so the app requested mode will be selected and user settings and display
+ policies will be ignored.
+ @hide
+ @TestApi -->
+ <permission android:name="android.permission.OVERRIDE_DISPLAY_MODE_REQUESTS"
+ android:protectionLevel="signature" />
+
<!-- @SystemApi Allows an application to control VPN.
<p>Not for use by third-party applications.</p>
@hide -->
@@ -3974,6 +3985,15 @@
<permission android:name="android.permission.CAPTURE_AUDIO_HOTWORD"
android:protectionLevel="signature|privileged" />
+ <!-- Puts an application in the chain of trust for sound trigger
+ operations. Being in the chain of trust allows an application to
+ delegate an identity of a separate entity to the sound trigger system
+ and vouch for the authenticity of this identity.
+ <p>Not for use by third-party applications.</p>
+ @hide -->
+ <permission android:name="android.permission.SOUNDTRIGGER_DELEGATE_IDENTITY"
+ android:protectionLevel="signature|privileged" />
+
<!-- @SystemApi Allows an application to modify audio routing and override policy decisions.
<p>Not for use by third-party applications.</p>
@hide -->
diff --git a/core/res/res/values-af/strings.xml b/core/res/res/values-af/strings.xml
index f5facea7fc26..b54dfc011735 100644
--- a/core/res/res/values-af/strings.xml
+++ b/core/res/res/values-af/strings.xml
@@ -1829,6 +1829,7 @@
<item quantity="other">%d uur lank</item>
<item quantity="one">1 uur lank</item>
</plurals>
+ <string name="zen_mode_until_next_day" msgid="1403042784161725038">"Tot <xliff:g id="FORMATTEDTIME">%1$s</xliff:g>"</string>
<string name="zen_mode_until" msgid="2250286190237669079">"Tot <xliff:g id="FORMATTEDTIME">%1$s</xliff:g>"</string>
<string name="zen_mode_alarm" msgid="7046911727540499275">"Tot <xliff:g id="FORMATTEDTIME">%1$s</xliff:g> (volgende wekker)"</string>
<string name="zen_mode_forever" msgid="740585666364912448">"Totdat jy dit afskakel"</string>
diff --git a/core/res/res/values-am/strings.xml b/core/res/res/values-am/strings.xml
index b1183689c5ee..668ce4232f76 100644
--- a/core/res/res/values-am/strings.xml
+++ b/core/res/res/values-am/strings.xml
@@ -1219,7 +1219,7 @@
<string name="dump_heap_ready_text" msgid="5849618132123045516">"የ<xliff:g id="PROC">%1$s</xliff:g> ሂደት ተራጋፊ ክምር ለማጋራት ለእርስዎ ይገኛል። ይጠንቀቁ፦ ይህ ተራጋፊ ክምር ሂደቱ ሊደርስባቸው የሚችለው ማንኛውም የግል መረጃ ሊኖረው ይችላል፣ ይህ እርስዎ የተየቧቸውን ነገሮች ሊያካትት ይችላል።"</string>
<string name="sendText" msgid="493003724401350724">"ለፅሁፍ ድርጊት ምረጥ"</string>
<string name="volume_ringtone" msgid="134784084629229029">"የስልክ ጥሪ ድምፅ"</string>
- <string name="volume_music" msgid="7727274216734955095">" ማህደረ መረጃ ክፍልፍል"</string>
+ <string name="volume_music" msgid="7727274216734955095">"የማህደረ መረጃ ድምጽ መጠን"</string>
<string name="volume_music_hint_playing_through_bluetooth" msgid="2614142915948898228">"በብሉቱዝ በኩል ማጫወት"</string>
<string name="volume_music_hint_silent_ringtone_selected" msgid="1514829655029062233">"የፀጥታ የስልክ የደውል ድምፅ ተዘጋጅቷል"</string>
<string name="volume_call" msgid="7625321655265747433">"የጥሪ ላይ ድም ፅ መጨመሪያ/መቀነሻ"</string>
@@ -1230,7 +1230,7 @@
<string name="volume_icon_description_bluetooth" msgid="7540388479345558400">"የብሉቱዝ ድምፅ መጠን"</string>
<string name="volume_icon_description_ringer" msgid="2187800636867423459">"የስልክ ጥሪ ድምፅ መጠን"</string>
<string name="volume_icon_description_incall" msgid="4491255105381227919">"የስልክ ጥሪ ድምፅ መጠን"</string>
- <string name="volume_icon_description_media" msgid="4997633254078171233">"የማህደረ መረጃ ክፍልፍል"</string>
+ <string name="volume_icon_description_media" msgid="4997633254078171233">"የማህደረ መረጃ ድምጽ መጠን"</string>
<string name="volume_icon_description_notification" msgid="579091344110747279">"የማሳወቂያ ክፍልፍል"</string>
<string name="ringtone_default" msgid="9118299121288174597">"ነባሪ የስልክ ላይ ጥሪ"</string>
<string name="ringtone_default_with_actual" msgid="2709686194556159773">"ነባሪ (<xliff:g id="ACTUAL_RINGTONE">%1$s</xliff:g>)"</string>
@@ -1829,6 +1829,7 @@
<item quantity="one">ለ%d ሰዓት</item>
<item quantity="other">ለ%d ሰዓት</item>
</plurals>
+ <string name="zen_mode_until_next_day" msgid="1403042784161725038">"እስከ <xliff:g id="FORMATTEDTIME">%1$s</xliff:g>"</string>
<string name="zen_mode_until" msgid="2250286190237669079">"እስከ <xliff:g id="FORMATTEDTIME">%1$s</xliff:g> ድረስ"</string>
<string name="zen_mode_alarm" msgid="7046911727540499275">"እስከ <xliff:g id="FORMATTEDTIME">%1$s</xliff:g> (ቀጣይ ማንቂያ)"</string>
<string name="zen_mode_forever" msgid="740585666364912448">"እስኪያጠፉት ድረስ"</string>
diff --git a/core/res/res/values-ar/strings.xml b/core/res/res/values-ar/strings.xml
index f07fea7bcbbe..fdb351ca52a2 100644
--- a/core/res/res/values-ar/strings.xml
+++ b/core/res/res/values-ar/strings.xml
@@ -1301,7 +1301,7 @@
<string name="volume_ringtone" msgid="134784084629229029">"مستوى صوت الرنين"</string>
<string name="volume_music" msgid="7727274216734955095">"مستوى صوت الوسائط"</string>
<string name="volume_music_hint_playing_through_bluetooth" msgid="2614142915948898228">"تشغيل من خلال البلوتوث"</string>
- <string name="volume_music_hint_silent_ringtone_selected" msgid="1514829655029062233">"تم تعيين نغمة الرنين الصامتة"</string>
+ <string name="volume_music_hint_silent_ringtone_selected" msgid="1514829655029062233">"تم ضبط نغمة الرنين الصامتة"</string>
<string name="volume_call" msgid="7625321655265747433">"مستوى صوت المكالمات الواردة"</string>
<string name="volume_bluetooth_call" msgid="2930204618610115061">"مستوى صوت المكالمة الواردة بالبلوتوث"</string>
<string name="volume_alarm" msgid="4486241060751798448">"مستوى صوت المنبّه"</string>
@@ -1953,6 +1953,7 @@
<item quantity="other">‏لمدة %d من الساعات</item>
<item quantity="one">لمدة ساعة</item>
</plurals>
+ <string name="zen_mode_until_next_day" msgid="1403042784161725038">"حتى <xliff:g id="FORMATTEDTIME">%1$s</xliff:g>"</string>
<string name="zen_mode_until" msgid="2250286190237669079">"حتى <xliff:g id="FORMATTEDTIME">%1$s</xliff:g>"</string>
<string name="zen_mode_alarm" msgid="7046911727540499275">"حتى <xliff:g id="FORMATTEDTIME">%1$s</xliff:g> (التنبيه التالي)"</string>
<string name="zen_mode_forever" msgid="740585666364912448">"إلى أن يتم إيقاف الوضع"</string>
diff --git a/core/res/res/values-as/strings.xml b/core/res/res/values-as/strings.xml
index bad330e75864..93ce15fc73b4 100644
--- a/core/res/res/values-as/strings.xml
+++ b/core/res/res/values-as/strings.xml
@@ -1829,6 +1829,7 @@
<item quantity="one">%d ঘণ্টাৰ বাবে</item>
<item quantity="other">%d ঘণ্টাৰ বাবে</item>
</plurals>
+ <string name="zen_mode_until_next_day" msgid="1403042784161725038">"<xliff:g id="FORMATTEDTIME">%1$s</xliff:g> পৰ্যন্ত"</string>
<string name="zen_mode_until" msgid="2250286190237669079">"<xliff:g id="FORMATTEDTIME">%1$s</xliff:g> পৰ্যন্ত"</string>
<string name="zen_mode_alarm" msgid="7046911727540499275">"<xliff:g id="FORMATTEDTIME">%1$s</xliff:g> (পৰৱৰ্তী এলার্ম) পর্যন্ত"</string>
<string name="zen_mode_forever" msgid="740585666364912448">"আপুনি অফ নকৰা পর্যন্ত"</string>
diff --git a/core/res/res/values-az/strings.xml b/core/res/res/values-az/strings.xml
index c721bbf8c829..01b815b43202 100644
--- a/core/res/res/values-az/strings.xml
+++ b/core/res/res/values-az/strings.xml
@@ -1829,6 +1829,7 @@
<item quantity="other">%d saat üçün</item>
<item quantity="one">1 saat üçün</item>
</plurals>
+ <string name="zen_mode_until_next_day" msgid="1403042784161725038">"Bu vaxtadək: <xliff:g id="FORMATTEDTIME">%1$s</xliff:g>"</string>
<string name="zen_mode_until" msgid="2250286190237669079">"Saat <xliff:g id="FORMATTEDTIME">%1$s</xliff:g> qədər"</string>
<string name="zen_mode_alarm" msgid="7046911727540499275">"<xliff:g id="FORMATTEDTIME">%1$s</xliff:g> radəsinə qədər (növbəti siqnal)"</string>
<string name="zen_mode_forever" msgid="740585666364912448">"Deaktiv edənə qədər"</string>
diff --git a/core/res/res/values-b+sr+Latn/strings.xml b/core/res/res/values-b+sr+Latn/strings.xml
index 93ddbe521ac5..e2fc8f1f7862 100644
--- a/core/res/res/values-b+sr+Latn/strings.xml
+++ b/core/res/res/values-b+sr+Latn/strings.xml
@@ -243,7 +243,7 @@
<string name="global_action_power_off" msgid="4404936470711393203">"Isključi"</string>
<string name="global_action_power_options" msgid="1185286119330160073">"Napajanje"</string>
<string name="global_action_restart" msgid="4678451019561687074">"Restartuj"</string>
- <string name="global_action_emergency" msgid="1387617624177105088">"Hitni poziv"</string>
+ <string name="global_action_emergency" msgid="1387617624177105088">"Hitan poziv"</string>
<string name="global_action_bug_report" msgid="5127867163044170003">"Izveštaj o grešci"</string>
<string name="global_action_logout" msgid="6093581310002476511">"Završi sesiju"</string>
<string name="global_action_screenshot" msgid="2610053466156478564">"Snimak ekrana"</string>
@@ -835,7 +835,7 @@
<string name="lockscreen_instructions_when_pattern_enabled" msgid="7982445492532123308">"Pritisnite „Meni“ da biste otključali telefon ili uputite hitan poziv."</string>
<string name="lockscreen_instructions_when_pattern_disabled" msgid="7434061749374801753">"Pritisnite „Meni“ za otključavanje."</string>
<string name="lockscreen_pattern_instructions" msgid="3169991838169244941">"Unesite šablon za otključavanje"</string>
- <string name="lockscreen_emergency_call" msgid="7549683825868928636">"Hitni poziv"</string>
+ <string name="lockscreen_emergency_call" msgid="7549683825868928636">"Hitan poziv"</string>
<string name="lockscreen_return_to_call" msgid="3156883574692006382">"Nazad na poziv"</string>
<string name="lockscreen_pattern_correct" msgid="8050630103651508582">"Tačno!"</string>
<string name="lockscreen_pattern_wrong" msgid="2940138714468358458">"Probajte ponovo"</string>
@@ -1815,7 +1815,7 @@
<string name="package_updated_device_owner" msgid="7560272363805506941">"Ažurirao je administrator"</string>
<string name="package_deleted_device_owner" msgid="2292335928930293023">"Izbrisao je administrator"</string>
<string name="confirm_battery_saver" msgid="5247976246208245754">"Potvrdi"</string>
- <string name="battery_saver_description_with_learn_more" msgid="4424488535318105801">"Da bi se produžilo trajanje baterije, Ušteda baterije:\n\n• uključuje tamnu temu\n• isključuje ili ograničava aktivnosti u pozadini, neke vizuelne efekte i druge funkcije, na primer, „Ok Google“\n\n"<annotation id="url">"Saznajte više"</annotation></string>
+ <string name="battery_saver_description_with_learn_more" msgid="4424488535318105801">"Da bi se produžilo trajanje baterije, Ušteda baterije:\n\n• uključuje tamnu temu\n• isključuje ili ograničava aktivnosti u pozadini, neke vizuelne efekte i druge funkcije, na primer „Ok Google“\n\n"<annotation id="url">"Saznajte više"</annotation></string>
<string name="battery_saver_description" msgid="6794188153647295212">"Da bi se produžilo trajanje baterije, Ušteda baterije:\n\n• uključuje tamnu temu\n• isključuje ili ograničava aktivnosti u pozadini, neke vizuelne efekte i druge funkcije, na primer, „Ok Google“"</string>
<string name="data_saver_description" msgid="4995164271550590517">"Da bi se smanjila potrošnja podataka, Ušteda podataka sprečava neke aplikacije da šalju ili primaju podatke u pozadini. Aplikacija koju trenutno koristite može da pristupa podacima, ali će to činiti ređe. Na primer, slike se neće prikazivati dok ih ne dodirnete."</string>
<string name="data_saver_enable_title" msgid="7080620065745260137">"Želite da uključite Uštedu podataka?"</string>
@@ -1860,6 +1860,7 @@
<item quantity="few">Za %d s</item>
<item quantity="other">Za %d s</item>
</plurals>
+ <string name="zen_mode_until_next_day" msgid="1403042784161725038">"Do <xliff:g id="FORMATTEDTIME">%1$s</xliff:g>"</string>
<string name="zen_mode_until" msgid="2250286190237669079">"Do <xliff:g id="FORMATTEDTIME">%1$s</xliff:g>"</string>
<string name="zen_mode_alarm" msgid="7046911727540499275">"Do <xliff:g id="FORMATTEDTIME">%1$s</xliff:g> (sledeći alarm)"</string>
<string name="zen_mode_forever" msgid="740585666364912448">"Dok ne isključite"</string>
diff --git a/core/res/res/values-be/strings.xml b/core/res/res/values-be/strings.xml
index 56736c0d6780..45008f2458bb 100644
--- a/core/res/res/values-be/strings.xml
+++ b/core/res/res/values-be/strings.xml
@@ -720,10 +720,10 @@
<item msgid="6216981255272016212">"Асаблівы"</item>
</string-array>
<string-array name="emailAddressTypes">
- <item msgid="7786349763648997741">"Хатні"</item>
- <item msgid="435564470865989199">"Працоўны"</item>
- <item msgid="4199433197875490373">"Іншы"</item>
- <item msgid="3233938986670468328">"Карыстальніцкі"</item>
+ <item msgid="7786349763648997741">"Асабістая"</item>
+ <item msgid="435564470865989199">"Працоўная"</item>
+ <item msgid="4199433197875490373">"Іншая"</item>
+ <item msgid="3233938986670468328">"Карыстальніцкая"</item>
</string-array>
<string-array name="postalAddressTypes">
<item msgid="3861463339764243038">"На Галоўную старонку"</item>
@@ -773,14 +773,14 @@
<string name="phoneTypeWorkPager" msgid="3748332310638505234">"Працоўны пэйджар"</string>
<string name="phoneTypeAssistant" msgid="757550783842231039">"Асістэнт"</string>
<string name="phoneTypeMms" msgid="1799747455131365989">"MMS"</string>
- <string name="eventTypeCustom" msgid="3257367158986466481">"Карыстальніцкі"</string>
+ <string name="eventTypeCustom" msgid="3257367158986466481">"Карыстальніцкае"</string>
<string name="eventTypeBirthday" msgid="7770026752793912283">"Дзень нараджэння"</string>
<string name="eventTypeAnniversary" msgid="4684702412407916888">"Гадавіна"</string>
<string name="eventTypeOther" msgid="530671238533887997">"Іншае"</string>
- <string name="emailTypeCustom" msgid="1809435350482181786">"Карыстальніцкі"</string>
+ <string name="emailTypeCustom" msgid="1809435350482181786">"Карыстальніцкая"</string>
<string name="emailTypeHome" msgid="1597116303154775999">"Хатні"</string>
- <string name="emailTypeWork" msgid="2020095414401882111">"Працоўны"</string>
- <string name="emailTypeOther" msgid="5131130857030897465">"Іншы"</string>
+ <string name="emailTypeWork" msgid="2020095414401882111">"Працоўная"</string>
+ <string name="emailTypeOther" msgid="5131130857030897465">"Іншая"</string>
<string name="emailTypeMobile" msgid="787155077375364230">"Мабільны"</string>
<string name="postalTypeCustom" msgid="5645590470242939129">"Карыстальніцкі"</string>
<string name="postalTypeHome" msgid="7562272480949727912">"Хатні"</string>
@@ -803,19 +803,19 @@
<string name="orgTypeWork" msgid="8684458700669564172">"Працоўная"</string>
<string name="orgTypeOther" msgid="5450675258408005553">"Іншая"</string>
<string name="orgTypeCustom" msgid="1126322047677329218">"Карыстальніцкі"</string>
- <string name="relationTypeCustom" msgid="282938315217441351">"Карыстальніцкі"</string>
+ <string name="relationTypeCustom" msgid="282938315217441351">"Карыстальніцкае"</string>
<string name="relationTypeAssistant" msgid="4057605157116589315">"Памочнік"</string>
<string name="relationTypeBrother" msgid="7141662427379247820">"Брат"</string>
<string name="relationTypeChild" msgid="9076258911292693601">"Дзіця"</string>
- <string name="relationTypeDomesticPartner" msgid="7825306887697559238">"Унутраны Партнёр"</string>
+ <string name="relationTypeDomesticPartner" msgid="7825306887697559238">"Сужыцель/сужыцелька"</string>
<string name="relationTypeFather" msgid="3856225062864790596">"Бацька"</string>
<string name="relationTypeFriend" msgid="3192092625893980574">"Сябар/сяброўка"</string>
<string name="relationTypeManager" msgid="2272860813153171857">"Кіраўнік"</string>
<string name="relationTypeMother" msgid="2331762740982699460">"Маці"</string>
- <string name="relationTypeParent" msgid="4177920938333039882">"Бацька"</string>
+ <string name="relationTypeParent" msgid="4177920938333039882">"Бацька/маці"</string>
<string name="relationTypePartner" msgid="4018017075116766194">"Партнёр"</string>
- <string name="relationTypeReferredBy" msgid="5285082289602849400">"Запрошаны"</string>
- <string name="relationTypeRelative" msgid="3396498519818009134">"Адносны"</string>
+ <string name="relationTypeReferredBy" msgid="5285082289602849400">"Рэкамендацыя"</string>
+ <string name="relationTypeRelative" msgid="3396498519818009134">"Радня"</string>
<string name="relationTypeSister" msgid="3721676005094140671">"Сястра"</string>
<string name="relationTypeSpouse" msgid="6916682664436031703">"Муж/жонка"</string>
<string name="sipAddressTypeCustom" msgid="6283889809842649336">"Карыстальніцкі"</string>
@@ -1891,6 +1891,7 @@
<item quantity="many">На %d гадз</item>
<item quantity="other">На %d гадз</item>
</plurals>
+ <string name="zen_mode_until_next_day" msgid="1403042784161725038">"Да <xliff:g id="FORMATTEDTIME">%1$s</xliff:g>"</string>
<string name="zen_mode_until" msgid="2250286190237669079">"Да <xliff:g id="FORMATTEDTIME">%1$s</xliff:g>"</string>
<string name="zen_mode_alarm" msgid="7046911727540499275">"Да <xliff:g id="FORMATTEDTIME">%1$s</xliff:g> (наступны будзільнік)"</string>
<string name="zen_mode_forever" msgid="740585666364912448">"Пакуль не выключыце"</string>
diff --git a/core/res/res/values-bg/strings.xml b/core/res/res/values-bg/strings.xml
index e49ae26a93ac..0c2c0225eab9 100644
--- a/core/res/res/values-bg/strings.xml
+++ b/core/res/res/values-bg/strings.xml
@@ -1829,6 +1829,7 @@
<item quantity="other">За %d ч</item>
<item quantity="one">За 1 ч</item>
</plurals>
+ <string name="zen_mode_until_next_day" msgid="1403042784161725038">"До <xliff:g id="FORMATTEDTIME">%1$s</xliff:g>"</string>
<string name="zen_mode_until" msgid="2250286190237669079">"До <xliff:g id="FORMATTEDTIME">%1$s</xliff:g>"</string>
<string name="zen_mode_alarm" msgid="7046911727540499275">"До следващия будилник (<xliff:g id="FORMATTEDTIME">%1$s</xliff:g>)"</string>
<string name="zen_mode_forever" msgid="740585666364912448">"До изключване"</string>
diff --git a/core/res/res/values-bn/strings.xml b/core/res/res/values-bn/strings.xml
index 22589cd00b06..b10d8dcad74f 100644
--- a/core/res/res/values-bn/strings.xml
+++ b/core/res/res/values-bn/strings.xml
@@ -1829,6 +1829,8 @@
<item quantity="one">%d ঘন্টার জন্য</item>
<item quantity="other">%d ঘন্টার জন্য</item>
</plurals>
+ <!-- no translation found for zen_mode_until_next_day (1403042784161725038) -->
+ <skip />
<string name="zen_mode_until" msgid="2250286190237669079">"<xliff:g id="FORMATTEDTIME">%1$s</xliff:g> পর্যন্ত"</string>
<string name="zen_mode_alarm" msgid="7046911727540499275">"<xliff:g id="FORMATTEDTIME">%1$s</xliff:g> পর্যন্ত (পরবর্তী অ্যালার্ম)"</string>
<string name="zen_mode_forever" msgid="740585666364912448">"যতক্ষণ না আপনি বন্ধ করছেন"</string>
diff --git a/core/res/res/values-bs/strings.xml b/core/res/res/values-bs/strings.xml
index 755ee472afb3..17c0d1daa0fb 100644
--- a/core/res/res/values-bs/strings.xml
+++ b/core/res/res/values-bs/strings.xml
@@ -593,7 +593,7 @@
<string name="face_acquired_not_detected" msgid="2945945257956443257">"Postavite lice direktno ispred telefona"</string>
<string name="face_acquired_too_much_motion" msgid="8199691445085189528">"Previše pokreta. Držite telefon mirno."</string>
<string name="face_acquired_recalibrate" msgid="8724013080976469746">"Ponovo registrirajte lice."</string>
- <string name="face_acquired_too_different" msgid="4699657338753282542">"Nije više moguće prepoznati lice. Pokušajte opet."</string>
+ <string name="face_acquired_too_different" msgid="4699657338753282542">"Više nije moguće prepoznati lice. Pokušajte opet."</string>
<string name="face_acquired_too_similar" msgid="7684650785108399370">"Previše slično, promijenite položaj."</string>
<string name="face_acquired_pan_too_extreme" msgid="7822191262299152527">"Malo manje zakrenite glavu."</string>
<string name="face_acquired_tilt_too_extreme" msgid="8119978324129248059">"Malo manje zakrenite glavu."</string>
@@ -1860,6 +1860,7 @@
<item quantity="few">%d sata</item>
<item quantity="other">%d sati</item>
</plurals>
+ <string name="zen_mode_until_next_day" msgid="1403042784161725038">"Do <xliff:g id="FORMATTEDTIME">%1$s</xliff:g>"</string>
<string name="zen_mode_until" msgid="2250286190237669079">"Do <xliff:g id="FORMATTEDTIME">%1$s</xliff:g>"</string>
<string name="zen_mode_alarm" msgid="7046911727540499275">"Do <xliff:g id="FORMATTEDTIME">%1$s</xliff:g> (sljedeći alarm)"</string>
<string name="zen_mode_forever" msgid="740585666364912448">"Dok ne isključite"</string>
diff --git a/core/res/res/values-ca/strings.xml b/core/res/res/values-ca/strings.xml
index 4f55bf51c826..e0c709d4e380 100644
--- a/core/res/res/values-ca/strings.xml
+++ b/core/res/res/values-ca/strings.xml
@@ -1224,7 +1224,7 @@
<string name="volume_music_hint_silent_ringtone_selected" msgid="1514829655029062233">"S\'ha establert el so de silenci"</string>
<string name="volume_call" msgid="7625321655265747433">"Volum en trucada"</string>
<string name="volume_bluetooth_call" msgid="2930204618610115061">"Volum en trucada per Bluetooth"</string>
- <string name="volume_alarm" msgid="4486241060751798448">"Volum de l\'alarma"</string>
+ <string name="volume_alarm" msgid="4486241060751798448">"Volum d\'alarma"</string>
<string name="volume_notification" msgid="6864412249031660057">"Volum de notificacions"</string>
<string name="volume_unknown" msgid="4041914008166576293">"Volum"</string>
<string name="volume_icon_description_bluetooth" msgid="7540388479345558400">"Volum del Bluetooth"</string>
@@ -1829,6 +1829,7 @@
<item quantity="other">Durant %d h</item>
<item quantity="one">Durant 1 h</item>
</plurals>
+ <string name="zen_mode_until_next_day" msgid="1403042784161725038">"Finalitza: <xliff:g id="FORMATTEDTIME">%1$s</xliff:g>"</string>
<string name="zen_mode_until" msgid="2250286190237669079">"Fins a les <xliff:g id="FORMATTEDTIME">%1$s</xliff:g>"</string>
<string name="zen_mode_alarm" msgid="7046911727540499275">"Fins a les <xliff:g id="FORMATTEDTIME">%1$s</xliff:g> (propera alarma)"</string>
<string name="zen_mode_forever" msgid="740585666364912448">"Fins que no el desactivis"</string>
diff --git a/core/res/res/values-cs/strings.xml b/core/res/res/values-cs/strings.xml
index fcf9b837cfa1..095ec69af783 100644
--- a/core/res/res/values-cs/strings.xml
+++ b/core/res/res/values-cs/strings.xml
@@ -1891,6 +1891,7 @@
<item quantity="other">%d h</item>
<item quantity="one">1 h</item>
</plurals>
+ <string name="zen_mode_until_next_day" msgid="1403042784161725038">"Do <xliff:g id="FORMATTEDTIME">%1$s</xliff:g>"</string>
<string name="zen_mode_until" msgid="2250286190237669079">"Do <xliff:g id="FORMATTEDTIME">%1$s</xliff:g>"</string>
<string name="zen_mode_alarm" msgid="7046911727540499275">"Do <xliff:g id="FORMATTEDTIME">%1$s</xliff:g> (příští budík)"</string>
<string name="zen_mode_forever" msgid="740585666364912448">"Dokud tuto funkci nevypnete"</string>
diff --git a/core/res/res/values-da/strings.xml b/core/res/res/values-da/strings.xml
index 0b7e396839ef..499b9dee0d87 100644
--- a/core/res/res/values-da/strings.xml
+++ b/core/res/res/values-da/strings.xml
@@ -1829,6 +1829,7 @@
<item quantity="one">I %d t.</item>
<item quantity="other">I %d t.</item>
</plurals>
+ <string name="zen_mode_until_next_day" msgid="1403042784161725038">"Indtil <xliff:g id="FORMATTEDTIME">%1$s</xliff:g>"</string>
<string name="zen_mode_until" msgid="2250286190237669079">"Indtil <xliff:g id="FORMATTEDTIME">%1$s</xliff:g>"</string>
<string name="zen_mode_alarm" msgid="7046911727540499275">"Indtil <xliff:g id="FORMATTEDTIME">%1$s</xliff:g> (næste alarm)"</string>
<string name="zen_mode_forever" msgid="740585666364912448">"Indtil du deaktiverer"</string>
diff --git a/core/res/res/values-de/strings.xml b/core/res/res/values-de/strings.xml
index faa78df458e0..867efac7df21 100644
--- a/core/res/res/values-de/strings.xml
+++ b/core/res/res/values-de/strings.xml
@@ -1829,6 +1829,8 @@
<item quantity="other">Für %d h</item>
<item quantity="one">Für 1 h</item>
</plurals>
+ <!-- no translation found for zen_mode_until_next_day (1403042784161725038) -->
+ <skip />
<string name="zen_mode_until" msgid="2250286190237669079">"Bis <xliff:g id="FORMATTEDTIME">%1$s</xliff:g>"</string>
<string name="zen_mode_alarm" msgid="7046911727540499275">"Bis <xliff:g id="FORMATTEDTIME">%1$s</xliff:g> (nächste Weckzeit)"</string>
<string name="zen_mode_forever" msgid="740585666364912448">"Bis zur Deaktivierung"</string>
diff --git a/core/res/res/values-el/strings.xml b/core/res/res/values-el/strings.xml
index 2e1b56ad9ff9..d4d1c5a43374 100644
--- a/core/res/res/values-el/strings.xml
+++ b/core/res/res/values-el/strings.xml
@@ -1829,6 +1829,7 @@
<item quantity="other">Για %d ώρες</item>
<item quantity="one">Για 1 ώρα</item>
</plurals>
+ <string name="zen_mode_until_next_day" msgid="1403042784161725038">"Έως <xliff:g id="FORMATTEDTIME">%1$s</xliff:g>"</string>
<string name="zen_mode_until" msgid="2250286190237669079">"Έως τις <xliff:g id="FORMATTEDTIME">%1$s</xliff:g>"</string>
<string name="zen_mode_alarm" msgid="7046911727540499275">"Μέχρι τις <xliff:g id="FORMATTEDTIME">%1$s</xliff:g> (επόμενο ξυπνητήρι)"</string>
<string name="zen_mode_forever" msgid="740585666364912448">"Μέχρι την απενεργοποίηση"</string>
diff --git a/core/res/res/values-en-rAU/strings.xml b/core/res/res/values-en-rAU/strings.xml
index dcfbf5a49748..f140ce3fe155 100644
--- a/core/res/res/values-en-rAU/strings.xml
+++ b/core/res/res/values-en-rAU/strings.xml
@@ -1829,6 +1829,7 @@
<item quantity="other">For %d hr</item>
<item quantity="one">For 1 hr</item>
</plurals>
+ <string name="zen_mode_until_next_day" msgid="1403042784161725038">"Until <xliff:g id="FORMATTEDTIME">%1$s</xliff:g>"</string>
<string name="zen_mode_until" msgid="2250286190237669079">"Until <xliff:g id="FORMATTEDTIME">%1$s</xliff:g>"</string>
<string name="zen_mode_alarm" msgid="7046911727540499275">"Until <xliff:g id="FORMATTEDTIME">%1$s</xliff:g> (next alarm)"</string>
<string name="zen_mode_forever" msgid="740585666364912448">"Until you turn off"</string>
diff --git a/core/res/res/values-en-rCA/strings.xml b/core/res/res/values-en-rCA/strings.xml
index 65f2426b6e08..e814bdfbad83 100644
--- a/core/res/res/values-en-rCA/strings.xml
+++ b/core/res/res/values-en-rCA/strings.xml
@@ -1829,6 +1829,7 @@
<item quantity="other">For %d hr</item>
<item quantity="one">For 1 hr</item>
</plurals>
+ <string name="zen_mode_until_next_day" msgid="1403042784161725038">"Until <xliff:g id="FORMATTEDTIME">%1$s</xliff:g>"</string>
<string name="zen_mode_until" msgid="2250286190237669079">"Until <xliff:g id="FORMATTEDTIME">%1$s</xliff:g>"</string>
<string name="zen_mode_alarm" msgid="7046911727540499275">"Until <xliff:g id="FORMATTEDTIME">%1$s</xliff:g> (next alarm)"</string>
<string name="zen_mode_forever" msgid="740585666364912448">"Until you turn off"</string>
diff --git a/core/res/res/values-en-rGB/strings.xml b/core/res/res/values-en-rGB/strings.xml
index d38e2fe844ad..73f1562e5058 100644
--- a/core/res/res/values-en-rGB/strings.xml
+++ b/core/res/res/values-en-rGB/strings.xml
@@ -1829,6 +1829,7 @@
<item quantity="other">For %d hr</item>
<item quantity="one">For 1 hr</item>
</plurals>
+ <string name="zen_mode_until_next_day" msgid="1403042784161725038">"Until <xliff:g id="FORMATTEDTIME">%1$s</xliff:g>"</string>
<string name="zen_mode_until" msgid="2250286190237669079">"Until <xliff:g id="FORMATTEDTIME">%1$s</xliff:g>"</string>
<string name="zen_mode_alarm" msgid="7046911727540499275">"Until <xliff:g id="FORMATTEDTIME">%1$s</xliff:g> (next alarm)"</string>
<string name="zen_mode_forever" msgid="740585666364912448">"Until you turn off"</string>
diff --git a/core/res/res/values-en-rIN/strings.xml b/core/res/res/values-en-rIN/strings.xml
index 30a9bf88619d..122ea7fa7386 100644
--- a/core/res/res/values-en-rIN/strings.xml
+++ b/core/res/res/values-en-rIN/strings.xml
@@ -1829,6 +1829,7 @@
<item quantity="other">For %d hr</item>
<item quantity="one">For 1 hr</item>
</plurals>
+ <string name="zen_mode_until_next_day" msgid="1403042784161725038">"Until <xliff:g id="FORMATTEDTIME">%1$s</xliff:g>"</string>
<string name="zen_mode_until" msgid="2250286190237669079">"Until <xliff:g id="FORMATTEDTIME">%1$s</xliff:g>"</string>
<string name="zen_mode_alarm" msgid="7046911727540499275">"Until <xliff:g id="FORMATTEDTIME">%1$s</xliff:g> (next alarm)"</string>
<string name="zen_mode_forever" msgid="740585666364912448">"Until you turn off"</string>
diff --git a/core/res/res/values-en-rXC/strings.xml b/core/res/res/values-en-rXC/strings.xml
index 6503f7ce65a7..53d8c6653461 100644
--- a/core/res/res/values-en-rXC/strings.xml
+++ b/core/res/res/values-en-rXC/strings.xml
@@ -1829,6 +1829,7 @@
<item quantity="other">‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‏‎‏‎‏‎‎‎‎‏‎‏‏‏‎‏‎‎‎‎‏‏‏‎‎‏‎‎‏‎‏‎‏‎‏‎‎‏‏‏‏‏‏‏‎‎‏‏‎‏‎‏‎‏‎‎‎‏‏‎‎‎‎For %d hr‎‏‎‎‏‎</item>
<item quantity="one">‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‏‎‏‎‏‎‎‎‎‏‎‏‏‏‎‏‎‎‎‎‏‏‏‎‎‏‎‎‏‎‏‎‏‎‏‎‎‏‏‏‏‏‏‏‎‎‏‏‎‏‎‏‎‏‎‎‎‏‏‎‎‎‎For 1 hr‎‏‎‎‏‎</item>
</plurals>
+ <string name="zen_mode_until_next_day" msgid="1403042784161725038">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‎‏‏‎‎‏‏‎‏‏‏‏‎‎‎‏‎‎‏‏‎‏‏‏‏‏‏‏‎‏‎‏‎‎‎‎‎‏‎‏‎‏‎‎‎‏‎‎‏‏‏‏‎‏‎‎‏‏‎‏‏‏‎‎Until ‎‏‎‎‏‏‎<xliff:g id="FORMATTEDTIME">%1$s</xliff:g>‎‏‎‎‏‏‏‎‎‏‎‎‏‎"</string>
<string name="zen_mode_until" msgid="2250286190237669079">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‎‏‏‏‏‏‏‎‎‏‏‏‎‏‎‏‎‎‏‏‏‏‏‎‏‎‏‏‏‏‎‎‎‎‏‎‏‎‎‎‎‎‏‎‏‎‏‎‏‎‏‎‏‏‎‏‏‎‏‎‏‏‏‎Until ‎‏‎‎‏‏‎<xliff:g id="FORMATTEDTIME">%1$s</xliff:g>‎‏‎‎‏‏‏‎‎‏‎‎‏‎"</string>
<string name="zen_mode_alarm" msgid="7046911727540499275">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‏‎‎‎‎‏‏‏‎‎‏‎‏‏‏‎‏‎‏‎‎‎‏‏‏‎‎‎‎‏‎‏‎‎‏‎‏‏‏‎‎‏‎‏‎‏‏‏‏‏‎‎‏‏‎‏‎‎‏‎‏‏‎Until ‎‏‎‎‏‏‎<xliff:g id="FORMATTEDTIME">%1$s</xliff:g>‎‏‎‎‏‏‏‎ (next alarm)‎‏‎‎‏‎"</string>
<string name="zen_mode_forever" msgid="740585666364912448">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‎‎‏‎‏‎‎‏‎‎‎‏‏‏‎‎‎‏‎‏‏‎‏‎‏‏‏‏‎‏‎‏‏‎‎‎‎‏‎‏‏‎‏‏‎‏‎‎‏‏‎‎‏‏‎‏‎‎‎‎‎‎‎Until you turn off‎‏‎‎‏‎"</string>
diff --git a/core/res/res/values-es-rUS/strings.xml b/core/res/res/values-es-rUS/strings.xml
index 2308b5a521bd..953b6fc3a9d7 100644
--- a/core/res/res/values-es-rUS/strings.xml
+++ b/core/res/res/values-es-rUS/strings.xml
@@ -1829,6 +1829,7 @@
<item quantity="other">Durante %d horas</item>
<item quantity="one">Durante 1 hora</item>
</plurals>
+ <string name="zen_mode_until_next_day" msgid="1403042784161725038">"Hasta las <xliff:g id="FORMATTEDTIME">%1$s</xliff:g>"</string>
<string name="zen_mode_until" msgid="2250286190237669079">"Hasta la(s) <xliff:g id="FORMATTEDTIME">%1$s</xliff:g>"</string>
<string name="zen_mode_alarm" msgid="7046911727540499275">"Hasta la hora <xliff:g id="FORMATTEDTIME">%1$s</xliff:g> (próxima alarma)"</string>
<string name="zen_mode_forever" msgid="740585666364912448">"Hasta que lo desactives"</string>
diff --git a/core/res/res/values-es/strings.xml b/core/res/res/values-es/strings.xml
index 67350cb04fe1..465d2ec63d3a 100644
--- a/core/res/res/values-es/strings.xml
+++ b/core/res/res/values-es/strings.xml
@@ -1829,6 +1829,7 @@
<item quantity="other">Durante %d horas</item>
<item quantity="one">Durante 1 hora</item>
</plurals>
+ <string name="zen_mode_until_next_day" msgid="1403042784161725038">"Hasta las <xliff:g id="FORMATTEDTIME">%1$s</xliff:g>"</string>
<string name="zen_mode_until" msgid="2250286190237669079">"Hasta <xliff:g id="FORMATTEDTIME">%1$s</xliff:g>"</string>
<string name="zen_mode_alarm" msgid="7046911727540499275">"Hasta las <xliff:g id="FORMATTEDTIME">%1$s</xliff:g> (próxima alarma)"</string>
<string name="zen_mode_forever" msgid="740585666364912448">"Hasta que lo desactives"</string>
diff --git a/core/res/res/values-et/strings.xml b/core/res/res/values-et/strings.xml
index 3929ed4d50da..6d2a4c0f2bfb 100644
--- a/core/res/res/values-et/strings.xml
+++ b/core/res/res/values-et/strings.xml
@@ -1829,6 +1829,7 @@
<item quantity="other">%d h</item>
<item quantity="one">1 h</item>
</plurals>
+ <string name="zen_mode_until_next_day" msgid="1403042784161725038">"Kuni <xliff:g id="FORMATTEDTIME">%1$s</xliff:g>"</string>
<string name="zen_mode_until" msgid="2250286190237669079">"Kuni <xliff:g id="FORMATTEDTIME">%1$s</xliff:g>"</string>
<string name="zen_mode_alarm" msgid="7046911727540499275">"Kuni <xliff:g id="FORMATTEDTIME">%1$s</xliff:g> (järgmine äratus)"</string>
<string name="zen_mode_forever" msgid="740585666364912448">"Kuni välja lülitate"</string>
diff --git a/core/res/res/values-eu/strings.xml b/core/res/res/values-eu/strings.xml
index c93dbbfda5ef..7241b974c3c8 100644
--- a/core/res/res/values-eu/strings.xml
+++ b/core/res/res/values-eu/strings.xml
@@ -1829,6 +1829,7 @@
<item quantity="other">%d orduz</item>
<item quantity="one">Ordubetez</item>
</plurals>
+ <string name="zen_mode_until_next_day" msgid="1403042784161725038">"<xliff:g id="FORMATTEDTIME">%1$s</xliff:g> arte"</string>
<string name="zen_mode_until" msgid="2250286190237669079">"<xliff:g id="FORMATTEDTIME">%1$s</xliff:g> arte"</string>
<string name="zen_mode_alarm" msgid="7046911727540499275">"<xliff:g id="FORMATTEDTIME">%1$s</xliff:g> arte (hurrengo alarma)"</string>
<string name="zen_mode_forever" msgid="740585666364912448">"Zuk desaktibatu arte"</string>
diff --git a/core/res/res/values-fa/strings.xml b/core/res/res/values-fa/strings.xml
index 1ee4be59733b..02f4728de45e 100644
--- a/core/res/res/values-fa/strings.xml
+++ b/core/res/res/values-fa/strings.xml
@@ -1267,7 +1267,7 @@
<string name="sms_control_title" msgid="4748684259903148341">"درحال ارسال پیامک‌ها"</string>
<string name="sms_control_message" msgid="6574313876316388239">"‏&lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; درحال ارسال تعداد زیادی پیامک است. آیا اجازه می‌دهید این برنامه همچنان پیامک ارسال کند؟"</string>
<string name="sms_control_yes" msgid="4858845109269524622">"مجاز است"</string>
- <string name="sms_control_no" msgid="4845717880040355570">"اجازه ندارد"</string>
+ <string name="sms_control_no" msgid="4845717880040355570">"مجاز نبودن"</string>
<string name="sms_short_code_confirm_message" msgid="1385416688897538724">"‏&lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; مایل است پیامی به &lt;b&gt;<xliff:g id="DEST_ADDRESS">%2$s</xliff:g>&lt;/b&gt; ارسال کند."</string>
<string name="sms_short_code_details" msgid="2723725738333388351">"این مورد "<b>"شاید هزینه‌ای"</b>" را به حساب دستگاه همراهتان بگذارد."</string>
<string name="sms_premium_short_code_details" msgid="1400296309866638111"><b>"این مورد هزینه‌ای را به حساب دستگاه همراهتان می‌گذارد."</b></string>
@@ -1413,7 +1413,7 @@
<string name="grant_credentials_permission_message_footer" msgid="1886710210516246461">"می‌خواهید به این درخواست اجازه دهید؟"</string>
<string name="grant_permissions_header_text" msgid="3420736827804657201">"درخواست دسترسی"</string>
<string name="allow" msgid="6195617008611933762">"ارزیابی‌شده"</string>
- <string name="deny" msgid="6632259981847676572">"اجازه ندارد"</string>
+ <string name="deny" msgid="6632259981847676572">"مجاز نبودن"</string>
<string name="permission_request_notification_title" msgid="1810025922441048273">"مجوز درخواست شد"</string>
<string name="permission_request_notification_with_subtitle" msgid="3743417870360129298">"مجوز\nبرای حساب <xliff:g id="ACCOUNT">%s</xliff:g> درخواست شد."</string>
<string name="forward_intent_to_owner" msgid="4620359037192871015">"شما از این برنامه در خارج از نمایه کاری‌تان استفاده می‌کنید"</string>
@@ -1639,7 +1639,7 @@
<string name="accessibility_service_action_perform_title" msgid="779670378951658160">"مشاهده و انجام کنش‌ها"</string>
<string name="accessibility_service_action_perform_description" msgid="2718852014003170558">"این عملکرد می‌تواند با برنامه یا حسگری سخت‌افزاری تعاملاتتان را ردیابی کند و ازطرف شما با برنامه‌ها تعامل داشته باشد."</string>
<string name="accessibility_dialog_button_allow" msgid="2092558122987144530">"مجاز"</string>
- <string name="accessibility_dialog_button_deny" msgid="4129575637812472671">"رد کردن"</string>
+ <string name="accessibility_dialog_button_deny" msgid="4129575637812472671">"مجاز نبودن"</string>
<string name="accessibility_select_shortcut_menu_title" msgid="6002726538854613272">"برای استفاده از ویژگی، روی آن ضربه بزنید:"</string>
<string name="accessibility_edit_shortcut_menu_button_title" msgid="239446795930436325">"انتخاب ویژگی‌های موردنظر برای استفاده با دکمه دسترس‌پذیری"</string>
<string name="accessibility_edit_shortcut_menu_volume_title" msgid="1077294237378645981">"انتخاب ویژگی‌های موردنظر برای استفاده با میان‌بر کلید میزان صدا"</string>
@@ -1829,6 +1829,7 @@
<item quantity="one">‏برای %d ساعت</item>
<item quantity="other">‏برای %d ساعت</item>
</plurals>
+ <string name="zen_mode_until_next_day" msgid="1403042784161725038">"تا <xliff:g id="FORMATTEDTIME">%1$s</xliff:g>"</string>
<string name="zen_mode_until" msgid="2250286190237669079">"تا <xliff:g id="FORMATTEDTIME">%1$s</xliff:g>"</string>
<string name="zen_mode_alarm" msgid="7046911727540499275">"تا <xliff:g id="FORMATTEDTIME">%1$s</xliff:g> (زنگ بعدی)"</string>
<string name="zen_mode_forever" msgid="740585666364912448">"تا زمانی‌که آن را خاموش کنید"</string>
diff --git a/core/res/res/values-fi/strings.xml b/core/res/res/values-fi/strings.xml
index 1eff983e803f..0c94f35de91b 100644
--- a/core/res/res/values-fi/strings.xml
+++ b/core/res/res/values-fi/strings.xml
@@ -1829,6 +1829,7 @@
<item quantity="other">%d tunnin ajan</item>
<item quantity="one">1 tunnin ajan</item>
</plurals>
+ <string name="zen_mode_until_next_day" msgid="1403042784161725038">"<xliff:g id="FORMATTEDTIME">%1$s</xliff:g> asti"</string>
<string name="zen_mode_until" msgid="2250286190237669079">"Kunnes kello on <xliff:g id="FORMATTEDTIME">%1$s</xliff:g>"</string>
<string name="zen_mode_alarm" msgid="7046911727540499275">"<xliff:g id="FORMATTEDTIME">%1$s</xliff:g> asti (seuraava hälytys)"</string>
<string name="zen_mode_forever" msgid="740585666364912448">"Kunnes laitat pois päältä"</string>
diff --git a/core/res/res/values-fr-rCA/strings.xml b/core/res/res/values-fr-rCA/strings.xml
index c6f347b1833b..dcfe8ad100cb 100644
--- a/core/res/res/values-fr-rCA/strings.xml
+++ b/core/res/res/values-fr-rCA/strings.xml
@@ -1829,6 +1829,7 @@
<item quantity="one">Pendant %d h</item>
<item quantity="other">Pendant %d h</item>
</plurals>
+ <string name="zen_mode_until_next_day" msgid="1403042784161725038">"Jusqu\'à <xliff:g id="FORMATTEDTIME">%1$s</xliff:g>"</string>
<string name="zen_mode_until" msgid="2250286190237669079">"Jusqu\'à <xliff:g id="FORMATTEDTIME">%1$s</xliff:g>"</string>
<string name="zen_mode_alarm" msgid="7046911727540499275">"Jusqu\'à <xliff:g id="FORMATTEDTIME">%1$s</xliff:g> (alarme suivante)"</string>
<string name="zen_mode_forever" msgid="740585666364912448">"Jusqu\'à la désactivation"</string>
diff --git a/core/res/res/values-fr/strings.xml b/core/res/res/values-fr/strings.xml
index 6307c354317b..5fc2d6918a19 100644
--- a/core/res/res/values-fr/strings.xml
+++ b/core/res/res/values-fr/strings.xml
@@ -1829,6 +1829,7 @@
<item quantity="one">Pendant %d h</item>
<item quantity="other">Pendant %d h</item>
</plurals>
+ <string name="zen_mode_until_next_day" msgid="1403042784161725038">"Jusqu\'à <xliff:g id="FORMATTEDTIME">%1$s</xliff:g>"</string>
<string name="zen_mode_until" msgid="2250286190237669079">"Jusqu\'à <xliff:g id="FORMATTEDTIME">%1$s</xliff:g>"</string>
<string name="zen_mode_alarm" msgid="7046911727540499275">"Jusqu\'à <xliff:g id="FORMATTEDTIME">%1$s</xliff:g> (alarme suivante)"</string>
<string name="zen_mode_forever" msgid="740585666364912448">"Jusqu\'à la désactivation"</string>
diff --git a/core/res/res/values-gl/strings.xml b/core/res/res/values-gl/strings.xml
index 0f281469bbf1..a6eac55b8caa 100644
--- a/core/res/res/values-gl/strings.xml
+++ b/core/res/res/values-gl/strings.xml
@@ -1829,6 +1829,7 @@
<item quantity="other">Durante %d h</item>
<item quantity="one">Durante unha h</item>
</plurals>
+ <string name="zen_mode_until_next_day" msgid="1403042784161725038">"Ata: <xliff:g id="FORMATTEDTIME">%1$s</xliff:g>"</string>
<string name="zen_mode_until" msgid="2250286190237669079">"Ata as <xliff:g id="FORMATTEDTIME">%1$s</xliff:g>"</string>
<string name="zen_mode_alarm" msgid="7046911727540499275">"Ata as <xliff:g id="FORMATTEDTIME">%1$s</xliff:g> (próxima alarma)"</string>
<string name="zen_mode_forever" msgid="740585666364912448">"Ata a desactivación"</string>
diff --git a/core/res/res/values-gu/strings.xml b/core/res/res/values-gu/strings.xml
index 5ffc420b13b4..6b4bbf36a447 100644
--- a/core/res/res/values-gu/strings.xml
+++ b/core/res/res/values-gu/strings.xml
@@ -1138,7 +1138,7 @@
<string name="whichGiveAccessToApplicationLabel" msgid="7805857277166106236">"ઍક્સેસ આપો"</string>
<string name="whichEditApplication" msgid="6191568491456092812">"આનાથી સંપાદિત કરો"</string>
<string name="whichEditApplicationNamed" msgid="8096494987978521514">"%1$s સાથે સંપાદિત કરો"</string>
- <string name="whichEditApplicationLabel" msgid="1463288652070140285">"સંપાદિત કરો"</string>
+ <string name="whichEditApplicationLabel" msgid="1463288652070140285">"ફેરફાર કરો"</string>
<string name="whichSendApplication" msgid="4143847974460792029">"શેર કરો"</string>
<string name="whichSendApplicationNamed" msgid="4470386782693183461">"%1$s સાથે શેર કરો"</string>
<string name="whichSendApplicationLabel" msgid="7467813004769188515">"શેર કરો"</string>
@@ -1224,7 +1224,7 @@
<string name="volume_music_hint_silent_ringtone_selected" msgid="1514829655029062233">"સાઇલેન્ટ રિંગટોન સેટ કરી"</string>
<string name="volume_call" msgid="7625321655265747433">"ઇન-કૉલ વૉલ્યૂમ"</string>
<string name="volume_bluetooth_call" msgid="2930204618610115061">"બ્લૂટૂથ ઇન-કૉલ વૉલ્યૂમ"</string>
- <string name="volume_alarm" msgid="4486241060751798448">"એલાર્મ વૉલ્યૂમ"</string>
+ <string name="volume_alarm" msgid="4486241060751798448">"અલાર્મ વૉલ્યૂમ"</string>
<string name="volume_notification" msgid="6864412249031660057">"સૂચના વૉલ્યૂમ"</string>
<string name="volume_unknown" msgid="4041914008166576293">"વૉલ્યૂમ"</string>
<string name="volume_icon_description_bluetooth" msgid="7540388479345558400">"બ્લૂટૂથ વૉલ્યૂમ"</string>
@@ -1513,7 +1513,7 @@
<string name="storage_usb_drive" msgid="448030813201444573">"USB ડ્રાઇવ"</string>
<string name="storage_usb_drive_label" msgid="6631740655876540521">"<xliff:g id="MANUFACTURER">%s</xliff:g> USB ડ્રાઇવ"</string>
<string name="storage_usb" msgid="2391213347883616886">"USB સ્ટોરેજ"</string>
- <string name="extract_edit_menu_button" msgid="63954536535863040">"સંપાદિત કરો"</string>
+ <string name="extract_edit_menu_button" msgid="63954536535863040">"ફેરફાર કરો"</string>
<string name="data_usage_warning_title" msgid="9034893717078325845">"ડેટા ચેતવણી"</string>
<string name="data_usage_warning_body" msgid="1669325367188029454">"તમે <xliff:g id="APP">%s</xliff:g> ડેટા વાપર્યો છે"</string>
<string name="data_usage_mobile_limit_title" msgid="3911447354393775241">"મોબાઇલ ડેટાની મર્યાદા આવી ગઈ"</string>
@@ -1829,6 +1829,8 @@
<item quantity="one">%d કલાક માટે</item>
<item quantity="other">%d કલાક માટે</item>
</plurals>
+ <!-- no translation found for zen_mode_until_next_day (1403042784161725038) -->
+ <skip />
<string name="zen_mode_until" msgid="2250286190237669079">"<xliff:g id="FORMATTEDTIME">%1$s</xliff:g> સુધી"</string>
<string name="zen_mode_alarm" msgid="7046911727540499275">"<xliff:g id="FORMATTEDTIME">%1$s</xliff:g> (આગલા એલાર્મ) સુધી"</string>
<string name="zen_mode_forever" msgid="740585666364912448">"તમે બંધ ન કરો ત્યાં સુધી"</string>
diff --git a/core/res/res/values-hi/strings.xml b/core/res/res/values-hi/strings.xml
index 7b0bd238d7f7..3bf9dcd90ee2 100644
--- a/core/res/res/values-hi/strings.xml
+++ b/core/res/res/values-hi/strings.xml
@@ -1829,6 +1829,7 @@
<item quantity="one">%d घंटे के लिए</item>
<item quantity="other">%d घंटे के लिए</item>
</plurals>
+ <string name="zen_mode_until_next_day" msgid="1403042784161725038">"<xliff:g id="FORMATTEDTIME">%1$s</xliff:g> तक"</string>
<string name="zen_mode_until" msgid="2250286190237669079">"<xliff:g id="FORMATTEDTIME">%1$s</xliff:g> तक"</string>
<string name="zen_mode_alarm" msgid="7046911727540499275">"<xliff:g id="FORMATTEDTIME">%1$s</xliff:g> (अगले अलार्म) तक"</string>
<string name="zen_mode_forever" msgid="740585666364912448">"जब तक आप बंद नहीं करते"</string>
diff --git a/core/res/res/values-hr/strings.xml b/core/res/res/values-hr/strings.xml
index eb41c9f8f6f8..d350d04b15f4 100644
--- a/core/res/res/values-hr/strings.xml
+++ b/core/res/res/values-hr/strings.xml
@@ -1860,6 +1860,7 @@
<item quantity="few">%d h</item>
<item quantity="other">%d h</item>
</plurals>
+ <string name="zen_mode_until_next_day" msgid="1403042784161725038">"Do <xliff:g id="FORMATTEDTIME">%1$s</xliff:g>"</string>
<string name="zen_mode_until" msgid="2250286190237669079">"Do <xliff:g id="FORMATTEDTIME">%1$s</xliff:g>"</string>
<string name="zen_mode_alarm" msgid="7046911727540499275">"Do <xliff:g id="FORMATTEDTIME">%1$s</xliff:g> (sljedeći alarm)"</string>
<string name="zen_mode_forever" msgid="740585666364912448">"Dok ne isključite"</string>
diff --git a/core/res/res/values-hu/strings.xml b/core/res/res/values-hu/strings.xml
index 3b44295755c1..70f87b689edc 100644
--- a/core/res/res/values-hu/strings.xml
+++ b/core/res/res/values-hu/strings.xml
@@ -1829,6 +1829,7 @@
<item quantity="other">%d órán keresztül</item>
<item quantity="one">1 órán keresztül</item>
</plurals>
+ <string name="zen_mode_until_next_day" msgid="1403042784161725038">"Eddig: <xliff:g id="FORMATTEDTIME">%1$s</xliff:g>"</string>
<string name="zen_mode_until" msgid="2250286190237669079">"Eddig: <xliff:g id="FORMATTEDTIME">%1$s</xliff:g>"</string>
<string name="zen_mode_alarm" msgid="7046911727540499275">"Eddig: <xliff:g id="FORMATTEDTIME">%1$s</xliff:g> (ez a következő ébresztés)"</string>
<string name="zen_mode_forever" msgid="740585666364912448">"Kikapcsolásig"</string>
diff --git a/core/res/res/values-hy/strings.xml b/core/res/res/values-hy/strings.xml
index 4f66b6e8a947..32ac6a8beb2a 100644
--- a/core/res/res/values-hy/strings.xml
+++ b/core/res/res/values-hy/strings.xml
@@ -48,7 +48,7 @@
<string name="invalidPin" msgid="7542498253319440408">"Մուտքագրեք PIN, որը 4-ից 8 թիվ է:"</string>
<string name="invalidPuk" msgid="8831151490931907083">"Մուտքագրեք PUK, որն 8 կամ ավել թիվ ունի:"</string>
<string name="needPuk" msgid="7321876090152422918">"Ձեր SIM քարտը PUK-ով կողպված է: Մուտքագրեք PUK կոդը այն ապակողպելու համար:"</string>
- <string name="needPuk2" msgid="7032612093451537186">"Մուտքագրեք PUK2-ը` SIM քարտն արգելահանելու համար:"</string>
+ <string name="needPuk2" msgid="7032612093451537186">"Մուտքագրեք PUK2-ը՝ SIM քարտն արգելահանելու համար:"</string>
<string name="enablePin" msgid="2543771964137091212">"Ձախողվեց: Միացրեք SIM/RUIM կողպումը:"</string>
<plurals name="pinpuk_attempts" formatted="false" msgid="1619867269012213584">
<item quantity="one">Մնաց <xliff:g id="NUMBER_1">%d</xliff:g> փորձ, որից հետո SIM քարտն արգելափակվելու է:</item>
@@ -411,7 +411,7 @@
<string name="permdesc_writeCallLog" product="tablet" msgid="2657525794731690397">"Թույլ է տալիս հավելվածին փոփոխել ձեր պլանշետի զանգերի մատյանը, այդ թվում` մուտքային և ելքային զանգերի մասին տվյալները: Վնասարար հավելվածները կարող են սա օգտագործել` ձեր զանգերի մատյանը ջնջելու կամ փոփոխելու համար:"</string>
<string name="permdesc_writeCallLog" product="tv" msgid="3934939195095317432">"Թույլ է տալիս հավելվածին փոփոխել Android TV սարքի զանգերի մատյանը, այդ թվում՝ մուտքային և ելքային զանգերի մասին տվյալները: Վնասարար հավելվածները կարող են սա օգտագործել՝ ձեր զանգերի մատյանը ջնջելու կամ փոփոխելու համար:"</string>
<string name="permdesc_writeCallLog" product="default" msgid="5903033505665134802">"Թույլ է տալիս հավելվածին փոփոխել ձեր հեռախոսի զանգերի մատյանը, այդ թվում` մուտքային և ելքային զանգերի մասին տվյալները: Վնասարար հավելվածները կարող են սա օգտագործել` ձեր զանգերի մատյանը ջնջելու կամ փոփոխելու համար:"</string>
- <string name="permlab_bodySensors" msgid="3411035315357380862">"օգտագործել մարմնի սենսորները (օրինակ` սրտի կծկումների հաճախականության չափիչ)"</string>
+ <string name="permlab_bodySensors" msgid="3411035315357380862">"օգտագործել մարմնի սենսորները (օրինակ՝ սրտի կծկումների հաճախականության չափիչ)"</string>
<string name="permdesc_bodySensors" product="default" msgid="2365357960407973997">"Հավելվածին թույլ է տալիս մուտք ունենալ սենսորների տվյալներին, որոնք վերահսկում են ձեր ֆիզիկական վիճակը, օրինակ՝ ձեր սրտի զարկերը:"</string>
<string name="permlab_readCalendar" msgid="6408654259475396200">"Կարդալ օրացույցի միջոցառումները և տվյալները"</string>
<string name="permdesc_readCalendar" product="tablet" msgid="515452384059803326">"Այս հավելվածը կարող է կարդալ օրացույցի՝ ձեր պլանշետում պահված բոլոր միջոցառումները, ինչպես նաև հրապարակել կամ պահել ձեր օրացույցի տվյալները:"</string>
@@ -430,7 +430,7 @@
<string name="permlab_accessBackgroundLocation" msgid="1721164702777366138">"տեղադրության մասին տվյալների հասանելիություն ֆոնային ռեժիմում"</string>
<string name="permdesc_accessBackgroundLocation" msgid="8264885066095638105">"Այս հավելվածը ցանկացած ժամանակ կարող է տեսնել տեղադրության տվյալները, նույնիսկ երբ այն ակտիվ չէ։"</string>
<string name="permlab_modifyAudioSettings" msgid="6129039778010031815">"փոխել ձեր աուդիո կարգավորումները"</string>
- <string name="permdesc_modifyAudioSettings" msgid="8687227609663124921">"Թույլ է տալիս հավելվածին փոփոխել ձայնանյութի գլոբալ կարգավորումները, ինչպես օրինակ` ձայնը և թե որ խոսափողն է օգտագործված արտածման համար:"</string>
+ <string name="permdesc_modifyAudioSettings" msgid="8687227609663124921">"Թույլ է տալիս հավելվածին փոփոխել ձայնանյութի գլոբալ կարգավորումները, ինչպես օրինակ՝ ձայնը և թե որ խոսափողն է օգտագործված արտածման համար:"</string>
<string name="permlab_recordAudio" msgid="1208457423054219147">"ձայնագրել աուդիո ֆայլ"</string>
<string name="permdesc_recordAudio" msgid="3976213377904701093">"Այս հավելվածը ցանկացած պահի կարող է ձայնագրել խոսափողի օգնությամբ:"</string>
<string name="permlab_sim_communication" msgid="176788115994050692">"ուղարկել հրամաններ SIM քարտին"</string>
@@ -495,7 +495,7 @@
<string name="permlab_changeTetherState" msgid="9079611809931863861">"փոխել միացված կապը"</string>
<string name="permdesc_changeTetherState" msgid="3025129606422533085">"Թույլ է տալիս հավելվածին փոխել կապված ցանցի միացման կարգավիճակը:"</string>
<string name="permlab_accessWifiState" msgid="5552488500317911052">"դիտել Wi-Fi կապերը"</string>
- <string name="permdesc_accessWifiState" msgid="6913641669259483363">"Թույլ է տալիս հավելվածին տեսնել Wi-Fi ցանցի տեղեկություններ, ինչպես օրինակ` արդյոք Wi-Fi-ը միացված է, թե` ոչ, և միացված Wi-Fi սարքի անունը:"</string>
+ <string name="permdesc_accessWifiState" msgid="6913641669259483363">"Թույլ է տալիս հավելվածին տեսնել Wi-Fi ցանցի տեղեկություններ, ինչպես օրինակ՝ արդյոք Wi-Fi-ը միացված է, թե` ոչ, և միացված Wi-Fi սարքի անունը:"</string>
<string name="permlab_changeWifiState" msgid="7947824109713181554">"միանալ Wi-Fi-ին և անջատվել դրանից"</string>
<string name="permdesc_changeWifiState" msgid="7170350070554505384">"Թույլ է տալիս հավելվածին միանալ Wi-Fi մուտքի կետերին և անջատվել այդ կետերից, ինչպես նաև կատարել սարքի կարգավորման փոփոխություններ Wi-Fi ցանցերի համար:"</string>
<string name="permlab_changeWifiMulticastState" msgid="285626875870754696">"թույլատրել Բազմասփյուռ Wi-Fi-ի ընդունումը"</string>
@@ -615,7 +615,7 @@
</string-array>
<string name="face_icon_content_description" msgid="465030547475916280">"Դեմքի պատկերակ"</string>
<string name="permlab_readSyncSettings" msgid="6250532864893156277">"կարդալ համաժամացման կարգավորումները"</string>
- <string name="permdesc_readSyncSettings" msgid="1325658466358779298">"Թույլ է տալիս հավելվածին կարդալ համաժամացման կարգավորումները հաշվի համար: Օրինակ` այն կարող է որոշել, արդյոք Մարդիկ հավելվածը համաժամացված է հաշվի հետ:"</string>
+ <string name="permdesc_readSyncSettings" msgid="1325658466358779298">"Թույլ է տալիս հավելվածին կարդալ համաժամացման կարգավորումները հաշվի համար: Օրինակ՝ այն կարող է որոշել, արդյոք Մարդիկ հավելվածը համաժամացված է հաշվի հետ:"</string>
<string name="permlab_writeSyncSettings" msgid="6583154300780427399">"համաժամացումը փոխարկել միացվածի և անջատվածի"</string>
<string name="permdesc_writeSyncSettings" msgid="6029151549667182687">"Թույլ է տալիս հավելվածին փոփոխել համաժամացման կարգավորումները հաշվի համար: Օրինակ, այն կարող է օգտագործվել` միացնելու Մարդիկ հավելվածի համաժամացումը հաշվի հետ:"</string>
<string name="permlab_readSyncStats" msgid="3747407238320105332">"կարդալ համաժամացման վիճակագրությունը"</string>
@@ -876,7 +876,7 @@
<string name="lockscreen_forgot_pattern_button_text" msgid="8362442730606839031">"Մոռացե՞լ եք սխեման:"</string>
<string name="lockscreen_glogin_forgot_pattern" msgid="9218940117797602518">"Հաշվի ապակողպում"</string>
<string name="lockscreen_glogin_too_many_attempts" msgid="3775904917743034195">"Չափից շատ սխեմայի փորձեր"</string>
- <string name="lockscreen_glogin_instructions" msgid="4695162942525531700">"Ապակողպելու համար` մուտք գործեք ձեր Google հաշվով:"</string>
+ <string name="lockscreen_glogin_instructions" msgid="4695162942525531700">"Ապակողպելու համար՝ մուտք գործեք ձեր Google հաշվով:"</string>
<string name="lockscreen_glogin_username_hint" msgid="6916101478673157045">"Օգտանուն (էլփոստ)"</string>
<string name="lockscreen_glogin_password_hint" msgid="3031027901286812848">"Գաղտնաբառ"</string>
<string name="lockscreen_glogin_submit_button" msgid="3590556636347843733">"Մուտք գործել"</string>
@@ -1407,7 +1407,7 @@
<string name="ime_action_done" msgid="6299921014822891569">"Պատրաստ է"</string>
<string name="ime_action_previous" msgid="6548799326860401611">"Նախորդ"</string>
<string name="ime_action_default" msgid="8265027027659800121">"Կատարել"</string>
- <string name="dial_number_using" msgid="6060769078933953531">"Հավաքել հեռախոսահամարը`\nօգտագործելով <xliff:g id="NUMBER">%s</xliff:g>-ը"</string>
+ <string name="dial_number_using" msgid="6060769078933953531">"Հավաքել հեռախոսահամարը՝\nօգտագործելով <xliff:g id="NUMBER">%s</xliff:g>-ը"</string>
<string name="create_contact_using" msgid="6200708808003692594">"Ստեղծել կոնտակտ`\nօգտագործելով <xliff:g id="NUMBER">%s</xliff:g>-ը"</string>
<string name="grant_credentials_permission_message_header" msgid="5365733888842570481">"Հետևյալ մեկ կամ մի քանի հավելվածներին թույլտվություն է անհրաժեշտ՝ այժմ և հետագայում ձեր հաշվի տվյալներն օգտագործելու համար։"</string>
<string name="grant_credentials_permission_message_footer" msgid="1886710210516246461">"Թույլատրե՞լ"</string>
@@ -1474,7 +1474,7 @@
<string name="number_picker_increment_button" msgid="7621013714795186298">"Ավելացնել"</string>
<string name="number_picker_decrement_button" msgid="5116948444762708204">"Նվազեցնել"</string>
<string name="number_picker_increment_scroll_mode" msgid="8403893549806805985">"<xliff:g id="VALUE">%s</xliff:g> հպեք և պահեք:"</string>
- <string name="number_picker_increment_scroll_action" msgid="8310191318914268271">"Սահեցրեք վերև` ավելացնելու համար, և ներքև` նվազեցնելու համար:"</string>
+ <string name="number_picker_increment_scroll_action" msgid="8310191318914268271">"Սահեցրեք վերև՝ ավելացնելու համար, և ներքև՝ նվազեցնելու համար:"</string>
<string name="time_picker_increment_minute_button" msgid="7195870222945784300">"Աճեցնել րոպեն"</string>
<string name="time_picker_decrement_minute_button" msgid="230925389943411490">"Նվազեցնել րոպեն"</string>
<string name="time_picker_increment_hour_button" msgid="3063572723197178242">"Աճեցնել ժամը"</string>
@@ -1598,7 +1598,7 @@
<string name="kg_invalid_puk" msgid="4809502818518963344">"Վերամուտքագրեք ճիշտ PUK ծածկագիրը: Կրկնվող փորձերը ընդմիշտ կկասեցնեն SIM քարտը:"</string>
<string name="kg_invalid_confirm_pin_hint" product="default" msgid="4705368340409816254">"PIN ծածկագրերը չեն համընկնում"</string>
<string name="kg_login_too_many_attempts" msgid="699292728290654121">"Չափից շատ սխեմայի փորձեր"</string>
- <string name="kg_login_instructions" msgid="3619844310339066827">"Ապակողպելու համար` մուտք գործեք ձեր Google հաշվով:"</string>
+ <string name="kg_login_instructions" msgid="3619844310339066827">"Ապակողպելու համար՝ մուտք գործեք ձեր Google հաշվով:"</string>
<string name="kg_login_username_hint" msgid="1765453775467133251">"Օգտանուն (էլփոստ)"</string>
<string name="kg_login_password_hint" msgid="3330530727273164402">"Գաղտնաբառը"</string>
<string name="kg_login_submit_button" msgid="893611277617096870">"Մուտք գործել"</string>
@@ -1829,6 +1829,7 @@
<item quantity="one">%d ժամով</item>
<item quantity="other">%d ժամով</item>
</plurals>
+ <string name="zen_mode_until_next_day" msgid="1403042784161725038">"Մինչև <xliff:g id="FORMATTEDTIME">%1$s</xliff:g>"</string>
<string name="zen_mode_until" msgid="2250286190237669079">"Մինչև <xliff:g id="FORMATTEDTIME">%1$s</xliff:g>"</string>
<string name="zen_mode_alarm" msgid="7046911727540499275">"Մինչև ժ. <xliff:g id="FORMATTEDTIME">%1$s</xliff:g>-ը (հաջորդ զարթուցիչը)"</string>
<string name="zen_mode_forever" msgid="740585666364912448">"Մինչև չանջատեք"</string>
diff --git a/core/res/res/values-in/strings.xml b/core/res/res/values-in/strings.xml
index f4fb83deba39..f8ba016e1530 100644
--- a/core/res/res/values-in/strings.xml
+++ b/core/res/res/values-in/strings.xml
@@ -1234,7 +1234,7 @@
<string name="volume_icon_description_notification" msgid="579091344110747279">"Volume pemberitahuan"</string>
<string name="ringtone_default" msgid="9118299121288174597">"Nada dering default"</string>
<string name="ringtone_default_with_actual" msgid="2709686194556159773">"Default (<xliff:g id="ACTUAL_RINGTONE">%1$s</xliff:g>)"</string>
- <string name="ringtone_silent" msgid="397111123930141876">"Tidak Ada"</string>
+ <string name="ringtone_silent" msgid="397111123930141876">"Tidak ada"</string>
<string name="ringtone_picker_title" msgid="667342618626068253">"Nada dering"</string>
<string name="ringtone_picker_title_alarm" msgid="7438934548339024767">"Suara alarm"</string>
<string name="ringtone_picker_title_notification" msgid="6387191794719608122">"Suara notifikasi"</string>
@@ -1829,6 +1829,7 @@
<item quantity="other">Selama %d jam</item>
<item quantity="one">Selama 1 jam</item>
</plurals>
+ <string name="zen_mode_until_next_day" msgid="1403042784161725038">"Sampai <xliff:g id="FORMATTEDTIME">%1$s</xliff:g>"</string>
<string name="zen_mode_until" msgid="2250286190237669079">"Hingga <xliff:g id="FORMATTEDTIME">%1$s</xliff:g>"</string>
<string name="zen_mode_alarm" msgid="7046911727540499275">"Hingga <xliff:g id="FORMATTEDTIME">%1$s</xliff:g> (alarm berikutnya)"</string>
<string name="zen_mode_forever" msgid="740585666364912448">"Sampai Anda menonaktifkannya"</string>
diff --git a/core/res/res/values-is/strings.xml b/core/res/res/values-is/strings.xml
index 899fa52fbb9f..9278bbb5f595 100644
--- a/core/res/res/values-is/strings.xml
+++ b/core/res/res/values-is/strings.xml
@@ -1829,6 +1829,7 @@
<item quantity="one">Í %d klst.</item>
<item quantity="other">Í %d klst.</item>
</plurals>
+ <string name="zen_mode_until_next_day" msgid="1403042784161725038">"Til <xliff:g id="FORMATTEDTIME">%1$s</xliff:g>"</string>
<string name="zen_mode_until" msgid="2250286190237669079">"Til <xliff:g id="FORMATTEDTIME">%1$s</xliff:g>"</string>
<string name="zen_mode_alarm" msgid="7046911727540499275">"Þangað til <xliff:g id="FORMATTEDTIME">%1$s</xliff:g> (næsta viðvörun)"</string>
<string name="zen_mode_forever" msgid="740585666364912448">"Þar til þú slekkur"</string>
diff --git a/core/res/res/values-it/strings.xml b/core/res/res/values-it/strings.xml
index 14bfa03bb706..b962d8b7c3c8 100644
--- a/core/res/res/values-it/strings.xml
+++ b/core/res/res/values-it/strings.xml
@@ -1829,6 +1829,7 @@
<item quantity="other">Per %d ore</item>
<item quantity="one">Per 1 ora</item>
</plurals>
+ <string name="zen_mode_until_next_day" msgid="1403042784161725038">"Fino a: <xliff:g id="FORMATTEDTIME">%1$s</xliff:g>"</string>
<string name="zen_mode_until" msgid="2250286190237669079">"Fino a <xliff:g id="FORMATTEDTIME">%1$s</xliff:g>"</string>
<string name="zen_mode_alarm" msgid="7046911727540499275">"Fino a <xliff:g id="FORMATTEDTIME">%1$s</xliff:g> (prossima sveglia)"</string>
<string name="zen_mode_forever" msgid="740585666364912448">"Fino alla disattivazione"</string>
diff --git a/core/res/res/values-iw/strings.xml b/core/res/res/values-iw/strings.xml
index 2b6d22681f52..821e4fd0acbf 100644
--- a/core/res/res/values-iw/strings.xml
+++ b/core/res/res/values-iw/strings.xml
@@ -1891,6 +1891,7 @@
<item quantity="other">‏למשך %d שעות</item>
<item quantity="one">למשך שעה אחת</item>
</plurals>
+ <string name="zen_mode_until_next_day" msgid="1403042784161725038">"עד <xliff:g id="FORMATTEDTIME">%1$s</xliff:g>"</string>
<string name="zen_mode_until" msgid="2250286190237669079">"עד <xliff:g id="FORMATTEDTIME">%1$s</xliff:g>"</string>
<string name="zen_mode_alarm" msgid="7046911727540499275">"עד <xliff:g id="FORMATTEDTIME">%1$s</xliff:g> (ההתראה הבאה)"</string>
<string name="zen_mode_forever" msgid="740585666364912448">"עד הכיבוי"</string>
diff --git a/core/res/res/values-ja/strings.xml b/core/res/res/values-ja/strings.xml
index fdb505b696dc..4b48495a562e 100644
--- a/core/res/res/values-ja/strings.xml
+++ b/core/res/res/values-ja/strings.xml
@@ -1829,6 +1829,7 @@
<item quantity="other">%d時間</item>
<item quantity="one">1時間</item>
</plurals>
+ <string name="zen_mode_until_next_day" msgid="1403042784161725038">"<xliff:g id="FORMATTEDTIME">%1$s</xliff:g>まで"</string>
<string name="zen_mode_until" msgid="2250286190237669079">"<xliff:g id="FORMATTEDTIME">%1$s</xliff:g>まで"</string>
<string name="zen_mode_alarm" msgid="7046911727540499275">"<xliff:g id="FORMATTEDTIME">%1$s</xliff:g>(次のアラーム)まで"</string>
<string name="zen_mode_forever" msgid="740585666364912448">"自分が OFF にするまで"</string>
diff --git a/core/res/res/values-ka/strings.xml b/core/res/res/values-ka/strings.xml
index 3d0d88b9e823..cdac64757058 100644
--- a/core/res/res/values-ka/strings.xml
+++ b/core/res/res/values-ka/strings.xml
@@ -1829,6 +1829,7 @@
<item quantity="other">%d სთ.</item>
<item quantity="one">1 სთ.</item>
</plurals>
+ <string name="zen_mode_until_next_day" msgid="1403042784161725038">"<xliff:g id="FORMATTEDTIME">%1$s</xliff:g>-მდე"</string>
<string name="zen_mode_until" msgid="2250286190237669079">"<xliff:g id="FORMATTEDTIME">%1$s</xliff:g>-მდე"</string>
<string name="zen_mode_alarm" msgid="7046911727540499275">"<xliff:g id="FORMATTEDTIME">%1$s</xliff:g>-მდე (შემდეგი მაღვიძარა)"</string>
<string name="zen_mode_forever" msgid="740585666364912448">"გამორთვამდე"</string>
diff --git a/core/res/res/values-kk/strings.xml b/core/res/res/values-kk/strings.xml
index 6d063b647c41..608ed1e0186b 100644
--- a/core/res/res/values-kk/strings.xml
+++ b/core/res/res/values-kk/strings.xml
@@ -1792,7 +1792,7 @@
<string name="package_updated_device_owner" msgid="7560272363805506941">"Әкімші жаңартқан"</string>
<string name="package_deleted_device_owner" msgid="2292335928930293023">"Әкімші жойған"</string>
<string name="confirm_battery_saver" msgid="5247976246208245754">"Жарайды"</string>
- <string name="battery_saver_description_with_learn_more" msgid="4424488535318105801">"Батарея жұмысының ұзақтығын арттыру үшін Battery Saver:\n\n•қараңғы тақырыпты іске қосады;\n•фондық әрекеттерді, кейбір көрнекі әсерлерді және \"Ok Google\" сияқты басқа да функцияларды өшіреді не шектейді.\n\n"<annotation id="url">"Толығырақ"</annotation></string>
+ <string name="battery_saver_description_with_learn_more" msgid="4424488535318105801">"Батарея жұмысының ұзақтығын арттыру үшін Батареяны үнемдеу режимі:\n\n•қараңғы тақырыпты іске қосады;\n•фондық әрекеттерді, кейбір көрнекі әсерлерді және \"Ok Google\" сияқты басқа да функцияларды өшіреді не шектейді.\n\n"<annotation id="url">"Толығырақ"</annotation></string>
<string name="battery_saver_description" msgid="6794188153647295212">"Батарея ұзағырақ жұмыс істеуі үшін, Battery Saver:\n\n• қараңғы тақырыпты қосады;\n•фондық жұмысты, кейбір визуалды әсерлерді және \"Ok Google\" сияқты басқа функцияларды өшіреді не шектейді."</string>
<string name="data_saver_description" msgid="4995164271550590517">"Дерек шығынын азайту үшін Data Saver функциясы кейбір қолданбаларға деректерді фондық режимде жіберуге және алуға жол бермейді. Ашық тұрған қолданба деректерді пайдаланады, бірақ шектеулі шамада (мысалы, кескіндер оларды түрткенге дейін көрсетілмейді)."</string>
<string name="data_saver_enable_title" msgid="7080620065745260137">"Data Saver функциясын қосу керек пе?"</string>
@@ -1829,6 +1829,7 @@
<item quantity="other">%d сағат</item>
<item quantity="one">1 сағат</item>
</plurals>
+ <string name="zen_mode_until_next_day" msgid="1403042784161725038">"<xliff:g id="FORMATTEDTIME">%1$s</xliff:g> дейін"</string>
<string name="zen_mode_until" msgid="2250286190237669079">"<xliff:g id="FORMATTEDTIME">%1$s</xliff:g> дейін"</string>
<string name="zen_mode_alarm" msgid="7046911727540499275">"<xliff:g id="FORMATTEDTIME">%1$s</xliff:g> дейін (келесі дабыл)"</string>
<string name="zen_mode_forever" msgid="740585666364912448">"Өшірілгенге дейін"</string>
@@ -1912,7 +1913,7 @@
<string name="conference_call" msgid="5731633152336490471">"Конференциялық қоңырау"</string>
<string name="tooltip_popup_title" msgid="7863719020269945722">"Қалқыма сөзкөмек"</string>
<string name="app_category_game" msgid="4534216074910244790">"Ойындар"</string>
- <string name="app_category_audio" msgid="8296029904794676222">"Музыка және аудиомазмұн"</string>
+ <string name="app_category_audio" msgid="8296029904794676222">"Музыка және аудио"</string>
<string name="app_category_video" msgid="2590183854839565814">"Фильм және бейне"</string>
<string name="app_category_image" msgid="7307840291864213007">"Суреттер және кескіндер"</string>
<string name="app_category_social" msgid="2278269325488344054">"Әлеуметтік қолданба мен байланыс"</string>
@@ -2001,7 +2002,7 @@
<string name="dynamic_mode_notification_channel_name" msgid="2986926422100223328">"Режим туралы хабарландыру"</string>
<string name="dynamic_mode_notification_title" msgid="9205715501274608016">"Батарея заряды азаюы мүмкін"</string>
<string name="dynamic_mode_notification_summary" msgid="4141614604437372157">"Батарея ұзаққа жетуі үшін, Battery Saver іске қосылды"</string>
- <string name="battery_saver_notification_channel_name" msgid="3918243458067916913">"Battery Saver"</string>
+ <string name="battery_saver_notification_channel_name" msgid="3918243458067916913">"Батареяны үнемдеу режимі"</string>
<string name="battery_saver_off_notification_title" msgid="7637255960468032515">"Battery Saver өшірілді"</string>
<string name="battery_saver_charged_notification_summary" product="default" msgid="5544457317418624367">"Телефонның заряды жеткілікті. Функцияларға енді шектеу қойылмайды."</string>
<string name="battery_saver_charged_notification_summary" product="tablet" msgid="4426317048139996888">"Планшеттің заряды жеткілікті. Функцияларға енді шектеу қойылмайды."</string>
diff --git a/core/res/res/values-km/strings.xml b/core/res/res/values-km/strings.xml
index b9378def3491..14b0189a4aa0 100644
--- a/core/res/res/values-km/strings.xml
+++ b/core/res/res/values-km/strings.xml
@@ -1829,6 +1829,7 @@
<item quantity="other">អស់រយៈពេល %d ម៉ោង</item>
<item quantity="one">អស់រយៈពេល 1 ម៉ោង</item>
</plurals>
+ <string name="zen_mode_until_next_day" msgid="1403042784161725038">"រហូត​ដល់​ម៉ោង <xliff:g id="FORMATTEDTIME">%1$s</xliff:g>"</string>
<string name="zen_mode_until" msgid="2250286190237669079">"រហូត​ដល់ <xliff:g id="FORMATTEDTIME">%1$s</xliff:g>"</string>
<string name="zen_mode_alarm" msgid="7046911727540499275">"រហូតដល់ <xliff:g id="FORMATTEDTIME">%1$s</xliff:g> (ម៉ោងរោទិ៍បន្ទាប់)"</string>
<string name="zen_mode_forever" msgid="740585666364912448">"រហូតទាល់តែ​អ្នកបិទ"</string>
@@ -2001,7 +2002,7 @@
<string name="dynamic_mode_notification_channel_name" msgid="2986926422100223328">"ការ​ជូនដំណឹង​ព័ត៌មាន​របស់​មុខងារ​ទម្លាប់"</string>
<string name="dynamic_mode_notification_title" msgid="9205715501274608016">"ថ្ម​អាច​នឹង​អស់ មុនពេល​សាកថ្មធម្មតា"</string>
<string name="dynamic_mode_notification_summary" msgid="4141614604437372157">"បាន​បើក​ដំណើរការកម្មវិធី​សន្សំ​ថ្ម ដើម្បីបង្កើនកម្រិត​ថាមពល​​ថ្ម"</string>
- <string name="battery_saver_notification_channel_name" msgid="3918243458067916913">"កម្មវិធីសន្សំថ្ម"</string>
+ <string name="battery_saver_notification_channel_name" msgid="3918243458067916913">"មុខងារ​សន្សំ​ថ្ម"</string>
<string name="battery_saver_off_notification_title" msgid="7637255960468032515">"កម្មវិធី​សន្សំ​ថ្ម​ត្រូវបានបិទ"</string>
<string name="battery_saver_charged_notification_summary" product="default" msgid="5544457317418624367">"ទូរសព្ទ​មាន​កម្រិតថ្ម​គ្រប់គ្រាន់​។ មុខងារ​ផ្សេងៗ​មិន​ត្រូវបាន​រឹតបន្តឹងទៀត​ទេ។"</string>
<string name="battery_saver_charged_notification_summary" product="tablet" msgid="4426317048139996888">"ថេប្លេត​មាន​កម្រិតថ្ម​គ្រប់គ្រាន់​។ មុខងារ​ផ្សេងៗ​មិន​ត្រូវបាន​រឹតបន្តឹងទៀត​ទេ។"</string>
diff --git a/core/res/res/values-kn/strings.xml b/core/res/res/values-kn/strings.xml
index c7786aebe9e4..0fe1c24bddf0 100644
--- a/core/res/res/values-kn/strings.xml
+++ b/core/res/res/values-kn/strings.xml
@@ -1829,6 +1829,8 @@
<item quantity="one">%d ಗಂಟೆಗೆ</item>
<item quantity="other">%d ಗಂಟೆಗೆ</item>
</plurals>
+ <!-- no translation found for zen_mode_until_next_day (1403042784161725038) -->
+ <skip />
<string name="zen_mode_until" msgid="2250286190237669079">"<xliff:g id="FORMATTEDTIME">%1$s</xliff:g> ವರೆಗೆ"</string>
<string name="zen_mode_alarm" msgid="7046911727540499275">"<xliff:g id="FORMATTEDTIME">%1$s</xliff:g> ವರೆಗೆ (ಮುಂದಿನ ಅಲಾರಮ್)"</string>
<string name="zen_mode_forever" msgid="740585666364912448">"ನೀವು ಆಫ್ ಮಾಡುವವರೆಗೆ"</string>
diff --git a/core/res/res/values-ko/strings.xml b/core/res/res/values-ko/strings.xml
index 78c3286eecf3..e3406c1bf003 100644
--- a/core/res/res/values-ko/strings.xml
+++ b/core/res/res/values-ko/strings.xml
@@ -1829,6 +1829,7 @@
<item quantity="other">%d시간 동안</item>
<item quantity="one">1시간 동안</item>
</plurals>
+ <string name="zen_mode_until_next_day" msgid="1403042784161725038">"<xliff:g id="FORMATTEDTIME">%1$s</xliff:g>까지"</string>
<string name="zen_mode_until" msgid="2250286190237669079">"<xliff:g id="FORMATTEDTIME">%1$s</xliff:g>까지"</string>
<string name="zen_mode_alarm" msgid="7046911727540499275">"<xliff:g id="FORMATTEDTIME">%1$s</xliff:g>(다음 알람)까지"</string>
<string name="zen_mode_forever" msgid="740585666364912448">"사용 중지할 때까지"</string>
diff --git a/core/res/res/values-ky/strings.xml b/core/res/res/values-ky/strings.xml
index 162b4d3fdb26..3268d16c1df9 100644
--- a/core/res/res/values-ky/strings.xml
+++ b/core/res/res/values-ky/strings.xml
@@ -1829,6 +1829,7 @@
<item quantity="other">%d саатка</item>
<item quantity="one">1 саатка</item>
</plurals>
+ <string name="zen_mode_until_next_day" msgid="1403042784161725038">"<xliff:g id="FORMATTEDTIME">%1$s</xliff:g> чейин"</string>
<string name="zen_mode_until" msgid="2250286190237669079">"<xliff:g id="FORMATTEDTIME">%1$s</xliff:g> чейин"</string>
<string name="zen_mode_alarm" msgid="7046911727540499275">"<xliff:g id="FORMATTEDTIME">%1$s</xliff:g> чейин (кийинки ойготкуч)"</string>
<string name="zen_mode_forever" msgid="740585666364912448">"Бул функция өчүрүлгөнгө чейин"</string>
diff --git a/core/res/res/values-lo/strings.xml b/core/res/res/values-lo/strings.xml
index 44511398c694..d22ce0c1f964 100644
--- a/core/res/res/values-lo/strings.xml
+++ b/core/res/res/values-lo/strings.xml
@@ -1829,6 +1829,7 @@
<item quantity="other">ເປັນ​ເວ​ລາ %d ຊມ</item>
<item quantity="one">ເປັນ​ເວ​ລາ 1 ຊມ</item>
</plurals>
+ <string name="zen_mode_until_next_day" msgid="1403042784161725038">"ຈົນຮອດ <xliff:g id="FORMATTEDTIME">%1$s</xliff:g>"</string>
<string name="zen_mode_until" msgid="2250286190237669079">"ຈົນ​ຮອດ <xliff:g id="FORMATTEDTIME">%1$s</xliff:g>"</string>
<string name="zen_mode_alarm" msgid="7046911727540499275">"ຈົນ​ກ​່​ວາ <xliff:g id="FORMATTEDTIME">%1$s</xliff:g> (ສັນ​ຍານ​ເຕືອນ​ຕໍ່ໄປ​)"</string>
<string name="zen_mode_forever" msgid="740585666364912448">"ຈົນກວ່າທ່ານຈະປິດ"</string>
diff --git a/core/res/res/values-lt/strings.xml b/core/res/res/values-lt/strings.xml
index fa0797846de4..ff687c575396 100644
--- a/core/res/res/values-lt/strings.xml
+++ b/core/res/res/values-lt/strings.xml
@@ -1891,6 +1891,7 @@
<item quantity="many">%d val.</item>
<item quantity="other">%d val.</item>
</plurals>
+ <string name="zen_mode_until_next_day" msgid="1403042784161725038">"Iki <xliff:g id="FORMATTEDTIME">%1$s</xliff:g>"</string>
<string name="zen_mode_until" msgid="2250286190237669079">"Iki <xliff:g id="FORMATTEDTIME">%1$s</xliff:g>"</string>
<string name="zen_mode_alarm" msgid="7046911727540499275">"Iki <xliff:g id="FORMATTEDTIME">%1$s</xliff:g> (kitas signalas)"</string>
<string name="zen_mode_forever" msgid="740585666364912448">"Kol išjungsite"</string>
diff --git a/core/res/res/values-lv/strings.xml b/core/res/res/values-lv/strings.xml
index e8107fb0c744..2cb6058c1ef7 100644
--- a/core/res/res/values-lv/strings.xml
+++ b/core/res/res/values-lv/strings.xml
@@ -1860,6 +1860,7 @@
<item quantity="one">%d h</item>
<item quantity="other">%d h</item>
</plurals>
+ <string name="zen_mode_until_next_day" msgid="1403042784161725038">"Līdz: <xliff:g id="FORMATTEDTIME">%1$s</xliff:g>"</string>
<string name="zen_mode_until" msgid="2250286190237669079">"Līdz <xliff:g id="FORMATTEDTIME">%1$s</xliff:g>"</string>
<string name="zen_mode_alarm" msgid="7046911727540499275">"Līdz plkst. <xliff:g id="FORMATTEDTIME">%1$s</xliff:g> (nākamais signāls)"</string>
<string name="zen_mode_forever" msgid="740585666364912448">"Līdz brīdim, kad izslēgsiet"</string>
diff --git a/core/res/res/values-mcc310-mnc150-as/strings.xml b/core/res/res/values-mcc310-mnc150-as/strings.xml
deleted file mode 100644
index 2a4e46bd6107..000000000000
--- a/core/res/res/values-mcc310-mnc150-as/strings.xml
+++ /dev/null
@@ -1,24 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!--
-/* //device/apps/common/assets/res/any/strings.xml
-**
-** Copyright 2006, 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:android="http://schemas.android.com/apk/res/android"
- xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="mmcc_illegal_me" msgid="8004509200390992737">"ফ\'নৰ অনুমতি নাই MM#6"</string>
-</resources>
diff --git a/core/res/res/values-mk/strings.xml b/core/res/res/values-mk/strings.xml
index df7781e089dd..bf7220c7586e 100644
--- a/core/res/res/values-mk/strings.xml
+++ b/core/res/res/values-mk/strings.xml
@@ -1829,6 +1829,7 @@
<item quantity="one">За %d ч.</item>
<item quantity="other">За %d ч.</item>
</plurals>
+ <string name="zen_mode_until_next_day" msgid="1403042784161725038">"До <xliff:g id="FORMATTEDTIME">%1$s</xliff:g>"</string>
<string name="zen_mode_until" msgid="2250286190237669079">"До <xliff:g id="FORMATTEDTIME">%1$s</xliff:g>"</string>
<string name="zen_mode_alarm" msgid="7046911727540499275">"До <xliff:g id="FORMATTEDTIME">%1$s</xliff:g> (следниот аларм)"</string>
<string name="zen_mode_forever" msgid="740585666364912448">"Додека не го исклучите"</string>
diff --git a/core/res/res/values-ml/strings.xml b/core/res/res/values-ml/strings.xml
index 12c0cb4025ee..513ed1d6c2e6 100644
--- a/core/res/res/values-ml/strings.xml
+++ b/core/res/res/values-ml/strings.xml
@@ -1829,6 +1829,7 @@
<item quantity="other">%d മണിക്കൂറത്തേക്ക്</item>
<item quantity="one">ഒരു മണിക്കൂറത്തേക്ക്</item>
</plurals>
+ <string name="zen_mode_until_next_day" msgid="1403042784161725038">"<xliff:g id="FORMATTEDTIME">%1$s</xliff:g> വരെ"</string>
<string name="zen_mode_until" msgid="2250286190237669079">"<xliff:g id="FORMATTEDTIME">%1$s</xliff:g> വരെ"</string>
<string name="zen_mode_alarm" msgid="7046911727540499275">"<xliff:g id="FORMATTEDTIME">%1$s</xliff:g> വരെ (അടുത്ത അലാറം)"</string>
<string name="zen_mode_forever" msgid="740585666364912448">"നിങ്ങൾ ഓഫാക്കുന്നത് വരെ"</string>
diff --git a/core/res/res/values-mn/strings.xml b/core/res/res/values-mn/strings.xml
index 2c041dced482..b95b6897eb7a 100644
--- a/core/res/res/values-mn/strings.xml
+++ b/core/res/res/values-mn/strings.xml
@@ -1829,6 +1829,7 @@
<item quantity="other">%d цагийн турш</item>
<item quantity="one">1 цагийн турш:</item>
</plurals>
+ <string name="zen_mode_until_next_day" msgid="1403042784161725038">"<xliff:g id="FORMATTEDTIME">%1$s</xliff:g> хүртэл"</string>
<string name="zen_mode_until" msgid="2250286190237669079">"<xliff:g id="FORMATTEDTIME">%1$s</xliff:g> хүртэл"</string>
<string name="zen_mode_alarm" msgid="7046911727540499275">"<xliff:g id="FORMATTEDTIME">%1$s</xliff:g> хүртэл (дараагийн сэрүүлэг)"</string>
<string name="zen_mode_forever" msgid="740585666364912448">"Таныг унтраах хүртэл"</string>
diff --git a/core/res/res/values-mr/strings.xml b/core/res/res/values-mr/strings.xml
index da6f2b71c65a..40c7dbd384c9 100644
--- a/core/res/res/values-mr/strings.xml
+++ b/core/res/res/values-mr/strings.xml
@@ -1829,6 +1829,8 @@
<item quantity="other">%d तासासाठी</item>
<item quantity="one">1 तासासाठी</item>
</plurals>
+ <!-- no translation found for zen_mode_until_next_day (1403042784161725038) -->
+ <skip />
<string name="zen_mode_until" msgid="2250286190237669079">"<xliff:g id="FORMATTEDTIME">%1$s</xliff:g> पर्यंत"</string>
<string name="zen_mode_alarm" msgid="7046911727540499275">"<xliff:g id="FORMATTEDTIME">%1$s</xliff:g> पर्यंत (पुढील अलार्म)"</string>
<string name="zen_mode_forever" msgid="740585666364912448">"तुम्ही बंद करेपर्यंत"</string>
diff --git a/core/res/res/values-ms/strings.xml b/core/res/res/values-ms/strings.xml
index 2e78b45cbb67..fb24a06dd3a5 100644
--- a/core/res/res/values-ms/strings.xml
+++ b/core/res/res/values-ms/strings.xml
@@ -1829,6 +1829,7 @@
<item quantity="other">Selama %d jam</item>
<item quantity="one">Selama 1 jam</item>
</plurals>
+ <string name="zen_mode_until_next_day" msgid="1403042784161725038">"Hingga <xliff:g id="FORMATTEDTIME">%1$s</xliff:g>"</string>
<string name="zen_mode_until" msgid="2250286190237669079">"Sehingga <xliff:g id="FORMATTEDTIME">%1$s</xliff:g>"</string>
<string name="zen_mode_alarm" msgid="7046911727540499275">"Sehingga <xliff:g id="FORMATTEDTIME">%1$s</xliff:g> (penggera akan datang)"</string>
<string name="zen_mode_forever" msgid="740585666364912448">"Sehingga anda matikan"</string>
diff --git a/core/res/res/values-my/strings.xml b/core/res/res/values-my/strings.xml
index 1ebafa47f76e..2c3fbcbeac41 100644
--- a/core/res/res/values-my/strings.xml
+++ b/core/res/res/values-my/strings.xml
@@ -1829,6 +1829,7 @@
<item quantity="other">%d နာရီ အတွက်</item>
<item quantity="one">၁ နာရီအတွက်</item>
</plurals>
+ <string name="zen_mode_until_next_day" msgid="1403042784161725038">"<xliff:g id="FORMATTEDTIME">%1$s</xliff:g> အထိ"</string>
<string name="zen_mode_until" msgid="2250286190237669079">"<xliff:g id="FORMATTEDTIME">%1$s</xliff:g>အထိ"</string>
<string name="zen_mode_alarm" msgid="7046911727540499275">"<xliff:g id="FORMATTEDTIME">%1$s</xliff:g> အထိ (လာမည့် နှိုးစက်)"</string>
<string name="zen_mode_forever" msgid="740585666364912448">"သင်ပိတ်လိုက်သည် အထိ"</string>
diff --git a/core/res/res/values-nb/strings.xml b/core/res/res/values-nb/strings.xml
index ac21aec0080e..fced2070c93b 100644
--- a/core/res/res/values-nb/strings.xml
+++ b/core/res/res/values-nb/strings.xml
@@ -1829,6 +1829,7 @@
<item quantity="other">I %d timer</item>
<item quantity="one">I én time</item>
</plurals>
+ <string name="zen_mode_until_next_day" msgid="1403042784161725038">"Til <xliff:g id="FORMATTEDTIME">%1$s</xliff:g>"</string>
<string name="zen_mode_until" msgid="2250286190237669079">"Til <xliff:g id="FORMATTEDTIME">%1$s</xliff:g>"</string>
<string name="zen_mode_alarm" msgid="7046911727540499275">"Til <xliff:g id="FORMATTEDTIME">%1$s</xliff:g> (neste alarm)"</string>
<string name="zen_mode_forever" msgid="740585666364912448">"Til du slår av"</string>
diff --git a/core/res/res/values-ne/strings.xml b/core/res/res/values-ne/strings.xml
index ee0362e4a184..fadabe83dd55 100644
--- a/core/res/res/values-ne/strings.xml
+++ b/core/res/res/values-ne/strings.xml
@@ -1829,6 +1829,8 @@
<item quantity="other">%d घन्टाका लागि</item>
<item quantity="one">१ घन्टाको लागि</item>
</plurals>
+ <!-- no translation found for zen_mode_until_next_day (1403042784161725038) -->
+ <skip />
<string name="zen_mode_until" msgid="2250286190237669079">"<xliff:g id="FORMATTEDTIME">%1$s</xliff:g> सम्म"</string>
<string name="zen_mode_alarm" msgid="7046911727540499275">"<xliff:g id="FORMATTEDTIME">%1$s</xliff:g> (अर्को अलार्म) सम्म"</string>
<string name="zen_mode_forever" msgid="740585666364912448">"तपाईंले निष्क्रिय नपार्नुभएसम्म"</string>
diff --git a/core/res/res/values-nl/strings.xml b/core/res/res/values-nl/strings.xml
index 29666a9aa5bd..337bf17fd7fa 100644
--- a/core/res/res/values-nl/strings.xml
+++ b/core/res/res/values-nl/strings.xml
@@ -934,9 +934,9 @@
<string name="js_dialog_before_unload" msgid="7213364985774778744">"<xliff:g id="MESSAGE">%s</xliff:g>\n\nWeet u zeker dat u deze pagina wilt verlaten?"</string>
<string name="save_password_label" msgid="9161712335355510035">"Bevestigen"</string>
<string name="double_tap_toast" msgid="7065519579174882778">"Tip: dubbeltik om in en uit te zoomen."</string>
- <string name="autofill_this_form" msgid="3187132440451621492">"Autom. aanvullen"</string>
- <string name="setup_autofill" msgid="5431369130866618567">"Autom. aanvullen instellen"</string>
- <string name="autofill_window_title" msgid="4379134104008111961">"Automatisch aanvullen met <xliff:g id="SERVICENAME">%1$s</xliff:g>"</string>
+ <string name="autofill_this_form" msgid="3187132440451621492">"Autom. invullen"</string>
+ <string name="setup_autofill" msgid="5431369130866618567">"Autom. invullen instellen"</string>
+ <string name="autofill_window_title" msgid="4379134104008111961">"Automatisch invullen met <xliff:g id="SERVICENAME">%1$s</xliff:g>"</string>
<string name="autofill_address_name_separator" msgid="8190155636149596125">" "</string>
<string name="autofill_address_summary_name_format" msgid="3402882515222673691">"$1$2$3"</string>
<string name="autofill_address_summary_separator" msgid="760522655085707045">", "</string>
@@ -1104,7 +1104,7 @@
<string name="selectTextMode" msgid="3225108910999318778">"Tekst selecteren"</string>
<string name="undo" msgid="3175318090002654673">"Ongedaan maken"</string>
<string name="redo" msgid="7231448494008532233">"Opnieuw"</string>
- <string name="autofill" msgid="511224882647795296">"Automatisch aanvullen"</string>
+ <string name="autofill" msgid="511224882647795296">"Automatisch invullen"</string>
<string name="textSelectionCABTitle" msgid="5151441579532476940">"Tekstselectie"</string>
<string name="addToDictionary" msgid="8041821113480950096">"Toevoegen aan woordenboek"</string>
<string name="deleteText" msgid="4200807474529938112">"Verwijderen"</string>
@@ -1829,6 +1829,7 @@
<item quantity="other">Gedurende %d u</item>
<item quantity="one">Gedurende 1 u</item>
</plurals>
+ <string name="zen_mode_until_next_day" msgid="1403042784161725038">"Tot <xliff:g id="FORMATTEDTIME">%1$s</xliff:g>"</string>
<string name="zen_mode_until" msgid="2250286190237669079">"Tot <xliff:g id="FORMATTEDTIME">%1$s</xliff:g>"</string>
<string name="zen_mode_alarm" msgid="7046911727540499275">"Tot <xliff:g id="FORMATTEDTIME">%1$s</xliff:g> (volgende wekker)"</string>
<string name="zen_mode_forever" msgid="740585666364912448">"Totdat je uitschakelt"</string>
@@ -1928,13 +1929,13 @@
<string name="time_picker_prompt_label" msgid="303588544656363889">"Typ een tijd"</string>
<string name="time_picker_text_input_mode_description" msgid="4761160667516611576">"Schakel naar de tekstinvoermodus om de tijd in te voeren."</string>
<string name="time_picker_radial_mode_description" msgid="1222342577115016953">"Schakel naar de klokmodus om de tijd in te voeren."</string>
- <string name="autofill_picker_accessibility_title" msgid="4425806874792196599">"Opties voor automatisch aanvullen"</string>
- <string name="autofill_save_accessibility_title" msgid="1523225776218450005">"Opslaan voor Automatisch aanvullen"</string>
+ <string name="autofill_picker_accessibility_title" msgid="4425806874792196599">"Opties voor automatisch invullen"</string>
+ <string name="autofill_save_accessibility_title" msgid="1523225776218450005">"Opslaan voor Automatisch invullen"</string>
<string name="autofill_error_cannot_autofill" msgid="6528827648643138596">"Content kan niet automatisch worden aangevuld"</string>
- <string name="autofill_picker_no_suggestions" msgid="1076022650427481509">"Geen suggesties van Automatisch aanvullen"</string>
+ <string name="autofill_picker_no_suggestions" msgid="1076022650427481509">"Geen suggesties van Automatisch invullen"</string>
<plurals name="autofill_picker_some_suggestions" formatted="false" msgid="6651883186966959978">
- <item quantity="other"><xliff:g id="COUNT">%1$s</xliff:g> suggesties van Automatisch aanvullen</item>
- <item quantity="one">Eén suggestie van Automatisch aanvullen</item>
+ <item quantity="other"><xliff:g id="COUNT">%1$s</xliff:g> suggesties van Automatisch invullen</item>
+ <item quantity="one">Eén suggestie van Automatisch invullen</item>
</plurals>
<string name="autofill_save_title" msgid="7719802414283739775">"Opslaan in "<b>"<xliff:g id="LABEL">%1$s</xliff:g>"</b>"?"</string>
<string name="autofill_save_title_with_type" msgid="3002460014579799605">"<xliff:g id="TYPE">%1$s</xliff:g> opslaan in "<b>"<xliff:g id="LABEL">%2$s</xliff:g>"</b>"?"</string>
diff --git a/core/res/res/values-or/strings.xml b/core/res/res/values-or/strings.xml
index 1cb01ead277b..85ca06564d7a 100644
--- a/core/res/res/values-or/strings.xml
+++ b/core/res/res/values-or/strings.xml
@@ -1829,6 +1829,7 @@
<item quantity="other">%d ଘଣ୍ଟା ପାଇଁ</item>
<item quantity="one">1 ଘଣ୍ଟା ପାଇଁ</item>
</plurals>
+ <string name="zen_mode_until_next_day" msgid="1403042784161725038">"<xliff:g id="FORMATTEDTIME">%1$s</xliff:g> ପର୍ଯ୍ୟନ୍ତ"</string>
<string name="zen_mode_until" msgid="2250286190237669079">"<xliff:g id="FORMATTEDTIME">%1$s</xliff:g> ପର୍ଯ୍ୟନ୍ତ"</string>
<string name="zen_mode_alarm" msgid="7046911727540499275">"<xliff:g id="FORMATTEDTIME">%1$s</xliff:g> (ପରବର୍ତ୍ତୀ ଆଲାର୍ମ) ପର୍ଯ୍ୟନ୍ତ"</string>
<string name="zen_mode_forever" msgid="740585666364912448">"ବନ୍ଦ ନକରିବା ପର୍ଯ୍ୟନ୍ତ"</string>
diff --git a/core/res/res/values-pa/strings.xml b/core/res/res/values-pa/strings.xml
index 903ff83a0572..ed663a8bb5d6 100644
--- a/core/res/res/values-pa/strings.xml
+++ b/core/res/res/values-pa/strings.xml
@@ -899,7 +899,7 @@
<string name="keyguard_accessibility_unlock_area_collapsed" msgid="4729922043778400434">"ਅਣਲਾਕ ਖੇਤਰ ਨਸ਼ਟ ਕੀਤਾ।"</string>
<string name="keyguard_accessibility_widget" msgid="6776892679715699875">"<xliff:g id="WIDGET_INDEX">%1$s</xliff:g> ਵਿਜੇਟ।"</string>
<string name="keyguard_accessibility_user_selector" msgid="1466067610235696600">"ਉਪਭੋਗਤਾ ਚੋਣਕਾਰ"</string>
- <string name="keyguard_accessibility_status" msgid="6792745049712397237">"ਅਵਸਥਾ"</string>
+ <string name="keyguard_accessibility_status" msgid="6792745049712397237">"ਸਥਿਤੀ"</string>
<string name="keyguard_accessibility_camera" msgid="7862557559464986528">"ਕੈਮਰਾ"</string>
<string name="keygaurd_accessibility_media_controls" msgid="2267379779900620614">"ਮੀਡੀਆ ਨਿਯੰਤਰਣ"</string>
<string name="keyguard_accessibility_widget_reorder_start" msgid="7066213328912939191">"ਵਿਜੇਟ ਨੂੰ ਪੁਨਰ ਤਰਤੀਬ ਦੇਣਾ ਸ਼ੁਰੂ ਹੋਇਆ।"</string>
@@ -1829,6 +1829,8 @@
<item quantity="one">%d ਘੰਟਿਆਂ ਲਈ</item>
<item quantity="other">%d ਘੰਟਿਆਂ ਲਈ</item>
</plurals>
+ <!-- no translation found for zen_mode_until_next_day (1403042784161725038) -->
+ <skip />
<string name="zen_mode_until" msgid="2250286190237669079">"<xliff:g id="FORMATTEDTIME">%1$s</xliff:g> ਤੱਕ"</string>
<string name="zen_mode_alarm" msgid="7046911727540499275">"<xliff:g id="FORMATTEDTIME">%1$s</xliff:g> ਤੱਕ (ਅਗਲਾ ਅਲਾਰਮ)"</string>
<string name="zen_mode_forever" msgid="740585666364912448">"ਜਦੋਂ ਤੱਕ ਤੁਸੀਂ ਬੰਦ ਨਹੀਂ ਕਰਦੇ ਹੋ"</string>
diff --git a/core/res/res/values-pl/strings.xml b/core/res/res/values-pl/strings.xml
index aeebf8845567..16b3fafe3520 100644
--- a/core/res/res/values-pl/strings.xml
+++ b/core/res/res/values-pl/strings.xml
@@ -1891,6 +1891,7 @@
<item quantity="other">%d godz.</item>
<item quantity="one">1 godz.</item>
</plurals>
+ <string name="zen_mode_until_next_day" msgid="1403042784161725038">"Do <xliff:g id="FORMATTEDTIME">%1$s</xliff:g>"</string>
<string name="zen_mode_until" msgid="2250286190237669079">"Do <xliff:g id="FORMATTEDTIME">%1$s</xliff:g>"</string>
<string name="zen_mode_alarm" msgid="7046911727540499275">"Do <xliff:g id="FORMATTEDTIME">%1$s</xliff:g> (następny alarm)"</string>
<string name="zen_mode_forever" msgid="740585666364912448">"Dopóki nie wyłączysz"</string>
diff --git a/core/res/res/values-pt-rBR/strings.xml b/core/res/res/values-pt-rBR/strings.xml
index aedc0471ac6f..2883d6fd8ae2 100644
--- a/core/res/res/values-pt-rBR/strings.xml
+++ b/core/res/res/values-pt-rBR/strings.xml
@@ -1829,6 +1829,7 @@
<item quantity="one">Por %d horas</item>
<item quantity="other">Por %d horas</item>
</plurals>
+ <string name="zen_mode_until_next_day" msgid="1403042784161725038">"Até <xliff:g id="FORMATTEDTIME">%1$s</xliff:g>"</string>
<string name="zen_mode_until" msgid="2250286190237669079">"Até às <xliff:g id="FORMATTEDTIME">%1$s</xliff:g>"</string>
<string name="zen_mode_alarm" msgid="7046911727540499275">"Até <xliff:g id="FORMATTEDTIME">%1$s</xliff:g> (próximo alarme)"</string>
<string name="zen_mode_forever" msgid="740585666364912448">"Até você desativar"</string>
diff --git a/core/res/res/values-pt-rPT/strings.xml b/core/res/res/values-pt-rPT/strings.xml
index 883e4bf09251..3bc9530ab46e 100644
--- a/core/res/res/values-pt-rPT/strings.xml
+++ b/core/res/res/values-pt-rPT/strings.xml
@@ -1829,6 +1829,7 @@
<item quantity="other">Durante %d h</item>
<item quantity="one">Durante 1 h</item>
</plurals>
+ <string name="zen_mode_until_next_day" msgid="1403042784161725038">"Até <xliff:g id="FORMATTEDTIME">%1$s</xliff:g>"</string>
<string name="zen_mode_until" msgid="2250286190237669079">"Até às <xliff:g id="FORMATTEDTIME">%1$s</xliff:g>"</string>
<string name="zen_mode_alarm" msgid="7046911727540499275">"Até <xliff:g id="FORMATTEDTIME">%1$s</xliff:g> (próximo alarme)"</string>
<string name="zen_mode_forever" msgid="740585666364912448">"Até desativar"</string>
diff --git a/core/res/res/values-pt/strings.xml b/core/res/res/values-pt/strings.xml
index aedc0471ac6f..2883d6fd8ae2 100644
--- a/core/res/res/values-pt/strings.xml
+++ b/core/res/res/values-pt/strings.xml
@@ -1829,6 +1829,7 @@
<item quantity="one">Por %d horas</item>
<item quantity="other">Por %d horas</item>
</plurals>
+ <string name="zen_mode_until_next_day" msgid="1403042784161725038">"Até <xliff:g id="FORMATTEDTIME">%1$s</xliff:g>"</string>
<string name="zen_mode_until" msgid="2250286190237669079">"Até às <xliff:g id="FORMATTEDTIME">%1$s</xliff:g>"</string>
<string name="zen_mode_alarm" msgid="7046911727540499275">"Até <xliff:g id="FORMATTEDTIME">%1$s</xliff:g> (próximo alarme)"</string>
<string name="zen_mode_forever" msgid="740585666364912448">"Até você desativar"</string>
diff --git a/core/res/res/values-ro/strings.xml b/core/res/res/values-ro/strings.xml
index ebc5827c2a7a..d5a9297c8298 100644
--- a/core/res/res/values-ro/strings.xml
+++ b/core/res/res/values-ro/strings.xml
@@ -1860,6 +1860,7 @@
<item quantity="other">Pentru %d h</item>
<item quantity="one">Pentru 1 h</item>
</plurals>
+ <string name="zen_mode_until_next_day" msgid="1403042784161725038">"Până <xliff:g id="FORMATTEDTIME">%1$s</xliff:g>"</string>
<string name="zen_mode_until" msgid="2250286190237669079">"Până la <xliff:g id="FORMATTEDTIME">%1$s</xliff:g>"</string>
<string name="zen_mode_alarm" msgid="7046911727540499275">"Până la <xliff:g id="FORMATTEDTIME">%1$s</xliff:g> (următoarea alarmă)"</string>
<string name="zen_mode_forever" msgid="740585666364912448">"Până când dezactivați"</string>
diff --git a/core/res/res/values-ru/strings.xml b/core/res/res/values-ru/strings.xml
index f3d13ceec060..3339fb52b72a 100644
--- a/core/res/res/values-ru/strings.xml
+++ b/core/res/res/values-ru/strings.xml
@@ -1891,6 +1891,7 @@
<item quantity="many">На %d часов</item>
<item quantity="other">На %d часа</item>
</plurals>
+ <string name="zen_mode_until_next_day" msgid="1403042784161725038">"До <xliff:g id="FORMATTEDTIME">%1$s</xliff:g>"</string>
<string name="zen_mode_until" msgid="2250286190237669079">"До <xliff:g id="FORMATTEDTIME">%1$s</xliff:g>"</string>
<string name="zen_mode_alarm" msgid="7046911727540499275">"До <xliff:g id="FORMATTEDTIME">%1$s</xliff:g> (будильник)"</string>
<string name="zen_mode_forever" msgid="740585666364912448">"Пока вы не отключите"</string>
diff --git a/core/res/res/values-si/strings.xml b/core/res/res/values-si/strings.xml
index cc6783a4fb3d..c26f23f3cc6d 100644
--- a/core/res/res/values-si/strings.xml
+++ b/core/res/res/values-si/strings.xml
@@ -1829,6 +1829,7 @@
<item quantity="one">පැය %d ක් සඳහා</item>
<item quantity="other">පැය %d ක් සඳහා</item>
</plurals>
+ <string name="zen_mode_until_next_day" msgid="1403042784161725038">"<xliff:g id="FORMATTEDTIME">%1$s</xliff:g> දක්වා"</string>
<string name="zen_mode_until" msgid="2250286190237669079">"<xliff:g id="FORMATTEDTIME">%1$s</xliff:g> තෙක්"</string>
<string name="zen_mode_alarm" msgid="7046911727540499275">"<xliff:g id="FORMATTEDTIME">%1$s</xliff:g> තෙක් (ඊළඟ එලාමය)"</string>
<string name="zen_mode_forever" msgid="740585666364912448">"ඔබ ක්‍රියාවිරහිත කරන තුරු"</string>
diff --git a/core/res/res/values-sk/strings.xml b/core/res/res/values-sk/strings.xml
index b6125657f408..1a4f07e397e6 100644
--- a/core/res/res/values-sk/strings.xml
+++ b/core/res/res/values-sk/strings.xml
@@ -1891,6 +1891,7 @@
<item quantity="other">Na %d h</item>
<item quantity="one">Na 1 h</item>
</plurals>
+ <string name="zen_mode_until_next_day" msgid="1403042784161725038">"Do <xliff:g id="FORMATTEDTIME">%1$s</xliff:g>"</string>
<string name="zen_mode_until" msgid="2250286190237669079">"Do <xliff:g id="FORMATTEDTIME">%1$s</xliff:g>"</string>
<string name="zen_mode_alarm" msgid="7046911727540499275">"Do <xliff:g id="FORMATTEDTIME">%1$s</xliff:g> (ďalší budík)"</string>
<string name="zen_mode_forever" msgid="740585666364912448">"Dokým funkciu nevypnete"</string>
diff --git a/core/res/res/values-sl/strings.xml b/core/res/res/values-sl/strings.xml
index 3ef51a7e6dab..0079e389116f 100644
--- a/core/res/res/values-sl/strings.xml
+++ b/core/res/res/values-sl/strings.xml
@@ -1891,6 +1891,7 @@
<item quantity="few">%d h</item>
<item quantity="other">%d h</item>
</plurals>
+ <string name="zen_mode_until_next_day" msgid="1403042784161725038">"Do <xliff:g id="FORMATTEDTIME">%1$s</xliff:g>"</string>
<string name="zen_mode_until" msgid="2250286190237669079">"Do <xliff:g id="FORMATTEDTIME">%1$s</xliff:g>"</string>
<string name="zen_mode_alarm" msgid="7046911727540499275">"Do <xliff:g id="FORMATTEDTIME">%1$s</xliff:g> (naslednji alarm)"</string>
<string name="zen_mode_forever" msgid="740585666364912448">"Dokler ne izklopite"</string>
diff --git a/core/res/res/values-sq/strings.xml b/core/res/res/values-sq/strings.xml
index 37a995a5a799..b29582c05d6b 100644
--- a/core/res/res/values-sq/strings.xml
+++ b/core/res/res/values-sq/strings.xml
@@ -1829,6 +1829,7 @@
<item quantity="other">Për %d orë</item>
<item quantity="one">Për 1 orë</item>
</plurals>
+ <string name="zen_mode_until_next_day" msgid="1403042784161725038">"Deri në <xliff:g id="FORMATTEDTIME">%1$s</xliff:g>"</string>
<string name="zen_mode_until" msgid="2250286190237669079">"Deri në <xliff:g id="FORMATTEDTIME">%1$s</xliff:g>"</string>
<string name="zen_mode_alarm" msgid="7046911727540499275">"Deri në <xliff:g id="FORMATTEDTIME">%1$s</xliff:g> (alarmi tjetër)"</string>
<string name="zen_mode_forever" msgid="740585666364912448">"Derisa ta çaktivizosh"</string>
diff --git a/core/res/res/values-sr/strings.xml b/core/res/res/values-sr/strings.xml
index a3eb3e412c55..f24e285700c5 100644
--- a/core/res/res/values-sr/strings.xml
+++ b/core/res/res/values-sr/strings.xml
@@ -243,7 +243,7 @@
<string name="global_action_power_off" msgid="4404936470711393203">"Искључи"</string>
<string name="global_action_power_options" msgid="1185286119330160073">"Напајање"</string>
<string name="global_action_restart" msgid="4678451019561687074">"Рестартуј"</string>
- <string name="global_action_emergency" msgid="1387617624177105088">"Хитни позив"</string>
+ <string name="global_action_emergency" msgid="1387617624177105088">"Хитан позив"</string>
<string name="global_action_bug_report" msgid="5127867163044170003">"Извештај о грешци"</string>
<string name="global_action_logout" msgid="6093581310002476511">"Заврши сесију"</string>
<string name="global_action_screenshot" msgid="2610053466156478564">"Снимак екрана"</string>
@@ -835,7 +835,7 @@
<string name="lockscreen_instructions_when_pattern_enabled" msgid="7982445492532123308">"Притисните „Мени“ да бисте откључали телефон или упутите хитан позив."</string>
<string name="lockscreen_instructions_when_pattern_disabled" msgid="7434061749374801753">"Притисните „Мени“ за откључавање."</string>
<string name="lockscreen_pattern_instructions" msgid="3169991838169244941">"Унесите шаблон за откључавање"</string>
- <string name="lockscreen_emergency_call" msgid="7549683825868928636">"Хитни позив"</string>
+ <string name="lockscreen_emergency_call" msgid="7549683825868928636">"Хитан позив"</string>
<string name="lockscreen_return_to_call" msgid="3156883574692006382">"Назад на позив"</string>
<string name="lockscreen_pattern_correct" msgid="8050630103651508582">"Тачно!"</string>
<string name="lockscreen_pattern_wrong" msgid="2940138714468358458">"Пробајте поново"</string>
@@ -1815,7 +1815,7 @@
<string name="package_updated_device_owner" msgid="7560272363805506941">"Ажурирао је администратор"</string>
<string name="package_deleted_device_owner" msgid="2292335928930293023">"Избрисао је администратор"</string>
<string name="confirm_battery_saver" msgid="5247976246208245754">"Потврди"</string>
- <string name="battery_saver_description_with_learn_more" msgid="4424488535318105801">"Да би се продужило трајање батерије, Уштеда батерије:\n\n• укључује тамну тему\n• искључује или ограничава активности у позадини, неке визуелне ефекте и друге функције, на пример, „Ок Google“\n\n"<annotation id="url">"Сазнајте више"</annotation></string>
+ <string name="battery_saver_description_with_learn_more" msgid="4424488535318105801">"Да би се продужило трајање батерије, Уштеда батерије:\n\n• укључује тамну тему\n• искључује или ограничава активности у позадини, неке визуелне ефекте и друге функције, на пример „Ок Google“\n\n"<annotation id="url">"Сазнајте више"</annotation></string>
<string name="battery_saver_description" msgid="6794188153647295212">"Да би се продужило трајање батерије, Уштеда батерије:\n\n• укључује тамну тему\n• искључује или ограничава активности у позадини, неке визуелне ефекте и друге функције, на пример, „Ок Google“"</string>
<string name="data_saver_description" msgid="4995164271550590517">"Да би се смањила потрошња података, Уштеда података спречава неке апликације да шаљу или примају податке у позадини. Апликација коју тренутно користите може да приступа подацима, али ће то чинити ређе. На пример, слике се неће приказивати док их не додирнете."</string>
<string name="data_saver_enable_title" msgid="7080620065745260137">"Желите да укључите Уштеду података?"</string>
@@ -1860,6 +1860,7 @@
<item quantity="few">За %d с</item>
<item quantity="other">За %d с</item>
</plurals>
+ <string name="zen_mode_until_next_day" msgid="1403042784161725038">"До <xliff:g id="FORMATTEDTIME">%1$s</xliff:g>"</string>
<string name="zen_mode_until" msgid="2250286190237669079">"До <xliff:g id="FORMATTEDTIME">%1$s</xliff:g>"</string>
<string name="zen_mode_alarm" msgid="7046911727540499275">"До <xliff:g id="FORMATTEDTIME">%1$s</xliff:g> (следећи аларм)"</string>
<string name="zen_mode_forever" msgid="740585666364912448">"Док не искључите"</string>
diff --git a/core/res/res/values-sv/strings.xml b/core/res/res/values-sv/strings.xml
index 3711e2bf0530..5c9ce2c8a608 100644
--- a/core/res/res/values-sv/strings.xml
+++ b/core/res/res/values-sv/strings.xml
@@ -1829,6 +1829,7 @@
<item quantity="other">I %d tim</item>
<item quantity="one">I en 1 tim</item>
</plurals>
+ <string name="zen_mode_until_next_day" msgid="1403042784161725038">"Till <xliff:g id="FORMATTEDTIME">%1$s</xliff:g>"</string>
<string name="zen_mode_until" msgid="2250286190237669079">"Till <xliff:g id="FORMATTEDTIME">%1$s</xliff:g>"</string>
<string name="zen_mode_alarm" msgid="7046911727540499275">"Till <xliff:g id="FORMATTEDTIME">%1$s</xliff:g> (nästa alarm)"</string>
<string name="zen_mode_forever" msgid="740585666364912448">"Tills du stänger av"</string>
diff --git a/core/res/res/values-sw/strings.xml b/core/res/res/values-sw/strings.xml
index 23b6c3b5a029..55db3eb45225 100644
--- a/core/res/res/values-sw/strings.xml
+++ b/core/res/res/values-sw/strings.xml
@@ -1829,6 +1829,7 @@
<item quantity="other">Kwa saa %d </item>
<item quantity="one">Kwa saa 1 </item>
</plurals>
+ <string name="zen_mode_until_next_day" msgid="1403042784161725038">"Hadi <xliff:g id="FORMATTEDTIME">%1$s</xliff:g>"</string>
<string name="zen_mode_until" msgid="2250286190237669079">"Hadi <xliff:g id="FORMATTEDTIME">%1$s</xliff:g>"</string>
<string name="zen_mode_alarm" msgid="7046911727540499275">"Mpaka <xliff:g id="FORMATTEDTIME">%1$s</xliff:g> (kengele inayofuata)"</string>
<string name="zen_mode_forever" msgid="740585666364912448">"Hadi utakapoizima"</string>
diff --git a/core/res/res/values-ta/strings.xml b/core/res/res/values-ta/strings.xml
index 42b2d9c2f273..be030d16b1ed 100644
--- a/core/res/res/values-ta/strings.xml
+++ b/core/res/res/values-ta/strings.xml
@@ -1829,6 +1829,7 @@
<item quantity="other">%d மணிநேரத்திற்கு</item>
<item quantity="one">1 மணிநேரத்திற்கு</item>
</plurals>
+ <string name="zen_mode_until_next_day" msgid="1403042784161725038">"<xliff:g id="FORMATTEDTIME">%1$s</xliff:g> வரை"</string>
<string name="zen_mode_until" msgid="2250286190237669079">"<xliff:g id="FORMATTEDTIME">%1$s</xliff:g> வரை"</string>
<string name="zen_mode_alarm" msgid="7046911727540499275">"<xliff:g id="FORMATTEDTIME">%1$s</xliff:g> மணி (அடுத்த அலாரம்) வரை"</string>
<string name="zen_mode_forever" msgid="740585666364912448">"ஆஃப் செய்யும் வரை"</string>
diff --git a/core/res/res/values-te/strings.xml b/core/res/res/values-te/strings.xml
index 6b9caf15a745..0966d235f5dd 100644
--- a/core/res/res/values-te/strings.xml
+++ b/core/res/res/values-te/strings.xml
@@ -1829,6 +1829,7 @@
<item quantity="other">%d గం పాటు</item>
<item quantity="one">1 గం పాటు</item>
</plurals>
+ <string name="zen_mode_until_next_day" msgid="1403042784161725038">"<xliff:g id="FORMATTEDTIME">%1$s</xliff:g> వరకు"</string>
<string name="zen_mode_until" msgid="2250286190237669079">"<xliff:g id="FORMATTEDTIME">%1$s</xliff:g> వరకు"</string>
<string name="zen_mode_alarm" msgid="7046911727540499275">"<xliff:g id="FORMATTEDTIME">%1$s</xliff:g> (తర్వాత అలారం) వరకు"</string>
<string name="zen_mode_forever" msgid="740585666364912448">"మీరు ఆఫ్‌ చేసే వరకు"</string>
diff --git a/core/res/res/values-th/strings.xml b/core/res/res/values-th/strings.xml
index 4b39c63a1bdf..7ff76ec7bde6 100644
--- a/core/res/res/values-th/strings.xml
+++ b/core/res/res/values-th/strings.xml
@@ -1829,6 +1829,7 @@
<item quantity="other">เป็นเวลา %d ชม.</item>
<item quantity="one">เป็นเวลา 1 ชม.</item>
</plurals>
+ <string name="zen_mode_until_next_day" msgid="1403042784161725038">"จนถึง <xliff:g id="FORMATTEDTIME">%1$s</xliff:g>"</string>
<string name="zen_mode_until" msgid="2250286190237669079">"จนถึงเวลา <xliff:g id="FORMATTEDTIME">%1$s</xliff:g>"</string>
<string name="zen_mode_alarm" msgid="7046911727540499275">"จนถึงเวลา <xliff:g id="FORMATTEDTIME">%1$s</xliff:g> (การปลุกครั้งถัดไป)"</string>
<string name="zen_mode_forever" msgid="740585666364912448">"จนกว่าคุณจะปิด"</string>
diff --git a/core/res/res/values-tl/strings.xml b/core/res/res/values-tl/strings.xml
index beebbd23448c..7d9c7173c84e 100644
--- a/core/res/res/values-tl/strings.xml
+++ b/core/res/res/values-tl/strings.xml
@@ -1829,6 +1829,7 @@
<item quantity="one">Sa loob ng %d oras</item>
<item quantity="other">Sa loob ng %d na oras</item>
</plurals>
+ <string name="zen_mode_until_next_day" msgid="1403042784161725038">"Hanggang <xliff:g id="FORMATTEDTIME">%1$s</xliff:g>"</string>
<string name="zen_mode_until" msgid="2250286190237669079">"Hanggang <xliff:g id="FORMATTEDTIME">%1$s</xliff:g>"</string>
<string name="zen_mode_alarm" msgid="7046911727540499275">"Hanggang <xliff:g id="FORMATTEDTIME">%1$s</xliff:g> (susunod na alarm)"</string>
<string name="zen_mode_forever" msgid="740585666364912448">"Hanggang sa i-off mo"</string>
diff --git a/core/res/res/values-tr/strings.xml b/core/res/res/values-tr/strings.xml
index 17852a6615de..a658bf18ab95 100644
--- a/core/res/res/values-tr/strings.xml
+++ b/core/res/res/values-tr/strings.xml
@@ -1829,6 +1829,7 @@
<item quantity="other">%d saat</item>
<item quantity="one">1 saat</item>
</plurals>
+ <string name="zen_mode_until_next_day" msgid="1403042784161725038">"Şu saate kadar: <xliff:g id="FORMATTEDTIME">%1$s</xliff:g>"</string>
<string name="zen_mode_until" msgid="2250286190237669079">"Şu saate kadar: <xliff:g id="FORMATTEDTIME">%1$s</xliff:g>"</string>
<string name="zen_mode_alarm" msgid="7046911727540499275">"<xliff:g id="FORMATTEDTIME">%1$s</xliff:g> (sonraki alarma) saatine kadar"</string>
<string name="zen_mode_forever" msgid="740585666364912448">"Siz kapatana kadar"</string>
diff --git a/core/res/res/values-uk/strings.xml b/core/res/res/values-uk/strings.xml
index 31684e0d1b29..7e81784d1043 100644
--- a/core/res/res/values-uk/strings.xml
+++ b/core/res/res/values-uk/strings.xml
@@ -1891,6 +1891,7 @@
<item quantity="many">Протягом %d год</item>
<item quantity="other">Протягом %d год</item>
</plurals>
+ <string name="zen_mode_until_next_day" msgid="1403042784161725038">"До: <xliff:g id="FORMATTEDTIME">%1$s</xliff:g>"</string>
<string name="zen_mode_until" msgid="2250286190237669079">"До <xliff:g id="FORMATTEDTIME">%1$s</xliff:g>"</string>
<string name="zen_mode_alarm" msgid="7046911727540499275">"До <xliff:g id="FORMATTEDTIME">%1$s</xliff:g> (наступний будильник)"</string>
<string name="zen_mode_forever" msgid="740585666364912448">"Доки ви не вимкнете"</string>
diff --git a/core/res/res/values-ur/strings.xml b/core/res/res/values-ur/strings.xml
index ed32b2453bf3..b9b9c586d8bb 100644
--- a/core/res/res/values-ur/strings.xml
+++ b/core/res/res/values-ur/strings.xml
@@ -1829,6 +1829,8 @@
<item quantity="other">‏‎%d گھنٹے کیلئے</item>
<item quantity="one">1 گھنٹہ کیلئے</item>
</plurals>
+ <!-- no translation found for zen_mode_until_next_day (1403042784161725038) -->
+ <skip />
<string name="zen_mode_until" msgid="2250286190237669079">"<xliff:g id="FORMATTEDTIME">%1$s</xliff:g> تک"</string>
<string name="zen_mode_alarm" msgid="7046911727540499275">"<xliff:g id="FORMATTEDTIME">%1$s</xliff:g> تک (اگلا الارم)"</string>
<string name="zen_mode_forever" msgid="740585666364912448">"یہاں تک کہ آپ آف کر دیں"</string>
diff --git a/core/res/res/values-uz/strings.xml b/core/res/res/values-uz/strings.xml
index 4f196440334c..5edd9dc8332b 100644
--- a/core/res/res/values-uz/strings.xml
+++ b/core/res/res/values-uz/strings.xml
@@ -1793,7 +1793,7 @@
<string name="package_deleted_device_owner" msgid="2292335928930293023">"Administrator tomonidan o‘chirilgan"</string>
<string name="confirm_battery_saver" msgid="5247976246208245754">"OK"</string>
<string name="battery_saver_description_with_learn_more" msgid="4424488535318105801">"Batareya quvvatini uzaytirish uchun Quvvat tejash funksiyasi:\n\n• Tungi mavzuni yoqadi\n• Fondagi harakatlar, vizual effektlar va “Hey Google” kabi boshqa funksiyalarni faolsizlantiradi\n\n"<annotation id="url">"Batafsil"</annotation></string>
- <string name="battery_saver_description" msgid="6794188153647295212">"Batareya quvvatini uzaytirish uchun Quvvat tejash funksiyasi:\n\n• Tungi mavzuni yoqadi\n• Fondagi harakatlar, vizual effektlar va “Hey Google” kabi boshqa funksiyalarni faolsizlantiradi"</string>
+ <string name="battery_saver_description" msgid="6794188153647295212">"Batareya quvvatini uzaytirish uchun Quvvat tejash funksiyasi:\n\n• Tungi mavzuni yoqadi\n• Fondagi harakatlar, vizual effektlar va “Ok Google” kabi boshqa funksiyalarni faolsizlantiradi"</string>
<string name="data_saver_description" msgid="4995164271550590517">"Trafik tejash rejimida ayrim ilovalar uchun orqa fonda internetdan foydalanish imkoniyati cheklanadi. Siz ishlatayotgan ilova zaruratga qarab internet-trafik sarflashi mumkin, biroq cheklangan miqdorda. Masalan, rasmlar ustiga bosmaguningizcha ular yuklanmaydi."</string>
<string name="data_saver_enable_title" msgid="7080620065745260137">"Trafik tejash yoqilsinmi?"</string>
<string name="data_saver_enable_button" msgid="4399405762586419726">"Yoqish"</string>
@@ -1829,6 +1829,7 @@
<item quantity="other">%d soat</item>
<item quantity="one">1 soat</item>
</plurals>
+ <string name="zen_mode_until_next_day" msgid="1403042784161725038">"<xliff:g id="FORMATTEDTIME">%1$s</xliff:g> gacha"</string>
<string name="zen_mode_until" msgid="2250286190237669079">"<xliff:g id="FORMATTEDTIME">%1$s</xliff:g> gacha"</string>
<string name="zen_mode_alarm" msgid="7046911727540499275">"<xliff:g id="FORMATTEDTIME">%1$s</xliff:g> gacha (keyingi signal)"</string>
<string name="zen_mode_forever" msgid="740585666364912448">"Rejimdan chiqilgunicha"</string>
diff --git a/core/res/res/values-vi/strings.xml b/core/res/res/values-vi/strings.xml
index 1899f8fd2077..e3c765872cca 100644
--- a/core/res/res/values-vi/strings.xml
+++ b/core/res/res/values-vi/strings.xml
@@ -1829,6 +1829,7 @@
<item quantity="other">Trong %d giờ</item>
<item quantity="one">Trong 1 giờ</item>
</plurals>
+ <string name="zen_mode_until_next_day" msgid="1403042784161725038">"Cho tới <xliff:g id="FORMATTEDTIME">%1$s</xliff:g>"</string>
<string name="zen_mode_until" msgid="2250286190237669079">"Cho đến <xliff:g id="FORMATTEDTIME">%1$s</xliff:g>"</string>
<string name="zen_mode_alarm" msgid="7046911727540499275">"Cho tới <xliff:g id="FORMATTEDTIME">%1$s</xliff:g> (cảnh báo tiếp theo)"</string>
<string name="zen_mode_forever" msgid="740585666364912448">"Cho đến khi bạn tắt"</string>
diff --git a/core/res/res/values-zh-rCN/strings.xml b/core/res/res/values-zh-rCN/strings.xml
index 0a241dab0954..dee9d95b3a93 100644
--- a/core/res/res/values-zh-rCN/strings.xml
+++ b/core/res/res/values-zh-rCN/strings.xml
@@ -1829,6 +1829,7 @@
<item quantity="other">%d 小时</item>
<item quantity="one">1 小时</item>
</plurals>
+ <string name="zen_mode_until_next_day" msgid="1403042784161725038">"结束时间:<xliff:g id="FORMATTEDTIME">%1$s</xliff:g>"</string>
<string name="zen_mode_until" msgid="2250286190237669079">"到<xliff:g id="FORMATTEDTIME">%1$s</xliff:g>"</string>
<string name="zen_mode_alarm" msgid="7046911727540499275">"直到<xliff:g id="FORMATTEDTIME">%1$s</xliff:g>(闹钟下次响铃时)"</string>
<string name="zen_mode_forever" msgid="740585666364912448">"直到您将其关闭"</string>
diff --git a/core/res/res/values-zh-rHK/strings.xml b/core/res/res/values-zh-rHK/strings.xml
index 6e584d6182d9..44ad8634223a 100644
--- a/core/res/res/values-zh-rHK/strings.xml
+++ b/core/res/res/values-zh-rHK/strings.xml
@@ -1829,6 +1829,7 @@
<item quantity="other">%d 小時</item>
<item quantity="one">1 小時</item>
</plurals>
+ <string name="zen_mode_until_next_day" msgid="1403042784161725038">"直至<xliff:g id="FORMATTEDTIME">%1$s</xliff:g>"</string>
<string name="zen_mode_until" msgid="2250286190237669079">"完成時間:<xliff:g id="FORMATTEDTIME">%1$s</xliff:g>"</string>
<string name="zen_mode_alarm" msgid="7046911727540499275">"直至<xliff:g id="FORMATTEDTIME">%1$s</xliff:g> (下一次響鬧)"</string>
<string name="zen_mode_forever" msgid="740585666364912448">"直至您關閉為止"</string>
diff --git a/core/res/res/values-zh-rTW/strings.xml b/core/res/res/values-zh-rTW/strings.xml
index ab1a42375871..8831bf0b0815 100644
--- a/core/res/res/values-zh-rTW/strings.xml
+++ b/core/res/res/values-zh-rTW/strings.xml
@@ -1829,6 +1829,7 @@
<item quantity="other">%d 小時</item>
<item quantity="one">1 小時</item>
</plurals>
+ <string name="zen_mode_until_next_day" msgid="1403042784161725038">"結束時間:<xliff:g id="FORMATTEDTIME">%1$s</xliff:g>"</string>
<string name="zen_mode_until" msgid="2250286190237669079">"結束時間:<xliff:g id="FORMATTEDTIME">%1$s</xliff:g>"</string>
<string name="zen_mode_alarm" msgid="7046911727540499275">"到<xliff:g id="FORMATTEDTIME">%1$s</xliff:g> 為止 (下一個鬧鐘)"</string>
<string name="zen_mode_forever" msgid="740585666364912448">"直到你關閉為止"</string>
diff --git a/core/res/res/values-zu/strings.xml b/core/res/res/values-zu/strings.xml
index 582d4350882c..5fb9fa61bb36 100644
--- a/core/res/res/values-zu/strings.xml
+++ b/core/res/res/values-zu/strings.xml
@@ -1829,6 +1829,7 @@
<item quantity="one">Ngamahora angu-%d</item>
<item quantity="other">Ngamahora angu-%d</item>
</plurals>
+ <string name="zen_mode_until_next_day" msgid="1403042784161725038">"Kuze kube ngu-<xliff:g id="FORMATTEDTIME">%1$s</xliff:g>"</string>
<string name="zen_mode_until" msgid="2250286190237669079">"Kuze kube ngu-<xliff:g id="FORMATTEDTIME">%1$s</xliff:g>"</string>
<string name="zen_mode_alarm" msgid="7046911727540499275">"Kuze kube ngu-<xliff:g id="FORMATTEDTIME">%1$s</xliff:g> (i-alamu elandelayo)"</string>
<string name="zen_mode_forever" msgid="740585666364912448">"Uze uvale isikrini"</string>
diff --git a/core/res/res/values/attrs_manifest.xml b/core/res/res/values/attrs_manifest.xml
index fbbdaa3d7358..1c71baeaf46a 100644
--- a/core/res/res/values/attrs_manifest.xml
+++ b/core/res/res/values/attrs_manifest.xml
@@ -239,7 +239,9 @@
<!-- Old synonym for "privileged". Deprecated in API level 23. -->
<flag name="system" value="0x10" />
<!-- Additional flag from base permission type: this permission can also
- (optionally) be granted to development applications. -->
+ (optionally) be granted to development applications. Although undocumented, the
+ permission state used to be shared by all users (including future users), but it is
+ managed per-user since API level 31. -->
<flag name="development" value="0x20" />
<!-- Additional flag from base permission type: this permission is closely
associated with an app op for controlling access. -->
@@ -359,7 +361,9 @@
package manager. As such, its use is strongly discouraged and may be
removed in a future version of Android. Instead, apps should use proper
communication mechanisms, such as services and content providers,
- to facilitate interoperability between shared components. -->
+ to facilitate interoperability between shared components. Note that
+ existing apps cannot remove this value, as migrating off a
+ shared user ID is not supported. -->
<attr name="sharedUserId" format="string" />
<!-- Specify a label for the shared user UID of this package. This is
@@ -992,6 +996,8 @@
<!-- The font scaling factor has changed, that is the user has
selected a new global font size. -->
<flag name="fontScale" value="0x40000000" />
+ <!-- The user has enabled or disabled displaying all text in bold -->
+ <flag name="forceBoldText" value="0x10000000" />
</attr>
<!-- Indicate that the activity can be launched as the embedded child of another
diff --git a/core/res/res/values/config.xml b/core/res/res/values/config.xml
index 592afa7012b5..89e348ab57b3 100644
--- a/core/res/res/values/config.xml
+++ b/core/res/res/values/config.xml
@@ -3436,7 +3436,7 @@
<string name="config_emergency_call_number" translatable="false">112</string>
<!-- Package name that provides Emergency Dialer -->
- <string name="config_emergency_dialer_package">com.android.phone</string>
+ <string name="config_emergency_dialer_package" translatable="false">com.android.phone</string>
<!-- Do not translate. Mcc codes whose existence trigger the presence of emergency
affordances-->
@@ -4418,4 +4418,7 @@
<!-- Component names of the services which will keep critical code path warm -->
<string-array name="config_keep_warming_services" translatable="false" />
+
+ <!-- WindowsManager JetPack display features -->
+ <string name="config_display_features" translatable="false" />
</resources>
diff --git a/core/res/res/values/strings.xml b/core/res/res/values/strings.xml
index a5e5fba9a14e..25a9bbd8b445 100644
--- a/core/res/res/values/strings.xml
+++ b/core/res/res/values/strings.xml
@@ -4374,7 +4374,7 @@
</string>
<!-- Dialog title for dialog shown when the multiple accessibility shortcut is activated, and we want to confirm that the user understands what's going to happen. [CHAR LIMIT=none] -->
- <string name="accessibility_shortcut_multiple_service_warning_title">Turn on accessibility features?</string>
+ <string name="accessibility_shortcut_multiple_service_warning_title">Turn on shortcut for accessibility features?</string>
<!-- Message shown in dialog when user is in the process of enabling the multiple accessibility service via the volume buttons shortcut for the first time. [CHAR LIMIT=none] -->
<string name="accessibility_shortcut_multiple_service_warning">Holding down both volume keys for a few seconds turns on accessibility features. This may change how your device works.\n\nCurrent features:\n<xliff:g id="service" example="TalkBack">%1$s</xliff:g>\nYou can change selected features in Settings > Accessibility.</string>
@@ -4383,7 +4383,7 @@
<string name="accessibility_shortcut_multiple_service_list">\t• <xliff:g id="service" example="TalkBack">%1$s</xliff:g>\n</string>
<!-- Dialog title for dialog shown when this accessibility shortcut is activated, and we want to confirm that the user understands what's going to happen. [CHAR LIMIT=none] -->
- <string name="accessibility_shortcut_single_service_warning_title">Turn on <xliff:g id="service" example="TalkBack">%1$s</xliff:g>?</string>
+ <string name="accessibility_shortcut_single_service_warning_title">Turn on <xliff:g id="service" example="TalkBack">%1$s</xliff:g> shortcut?</string>
<!-- Message shown in dialog when user is in the process of enabling this accessibility service via the volume buttons shortcut for the first time. [CHAR LIMIT=none] -->
<string name="accessibility_shortcut_single_service_warning">Holding down both volume keys for a few seconds turns on <xliff:g id="service" example="TalkBack">%1$s</xliff:g>, an accessibility feature. This may change how your device works.\n\nYou can change this shortcut to another feature in Settings > Accessibility.</string>
@@ -4888,6 +4888,9 @@
<item quantity="other">For %d hr</item>
</plurals>
+ <!-- Zen mode condition - line two: ending time indicating the next day. [CHAR LIMIT=NONE] -->
+ <string name="zen_mode_until_next_day">Until <xliff:g id="formattedTime" example="Tue, 10 PM">%1$s</xliff:g></string>
+
<!-- Zen mode condition - line two: ending time. [CHAR LIMIT=NONE] -->
<string name="zen_mode_until">Until <xliff:g id="formattedTime" example="10:00 PM">%1$s</xliff:g></string>
diff --git a/core/res/res/values/symbols.xml b/core/res/res/values/symbols.xml
index fdcd39a7f4cb..996e1f9fde9a 100644
--- a/core/res/res/values/symbols.xml
+++ b/core/res/res/values/symbols.xml
@@ -2393,6 +2393,7 @@
<java-symbol type="plurals" name="zen_mode_duration_hours_short" />
<java-symbol type="plurals" name="zen_mode_duration_minutes_summary_short" />
<java-symbol type="plurals" name="zen_mode_duration_hours_summary_short" />
+ <java-symbol type="string" name="zen_mode_until_next_day" />
<java-symbol type="string" name="zen_mode_until" />
<java-symbol type="string" name="zen_mode_feature_name" />
<java-symbol type="string" name="zen_mode_downtime_feature_name" />
@@ -4066,4 +4067,5 @@
<java-symbol type="dimen" name="config_defaultBinderHeavyHitterAutoSamplerThreshold" />
<java-symbol type="array" name="config_keep_warming_services" />
+ <java-symbol type="string" name="config_display_features" />
</resources>
diff --git a/core/tests/PackageInstallerSessions/Android.bp b/core/tests/PackageInstallerSessions/Android.bp
index e74f30ee10a4..055249d47cb3 100644
--- a/core/tests/PackageInstallerSessions/Android.bp
+++ b/core/tests/PackageInstallerSessions/Android.bp
@@ -36,6 +36,11 @@ android_test {
"framework-res",
],
+ java_resources: [
+ // Borrow an arbitrary test app from another module
+ ":PackageManagerTestAppVersion1",
+ ],
+
platform_apis: true,
sdk_version: "core_platform",
test_suites: ["device-tests"],
diff --git a/core/tests/PackageInstallerSessions/src/android/content/pm/PackageSessionTests.kt b/core/tests/PackageInstallerSessions/src/android/content/pm/PackageSessionTests.kt
index 494c92a8aa3f..b41018902b8b 100644
--- a/core/tests/PackageInstallerSessions/src/android/content/pm/PackageSessionTests.kt
+++ b/core/tests/PackageInstallerSessions/src/android/content/pm/PackageSessionTests.kt
@@ -16,7 +16,12 @@
package android.content.pm
+import android.app.Instrumentation
+import android.app.PendingIntent
+import android.content.BroadcastReceiver
import android.content.Context
+import android.content.Intent
+import android.content.IntentFilter
import android.content.pm.PackageInstaller.SessionParams
import android.platform.test.annotations.Presubmit
import androidx.test.InstrumentationRegistry
@@ -27,6 +32,8 @@ import org.junit.After
import org.junit.Before
import org.junit.Test
import org.testng.Assert.assertThrows
+import java.util.concurrent.ArrayBlockingQueue
+import java.util.concurrent.TimeUnit
import kotlin.random.Random
/**
@@ -53,15 +60,49 @@ class PackageSessionTests {
"android.permission.WRITE_CALL_LOG",
"android.permission.PROCESS_OUTGOING_CALLS"
)
+
+ private const val TEST_PKG_NAME = "com.android.server.pm.test.test_app"
+ private const val INTENT_ACTION = "com.android.server.pm.test.test_app.action"
}
private val context: Context = InstrumentationRegistry.getContext()
+ private val instrumentation: Instrumentation = InstrumentationRegistry.getInstrumentation()
private val installer = context.packageManager.packageInstaller
+ private val receiver = object : BroadcastReceiver() {
+ private val results = ArrayBlockingQueue<Intent>(1)
+
+ override fun onReceive(context: Context, intent: Intent) {
+ results.add(intent)
+ }
+
+ fun makeIntentSender(sessionId: Int) = PendingIntent.getBroadcast(context, sessionId,
+ Intent(INTENT_ACTION), PendingIntent.FLAG_UPDATE_CURRENT).intentSender
+
+ fun getResult(unit: TimeUnit, timeout: Long) = results.poll(timeout, unit)
+
+ fun clear() = results.clear()
+ }
+
+ @Before
+ fun registerReceiver() {
+ receiver.clear()
+ context.registerReceiver(receiver, IntentFilter(INTENT_ACTION))
+ }
+
+ @After
+ fun unregisterReceiver() {
+ context.unregisterReceiver(receiver)
+ }
+
@Before
@After
fun abandonAllSessions() {
+ instrumentation.uiAutomation
+ .executeShellCommand("pm uninstall com.android.server.pm.test.test_app")
+ .close()
+
installer.mySessions.asSequence()
.map { it.sessionId }
.forEach {
@@ -82,7 +123,7 @@ class PackageSessionTests {
setAppLabel(longLabel)
}
- createSession(params) {
+ createAndAbandonSession(params) {
assertThat(installer.getSessionInfo(it)?.appLabel)
.isEqualTo(longLabel.take(PackageItemInfo.MAX_SAFE_LABEL_LENGTH))
}
@@ -95,7 +136,7 @@ class PackageSessionTests {
setAppPackageName(longName)
}
- createSession(params) {
+ createAndAbandonSession(params) {
assertThat(installer.getSessionInfo(it)?.appPackageName)
.isEqualTo(null)
}
@@ -108,7 +149,7 @@ class PackageSessionTests {
setInstallerPackageName(longName)
}
- createSession(params) {
+ createAndAbandonSession(params) {
// If a custom installer name is dropped, it defaults to the caller
assertThat(installer.getSessionInfo(it)?.installerPackageName)
.isEqualTo(context.packageName)
@@ -121,12 +162,39 @@ class PackageSessionTests {
setWhitelistedRestrictedPermissions(invalidPermissions())
}
- createSession(params) {
+ createAndAbandonSession(params) {
assertThat(installer.getSessionInfo(it)?.whitelistedRestrictedPermissions!!)
.containsExactlyElementsIn(RESTRICTED_PERMISSIONS)
}
}
+ @Test
+ fun fillPackageNameWithParsedValue() {
+ val params = SessionParams(SessionParams.MODE_FULL_INSTALL)
+ val sessionId = installer.createSession(params)
+ val session = installer.openSession(sessionId)
+
+ javaClass.classLoader.getResourceAsStream("PackageManagerTestAppVersion1.apk")!!
+ .use { input ->
+ session.openWrite("base", 0, -1)
+ .use { output -> input.copyTo(output) }
+ }
+
+ // Test instrumentation doesn't have install permissions, so use shell
+ ShellIdentityUtils.invokeWithShellPermissions {
+ session.commit(receiver.makeIntentSender(sessionId))
+ }
+ session.close()
+
+ // The actual contents aren't verified as part of this test. Only care about it finishing.
+ val result = receiver.getResult(TimeUnit.SECONDS, 30)
+ assertThat(result).isNotNull()
+
+ val sessionInfo = installer.getSessionInfo(sessionId)
+ assertThat(sessionInfo).isNotNull()
+ assertThat(sessionInfo!!.getAppPackageName()).isEqualTo(TEST_PKG_NAME)
+ }
+
@LargeTest
@Test
fun allocateMaxSessionsWithPermission() {
@@ -177,7 +245,7 @@ class PackageSessionTests {
repeat(10) { add(invalidPackageName(300)) }
}
- private fun createSession(params: SessionParams, block: (Int) -> Unit = {}) {
+ private fun createAndAbandonSession(params: SessionParams, block: (Int) -> Unit = {}) {
val sessionId = installer.createSession(params)
try {
block(sessionId)
diff --git a/core/tests/coretests/AndroidManifest.xml b/core/tests/coretests/AndroidManifest.xml
index 6ae6faa5446e..71cb2acde94e 100644
--- a/core/tests/coretests/AndroidManifest.xml
+++ b/core/tests/coretests/AndroidManifest.xml
@@ -228,15 +228,6 @@
</intent-filter>
</activity>
- <activity android:name="android.widget.focus.ListOfButtons"
- android:label="ListOfButtons"
- android:exported="true">
- <intent-filter>
- <action android:name="android.intent.action.MAIN" />
- <category android:name="android.intent.category.FRAMEWORK_INSTRUMENTATION_TEST" />
- </intent-filter>
- </activity>
-
<activity android:name="android.widget.focus.LinearLayoutGrid"
android:label="LinearLayoutGrid"
android:exported="true">
diff --git a/core/tests/coretests/src/android/app/activity/ActivityThreadTest.java b/core/tests/coretests/src/android/app/activity/ActivityThreadTest.java
index 0a751dd7c66b..23534e2ebebc 100644
--- a/core/tests/coretests/src/android/app/activity/ActivityThreadTest.java
+++ b/core/tests/coretests/src/android/app/activity/ActivityThreadTest.java
@@ -25,14 +25,14 @@ import static android.view.Display.INVALID_DISPLAY;
import static com.google.common.truth.Truth.assertThat;
import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertNotEquals;
-import static org.junit.Assert.assertNull;
import static org.junit.Assert.assertTrue;
-import static org.testng.Assert.assertFalse;
import android.annotation.Nullable;
import android.app.Activity;
import android.app.ActivityThread;
+import android.app.ActivityThread.ActivityClientRecord;
import android.app.IApplicationThread;
import android.app.PictureInPictureParams;
import android.app.ResourcesManager;
@@ -114,11 +114,12 @@ public class ActivityThreadTest {
final ActivityThread activityThread = activity.getActivityThread();
InstrumentationRegistry.getInstrumentation().runOnMainSync(() -> {
activityThread.executeTransaction(newResumeTransaction(activity));
- assertNull(activityThread.performResumeActivity(activity.getActivityToken(),
- true /* finalStateRequest */, "test"));
+ final ActivityClientRecord r = getActivityClientRecord(activity);
+ assertFalse(activityThread.performResumeActivity(r, true /* finalStateRequest */,
+ "test"));
- assertNull(activityThread.performResumeActivity(activity.getActivityToken(),
- false /* finalStateRequest */, "test"));
+ assertFalse(activityThread.performResumeActivity(r, false /* finalStateRequest */,
+ "test"));
});
}
@@ -244,20 +245,18 @@ public class ActivityThreadTest {
newerConfig.orientation = orientation == ORIENTATION_LANDSCAPE
? ORIENTATION_PORTRAIT : ORIENTATION_LANDSCAPE;
newerConfig.seq = seq + 2;
- activityThread.updatePendingActivityConfiguration(activity.getActivityToken(),
- newerConfig);
+ final ActivityClientRecord r = getActivityClientRecord(activity);
+ activityThread.updatePendingActivityConfiguration(r, newerConfig);
final Configuration olderConfig = new Configuration();
olderConfig.orientation = orientation;
olderConfig.seq = seq + 1;
- activityThread.handleActivityConfigurationChanged(activity.getActivityToken(),
- olderConfig, INVALID_DISPLAY);
+ activityThread.handleActivityConfigurationChanged(r, olderConfig, INVALID_DISPLAY);
assertEquals(numOfConfig, activity.mNumOfConfigChanges);
assertEquals(olderConfig.orientation, activity.mConfig.orientation);
- activityThread.handleActivityConfigurationChanged(activity.getActivityToken(),
- newerConfig, INVALID_DISPLAY);
+ activityThread.handleActivityConfigurationChanged(r, newerConfig, INVALID_DISPLAY);
assertEquals(numOfConfig + 1, activity.mNumOfConfigChanges);
assertEquals(newerConfig.orientation, activity.mConfig.orientation);
});
@@ -274,7 +273,7 @@ public class ActivityThreadTest {
config.seq = BASE_SEQ;
config.orientation = ORIENTATION_PORTRAIT;
- activityThread.handleActivityConfigurationChanged(activity.getActivityToken(),
+ activityThread.handleActivityConfigurationChanged(getActivityClientRecord(activity),
config, INVALID_DISPLAY);
});
@@ -335,7 +334,7 @@ public class ActivityThreadTest {
config.seq = BASE_SEQ;
config.orientation = ORIENTATION_PORTRAIT;
- activityThread.handleActivityConfigurationChanged(activity.getActivityToken(),
+ activityThread.handleActivityConfigurationChanged(getActivityClientRecord(activity),
config, INVALID_DISPLAY);
});
@@ -472,10 +471,10 @@ public class ActivityThreadTest {
newActivityConfig.orientation = newActivityConfig.orientation == ORIENTATION_PORTRAIT
? ORIENTATION_LANDSCAPE : ORIENTATION_PORTRAIT;
- activityThread.updatePendingActivityConfiguration(activity.getActivityToken(),
- newActivityConfig);
- activityThread.handleActivityConfigurationChanged(activity.getActivityToken(),
- newActivityConfig, INVALID_DISPLAY);
+ final ActivityClientRecord r = getActivityClientRecord(activity);
+ activityThread.updatePendingActivityConfiguration(r, newActivityConfig);
+ activityThread.handleActivityConfigurationChanged(r, newActivityConfig,
+ INVALID_DISPLAY);
assertEquals("Virtual display orientation must not change when activity"
+ " configuration orientation changes.",
@@ -560,9 +559,10 @@ public class ActivityThreadTest {
startIntent.putExtra(TestActivity.PIP_REQUESTED_OVERRIDE_ENTER, true);
final TestActivity activity = mActivityTestRule.launchActivity(startIntent);
final ActivityThread activityThread = activity.getActivityThread();
+ final ActivityClientRecord r = getActivityClientRecord(activity);
InstrumentationRegistry.getInstrumentation().runOnMainSync(() -> {
- activityThread.handlePictureInPictureRequested(activity.getActivityToken());
+ activityThread.handlePictureInPictureRequested(r);
});
assertTrue(activity.pipRequested());
@@ -575,9 +575,10 @@ public class ActivityThreadTest {
startIntent.putExtra(TestActivity.PIP_REQUESTED_OVERRIDE_SKIP, true);
final TestActivity activity = mActivityTestRule.launchActivity(startIntent);
final ActivityThread activityThread = activity.getActivityThread();
+ final ActivityClientRecord r = getActivityClientRecord(activity);
InstrumentationRegistry.getInstrumentation().runOnMainSync(() -> {
- activityThread.handlePictureInPictureRequested(activity.getActivityToken());
+ activityThread.handlePictureInPictureRequested(r);
});
assertTrue(activity.pipRequested());
@@ -588,9 +589,10 @@ public class ActivityThreadTest {
public void testHandlePictureInPictureRequested_notOverridden() {
final TestActivity activity = mActivityTestRule.launchActivity(new Intent());
final ActivityThread activityThread = activity.getActivityThread();
+ final ActivityClientRecord r = getActivityClientRecord(activity);
InstrumentationRegistry.getInstrumentation().runOnMainSync(() -> {
- activityThread.handlePictureInPictureRequested(activity.getActivityToken());
+ activityThread.handlePictureInPictureRequested(r);
});
assertTrue(activity.pipRequested());
@@ -599,8 +601,9 @@ public class ActivityThreadTest {
}
/**
- * Calls {@link ActivityThread#handleActivityConfigurationChanged(IBinder, Configuration, int)}
- * to try to push activity configuration to the activity for the given sequence number.
+ * Calls {@link ActivityThread#handleActivityConfigurationChanged(ActivityClientRecord,
+ * Configuration, int)} to try to push activity configuration to the activity for the given
+ * sequence number.
* <p>
* It uses orientation to push the configuration and it tries a different orientation if the
* first attempt doesn't make through, to rule out the possibility that the previous
@@ -613,13 +616,13 @@ public class ActivityThreadTest {
*/
private int applyConfigurationChange(TestActivity activity, int seq) {
final ActivityThread activityThread = activity.getActivityThread();
+ final ActivityClientRecord r = getActivityClientRecord(activity);
final int numOfConfig = activity.mNumOfConfigChanges;
Configuration config = new Configuration();
config.orientation = ORIENTATION_PORTRAIT;
config.seq = seq;
- activityThread.handleActivityConfigurationChanged(activity.getActivityToken(), config,
- INVALID_DISPLAY);
+ activityThread.handleActivityConfigurationChanged(r, config, INVALID_DISPLAY);
if (activity.mNumOfConfigChanges > numOfConfig) {
return config.seq;
@@ -628,12 +631,17 @@ public class ActivityThreadTest {
config = new Configuration();
config.orientation = ORIENTATION_LANDSCAPE;
config.seq = seq + 1;
- activityThread.handleActivityConfigurationChanged(activity.getActivityToken(), config,
- INVALID_DISPLAY);
+ activityThread.handleActivityConfigurationChanged(r, config, INVALID_DISPLAY);
return config.seq;
}
+ private static ActivityClientRecord getActivityClientRecord(Activity activity) {
+ final ActivityThread thread = activity.getActivityThread();
+ final IBinder token = activity.getActivityToken();
+ return thread.getActivityClient(token);
+ }
+
private static ClientTransaction newRelaunchResumeTransaction(Activity activity) {
final ClientTransactionItem callbackItem = ActivityRelaunchItem.obtain(null,
null, 0, new MergedConfiguration(), false /* preserveWindow */);
diff --git a/core/tests/coretests/src/android/app/servertransaction/TransactionExecutorTests.java b/core/tests/coretests/src/android/app/servertransaction/TransactionExecutorTests.java
index 3c32c71cf961..0ae789af477c 100644
--- a/core/tests/coretests/src/android/app/servertransaction/TransactionExecutorTests.java
+++ b/core/tests/coretests/src/android/app/servertransaction/TransactionExecutorTests.java
@@ -32,11 +32,12 @@ import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.inOrder;
import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.never;
import static org.mockito.Mockito.spy;
-import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
+import android.app.Activity;
import android.app.ActivityThread.ActivityClientRecord;
import android.app.ClientTransactionHandler;
import android.app.servertransaction.ActivityLifecycleItem.LifecycleState;
@@ -225,6 +226,7 @@ public class TransactionExecutorTests {
when(callback2.getPostExecutionState()).thenReturn(UNDEFINED);
ActivityLifecycleItem stateRequest = mock(ActivityLifecycleItem.class);
IBinder token = mock(IBinder.class);
+ when(mTransactionHandler.getActivity(token)).thenReturn(mock(Activity.class));
ClientTransaction transaction = ClientTransaction.obtain(null /* client */,
token /* activityToken */);
@@ -236,9 +238,9 @@ public class TransactionExecutorTests {
mExecutor.execute(transaction);
InOrder inOrder = inOrder(mTransactionHandler, callback1, callback2, stateRequest);
- inOrder.verify(callback1, times(1)).execute(eq(mTransactionHandler), eq(token), any());
- inOrder.verify(callback2, times(1)).execute(eq(mTransactionHandler), eq(token), any());
- inOrder.verify(stateRequest, times(1)).execute(eq(mTransactionHandler), eq(token), any());
+ inOrder.verify(callback1).execute(eq(mTransactionHandler), eq(token), any());
+ inOrder.verify(callback2).execute(eq(mTransactionHandler), eq(token), any());
+ inOrder.verify(stateRequest).execute(eq(mTransactionHandler), eq(mClientRecord), any());
}
@Test
@@ -273,7 +275,7 @@ public class TransactionExecutorTests {
// The launch transaction should not be executed because its token is in the
// to-be-destroyed container.
- verify(launchItem, times(0)).execute(any(), any(), any());
+ verify(launchItem, never()).execute(any(), any(), any());
// After the destroy transaction has been executed, the token should be removed.
mExecutor.execute(destroyTransaction);
@@ -282,6 +284,8 @@ public class TransactionExecutorTests {
@Test
public void testActivityResultRequiredStateResolution() {
+ when(mTransactionHandler.getActivity(any())).thenReturn(mock(Activity.class));
+
PostExecItem postExecItem = new PostExecItem(ON_RESUME);
IBinder token = mock(IBinder.class);
@@ -292,12 +296,12 @@ public class TransactionExecutorTests {
// Verify resolution that should get to onPause
mClientRecord.setState(ON_RESUME);
mExecutor.executeCallbacks(transaction);
- verify(mExecutor, times(1)).cycleToPath(eq(mClientRecord), eq(ON_PAUSE), eq(transaction));
+ verify(mExecutor).cycleToPath(eq(mClientRecord), eq(ON_PAUSE), eq(transaction));
// Verify resolution that should get to onStart
mClientRecord.setState(ON_STOP);
mExecutor.executeCallbacks(transaction);
- verify(mExecutor, times(1)).cycleToPath(eq(mClientRecord), eq(ON_START), eq(transaction));
+ verify(mExecutor).cycleToPath(eq(mClientRecord), eq(ON_START), eq(transaction));
}
@Test
@@ -433,6 +437,36 @@ public class TransactionExecutorTests {
mExecutorHelper.getClosestPreExecutionState(mClientRecord, ON_RESUME));
}
+ @Test(expected = IllegalArgumentException.class)
+ public void testActivityItemNullRecordThrowsException() {
+ final ActivityTransactionItem activityItem = mock(ActivityTransactionItem.class);
+ when(activityItem.getPostExecutionState()).thenReturn(UNDEFINED);
+ final IBinder token = mock(IBinder.class);
+ final ClientTransaction transaction = ClientTransaction.obtain(null /* client */, token);
+ transaction.addCallback(activityItem);
+ when(mTransactionHandler.getActivityClient(token)).thenReturn(null);
+
+ mExecutor.executeCallbacks(transaction);
+ }
+
+ @Test
+ public void testActivityItemExecute() {
+ final IBinder token = mock(IBinder.class);
+ final ClientTransaction transaction = ClientTransaction.obtain(null /* client */, token);
+ final ActivityTransactionItem activityItem = mock(ActivityTransactionItem.class);
+ when(activityItem.getPostExecutionState()).thenReturn(UNDEFINED);
+ transaction.addCallback(activityItem);
+ final ActivityLifecycleItem stateRequest = mock(ActivityLifecycleItem.class);
+ transaction.setLifecycleStateRequest(stateRequest);
+ when(mTransactionHandler.getActivity(token)).thenReturn(mock(Activity.class));
+
+ mExecutor.execute(transaction);
+
+ final InOrder inOrder = inOrder(activityItem, stateRequest);
+ inOrder.verify(activityItem).execute(eq(mTransactionHandler), eq(mClientRecord), any());
+ inOrder.verify(stateRequest).execute(eq(mTransactionHandler), eq(mClientRecord), any());
+ }
+
private static int[] shuffledArray(int[] inputArray) {
final List<Integer> list = Arrays.stream(inputArray).boxed().collect(Collectors.toList());
Collections.shuffle(list);
@@ -489,13 +523,13 @@ public class TransactionExecutorTests {
public static final Parcelable.Creator<StubItem> CREATOR =
new Parcelable.Creator<StubItem>() {
- public StubItem createFromParcel(Parcel in) {
- return new StubItem(in);
- }
-
- public StubItem[] newArray(int size) {
- return new StubItem[size];
- }
- };
+ public StubItem createFromParcel(Parcel in) {
+ return new StubItem(in);
+ }
+
+ public StubItem[] newArray(int size) {
+ return new StubItem[size];
+ }
+ };
}
}
diff --git a/core/tests/coretests/src/android/content/ContextTest.java b/core/tests/coretests/src/android/content/ContextTest.java
index 777f4a3e03a8..d1776fb7e5c1 100644
--- a/core/tests/coretests/src/android/content/ContextTest.java
+++ b/core/tests/coretests/src/android/content/ContextTest.java
@@ -19,6 +19,7 @@ package android.content;
import static android.hardware.display.DisplayManager.VIRTUAL_DISPLAY_FLAG_OWN_CONTENT_ONLY;
import static android.hardware.display.DisplayManager.VIRTUAL_DISPLAY_FLAG_PUBLIC;
import static android.view.Display.DEFAULT_DISPLAY;
+import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION_OVERLAY;
import static com.google.common.truth.Truth.assertThat;
@@ -188,19 +189,38 @@ public class ContextTest {
assertFalse(wrapper.isUiContext());
- wrapper = new ContextWrapper(new TestUiContext());
+ wrapper = new ContextWrapper(createUiContext());
assertTrue(wrapper.isUiContext());
}
- private static class TestUiContext extends ContextWrapper {
- TestUiContext() {
- super(null /* base */);
- }
+ @Test
+ public void testIsUiContext_UiContextDerivedContext() {
+ final Context uiContext = createUiContext();
+ Context context = uiContext.createAttributionContext(null /* attributionTag */);
- @Override
- public boolean isUiContext() {
- return true;
- }
+ assertTrue(context.isUiContext());
+
+ context = uiContext.createConfigurationContext(new Configuration());
+
+ assertTrue(context.isUiContext());
+ }
+
+ @Test
+ public void testIsUiContext_UiContextDerivedDisplayContext() {
+ final Context uiContext = createUiContext();
+ final Display secondaryDisplay =
+ getSecondaryDisplay(uiContext.getSystemService(DisplayManager.class));
+ final Context context = uiContext.createDisplayContext(secondaryDisplay);
+
+ assertFalse(context.isUiContext());
+ }
+
+ private Context createUiContext() {
+ final Context appContext = ApplicationProvider.getApplicationContext();
+ final DisplayManager displayManager = appContext.getSystemService(DisplayManager.class);
+ final Display display = displayManager.getDisplay(DEFAULT_DISPLAY);
+ return appContext.createDisplayContext(display)
+ .createWindowContext(TYPE_APPLICATION_OVERLAY, null /* options */);
}
}
diff --git a/core/tests/coretests/src/android/view/InsetsAnimationControlImplTest.java b/core/tests/coretests/src/android/view/InsetsAnimationControlImplTest.java
index a2b1e3d69cd2..8412e53060c7 100644
--- a/core/tests/coretests/src/android/view/InsetsAnimationControlImplTest.java
+++ b/core/tests/coretests/src/android/view/InsetsAnimationControlImplTest.java
@@ -18,7 +18,6 @@ package android.view;
import static android.view.InsetsState.ITYPE_NAVIGATION_BAR;
import static android.view.InsetsState.ITYPE_STATUS_BAR;
-import static android.view.ViewRootImpl.NEW_INSETS_MODE_FULL;
import static android.view.WindowInsets.Type.systemBars;
import static org.junit.Assert.assertEquals;
@@ -42,13 +41,10 @@ import android.util.SparseArray;
import android.view.SurfaceControl.Transaction;
import android.view.SyncRtSurfaceTransactionApplier.SurfaceParams;
import android.view.animation.LinearInterpolator;
-import android.view.test.InsetsModeSession;
import androidx.test.runner.AndroidJUnit4;
-import org.junit.AfterClass;
import org.junit.Before;
-import org.junit.BeforeClass;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.ArgumentCaptor;
@@ -76,22 +72,11 @@ public class InsetsAnimationControlImplTest {
private SurfaceControl mTopLeash;
private SurfaceControl mNavLeash;
private InsetsState mInsetsState;
- private static InsetsModeSession sInsetsModeSession;
@Mock Transaction mMockTransaction;
@Mock InsetsController mMockController;
@Mock WindowInsetsAnimationControlListener mMockListener;
- @BeforeClass
- public static void setupOnce() {
- sInsetsModeSession = new InsetsModeSession(NEW_INSETS_MODE_FULL);
- }
-
- @AfterClass
- public static void tearDownOnce() {
- sInsetsModeSession.close();
- }
-
@Before
public void setup() {
MockitoAnnotations.initMocks(this);
diff --git a/core/tests/coretests/src/android/view/InsetsControllerTest.java b/core/tests/coretests/src/android/view/InsetsControllerTest.java
index 88e1f5781e0e..cc97eb5988e6 100644
--- a/core/tests/coretests/src/android/view/InsetsControllerTest.java
+++ b/core/tests/coretests/src/android/view/InsetsControllerTest.java
@@ -26,7 +26,6 @@ import static android.view.InsetsState.ITYPE_CAPTION_BAR;
import static android.view.InsetsState.ITYPE_IME;
import static android.view.InsetsState.ITYPE_NAVIGATION_BAR;
import static android.view.InsetsState.ITYPE_STATUS_BAR;
-import static android.view.ViewRootImpl.NEW_INSETS_MODE_FULL;
import static android.view.WindowInsets.Type.ime;
import static android.view.WindowInsets.Type.navigationBars;
import static android.view.WindowInsets.Type.statusBars;
@@ -63,7 +62,6 @@ import android.view.WindowInsetsController.OnControllableInsetsChangedListener;
import android.view.WindowManager.BadTokenException;
import android.view.WindowManager.LayoutParams;
import android.view.animation.LinearInterpolator;
-import android.view.test.InsetsModeSession;
import android.widget.TextView;
import androidx.test.ext.junit.runners.AndroidJUnit4;
@@ -72,9 +70,7 @@ import androidx.test.platform.app.InstrumentationRegistry;
import com.android.server.testutils.OffsettableClock;
import com.android.server.testutils.TestHandler;
-import org.junit.AfterClass;
import org.junit.Before;
-import org.junit.BeforeClass;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.ArgumentCaptor;
@@ -102,17 +98,6 @@ public class InsetsControllerTest {
private TestHost mTestHost;
private TestHandler mTestHandler;
private OffsettableClock mTestClock;
- private static InsetsModeSession sInsetsModeSession;
-
- @BeforeClass
- public static void setupOnce() {
- sInsetsModeSession = new InsetsModeSession(NEW_INSETS_MODE_FULL);
- }
-
- @AfterClass
- public static void tearDownOnce() {
- sInsetsModeSession.close();
- }
@Before
public void setup() {
diff --git a/core/tests/coretests/src/android/view/InsetsStateTest.java b/core/tests/coretests/src/android/view/InsetsStateTest.java
index 4306e3e0fe7b..afab7696e87d 100644
--- a/core/tests/coretests/src/android/view/InsetsStateTest.java
+++ b/core/tests/coretests/src/android/view/InsetsStateTest.java
@@ -31,6 +31,7 @@ import static android.view.View.SYSTEM_UI_FLAG_LAYOUT_STABLE;
import static android.view.WindowInsets.Type.ime;
import static android.view.WindowInsets.Type.navigationBars;
import static android.view.WindowInsets.Type.statusBars;
+import static android.view.WindowInsets.Type.systemBars;
import static android.view.WindowManager.LayoutParams.FLAG_FULLSCREEN;
import static android.view.WindowManager.LayoutParams.FLAG_LAYOUT_NO_LIMITS;
import static android.view.WindowManager.LayoutParams.SOFT_INPUT_ADJUST_NOTHING;
@@ -51,7 +52,6 @@ import android.os.Parcel;
import android.platform.test.annotations.Presubmit;
import android.util.SparseIntArray;
import android.view.WindowInsets.Type;
-import android.view.test.InsetsModeSession;
import androidx.test.runner.AndroidJUnit4;
@@ -75,114 +75,96 @@ public class InsetsStateTest {
private InsetsState mState2 = new InsetsState();
@Test
- public void testCalculateInsets() throws Exception {
- try (final InsetsModeSession session =
- new InsetsModeSession(ViewRootImpl.NEW_INSETS_MODE_FULL)) {
- mState.getSource(ITYPE_STATUS_BAR).setFrame(new Rect(0, 0, 100, 100));
- mState.getSource(ITYPE_STATUS_BAR).setVisible(true);
- mState.getSource(ITYPE_IME).setFrame(new Rect(0, 200, 100, 300));
- mState.getSource(ITYPE_IME).setVisible(true);
- SparseIntArray typeSideMap = new SparseIntArray();
- WindowInsets insets = mState.calculateInsets(new Rect(0, 0, 100, 300), null, false,
- false, DisplayCutout.NO_CUTOUT, SOFT_INPUT_ADJUST_RESIZE, 0, 0,
- TYPE_APPLICATION, WINDOWING_MODE_UNDEFINED, typeSideMap);
- assertEquals(Insets.of(0, 100, 0, 100), insets.getSystemWindowInsets());
- assertEquals(Insets.of(0, 100, 0, 100), insets.getInsets(Type.all()));
- assertEquals(ISIDE_TOP, typeSideMap.get(ITYPE_STATUS_BAR));
- assertEquals(ISIDE_BOTTOM, typeSideMap.get(ITYPE_IME));
- assertEquals(Insets.of(0, 100, 0, 0), insets.getInsets(Type.statusBars()));
- assertEquals(Insets.of(0, 0, 0, 100), insets.getInsets(Type.ime()));
- }
+ public void testCalculateInsets() {
+ mState.getSource(ITYPE_STATUS_BAR).setFrame(new Rect(0, 0, 100, 100));
+ mState.getSource(ITYPE_STATUS_BAR).setVisible(true);
+ mState.getSource(ITYPE_IME).setFrame(new Rect(0, 200, 100, 300));
+ mState.getSource(ITYPE_IME).setVisible(true);
+ SparseIntArray typeSideMap = new SparseIntArray();
+ WindowInsets insets = mState.calculateInsets(new Rect(0, 0, 100, 300), null, false,
+ false, DisplayCutout.NO_CUTOUT, SOFT_INPUT_ADJUST_RESIZE, 0, 0,
+ TYPE_APPLICATION, WINDOWING_MODE_UNDEFINED, typeSideMap);
+ assertEquals(Insets.of(0, 100, 0, 100), insets.getSystemWindowInsets());
+ assertEquals(Insets.of(0, 100, 0, 100), insets.getInsets(Type.all()));
+ assertEquals(ISIDE_TOP, typeSideMap.get(ITYPE_STATUS_BAR));
+ assertEquals(ISIDE_BOTTOM, typeSideMap.get(ITYPE_IME));
+ assertEquals(Insets.of(0, 100, 0, 0), insets.getInsets(statusBars()));
+ assertEquals(Insets.of(0, 0, 0, 100), insets.getInsets(ime()));
}
@Test
- public void testCalculateInsets_imeAndNav() throws Exception{
- try (final InsetsModeSession session =
- new InsetsModeSession(ViewRootImpl.NEW_INSETS_MODE_FULL)) {
- mState.getSource(ITYPE_NAVIGATION_BAR).setFrame(new Rect(0, 200, 100, 300));
- mState.getSource(ITYPE_NAVIGATION_BAR).setVisible(true);
- mState.getSource(ITYPE_IME).setFrame(new Rect(0, 100, 100, 300));
- mState.getSource(ITYPE_IME).setVisible(true);
- WindowInsets insets = mState.calculateInsets(new Rect(0, 0, 100, 300), null, false,
- false, DisplayCutout.NO_CUTOUT, SOFT_INPUT_ADJUST_RESIZE, 0, 0,
- TYPE_APPLICATION, WINDOWING_MODE_UNDEFINED, null);
- assertEquals(100, insets.getStableInsetBottom());
- assertEquals(Insets.of(0, 0, 0, 100), insets.getInsetsIgnoringVisibility(Type.systemBars()));
- assertEquals(Insets.of(0, 0, 0, 200), insets.getSystemWindowInsets());
- assertEquals(Insets.of(0, 0, 0, 200), insets.getInsets(Type.all()));
- assertEquals(Insets.of(0, 0, 0, 100), insets.getInsets(Type.navigationBars()));
- assertEquals(Insets.of(0, 0, 0, 200), insets.getInsets(Type.ime()));
- }
+ public void testCalculateInsets_imeAndNav() {
+ mState.getSource(ITYPE_NAVIGATION_BAR).setFrame(new Rect(0, 200, 100, 300));
+ mState.getSource(ITYPE_NAVIGATION_BAR).setVisible(true);
+ mState.getSource(ITYPE_IME).setFrame(new Rect(0, 100, 100, 300));
+ mState.getSource(ITYPE_IME).setVisible(true);
+ WindowInsets insets = mState.calculateInsets(new Rect(0, 0, 100, 300), null, false,
+ false, DisplayCutout.NO_CUTOUT, SOFT_INPUT_ADJUST_RESIZE, 0, 0,
+ TYPE_APPLICATION, WINDOWING_MODE_UNDEFINED, null);
+ assertEquals(100, insets.getStableInsetBottom());
+ assertEquals(Insets.of(0, 0, 0, 100), insets.getInsetsIgnoringVisibility(systemBars()));
+ assertEquals(Insets.of(0, 0, 0, 200), insets.getSystemWindowInsets());
+ assertEquals(Insets.of(0, 0, 0, 200), insets.getInsets(Type.all()));
+ assertEquals(Insets.of(0, 0, 0, 100), insets.getInsets(navigationBars()));
+ assertEquals(Insets.of(0, 0, 0, 200), insets.getInsets(ime()));
}
@Test
- public void testCalculateInsets_navRightStatusTop() throws Exception {
- try (final InsetsModeSession session =
- new InsetsModeSession(ViewRootImpl.NEW_INSETS_MODE_FULL)) {
- mState.getSource(ITYPE_STATUS_BAR).setFrame(new Rect(0, 0, 100, 100));
- mState.getSource(ITYPE_STATUS_BAR).setVisible(true);
- mState.getSource(ITYPE_NAVIGATION_BAR).setFrame(new Rect(80, 0, 100, 300));
- mState.getSource(ITYPE_NAVIGATION_BAR).setVisible(true);
- WindowInsets insets = mState.calculateInsets(new Rect(0, 0, 100, 300), null, false,
- false, DisplayCutout.NO_CUTOUT, 0, 0, 0, TYPE_APPLICATION,
- WINDOWING_MODE_UNDEFINED, null);
- assertEquals(Insets.of(0, 100, 20, 0), insets.getSystemWindowInsets());
- assertEquals(Insets.of(0, 100, 0, 0), insets.getInsets(Type.statusBars()));
- assertEquals(Insets.of(0, 0, 20, 0), insets.getInsets(Type.navigationBars()));
- }
+ public void testCalculateInsets_navRightStatusTop() {
+ mState.getSource(ITYPE_STATUS_BAR).setFrame(new Rect(0, 0, 100, 100));
+ mState.getSource(ITYPE_STATUS_BAR).setVisible(true);
+ mState.getSource(ITYPE_NAVIGATION_BAR).setFrame(new Rect(80, 0, 100, 300));
+ mState.getSource(ITYPE_NAVIGATION_BAR).setVisible(true);
+ WindowInsets insets = mState.calculateInsets(new Rect(0, 0, 100, 300), null, false,
+ false, DisplayCutout.NO_CUTOUT, 0, 0, 0, TYPE_APPLICATION,
+ WINDOWING_MODE_UNDEFINED, null);
+ assertEquals(Insets.of(0, 100, 20, 0), insets.getSystemWindowInsets());
+ assertEquals(Insets.of(0, 100, 0, 0), insets.getInsets(statusBars()));
+ assertEquals(Insets.of(0, 0, 20, 0), insets.getInsets(navigationBars()));
}
@Test
public void testCalculateInsets_imeIgnoredWithoutAdjustResize() {
- try (final InsetsModeSession session =
- new InsetsModeSession(ViewRootImpl.NEW_INSETS_MODE_FULL)) {
- mState.getSource(ITYPE_STATUS_BAR).setFrame(new Rect(0, 0, 100, 100));
- mState.getSource(ITYPE_STATUS_BAR).setVisible(true);
- mState.getSource(ITYPE_IME).setFrame(new Rect(0, 200, 100, 300));
- mState.getSource(ITYPE_IME).setVisible(true);
- WindowInsets insets = mState.calculateInsets(new Rect(0, 0, 100, 300), null, false,
- false, DisplayCutout.NO_CUTOUT, SOFT_INPUT_ADJUST_NOTHING, 0, 0,
- TYPE_APPLICATION, WINDOWING_MODE_UNDEFINED, null);
- assertEquals(0, insets.getSystemWindowInsetBottom());
- assertEquals(100, insets.getInsets(ime()).bottom);
- assertTrue(insets.isVisible(ime()));
- }
+ mState.getSource(ITYPE_STATUS_BAR).setFrame(new Rect(0, 0, 100, 100));
+ mState.getSource(ITYPE_STATUS_BAR).setVisible(true);
+ mState.getSource(ITYPE_IME).setFrame(new Rect(0, 200, 100, 300));
+ mState.getSource(ITYPE_IME).setVisible(true);
+ WindowInsets insets = mState.calculateInsets(new Rect(0, 0, 100, 300), null, false,
+ false, DisplayCutout.NO_CUTOUT, SOFT_INPUT_ADJUST_NOTHING, 0, 0,
+ TYPE_APPLICATION, WINDOWING_MODE_UNDEFINED, null);
+ assertEquals(0, insets.getSystemWindowInsetBottom());
+ assertEquals(100, insets.getInsets(ime()).bottom);
+ assertTrue(insets.isVisible(ime()));
}
@Test
public void testCalculateInsets_systemUiFlagLayoutStable() {
- try (final InsetsModeSession session =
- new InsetsModeSession(ViewRootImpl.NEW_INSETS_MODE_FULL)) {
- mState.getSource(ITYPE_STATUS_BAR).setFrame(new Rect(0, 0, 100, 100));
- mState.getSource(ITYPE_STATUS_BAR).setVisible(false);
- mState.getSource(ITYPE_IME).setFrame(new Rect(0, 200, 100, 300));
- mState.getSource(ITYPE_IME).setVisible(true);
- WindowInsets insets = mState.calculateInsets(new Rect(0, 0, 100, 300), null, false,
- false, DisplayCutout.NO_CUTOUT, SOFT_INPUT_ADJUST_NOTHING, 0,
- SYSTEM_UI_FLAG_LAYOUT_STABLE, TYPE_APPLICATION, WINDOWING_MODE_UNDEFINED, null);
- assertEquals(100, insets.getSystemWindowInsetTop());
- insets = mState.calculateInsets(new Rect(0, 0, 100, 300), null, false, false,
- DisplayCutout.NO_CUTOUT, SOFT_INPUT_ADJUST_NOTHING, 0,
- 0 /* legacySystemUiFlags */, TYPE_APPLICATION, WINDOWING_MODE_UNDEFINED, null);
- assertEquals(0, insets.getSystemWindowInsetTop());
- }
+ mState.getSource(ITYPE_STATUS_BAR).setFrame(new Rect(0, 0, 100, 100));
+ mState.getSource(ITYPE_STATUS_BAR).setVisible(false);
+ mState.getSource(ITYPE_IME).setFrame(new Rect(0, 200, 100, 300));
+ mState.getSource(ITYPE_IME).setVisible(true);
+ WindowInsets insets = mState.calculateInsets(new Rect(0, 0, 100, 300), null, false,
+ false, DisplayCutout.NO_CUTOUT, SOFT_INPUT_ADJUST_NOTHING, 0,
+ SYSTEM_UI_FLAG_LAYOUT_STABLE, TYPE_APPLICATION, WINDOWING_MODE_UNDEFINED, null);
+ assertEquals(100, insets.getSystemWindowInsetTop());
+ insets = mState.calculateInsets(new Rect(0, 0, 100, 300), null, false, false,
+ DisplayCutout.NO_CUTOUT, SOFT_INPUT_ADJUST_NOTHING, 0,
+ 0 /* legacySystemUiFlags */, TYPE_APPLICATION, WINDOWING_MODE_UNDEFINED, null);
+ assertEquals(0, insets.getSystemWindowInsetTop());
}
@Test
public void testCalculateInsets_systemUiFlagLayoutStable_windowFlagFullscreen() {
- try (final InsetsModeSession session =
- new InsetsModeSession(ViewRootImpl.NEW_INSETS_MODE_FULL)) {
- mState.getSource(ITYPE_STATUS_BAR).setFrame(new Rect(0, 0, 100, 100));
- mState.getSource(ITYPE_STATUS_BAR).setVisible(false);
- WindowInsets insets = mState.calculateInsets(new Rect(0, 0, 100, 300), null, false,
- false, DisplayCutout.NO_CUTOUT, SOFT_INPUT_ADJUST_NOTHING, FLAG_FULLSCREEN,
- SYSTEM_UI_FLAG_LAYOUT_STABLE, TYPE_APPLICATION, WINDOWING_MODE_UNDEFINED, null);
- assertEquals(0, insets.getSystemWindowInsetTop());
- insets = mState.calculateInsets(new Rect(0, 0, 100, 300), null, false, false,
- DisplayCutout.NO_CUTOUT, SOFT_INPUT_ADJUST_NOTHING, 0,
- 0 /* legacySystemUiFlags */, TYPE_APPLICATION, WINDOWING_MODE_UNDEFINED, null);
- assertEquals(0, insets.getSystemWindowInsetTop());
- }
+ mState.getSource(ITYPE_STATUS_BAR).setFrame(new Rect(0, 0, 100, 100));
+ mState.getSource(ITYPE_STATUS_BAR).setVisible(false);
+ WindowInsets insets = mState.calculateInsets(new Rect(0, 0, 100, 300), null, false,
+ false, DisplayCutout.NO_CUTOUT, SOFT_INPUT_ADJUST_NOTHING, FLAG_FULLSCREEN,
+ SYSTEM_UI_FLAG_LAYOUT_STABLE, TYPE_APPLICATION, WINDOWING_MODE_UNDEFINED, null);
+ assertEquals(0, insets.getSystemWindowInsetTop());
+ insets = mState.calculateInsets(new Rect(0, 0, 100, 300), null, false, false,
+ DisplayCutout.NO_CUTOUT, SOFT_INPUT_ADJUST_NOTHING, 0,
+ 0 /* legacySystemUiFlags */, TYPE_APPLICATION, WINDOWING_MODE_UNDEFINED, null);
+ assertEquals(0, insets.getSystemWindowInsetTop());
}
@Test
@@ -209,65 +191,53 @@ public class InsetsStateTest {
@Test
- public void testCalculateInsets_captionStatusBarOverlap() throws Exception {
- try (InsetsModeSession session =
- new InsetsModeSession(ViewRootImpl.NEW_INSETS_MODE_FULL)) {
- mState.getSource(ITYPE_STATUS_BAR).setFrame(new Rect(0, 0, 100, 100));
- mState.getSource(ITYPE_STATUS_BAR).setVisible(true);
- mState.getSource(ITYPE_CAPTION_BAR).setFrame(new Rect(0, 0, 100, 300));
- mState.getSource(ITYPE_CAPTION_BAR).setVisible(true);
-
- Rect visibleInsets = mState.calculateVisibleInsets(
- new Rect(0, 0, 100, 400), SOFT_INPUT_ADJUST_NOTHING);
- assertEquals(new Rect(0, 300, 0, 0), visibleInsets);
- }
+ public void testCalculateInsets_captionStatusBarOverlap() {
+ mState.getSource(ITYPE_STATUS_BAR).setFrame(new Rect(0, 0, 100, 100));
+ mState.getSource(ITYPE_STATUS_BAR).setVisible(true);
+ mState.getSource(ITYPE_CAPTION_BAR).setFrame(new Rect(0, 0, 100, 300));
+ mState.getSource(ITYPE_CAPTION_BAR).setVisible(true);
+
+ Rect visibleInsets = mState.calculateVisibleInsets(
+ new Rect(0, 0, 100, 400), SOFT_INPUT_ADJUST_NOTHING);
+ assertEquals(new Rect(0, 300, 0, 0), visibleInsets);
}
@Test
- public void testCalculateInsets_captionBarOffset() throws Exception {
- try (InsetsModeSession session =
- new InsetsModeSession(ViewRootImpl.NEW_INSETS_MODE_FULL)) {
- mState.getSource(ITYPE_CAPTION_BAR).setFrame(new Rect(0, 0, 100, 300));
- mState.getSource(ITYPE_CAPTION_BAR).setVisible(true);
-
- Rect visibleInsets = mState.calculateVisibleInsets(
- new Rect(0, 0, 150, 400), SOFT_INPUT_ADJUST_NOTHING);
- assertEquals(new Rect(0, 300, 0, 0), visibleInsets);
- }
+ public void testCalculateInsets_captionBarOffset() {
+ mState.getSource(ITYPE_CAPTION_BAR).setFrame(new Rect(0, 0, 100, 300));
+ mState.getSource(ITYPE_CAPTION_BAR).setVisible(true);
+
+ Rect visibleInsets = mState.calculateVisibleInsets(
+ new Rect(0, 0, 150, 400), SOFT_INPUT_ADJUST_NOTHING);
+ assertEquals(new Rect(0, 300, 0, 0), visibleInsets);
}
@Test
- public void testCalculateInsets_extraNavRightStatusTop() throws Exception {
- try (InsetsModeSession session =
- new InsetsModeSession(ViewRootImpl.NEW_INSETS_MODE_FULL)) {
- mState.getSource(ITYPE_STATUS_BAR).setFrame(new Rect(0, 0, 100, 100));
- mState.getSource(ITYPE_STATUS_BAR).setVisible(true);
- mState.getSource(ITYPE_EXTRA_NAVIGATION_BAR).setFrame(new Rect(80, 0, 100, 300));
- mState.getSource(ITYPE_EXTRA_NAVIGATION_BAR).setVisible(true);
- WindowInsets insets = mState.calculateInsets(new Rect(0, 0, 100, 300), null, false,
- false, DisplayCutout.NO_CUTOUT, 0, 0, 0, TYPE_APPLICATION,
- WINDOWING_MODE_UNDEFINED, null);
- assertEquals(Insets.of(0, 100, 20, 0), insets.getSystemWindowInsets());
- assertEquals(Insets.of(0, 100, 0, 0), insets.getInsets(Type.statusBars()));
- assertEquals(Insets.of(0, 0, 20, 0), insets.getInsets(Type.navigationBars()));
- }
+ public void testCalculateInsets_extraNavRightStatusTop() {
+ mState.getSource(ITYPE_STATUS_BAR).setFrame(new Rect(0, 0, 100, 100));
+ mState.getSource(ITYPE_STATUS_BAR).setVisible(true);
+ mState.getSource(ITYPE_EXTRA_NAVIGATION_BAR).setFrame(new Rect(80, 0, 100, 300));
+ mState.getSource(ITYPE_EXTRA_NAVIGATION_BAR).setVisible(true);
+ WindowInsets insets = mState.calculateInsets(new Rect(0, 0, 100, 300), null, false,
+ false, DisplayCutout.NO_CUTOUT, 0, 0, 0, TYPE_APPLICATION,
+ WINDOWING_MODE_UNDEFINED, null);
+ assertEquals(Insets.of(0, 100, 20, 0), insets.getSystemWindowInsets());
+ assertEquals(Insets.of(0, 100, 0, 0), insets.getInsets(statusBars()));
+ assertEquals(Insets.of(0, 0, 20, 0), insets.getInsets(navigationBars()));
}
@Test
- public void testCalculateInsets_navigationRightClimateTop() throws Exception {
- try (InsetsModeSession session =
- new InsetsModeSession(ViewRootImpl.NEW_INSETS_MODE_FULL)) {
- mState.getSource(ITYPE_CLIMATE_BAR).setFrame(new Rect(0, 0, 100, 100));
- mState.getSource(ITYPE_CLIMATE_BAR).setVisible(true);
- mState.getSource(ITYPE_NAVIGATION_BAR).setFrame(new Rect(80, 0, 100, 300));
- mState.getSource(ITYPE_NAVIGATION_BAR).setVisible(true);
- WindowInsets insets = mState.calculateInsets(new Rect(0, 0, 100, 300), null, false,
- false, DisplayCutout.NO_CUTOUT, 0, 0, 0, TYPE_APPLICATION,
- WINDOWING_MODE_UNDEFINED, null);
- assertEquals(Insets.of(0, 100, 20, 0), insets.getSystemWindowInsets());
- assertEquals(Insets.of(0, 100, 0, 0), insets.getInsets(Type.statusBars()));
- assertEquals(Insets.of(0, 0, 20, 0), insets.getInsets(Type.navigationBars()));
- }
+ public void testCalculateInsets_navigationRightClimateTop() {
+ mState.getSource(ITYPE_CLIMATE_BAR).setFrame(new Rect(0, 0, 100, 100));
+ mState.getSource(ITYPE_CLIMATE_BAR).setVisible(true);
+ mState.getSource(ITYPE_NAVIGATION_BAR).setFrame(new Rect(80, 0, 100, 300));
+ mState.getSource(ITYPE_NAVIGATION_BAR).setVisible(true);
+ WindowInsets insets = mState.calculateInsets(new Rect(0, 0, 100, 300), null, false,
+ false, DisplayCutout.NO_CUTOUT, 0, 0, 0, TYPE_APPLICATION,
+ WINDOWING_MODE_UNDEFINED, null);
+ assertEquals(Insets.of(0, 100, 20, 0), insets.getSystemWindowInsets());
+ assertEquals(Insets.of(0, 100, 0, 0), insets.getInsets(statusBars()));
+ assertEquals(Insets.of(0, 0, 20, 0), insets.getInsets(navigationBars()));
}
@Test
@@ -371,59 +341,51 @@ public class InsetsStateTest {
}
@Test
- public void testCalculateVisibleInsets() throws Exception {
- try (final InsetsModeSession session =
- new InsetsModeSession(ViewRootImpl.NEW_INSETS_MODE_FULL)) {
- mState.getSource(ITYPE_STATUS_BAR).setFrame(new Rect(0, 0, 100, 100));
- mState.getSource(ITYPE_STATUS_BAR).setVisible(true);
- mState.getSource(ITYPE_IME).setFrame(new Rect(0, 200, 100, 300));
- mState.getSource(ITYPE_IME).setVisible(true);
-
- // Make sure bottom gestures are ignored
- mState.getSource(ITYPE_BOTTOM_GESTURES).setFrame(new Rect(0, 100, 100, 300));
- mState.getSource(ITYPE_BOTTOM_GESTURES).setVisible(true);
- Rect visibleInsets = mState.calculateVisibleInsets(
- new Rect(0, 0, 100, 300), SOFT_INPUT_ADJUST_PAN);
- assertEquals(new Rect(0, 100, 0, 100), visibleInsets);
- }
+ public void testCalculateVisibleInsets() {
+ mState.getSource(ITYPE_STATUS_BAR).setFrame(new Rect(0, 0, 100, 100));
+ mState.getSource(ITYPE_STATUS_BAR).setVisible(true);
+ mState.getSource(ITYPE_IME).setFrame(new Rect(0, 200, 100, 300));
+ mState.getSource(ITYPE_IME).setVisible(true);
+
+ // Make sure bottom gestures are ignored
+ mState.getSource(ITYPE_BOTTOM_GESTURES).setFrame(new Rect(0, 100, 100, 300));
+ mState.getSource(ITYPE_BOTTOM_GESTURES).setVisible(true);
+ Rect visibleInsets = mState.calculateVisibleInsets(
+ new Rect(0, 0, 100, 300), SOFT_INPUT_ADJUST_PAN);
+ assertEquals(new Rect(0, 100, 0, 100), visibleInsets);
}
@Test
- public void testCalculateVisibleInsets_adjustNothing() throws Exception {
- try (final InsetsModeSession session =
- new InsetsModeSession(ViewRootImpl.NEW_INSETS_MODE_FULL)) {
- mState.getSource(ITYPE_STATUS_BAR).setFrame(new Rect(0, 0, 100, 100));
- mState.getSource(ITYPE_STATUS_BAR).setVisible(true);
- mState.getSource(ITYPE_IME).setFrame(new Rect(0, 200, 100, 300));
- mState.getSource(ITYPE_IME).setVisible(true);
-
- // Make sure bottom gestures are ignored
- mState.getSource(ITYPE_BOTTOM_GESTURES).setFrame(new Rect(0, 100, 100, 300));
- mState.getSource(ITYPE_BOTTOM_GESTURES).setVisible(true);
- Rect visibleInsets = mState.calculateVisibleInsets(
- new Rect(0, 0, 100, 300), SOFT_INPUT_ADJUST_NOTHING);
- assertEquals(new Rect(0, 100, 0, 0), visibleInsets);
- }
+ public void testCalculateVisibleInsets_adjustNothing() {
+ mState.getSource(ITYPE_STATUS_BAR).setFrame(new Rect(0, 0, 100, 100));
+ mState.getSource(ITYPE_STATUS_BAR).setVisible(true);
+ mState.getSource(ITYPE_IME).setFrame(new Rect(0, 200, 100, 300));
+ mState.getSource(ITYPE_IME).setVisible(true);
+
+ // Make sure bottom gestures are ignored
+ mState.getSource(ITYPE_BOTTOM_GESTURES).setFrame(new Rect(0, 100, 100, 300));
+ mState.getSource(ITYPE_BOTTOM_GESTURES).setVisible(true);
+ Rect visibleInsets = mState.calculateVisibleInsets(
+ new Rect(0, 0, 100, 300), SOFT_INPUT_ADJUST_NOTHING);
+ assertEquals(new Rect(0, 100, 0, 0), visibleInsets);
}
@Test
- public void testCalculateUncontrollableInsets() throws Exception {
- try (InsetsModeSession session = new InsetsModeSession(ViewRootImpl.NEW_INSETS_MODE_FULL)) {
- mState.getSource(ITYPE_STATUS_BAR).setFrame(new Rect(0, 0, 200, 100));
- mState.getSource(ITYPE_STATUS_BAR).setVisible(true);
- mState.getSource(ITYPE_IME).setFrame(new Rect(0, 200, 200, 300));
- mState.getSource(ITYPE_IME).setVisible(true);
- mState.getSource(ITYPE_NAVIGATION_BAR).setFrame(new Rect(100, 0, 200, 300));
- mState.getSource(ITYPE_NAVIGATION_BAR).setVisible(true);
-
- mState.setDisplayFrame(new Rect(0, 0, 200, 300));
- assertEquals(0,
- mState.calculateUncontrollableInsetsFromFrame(new Rect(0, 0, 200, 300)));
- assertEquals(statusBars() | ime(),
- mState.calculateUncontrollableInsetsFromFrame(new Rect(0, 50, 200, 250)));
- assertEquals(navigationBars(),
- mState.calculateUncontrollableInsetsFromFrame(new Rect(50, 0, 150, 300)));
- }
+ public void testCalculateUncontrollableInsets() {
+ mState.getSource(ITYPE_STATUS_BAR).setFrame(new Rect(0, 0, 200, 100));
+ mState.getSource(ITYPE_STATUS_BAR).setVisible(true);
+ mState.getSource(ITYPE_IME).setFrame(new Rect(0, 200, 200, 300));
+ mState.getSource(ITYPE_IME).setVisible(true);
+ mState.getSource(ITYPE_NAVIGATION_BAR).setFrame(new Rect(100, 0, 200, 300));
+ mState.getSource(ITYPE_NAVIGATION_BAR).setVisible(true);
+
+ mState.setDisplayFrame(new Rect(0, 0, 200, 300));
+ assertEquals(0,
+ mState.calculateUncontrollableInsetsFromFrame(new Rect(0, 0, 200, 300)));
+ assertEquals(statusBars() | ime(),
+ mState.calculateUncontrollableInsetsFromFrame(new Rect(0, 50, 200, 250)));
+ assertEquals(navigationBars(),
+ mState.calculateUncontrollableInsetsFromFrame(new Rect(50, 0, 150, 300)));
}
private void assertEqualsAndHashCode() {
diff --git a/core/tests/coretests/src/android/view/inputmethod/EditorInfoTest.java b/core/tests/coretests/src/android/view/inputmethod/EditorInfoTest.java
index 93de03adfa84..5c410878c99d 100644
--- a/core/tests/coretests/src/android/view/inputmethod/EditorInfoTest.java
+++ b/core/tests/coretests/src/android/view/inputmethod/EditorInfoTest.java
@@ -274,13 +274,10 @@ public class EditorInfoTest {
editorInfo.inputType = EditorInfo.TYPE_CLASS_TEXT;
editorInfo.setInitialSurroundingText(sb);
- sb.setLength(0);
- editorInfo.writeToParcel(parcel, 0);
+ sb.setLength(/* newLength= */ 0);
+ editorInfo.writeToParcel(parcel, /* flags= */ 0);
- try {
- editorInfo.getInitialTextBeforeCursor(60, 1);
- fail("Test shouldn't have exception");
- } catch (AssertionError e) { }
+ editorInfo.getInitialTextBeforeCursor(/* length= */ 60, /* flags= */ 1);
}
private static void assertExpectedTextLength(EditorInfo editorInfo,
diff --git a/core/tests/coretests/src/android/widget/focus/ListOfEditTexts.java b/core/tests/coretests/src/android/widget/focus/ListOfEditTexts.java
deleted file mode 100644
index 936c9999e7f8..000000000000
--- a/core/tests/coretests/src/android/widget/focus/ListOfEditTexts.java
+++ /dev/null
@@ -1,130 +0,0 @@
-/*
- * Copyright (C) 2007 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.widget.focus;
-
-import android.app.Activity;
-import android.content.Context;
-import android.os.Bundle;
-import android.view.View;
-import android.view.ViewGroup;
-import android.widget.ArrayAdapter;
-import android.widget.Button;
-import android.widget.EditText;
-import android.widget.LinearLayout;
-import android.widget.ListView;
-
-import com.google.android.collect.Lists;
-
-import java.util.List;
-
-public class ListOfEditTexts extends Activity {
-
- private int mLinesPerEditText = 12;
-
- private ListView mListView;
- private LinearLayout mLinearLayout;
-
- public ListView getListView() {
- return mListView;
- }
-
- @Override
- protected void onCreate(Bundle icicle) {
- super.onCreate(icicle);
-
- // create linear layout
- mLinearLayout = new LinearLayout(this);
- mLinearLayout.setOrientation(LinearLayout.VERTICAL);
- mLinearLayout.setLayoutParams(new ViewGroup.LayoutParams(
- ViewGroup.LayoutParams.MATCH_PARENT,
- ViewGroup.LayoutParams.MATCH_PARENT));
-
- // add a button above
- Button buttonAbove = new Button(this);
- buttonAbove.setLayoutParams(
- new LinearLayout.LayoutParams(
- ViewGroup.LayoutParams.MATCH_PARENT,
- ViewGroup.LayoutParams.WRAP_CONTENT));
- buttonAbove.setText("button above list");
- mLinearLayout.addView(buttonAbove);
-
- // add a list view to it
- mListView = new ListView(this);
- mListView.setLayoutParams(new ViewGroup.LayoutParams(
- ViewGroup.LayoutParams.MATCH_PARENT,
- ViewGroup.LayoutParams.MATCH_PARENT));
- mListView.setDrawSelectorOnTop(false);
- mListView.setItemsCanFocus(true);
- mListView.setLayoutParams((new LinearLayout.LayoutParams(
- ViewGroup.LayoutParams.MATCH_PARENT,
- 0,
- 1f)));
-
- List<String> bodies = Lists.newArrayList(
- getBody("zero hello, my name is android"),
- getBody("one i'm a paranoid android"),
- getBody("two i robot. huh huh."),
- getBody("three not the g-phone!"));
-
- mListView.setAdapter(new MyAdapter(this, bodies));
- mLinearLayout.addView(mListView);
-
- // add button below
- Button buttonBelow = new Button(this);
- buttonBelow.setLayoutParams(
- new LinearLayout.LayoutParams(
- ViewGroup.LayoutParams.MATCH_PARENT,
- ViewGroup.LayoutParams.WRAP_CONTENT));
- buttonBelow.setText("button below list");
- mLinearLayout.addView(buttonBelow);
-
- setContentView(mLinearLayout);
- }
-
- String getBody(String line) {
- StringBuilder sb = new StringBuilder((line.length() + 5) * mLinesPerEditText);
- for (int i = 0; i < mLinesPerEditText; i++) {
- sb.append(i + 1).append(' ').append(line);
- if (i < mLinesPerEditText - 1) {
- sb.append('\n'); // all but last line
- }
- }
- return sb.toString();
- }
-
-
- private static class MyAdapter extends ArrayAdapter<String> {
-
- public MyAdapter(Context context, List<String> bodies) {
- super(context, 0, bodies);
- }
-
- @Override
- public View getView(int position, View convertView, ViewGroup parent) {
- String body = getItem(position);
-
- if (convertView != null) {
- ((EditText) convertView).setText(body);
- return convertView;
- }
-
- EditText editText = new EditText(getContext());
- editText.setText(body);
- return editText;
- }
- }
-}
diff --git a/core/tests/coretests/src/com/android/internal/jank/FrameTrackerTest.java b/core/tests/coretests/src/com/android/internal/jank/FrameTrackerTest.java
index ece5037de15f..e17800f7b6fd 100644
--- a/core/tests/coretests/src/com/android/internal/jank/FrameTrackerTest.java
+++ b/core/tests/coretests/src/com/android/internal/jank/FrameTrackerTest.java
@@ -16,7 +16,7 @@
package com.android.internal.jank;
-import static com.android.internal.jank.InteractionJankMonitor.CUJ_NOTIFICATION_SHADE_GESTURE;
+import static com.android.internal.jank.InteractionJankMonitor.CUJ_NOTIFICATION_SHADE_EXPAND_COLLAPSE;
import static com.google.common.truth.Truth.assertThat;
@@ -75,20 +75,13 @@ public class FrameTrackerTest {
doNothing().when(mRenderer).addObserver(any());
doNothing().when(mRenderer).removeObserver(any());
- Session session = new Session(CUJ_NOTIFICATION_SHADE_GESTURE);
- mTracker = Mockito.spy(new FrameTracker(session, handler, mRenderer, mWrapper));
+ Session session = new Session(CUJ_NOTIFICATION_SHADE_EXPAND_COLLAPSE);
+ mTracker = Mockito.spy(
+ new FrameTracker(session, handler, mRenderer, mWrapper));
doNothing().when(mTracker).triggerPerfetto();
}
@Test
- public void testIgnoresSecondBegin() {
- // Observer should be only added once in continuous calls.
- mTracker.begin();
- mTracker.begin();
- verify(mRenderer, only()).addObserver(any());
- }
-
- @Test
public void testOnlyFirstFrameOverThreshold() {
// Just provide current timestamp anytime mWrapper asked for VSYNC_TIMESTAMP
when(mWrapper.getMetric(FrameMetrics.VSYNC_TIMESTAMP))
@@ -170,6 +163,29 @@ public class FrameTrackerTest {
verify(mTracker).triggerPerfetto();
}
+ @Test
+ public void testBeginCancel() {
+ mTracker.begin();
+ verify(mRenderer).addObserver(any());
+
+ // First frame - not janky
+ setupFirstFrameMockWithDuration(4);
+ mTracker.onFrameMetricsAvailable(0);
+
+ // normal frame - not janky
+ setupOtherFrameMockWithDuration(12);
+ mTracker.onFrameMetricsAvailable(0);
+
+ // a janky frame
+ setupOtherFrameMockWithDuration(30);
+ mTracker.onFrameMetricsAvailable(0);
+
+ mTracker.cancel();
+ verify(mRenderer).removeObserver(any());
+ // Since the tracker has been cancelled, shouldn't trigger perfetto.
+ verify(mTracker, never()).triggerPerfetto();
+ }
+
private void setupFirstFrameMockWithDuration(long durationMillis) {
doReturn(1L).when(mWrapper).getMetric(FrameMetrics.FIRST_DRAW_FRAME);
doReturn(TimeUnit.MILLISECONDS.toNanos(durationMillis))
diff --git a/core/tests/coretests/src/com/android/internal/jank/InteractionJankMonitorTest.java b/core/tests/coretests/src/com/android/internal/jank/InteractionJankMonitorTest.java
index 5c0b0c94f6d0..b669cc609baf 100644
--- a/core/tests/coretests/src/com/android/internal/jank/InteractionJankMonitorTest.java
+++ b/core/tests/coretests/src/com/android/internal/jank/InteractionJankMonitorTest.java
@@ -16,15 +16,20 @@
package com.android.internal.jank;
-import static com.android.internal.jank.InteractionJankMonitor.CUJ_NOTIFICATION_SHADE_GESTURE;
+import static com.android.internal.jank.InteractionJankMonitor.CUJ_NOTIFICATION_SHADE_EXPAND_COLLAPSE;
import static com.google.common.truth.Truth.assertThat;
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.anyLong;
import static org.mockito.Mockito.doNothing;
+import static org.mockito.Mockito.doReturn;
+import static org.mockito.Mockito.spy;
import static org.mockito.Mockito.verify;
import android.os.Handler;
import android.os.HandlerThread;
+import android.os.Message;
import android.view.View;
import android.view.ViewAttachTestActivity;
@@ -38,17 +43,13 @@ import com.android.internal.jank.InteractionJankMonitor.Session;
import org.junit.Before;
import org.junit.Rule;
import org.junit.Test;
-import org.mockito.Mockito;
-import org.testng.Assert;
-
-import java.util.HashMap;
-import java.util.Map;
+import org.mockito.ArgumentCaptor;
@SmallTest
public class InteractionJankMonitorTest {
private ViewAttachTestActivity mActivity;
private View mView;
- private FrameTracker mTracker;
+ private HandlerThread mWorker;
@Rule
public ActivityTestRule<ViewAttachTestActivity> mRule =
@@ -61,55 +62,52 @@ public class InteractionJankMonitorTest {
mView = mActivity.getWindow().getDecorView();
assertThat(mView.isAttachedToWindow()).isTrue();
- InteractionJankMonitor.reset();
+ InteractionJankMonitor.abandon();
- // Prepare a FrameTracker to inject.
- Session session = new Session(CUJ_NOTIFICATION_SHADE_GESTURE);
- FrameMetricsWrapper wrapper = Mockito.spy(new FrameTracker.FrameMetricsWrapper());
- ThreadedRendererWrapper renderer =
- Mockito.spy(new ThreadedRendererWrapper(mView.getThreadedRenderer()));
- Handler handler = mActivity.getMainThreadHandler();
- mTracker = Mockito.spy(new FrameTracker(session, handler, renderer, wrapper));
+ Handler handler = spy(new Handler(mActivity.getMainLooper()));
+ doReturn(true).when(handler).sendMessageAtTime(any(), anyLong());
+ mWorker = spy(new HandlerThread("Interaction-jank-monitor-test"));
+ doNothing().when(mWorker).start();
+ doReturn(handler).when(mWorker).getThreadHandler();
}
@Test
public void testBeginEnd() {
- // Should throw exception if the view is not attached.
- Assert.assertThrows(IllegalStateException.class,
- () -> InteractionJankMonitor.init(new View(mActivity)));
+ // Should return false if the view is not attached.
+ InteractionJankMonitor monitor = spy(new InteractionJankMonitor(mWorker));
+ assertThat(monitor.init(new View(mActivity))).isFalse();
// Verify we init InteractionJankMonitor correctly.
- Map<String, FrameTracker> map = new HashMap<>();
- HandlerThread worker = Mockito.spy(new HandlerThread("Aot-test"));
- doNothing().when(worker).start();
- InteractionJankMonitor.init(mView, mView.getThreadedRenderer(), map, worker);
- verify(worker).start();
+ assertThat(monitor.init(mView)).isTrue();
+ verify(mWorker).start();
+
+ Session session = new Session(CUJ_NOTIFICATION_SHADE_EXPAND_COLLAPSE);
+ FrameTracker tracker = spy(new FrameTracker(session, mWorker.getThreadHandler(),
+ new ThreadedRendererWrapper(mView.getThreadedRenderer()),
+ new FrameMetricsWrapper()));
+ doReturn(tracker).when(monitor).createFrameTracker(any());
// Simulate a trace session and see if begin / end are invoked.
- Session session = new Session(CUJ_NOTIFICATION_SHADE_GESTURE);
- assertThat(map.get(session.getName())).isNull();
- InteractionJankMonitor.begin(CUJ_NOTIFICATION_SHADE_GESTURE, mTracker);
- verify(mTracker).begin();
- assertThat(map.get(session.getName())).isEqualTo(mTracker);
- InteractionJankMonitor.end(CUJ_NOTIFICATION_SHADE_GESTURE);
- verify(mTracker).end();
- assertThat(map.get(session.getName())).isNull();
+ assertThat(monitor.begin(session.getCuj())).isTrue();
+ verify(tracker).begin();
+ assertThat(monitor.end(session.getCuj())).isTrue();
+ verify(tracker).end();
}
@Test
public void testCheckInitState() {
- // Should throw exception if invoking begin / end without init invocation.
- Assert.assertThrows(IllegalStateException.class,
- () -> InteractionJankMonitor.begin(CUJ_NOTIFICATION_SHADE_GESTURE));
- Assert.assertThrows(IllegalStateException.class,
- () -> InteractionJankMonitor.end(CUJ_NOTIFICATION_SHADE_GESTURE));
+ InteractionJankMonitor monitor = new InteractionJankMonitor(mWorker);
+
+ // Should return false if invoking begin / end without init invocation.
+ assertThat(monitor.begin(CUJ_NOTIFICATION_SHADE_EXPAND_COLLAPSE)).isFalse();
+ assertThat(monitor.end(CUJ_NOTIFICATION_SHADE_EXPAND_COLLAPSE)).isFalse();
// Everything should be fine if invoking init first.
boolean thrown = false;
try {
- InteractionJankMonitor.init(mActivity.getWindow().getDecorView());
- InteractionJankMonitor.begin(CUJ_NOTIFICATION_SHADE_GESTURE);
- InteractionJankMonitor.end(CUJ_NOTIFICATION_SHADE_GESTURE);
+ monitor.init(mView);
+ assertThat(monitor.begin(CUJ_NOTIFICATION_SHADE_EXPAND_COLLAPSE)).isTrue();
+ assertThat(monitor.end(CUJ_NOTIFICATION_SHADE_EXPAND_COLLAPSE)).isTrue();
} catch (Exception ex) {
thrown = true;
} finally {
@@ -117,4 +115,27 @@ public class InteractionJankMonitorTest {
}
}
+ @Test
+ public void testBeginCancel() {
+ InteractionJankMonitor monitor = spy(new InteractionJankMonitor(mWorker));
+
+ ArgumentCaptor<Message> captor = ArgumentCaptor.forClass(Message.class);
+ assertThat(monitor.init(mView)).isTrue();
+
+ Session session = new Session(CUJ_NOTIFICATION_SHADE_EXPAND_COLLAPSE);
+ FrameTracker tracker = spy(new FrameTracker(session, mWorker.getThreadHandler(),
+ new ThreadedRendererWrapper(mView.getThreadedRenderer()),
+ new FrameMetricsWrapper()));
+ doReturn(tracker).when(monitor).createFrameTracker(any());
+
+ assertThat(monitor.begin(session.getCuj())).isTrue();
+ verify(tracker).begin();
+ verify(mWorker.getThreadHandler()).sendMessageAtTime(captor.capture(), anyLong());
+ Runnable runnable = captor.getValue().getCallback();
+ assertThat(runnable).isNotNull();
+ mWorker.getThreadHandler().removeCallbacks(runnable);
+ runnable.run();
+ verify(tracker).cancel();
+ }
+
}
diff --git a/core/tests/coretests/src/com/android/internal/os/BatteryStatsBinderCallStatsTest.java b/core/tests/coretests/src/com/android/internal/os/BatteryStatsBinderCallStatsTest.java
index 22c41f3c9622..85f9c97629e3 100644
--- a/core/tests/coretests/src/com/android/internal/os/BatteryStatsBinderCallStatsTest.java
+++ b/core/tests/coretests/src/com/android/internal/os/BatteryStatsBinderCallStatsTest.java
@@ -60,7 +60,7 @@ public class BatteryStatsBinderCallStatsTest extends TestCase {
stat1.cpuTimeMicros = 1000;
callStats.add(stat1);
- bi.noteBinderCallStats(workSourceUid, 42, callStats, null);
+ bi.noteBinderCallStats(workSourceUid, 42, callStats);
callStats.clear();
BinderCallsStats.CallStat stat2 = new BinderCallsStats.CallStat(callingUid,
@@ -70,7 +70,7 @@ public class BatteryStatsBinderCallStatsTest extends TestCase {
stat2.cpuTimeMicros = 500;
callStats.add(stat2);
- bi.noteBinderCallStats(workSourceUid, 8, callStats, null);
+ bi.noteBinderCallStats(workSourceUid, 8, callStats);
BatteryStatsImpl.Uid uid = bi.getUidStatsLocked(workSourceUid);
assertEquals(42 + 8, uid.getBinderCallCount());
@@ -112,7 +112,7 @@ public class BatteryStatsBinderCallStatsTest extends TestCase {
stat1b.cpuTimeMicros = 1500;
callStats.add(stat1b);
- bi.noteBinderCallStats(workSourceUid1, 65, callStats, null);
+ bi.noteBinderCallStats(workSourceUid1, 65, callStats);
// No recorded stats for some methods. Must use the global average.
callStats.clear();
@@ -121,11 +121,11 @@ public class BatteryStatsBinderCallStatsTest extends TestCase {
stat2.incrementalCallCount = 10;
callStats.add(stat2);
- bi.noteBinderCallStats(workSourceUid2, 40, callStats, null);
+ bi.noteBinderCallStats(workSourceUid2, 40, callStats);
// No stats for any calls. Must use the global average
callStats.clear();
- bi.noteBinderCallStats(workSourceUid3, 50, callStats, null);
+ bi.noteBinderCallStats(workSourceUid3, 50, callStats);
bi.updateSystemServiceCallStats();
diff --git a/core/tests/coretests/src/com/android/internal/os/BinderCallsStatsTest.java b/core/tests/coretests/src/com/android/internal/os/BinderCallsStatsTest.java
index 96250db4aa51..0eb34a993dec 100644
--- a/core/tests/coretests/src/com/android/internal/os/BinderCallsStatsTest.java
+++ b/core/tests/coretests/src/com/android/internal/os/BinderCallsStatsTest.java
@@ -47,8 +47,10 @@ import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.HashMap;
+import java.util.HashSet;
import java.util.List;
import java.util.Random;
+import java.util.Set;
@SmallTest
@RunWith(AndroidJUnit4.class)
@@ -770,13 +772,27 @@ public class BinderCallsStatsTest {
bcs.setSamplingInterval(1);
bcs.setTrackScreenInteractive(false);
+
final ArrayList<BinderCallsStats.CallStat> callStatsList = new ArrayList<>();
- bcs.setCallStatsObserver(
- (workSourceUid, incrementalCallCount, callStats, binderThreadIds) ->
- callStatsList.addAll(callStats));
+ final Set<Integer> nativeTids = new HashSet<>();
+ bcs.setCallStatsObserver(new BinderInternal.CallStatsObserver() {
+ @Override
+ public void noteCallStats(int workSourceUid, long incrementalCallCount,
+ Collection<BinderCallsStats.CallStat> callStats) {
+ callStatsList.addAll(callStats);
+ }
+
+ @Override
+ public void noteBinderThreadNativeIds(int[] binderThreadNativeTids) {
+ for (int tid : binderThreadNativeTids) {
+ nativeTids.add(tid);
+ }
+ }
+ });
Binder binder = new Binder();
+ bcs.nativeTid = 1000;
CallSession callSession = bcs.callStarted(binder, 1, WORKSOURCE_UID);
bcs.time += 10;
bcs.callEnded(callSession, REQUEST_SIZE, REPLY_SIZE, WORKSOURCE_UID);
@@ -785,6 +801,7 @@ public class BinderCallsStatsTest {
bcs.time += 20;
bcs.callEnded(callSession, REQUEST_SIZE, REPLY_SIZE, WORKSOURCE_UID);
+ bcs.nativeTid = 2000;
callSession = bcs.callStarted(binder, 2, WORKSOURCE_UID);
bcs.time += 30;
bcs.callEnded(callSession, REQUEST_SIZE, REPLY_SIZE, WORKSOURCE_UID);
@@ -809,6 +826,10 @@ public class BinderCallsStatsTest {
assertEquals(30, callStats.maxCpuTimeMicros);
}
}
+
+ assertEquals(2, nativeTids.size());
+ assertTrue(nativeTids.contains(1000));
+ assertTrue(nativeTids.contains(2000));
}
@Test
diff --git a/core/tests/coretests/src/com/android/internal/os/SystemServicePowerCalculatorTest.java b/core/tests/coretests/src/com/android/internal/os/SystemServicePowerCalculatorTest.java
index ac5443e1c7ce..2eee140b921f 100644
--- a/core/tests/coretests/src/com/android/internal/os/SystemServicePowerCalculatorTest.java
+++ b/core/tests/coretests/src/com/android/internal/os/SystemServicePowerCalculatorTest.java
@@ -87,7 +87,7 @@ public class SystemServicePowerCalculatorTest {
stat1.cpuTimeMicros = 1000000;
callStats.add(stat1);
- mMockBatteryStats.noteBinderCallStats(workSourceUid1, 100, callStats, null);
+ mMockBatteryStats.noteBinderCallStats(workSourceUid1, 100, callStats);
callStats.clear();
BinderCallsStats.CallStat stat2 = new BinderCallsStats.CallStat(workSourceUid2,
@@ -97,7 +97,7 @@ public class SystemServicePowerCalculatorTest {
stat2.cpuTimeMicros = 9000000;
callStats.add(stat2);
- mMockBatteryStats.noteBinderCallStats(workSourceUid2, 100, callStats, null);
+ mMockBatteryStats.noteBinderCallStats(workSourceUid2, 100, callStats);
mMockBatteryStats.updateSystemServiceCallStats();
mMockBatteryStats.updateSystemServerThreadStats();
diff --git a/core/tests/mockingcoretests/src/android/app/activity/ActivityThreadClientTest.java b/core/tests/mockingcoretests/src/android/app/activity/ActivityThreadClientTest.java
index 9d95de7eb60f..d266cdbccf79 100644
--- a/core/tests/mockingcoretests/src/android/app/activity/ActivityThreadClientTest.java
+++ b/core/tests/mockingcoretests/src/android/app/activity/ActivityThreadClientTest.java
@@ -85,7 +85,7 @@ import java.util.concurrent.TimeUnit;
@MediumTest
@Presubmit
public class ActivityThreadClientTest {
- private static final long WAIT_TIMEOUT_MS = TimeUnit.SECONDS.toMillis(5);
+ private static final long WAIT_TIMEOUT_MS = TimeUnit.SECONDS.toMillis(10);
@Test
@UiThreadTest
@@ -243,27 +243,27 @@ public class ActivityThreadClientTest {
}
private void startActivity(ActivityClientRecord r) {
- mThread.handleStartActivity(r.token, null /* pendingActions */);
+ mThread.handleStartActivity(r, null /* pendingActions */);
}
private void resumeActivity(ActivityClientRecord r) {
- mThread.handleResumeActivity(r.token, true /* finalStateRequest */,
+ mThread.handleResumeActivity(r, true /* finalStateRequest */,
true /* isForward */, "test");
}
private void pauseActivity(ActivityClientRecord r) {
- mThread.handlePauseActivity(r.token, false /* finished */,
+ mThread.handlePauseActivity(r, false /* finished */,
false /* userLeaving */, 0 /* configChanges */, null /* pendingActions */,
"test");
}
private void stopActivity(ActivityClientRecord r) {
- mThread.handleStopActivity(r.token, 0 /* configChanges */,
+ mThread.handleStopActivity(r, 0 /* configChanges */,
new PendingTransactionActions(), false /* finalStateRequest */, "test");
}
private void destroyActivity(ActivityClientRecord r) {
- mThread.handleDestroyActivity(r.token, true /* finishing */, 0 /* configChanges */,
+ mThread.handleDestroyActivity(r, true /* finishing */, 0 /* configChanges */,
false /* getNonConfigInstance */, "test");
}
diff --git a/core/tests/powertests/PowerStatsLoadTests/AndroidManifest.xml b/core/tests/powertests/PowerStatsLoadTests/AndroidManifest.xml
index b1c2a639aff4..9cecaedf1380 100644
--- a/core/tests/powertests/PowerStatsLoadTests/AndroidManifest.xml
+++ b/core/tests/powertests/PowerStatsLoadTests/AndroidManifest.xml
@@ -22,6 +22,7 @@
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE"/>
<uses-permission android:name="android.permission.BATTERY_STATS"/>
<uses-permission android:name="android.permission.CHANGE_WIFI_STATE"/>
+ <uses-permission android:name="android.permission.DEVICE_POWER"/>
<uses-permission android:name="android.permission.INTERNET"/>
<instrumentation
diff --git a/core/tests/powertests/PowerStatsLoadTests/src/com/android/frameworks/core/powerstatsloadtests/PowerMetricsCollector.java b/core/tests/powertests/PowerStatsLoadTests/src/com/android/frameworks/core/powerstatsloadtests/PowerMetricsCollector.java
index 0cdb404f6aaa..a71559b5ad6b 100644
--- a/core/tests/powertests/PowerStatsLoadTests/src/com/android/frameworks/core/powerstatsloadtests/PowerMetricsCollector.java
+++ b/core/tests/powertests/PowerStatsLoadTests/src/com/android/frameworks/core/powerstatsloadtests/PowerMetricsCollector.java
@@ -16,6 +16,7 @@
package com.android.frameworks.core.powerstatsloadtests;
+import static org.junit.Assert.assertTrue;
import static org.junit.Assert.fail;
import android.app.Instrumentation;
@@ -34,6 +35,7 @@ import android.util.TimeUtils;
import androidx.test.platform.app.InstrumentationRegistry;
+import com.android.compatibility.common.util.SystemUtil;
import com.android.internal.os.BatteryStatsHelper;
import com.android.internal.os.LoggingPrintStream;
@@ -45,6 +47,8 @@ import java.io.PrintStream;
import java.util.ArrayList;
import java.util.List;
import java.util.Locale;
+import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.TimeUnit;
public class PowerMetricsCollector implements TestRule {
private final String mTag;
@@ -55,6 +59,7 @@ public class PowerMetricsCollector implements TestRule {
private final UserManager mUserManager;
private final int mUid;
private final BatteryStatsHelper mStatsHelper;
+ private final CountDownLatch mSuspendingBatteryInput = new CountDownLatch(1);
private long mStartTime;
private volatile float mInitialBatteryLevel;
@@ -63,12 +68,21 @@ public class PowerMetricsCollector implements TestRule {
private PowerMetrics mInitialPowerMetrics;
private PowerMetrics mFinalPowerMetrics;
private List<PowerMetrics.Metric> mPowerMetricsDelta;
+ private Intent mBatteryStatus;
@Override
public Statement apply(Statement base, Description description) {
return new Statement() {
@Override
public void evaluate() throws Throwable {
+ BroadcastReceiver batteryBroadcastReceiver = new BroadcastReceiver() {
+ @Override
+ public void onReceive(Context context, Intent intent) {
+ handleBatteryStatus(intent);
+ }
+ };
+ mBatteryStatus = mContext.registerReceiver(batteryBroadcastReceiver,
+ new IntentFilter(Intent.ACTION_BATTERY_CHANGED));
disableCharger();
try {
prepareBatteryLevelMonitor();
@@ -76,6 +90,7 @@ public class PowerMetricsCollector implements TestRule {
base.evaluate();
captureFinalPowerStatsData();
} finally {
+ mContext.unregisterReceiver(batteryBroadcastReceiver);
enableCharger();
}
}
@@ -95,12 +110,14 @@ public class PowerMetricsCollector implements TestRule {
mStatsHelper.create((Bundle) null);
}
- private void disableCharger() {
- // TODO(b/167636754): implement this method once the charger suspension API is available
+ private void disableCharger() throws InterruptedException {
+ SystemUtil.runShellCommand("dumpsys battery suspend_input");
+ final boolean success = mSuspendingBatteryInput.await(10, TimeUnit.SECONDS);
+ assertTrue("Timed out waiting for battery input to be suspended", success);
}
private void enableCharger() {
- // TODO(b/167636754): implement this method once the charger suspension API is available
+ SystemUtil.runShellCommand("dumpsys battery reset");
}
private PowerMetrics readBatteryStatsData() {
@@ -111,19 +128,25 @@ public class PowerMetricsCollector implements TestRule {
}
protected void prepareBatteryLevelMonitor() {
- Intent batteryStatus = mContext.registerReceiver(new BroadcastReceiver() {
- @Override
- public void onReceive(Context context, Intent intent) {
- handleBatteryStatus(intent);
- }
- }, new IntentFilter(Intent.ACTION_BATTERY_CHANGED));
-
- handleBatteryStatus(batteryStatus);
+ handleBatteryStatus(mBatteryStatus);
mInitialBatteryLevel = mCurrentBatteryLevel;
}
protected void handleBatteryStatus(Intent intent) {
- if (intent.getIntExtra(BatteryManager.EXTRA_PLUGGED, -1) != 0) {
+ if (mFinalPowerMetrics != null) {
+ return;
+ }
+
+ final boolean isCharging = intent.getIntExtra(BatteryManager.EXTRA_PLUGGED, -1) != 0;
+
+ if (mSuspendingBatteryInput.getCount() > 0) {
+ if (!isCharging) {
+ mSuspendingBatteryInput.countDown();
+ }
+ return;
+ }
+
+ if (isCharging) {
fail("Device must remain disconnected from the power source "
+ "for the duration of the test");
}
diff --git a/core/tests/utiltests/src/com/android/internal/util/CharSequencesTest.java b/core/tests/utiltests/src/com/android/internal/util/CharSequencesTest.java
index 55d186c292f6..469a4ccd05e4 100644
--- a/core/tests/utiltests/src/com/android/internal/util/CharSequencesTest.java
+++ b/core/tests/utiltests/src/com/android/internal/util/CharSequencesTest.java
@@ -16,16 +16,17 @@
package com.android.internal.util;
-import com.android.internal.util.CharSequences;
import static com.android.internal.util.CharSequences.forAsciiBytes;
-import junit.framework.TestCase;
+
import android.test.suitebuilder.annotation.SmallTest;
+import junit.framework.TestCase;
+
public class CharSequencesTest extends TestCase {
@SmallTest
public void testCharSequences() {
- String s = "Crazy Bob";
+ String s = "Hello Bob";
byte[] bytes = s.getBytes();
String copy = toString(forAsciiBytes(bytes));
@@ -34,11 +35,11 @@ public class CharSequencesTest extends TestCase {
copy = toString(forAsciiBytes(bytes, 0, s.length()));
assertTrue(s.equals(copy));
- String crazy = toString(forAsciiBytes(bytes, 0, 5));
- assertTrue("Crazy".equals(crazy));
+ String hello = toString(forAsciiBytes(bytes, 0, 5));
+ assertTrue("Hello".equals(hello));
- String a = toString(forAsciiBytes(bytes, 0, 3).subSequence(2, 3));
- assertTrue("a".equals(a));
+ String l = toString(forAsciiBytes(bytes, 0, 3).subSequence(2, 3));
+ assertTrue("l".equals(l));
String empty = toString(forAsciiBytes(bytes, 0, 3).subSequence(3, 3));
assertTrue("".equals(empty));
diff --git a/data/etc/platform.xml b/data/etc/platform.xml
index dd8f40d586bc..10c2b096ef98 100644
--- a/data/etc/platform.xml
+++ b/data/etc/platform.xml
@@ -229,8 +229,7 @@
<library name="android.test.base"
file="/system/framework/android.test.base.jar" />
<library name="android.test.mock"
- file="/system/framework/android.test.mock.jar"
- dependency="android.test.base" />
+ file="/system/framework/android.test.mock.jar" />
<library name="android.test.runner"
file="/system/framework/android.test.runner.jar"
dependency="android.test.base:android.test.mock" />
diff --git a/data/etc/privapp-permissions-platform.xml b/data/etc/privapp-permissions-platform.xml
index 602ccfeca2f2..102c933dd84d 100644
--- a/data/etc/privapp-permissions-platform.xml
+++ b/data/etc/privapp-permissions-platform.xml
@@ -226,6 +226,7 @@ applications that come with the platform
<permission name="android.permission.MANAGE_EXTERNAL_STORAGE"/>
<permission name="android.permission.UPDATE_APP_OPS_STATS"/>
<permission name="android.permission.UPDATE_DEVICE_STATS"/>
+ <permission name="android.permission.CONNECTIVITY_USE_RESTRICTED_NETWORKS"/>
</privapp-permissions>
<privapp-permissions package="com.android.providers.media.module">
diff --git a/data/etc/services.core.protolog.json b/data/etc/services.core.protolog.json
index 217de84cfc3e..86e7adf945b7 100644
--- a/data/etc/services.core.protolog.json
+++ b/data/etc/services.core.protolog.json
@@ -151,6 +151,12 @@
"group": "WM_ERROR",
"at": "com\/android\/server\/wm\/WindowManagerService.java"
},
+ "-1910833551": {
+ "message": "SyncSet{%x:%d} Start for %s",
+ "level": "VERBOSE",
+ "group": "WM_DEBUG_SYNC_ENGINE",
+ "at": "com\/android\/server\/wm\/BLASTSyncEngine.java"
+ },
"-1895337367": {
"message": "Delete root task display=%d winMode=%d",
"level": "VERBOSE",
@@ -259,6 +265,12 @@
"group": "WM_DEBUG_ORIENTATION",
"at": "com\/android\/server\/wm\/WindowState.java"
},
+ "-1741065110": {
+ "message": "No app is requesting an orientation, return %d for display id=%d",
+ "level": "VERBOSE",
+ "group": "WM_DEBUG_ORIENTATION",
+ "at": "com\/android\/server\/wm\/DisplayContent.java"
+ },
"-1730156332": {
"message": "Display id=%d rotation changed to %d from %d, lastOrientation=%d",
"level": "VERBOSE",
@@ -457,6 +469,12 @@
"group": "WM_ERROR",
"at": "com\/android\/server\/wm\/WindowManagerService.java"
},
+ "-1387080937": {
+ "message": "SyncSet{%x:%d} Child ready, now ready=%b and waiting on %d transactions",
+ "level": "VERBOSE",
+ "group": "WM_DEBUG_SYNC_ENGINE",
+ "at": "com\/android\/server\/wm\/BLASTSyncEngine.java"
+ },
"-1364754753": {
"message": "Task vanished taskId=%d",
"level": "VERBOSE",
@@ -475,6 +493,12 @@
"group": "WM_DEBUG_BOOT",
"at": "com\/android\/server\/wm\/WindowManagerService.java"
},
+ "-1340783230": {
+ "message": "SyncSet{%x:%d} Added %s. now waiting on %d transactions",
+ "level": "VERBOSE",
+ "group": "WM_DEBUG_SYNC_ENGINE",
+ "at": "com\/android\/server\/wm\/BLASTSyncEngine.java"
+ },
"-1340540100": {
"message": "Creating SnapshotStartingData",
"level": "VERBOSE",
@@ -853,6 +877,12 @@
"group": "WM_DEBUG_ORIENTATION",
"at": "com\/android\/server\/wm\/DragState.java"
},
+ "-678300709": {
+ "message": "SyncSet{%x:%d} Trying to add %s",
+ "level": "VERBOSE",
+ "group": "WM_DEBUG_SYNC_ENGINE",
+ "at": "com\/android\/server\/wm\/BLASTSyncEngine.java"
+ },
"-668956537": {
"message": " THUMBNAIL %s: CREATE",
"level": "INFO",
@@ -1729,6 +1759,12 @@
"group": "WM_DEBUG_IME",
"at": "com\/android\/server\/wm\/ImeInsetsSourceProvider.java"
},
+ "590184240": {
+ "message": "- NOT adding to sync: visible=%b hasListener=%b",
+ "level": "VERBOSE",
+ "group": "WM_DEBUG_SYNC_ENGINE",
+ "at": "com\/android\/server\/wm\/WindowContainer.java"
+ },
"594260577": {
"message": "createWallpaperAnimations()",
"level": "DEBUG",
@@ -1909,6 +1945,12 @@
"group": "WM_ERROR",
"at": "com\/android\/server\/wm\/WindowToken.java"
},
+ "845234215": {
+ "message": "App is requesting an orientation, return %d for display id=%d",
+ "level": "VERBOSE",
+ "group": "WM_DEBUG_ORIENTATION",
+ "at": "com\/android\/server\/wm\/DisplayContent.java"
+ },
"853091290": {
"message": "Moved stack=%s behind stack=%s",
"level": "DEBUG",
@@ -1993,6 +2035,12 @@
"group": "WM_DEBUG_REMOTE_ANIMATIONS",
"at": "com\/android\/server\/wm\/RemoteAnimationController.java"
},
+ "1000601037": {
+ "message": "SyncSet{%x:%d} Set ready",
+ "level": "VERBOSE",
+ "group": "WM_DEBUG_SYNC_ENGINE",
+ "at": "com\/android\/server\/wm\/BLASTSyncEngine.java"
+ },
"1001904964": {
"message": "***** BOOT TIMEOUT: forcing display enabled",
"level": "WARN",
@@ -2179,12 +2227,6 @@
"group": "WM_DEBUG_IME",
"at": "com\/android\/server\/wm\/ImeInsetsSourceProvider.java"
},
- "1381227466": {
- "message": "App is requesting an orientation, return %d for display id=%d",
- "level": "VERBOSE",
- "group": "WM_DEBUG_ORIENTATION",
- "at": "com\/android\/server\/wm\/TaskDisplayArea.java"
- },
"1401295262": {
"message": "Mode default, asking user",
"level": "WARN",
@@ -2371,12 +2413,6 @@
"group": "WM_DEBUG_RESIZE",
"at": "com\/android\/server\/wm\/WindowState.java"
},
- "1640436199": {
- "message": "No app is requesting an orientation, return %d for display id=%d",
- "level": "VERBOSE",
- "group": "WM_DEBUG_ORIENTATION",
- "at": "com\/android\/server\/wm\/TaskDisplayArea.java"
- },
"1653210583": {
"message": "Removing app %s delayed=%b animation=%s animating=%b",
"level": "VERBOSE",
@@ -2599,6 +2635,12 @@
"group": "WM_DEBUG_CONFIGURATION",
"at": "com\/android\/server\/wm\/ActivityRecord.java"
},
+ "2001924866": {
+ "message": "SyncSet{%x:%d} Finished. Reporting %d containers to %s",
+ "level": "VERBOSE",
+ "group": "WM_DEBUG_SYNC_ENGINE",
+ "at": "com\/android\/server\/wm\/BLASTSyncEngine.java"
+ },
"2016061474": {
"message": "Prepare app transition: transit=%s %s alwaysKeepCurrent=%b displayId=%d Callers=%s",
"level": "VERBOSE",
@@ -2775,6 +2817,9 @@
"WM_DEBUG_SWITCH": {
"tag": "WindowManager"
},
+ "WM_DEBUG_SYNC_ENGINE": {
+ "tag": "WindowManager"
+ },
"WM_DEBUG_WINDOW_MOVEMENT": {
"tag": "WindowManager"
},
diff --git a/data/fonts/fonts.xml b/data/fonts/fonts.xml
index 4c214b529b39..c992bd8f8338 100644
--- a/data/fonts/fonts.xml
+++ b/data/fonts/fonts.xml
@@ -142,11 +142,30 @@
<font weight="700" style="normal">NotoSansThaiUI-Bold.ttf</font>
</family>
<family lang="und-Armn">
- <font weight="400" style="normal">NotoSansArmenian-Regular.otf</font>
- <font weight="500" style="normal">NotoSansArmenian-Medium.otf</font>
- <font weight="700" style="normal">NotoSansArmenian-Bold.otf</font>
- <font weight="400" style="normal" fallbackFor="serif">NotoSerifArmenian-Regular.otf</font>
- <font weight="700" style="normal" fallbackFor="serif">NotoSerifArmenian-Bold.otf</font>
+ <font weight="400" style="normal">NotoSansArmenian-VF.ttf
+ <axis tag="wght" stylevalue="400" />
+ </font>
+ <font weight="500" style="normal">NotoSansArmenian-VF.ttf
+ <axis tag="wght" stylevalue="500" />
+ </font>
+ <font weight="600" style="normal">NotoSansArmenian-VF.ttf
+ <axis tag="wght" stylevalue="600" />
+ </font>
+ <font weight="700" style="normal">NotoSansArmenian-VF.ttf
+ <axis tag="wght" stylevalue="700" />
+ </font>
+ <font weight="400" style="normal" fallbackFor="serif">NotoSerifArmenian-VF.ttf
+ <axis tag="wght" stylevalue="400" />
+ </font>
+ <font weight="500" style="normal" fallbackFor="serif">NotoSerifArmenian-VF.ttf
+ <axis tag="wght" stylevalue="500" />
+ </font>
+ <font weight="600" style="normal" fallbackFor="serif">NotoSerifArmenian-VF.ttf
+ <axis tag="wght" stylevalue="600" />
+ </font>
+ <font weight="700" style="normal" fallbackFor="serif">NotoSerifArmenian-VF.ttf
+ <axis tag="wght" stylevalue="700" />
+ </font>
</family>
<family lang="und-Geor,und-Geok">
<font weight="400" style="normal">NotoSansGeorgian-VF.ttf
diff --git a/data/keyboards/Vendor_27f8_Product_0bbe.kl b/data/keyboards/Vendor_27f8_Product_0bbe.kl
new file mode 100644
index 000000000000..211e532c8326
--- /dev/null
+++ b/data/keyboards/Vendor_27f8_Product_0bbe.kl
@@ -0,0 +1,54 @@
+# 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.
+
+#
+# Gamevice GV186 Mobile Controller
+#
+
+
+# Mapping according to https://developer.android.com/training/game-controllers/controller-input.html
+
+key 0x133 BUTTON_X
+key 0x130 BUTTON_A
+key 0x131 BUTTON_B
+key 0x134 BUTTON_Y
+
+key 0x136 BUTTON_L1
+key 0x137 BUTTON_R1
+key 0x138 BUTTON_L2
+key 0x139 BUTTON_R2
+
+axis 0x00 X
+axis 0x01 Y
+
+axis 0x02 Z
+axis 0x05 RZ
+
+axis 0x09 RTRIGGER
+axis 0x0a LTRIGGER
+
+key 0x13d BUTTON_THUMBL
+key 0x13e BUTTON_THUMBR
+
+# Hat
+axis 0x10 HAT_X
+axis 0x11 HAT_Y
+
+# Mapping according to https://www.kernel.org/doc/Documentation/input/gamepad.txt
+# Left Triangle Button
+key 0x13a BUTTON_SELECT
+# Right Triangle Button
+key 0x13b BUTTON_START
+# Home Button
+key 0x13c BUTTON_MODE
diff --git a/graphics/java/android/graphics/BlurShader.java b/graphics/java/android/graphics/BlurShader.java
index 779a89051060..3bc811983336 100644
--- a/graphics/java/android/graphics/BlurShader.java
+++ b/graphics/java/android/graphics/BlurShader.java
@@ -16,6 +16,7 @@
package android.graphics;
+import android.annotation.NonNull;
import android.annotation.Nullable;
/**
@@ -28,6 +29,7 @@ public final class BlurShader extends Shader {
private final float mRadiusX;
private final float mRadiusY;
private final Shader mInputShader;
+ private final TileMode mEdgeTreatment;
private long mNativeInputShader = 0;
@@ -35,22 +37,42 @@ public final class BlurShader extends Shader {
* Create a {@link BlurShader} that blurs the contents of the optional input shader
* with the specified radius along the x and y axis. If no input shader is provided
* then all drawing commands issued with a {@link android.graphics.Paint} that this
- * shader is installed in will be blurred
+ * shader is installed in will be blurred.
+ *
+ * This uses a default {@link TileMode#DECAL} for edge treatment
+ *
* @param radiusX Radius of blur along the X axis
* @param radiusY Radius of blur along the Y axis
* @param inputShader Input shader that provides the content to be blurred
*/
public BlurShader(float radiusX, float radiusY, @Nullable Shader inputShader) {
+ this(radiusX, radiusY, inputShader, TileMode.DECAL);
+ }
+
+ /**
+ * Create a {@link BlurShader} that blurs the contents of the optional input shader
+ * with the specified radius along the x and y axis. If no input shader is provided
+ * then all drawing commands issued with a {@link android.graphics.Paint} that this
+ * shader is installed in will be blurred
+ * @param radiusX Radius of blur along the X axis
+ * @param radiusY Radius of blur along the Y axis
+ * @param inputShader Input shader that provides the content to be blurred
+ * @param edgeTreatment Policy for how to blur content near edges of the blur shader
+ */
+ public BlurShader(float radiusX, float radiusY, @Nullable Shader inputShader,
+ @NonNull TileMode edgeTreatment) {
mRadiusX = radiusX;
mRadiusY = radiusY;
mInputShader = inputShader;
+ mEdgeTreatment = edgeTreatment;
}
/** @hide **/
@Override
protected long createNativeInstance(long nativeMatrix) {
mNativeInputShader = mInputShader != null ? mInputShader.getNativeInstance() : 0;
- return nativeCreate(nativeMatrix, mRadiusX, mRadiusY, mNativeInputShader);
+ return nativeCreate(nativeMatrix, mRadiusX, mRadiusY, mNativeInputShader,
+ mEdgeTreatment.nativeInt);
}
/** @hide **/
@@ -61,5 +83,5 @@ public final class BlurShader extends Shader {
}
private static native long nativeCreate(long nativeMatrix, float radiusX, float radiusY,
- long inputShader);
+ long inputShader, int edgeTreatment);
}
diff --git a/graphics/java/android/graphics/FrameInfo.java b/graphics/java/android/graphics/FrameInfo.java
index 42a5cc43eaa0..163823fde9f7 100644
--- a/graphics/java/android/graphics/FrameInfo.java
+++ b/graphics/java/android/graphics/FrameInfo.java
@@ -40,7 +40,7 @@ import java.lang.annotation.RetentionPolicy;
*/
public final class FrameInfo {
- public long[] frameInfo = new long[9];
+ public long[] frameInfo = new long[10];
// Various flags set to provide extra metadata about the current frame
private static final int FLAGS = 0;
@@ -51,38 +51,44 @@ public final class FrameInfo {
// A renderer associated with just a Surface, not with a ViewRootImpl instance.
public static final long FLAG_SURFACE_CANVAS = 1 << 2;
+ // An invalid vsync id to be used when FRAME_TIMELINE_VSYNC_ID is unknown
+ public static final long INVALID_VSYNC_ID = -1;
+
@LongDef(flag = true, value = {
FLAG_WINDOW_LAYOUT_CHANGED, FLAG_SURFACE_CANVAS })
@Retention(RetentionPolicy.SOURCE)
public @interface FrameInfoFlags {}
+ private static final int FRAME_TIMELINE_VSYNC_ID = 1;
+
// The intended vsync time, unadjusted by jitter
- private static final int INTENDED_VSYNC = 1;
+ private static final int INTENDED_VSYNC = 2;
// Jitter-adjusted vsync time, this is what was used as input into the
// animation & drawing system
- private static final int VSYNC = 2;
+ private static final int VSYNC = 3;
// The time of the oldest input event
- private static final int OLDEST_INPUT_EVENT = 3;
+ private static final int OLDEST_INPUT_EVENT = 4;
// The time of the newest input event
- private static final int NEWEST_INPUT_EVENT = 4;
+ private static final int NEWEST_INPUT_EVENT = 5;
// When input event handling started
- private static final int HANDLE_INPUT_START = 5;
+ private static final int HANDLE_INPUT_START = 6;
// When animation evaluations started
- private static final int ANIMATION_START = 6;
+ private static final int ANIMATION_START = 7;
// When ViewRootImpl#performTraversals() started
- private static final int PERFORM_TRAVERSALS_START = 7;
+ private static final int PERFORM_TRAVERSALS_START = 8;
// When View:draw() started
- private static final int DRAW_START = 8;
+ private static final int DRAW_START = 9;
/** checkstyle */
- public void setVsync(long intendedVsync, long usedVsync) {
+ public void setVsync(long intendedVsync, long usedVsync, long frameTimelineVsyncId) {
+ frameInfo[FRAME_TIMELINE_VSYNC_ID] = frameTimelineVsyncId;
frameInfo[INTENDED_VSYNC] = intendedVsync;
frameInfo[VSYNC] = usedVsync;
frameInfo[OLDEST_INPUT_EVENT] = Long.MAX_VALUE;
diff --git a/graphics/java/android/graphics/HardwareRenderer.java b/graphics/java/android/graphics/HardwareRenderer.java
index 0452933328e2..fd5916c2158d 100644
--- a/graphics/java/android/graphics/HardwareRenderer.java
+++ b/graphics/java/android/graphics/HardwareRenderer.java
@@ -354,7 +354,8 @@ public class HardwareRenderer {
* @return this instance
*/
public @NonNull FrameRenderRequest setVsyncTime(long vsyncTime) {
- mFrameInfo.setVsync(vsyncTime, vsyncTime);
+ // TODO(b/168552873): populate vsync Id once available to Choreographer public API
+ mFrameInfo.setVsync(vsyncTime, vsyncTime, FrameInfo.INVALID_VSYNC_ID);
mFrameInfo.addFlags(FrameInfo.FLAG_SURFACE_CANVAS);
return this;
}
diff --git a/graphics/java/android/graphics/ParcelableColorSpace.java b/graphics/java/android/graphics/ParcelableColorSpace.java
index f9033a53d7e6..d408ac3718ca 100644
--- a/graphics/java/android/graphics/ParcelableColorSpace.java
+++ b/graphics/java/android/graphics/ParcelableColorSpace.java
@@ -25,8 +25,6 @@ import android.os.Parcelable;
* A {@link Parcelable} {@link ColorSpace}. In order to enable parceling, the ColorSpace
* must be either a {@link ColorSpace.Named Named} ColorSpace or a {@link ColorSpace.Rgb} instance
* that has an ICC parametric transfer function as returned by {@link Rgb#getTransferParameters()}.
- * TODO: Make public
- * @hide
*/
public final class ParcelableColorSpace extends ColorSpace implements Parcelable {
private final ColorSpace mColorSpace;
diff --git a/graphics/java/android/graphics/Shader.java b/graphics/java/android/graphics/Shader.java
index 8154ebf1e508..d71ff1138b25 100644
--- a/graphics/java/android/graphics/Shader.java
+++ b/graphics/java/android/graphics/Shader.java
@@ -95,7 +95,11 @@ public class Shader {
* repeat the shader's image horizontally and vertically, alternating
* mirror images so that adjacent images always seam
*/
- MIRROR (2);
+ MIRROR(2),
+ /**
+ * Only draw within the original domain, return transparent-black everywhere else
+ */
+ DECAL(3);
TileMode(int nativeInt) {
this.nativeInt = nativeInt;
diff --git a/graphics/java/android/graphics/fonts/Font.java b/graphics/java/android/graphics/fonts/Font.java
index b09082e65ca4..cbae67507a64 100644
--- a/graphics/java/android/graphics/fonts/Font.java
+++ b/graphics/java/android/graphics/fonts/Font.java
@@ -22,6 +22,8 @@ import android.annotation.Nullable;
import android.content.res.AssetFileDescriptor;
import android.content.res.AssetManager;
import android.content.res.Resources;
+import android.graphics.Paint;
+import android.graphics.RectF;
import android.os.LocaleList;
import android.os.ParcelFileDescriptor;
import android.util.TypedValue;
@@ -29,6 +31,7 @@ import android.util.TypedValue;
import com.android.internal.util.Preconditions;
import dalvik.annotation.optimization.CriticalNative;
+import dalvik.annotation.optimization.FastNative;
import libcore.util.NativeAllocationRegistry;
@@ -63,6 +66,7 @@ public final class Font {
private @Nullable ByteBuffer mBuffer;
private @Nullable File mFile;
+ private @Nullable Font mFont;
private @NonNull String mLocaleList = "";
private @IntRange(from = -1, to = 1000) int mWeight = NOT_SPECIFIED;
private @IntRange(from = -1, to = 1) int mItalic = NOT_SPECIFIED;
@@ -204,6 +208,22 @@ public final class Font {
}
/**
+ * Constructs a builder from existing Font instance.
+ *
+ * @param font the font instance.
+ */
+ public Builder(@NonNull Font font) {
+ mFont = font;
+ // Copies all parameters as a default value.
+ mBuffer = font.getBuffer();
+ mWeight = font.getStyle().getWeight();
+ mItalic = font.getStyle().getSlant();
+ mAxes = font.getAxes();
+ mFile = font.getFile();
+ mTtcIndex = font.getTtcIndex();
+ }
+
+ /**
* Creates a buffer containing font data using the assetManager and other
* provided inputs.
*
@@ -430,8 +450,13 @@ public final class Font {
}
final ByteBuffer readonlyBuffer = mBuffer.asReadOnlyBuffer();
final String filePath = mFile == null ? "" : mFile.getAbsolutePath();
- final long ptr = nBuild(builderPtr, readonlyBuffer, filePath, mWeight, italic,
- mTtcIndex);
+
+ long ptr;
+ if (mFont == null) {
+ ptr = nBuild(builderPtr, readonlyBuffer, filePath, mWeight, italic, mTtcIndex);
+ } else {
+ ptr = nClone(mFont.getNativePtr(), builderPtr, mWeight, italic, mTtcIndex);
+ }
final Font font = new Font(ptr, readonlyBuffer, mFile,
new FontStyle(mWeight, slant), mTtcIndex, mAxes, mLocaleList);
sFontRegistry.registerNativeAllocation(font, ptr);
@@ -449,6 +474,10 @@ public final class Font {
boolean italic, int ttcIndex);
@CriticalNative
private static native long nGetReleaseNativeFont();
+
+ @FastNative
+ private static native long nClone(long fontPtr, long builderPtr, int weight,
+ boolean italic, int ttcIndex);
}
private final long mNativePtr; // address of the shared ptr of minikin::Font
@@ -538,6 +567,40 @@ public final class Font {
return LocaleList.forLanguageTags(mLocaleList);
}
+ /**
+ * Retrieve the glyph horizontal advance and bounding box.
+ *
+ * Note that {@link android.graphics.Typeface} in {@link android.graphics.Paint} is ignored.
+ *
+ * @param glyphId a glyph ID
+ * @param paint a paint object used for resolving glyph style
+ * @param rect a nullable destination object. If null is passed, this function just return the
+ * horizontal advance. If non-null is passed, this function fills bounding box
+ * information to this object.
+ * @return the amount of horizontal advance in pixels
+ */
+ public float getGlyphBounds(@IntRange(from = 0) int glyphId, @NonNull Paint paint,
+ @Nullable RectF rect) {
+ return nGetGlyphBounds(mNativePtr, glyphId, paint.getNativeInstance(), rect);
+ }
+
+ /**
+ * Retrieve the font metrics information.
+ *
+ * Note that {@link android.graphics.Typeface} in {@link android.graphics.Paint} is ignored.
+ *
+ * @param paint a paint object used for retrieving font metrics.
+ * @param metrics a nullable destination object. If null is passed, this function only retrieve
+ * recommended interline spacing. If non-null is passed, this function fills to
+ * font metrics to it.
+ *
+ * @see Paint#getFontMetrics()
+ * @see Paint#getFontMetricsInt()
+ */
+ public void getMetrics(@NonNull Paint paint, @Nullable Paint.FontMetrics metrics) {
+ nGetFontMetrics(mNativePtr, paint.getNativeInstance(), metrics);
+ }
+
/** @hide */
public long getNativePtr() {
return mNativePtr;
@@ -573,4 +636,10 @@ public final class Font {
+ ", buffer=" + mBuffer
+ "}";
}
+
+ @FastNative
+ private static native float nGetGlyphBounds(long font, int glyphId, long paint, RectF rect);
+
+ @FastNative
+ private static native float nGetFontMetrics(long font, long paint, Paint.FontMetrics metrics);
}
diff --git a/libs/WindowManager/Jetpack/src/androidx/window/sidecar/SettingsSidecarImpl.java b/libs/WindowManager/Jetpack/src/androidx/window/sidecar/SettingsSidecarImpl.java
index ca3a5112bc55..5397302f6882 100644
--- a/libs/WindowManager/Jetpack/src/androidx/window/sidecar/SettingsSidecarImpl.java
+++ b/libs/WindowManager/Jetpack/src/androidx/window/sidecar/SettingsSidecarImpl.java
@@ -37,6 +37,8 @@ import android.util.Log;
import androidx.annotation.NonNull;
+import com.android.internal.R;
+
import java.util.ArrayList;
import java.util.List;
import java.util.regex.Matcher;
@@ -151,13 +153,18 @@ class SettingsSidecarImpl extends StubSidecar {
return features;
}
- ContentResolver resolver = mContext.getContentResolver();
- final String displayFeaturesString = Settings.Global.getString(resolver, DISPLAY_FEATURES);
if (isInMultiWindow(windowToken)) {
// It is recommended not to report any display features in multi-window mode, since it
// won't be possible to synchronize the display feature positions with window movement.
return features;
}
+
+ ContentResolver resolver = mContext.getContentResolver();
+ String displayFeaturesString = Settings.Global.getString(resolver, DISPLAY_FEATURES);
+ if (TextUtils.isEmpty(displayFeaturesString)) {
+ displayFeaturesString = mContext.getResources().getString(
+ R.string.config_display_features);
+ }
if (TextUtils.isEmpty(displayFeaturesString)) {
return features;
}
diff --git a/libs/WindowManager/Shell/res/values/strings.xml b/libs/WindowManager/Shell/res/values/strings.xml
index b6668fbe4872..da5965dab71a 100644
--- a/libs/WindowManager/Shell/res/values/strings.xml
+++ b/libs/WindowManager/Shell/res/values/strings.xml
@@ -93,4 +93,8 @@
<string name="one_handed_tutorial_title">Using one-handed mode</string>
<!-- One-Handed Tutorial description [CHAR LIMIT=NONE] -->
<string name="one_handed_tutorial_description">To exit, swipe up from the bottom of the screen or tap anywhere above the app</string>
+ <!-- Accessibility description for start one-handed mode [CHAR LIMIT=NONE] -->
+ <string name="accessibility_action_start_one_handed">Start one-handed mode</string>
+ <!-- Accessibility description for stop one-handed mode [CHAR LIMIT=NONE] -->
+ <string name="accessibility_action_stop_one_handed">Exit one-handed mode</string>
</resources>
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/common/DisplayImeController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/common/DisplayImeController.java
index 283fd8d997c9..c18e9ce76153 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/common/DisplayImeController.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/common/DisplayImeController.java
@@ -178,7 +178,8 @@ public class DisplayImeController implements DisplayController.OnDisplaysChanged
}
}
- class PerDisplay extends IDisplayWindowInsetsController.Stub {
+ /** An implementation of {@link IDisplayWindowInsetsController} for a given display id. */
+ public class PerDisplay extends IDisplayWindowInsetsController.Stub {
final int mDisplayId;
final InsetsState mInsetsState = new InsetsState();
InsetsSourceControl mImeSourceControl = null;
@@ -189,7 +190,7 @@ public class DisplayImeController implements DisplayController.OnDisplaysChanged
final Rect mImeFrame = new Rect();
boolean mAnimateAlpha = true;
- PerDisplay(int displayId, int initialRotation) {
+ public PerDisplay(int displayId, int initialRotation) {
mDisplayId = displayId;
mRotation = initialRotation;
}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/onehanded/OneHandedController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/onehanded/OneHandedController.java
index c84b4781d19d..d060f6444463 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/onehanded/OneHandedController.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/onehanded/OneHandedController.java
@@ -172,8 +172,10 @@ public class OneHandedController implements OneHanded {
context, displayController);
OneHandedDisplayAreaOrganizer organizer = new OneHandedDisplayAreaOrganizer(
context, displayController, animationController, tutorialHandler);
+ IOverlayManager overlayManager = IOverlayManager.Stub.asInterface(
+ ServiceManager.getService(Context.OVERLAY_SERVICE));
return new OneHandedController(context, displayController, organizer, touchHandler,
- tutorialHandler, gestureHandler);
+ tutorialHandler, gestureHandler, overlayManager);
}
@VisibleForTesting
@@ -182,7 +184,8 @@ public class OneHandedController implements OneHanded {
OneHandedDisplayAreaOrganizer displayAreaOrganizer,
OneHandedTouchHandler touchHandler,
OneHandedTutorialHandler tutorialHandler,
- OneHandedGestureHandler gestureHandler) {
+ OneHandedGestureHandler gestureHandler,
+ IOverlayManager overlayManager) {
mHasOneHandedFeature = SystemProperties.getBoolean(SUPPORT_ONE_HANDED_MODE, false);
if (!mHasOneHandedFeature) {
Log.i(TAG, "Device config SUPPORT_ONE_HANDED_MODE off");
@@ -194,32 +197,32 @@ public class OneHandedController implements OneHanded {
mGestureHandler = null;
mTimeoutHandler = null;
mOverlayManager = null;
- return;
+ } else {
+ mContext = context;
+ mDisplayAreaOrganizer = displayAreaOrganizer;
+ mDisplayController = displayController;
+ mTouchHandler = touchHandler;
+ mTutorialHandler = tutorialHandler;
+ mGestureHandler = gestureHandler;
+ mOverlayManager = overlayManager;
+
+ mOffSetFraction = SystemProperties.getInt(ONE_HANDED_MODE_OFFSET_PERCENTAGE, 50)
+ / 100.0f;
+ mIsOneHandedEnabled = OneHandedSettingsUtil.getSettingsOneHandedModeEnabled(
+ context.getContentResolver());
+ mIsSwipeToNotificationEnabled =
+ OneHandedSettingsUtil.getSettingsSwipeToNotificationEnabled(
+ context.getContentResolver());
+ mTimeoutHandler = OneHandedTimeoutHandler.get();
+
+ mDisplayController.addDisplayChangingController(mRotationController);
+
+ setupCallback();
+ setupSettingObservers();
+ setupTimeoutListener();
+ setupGesturalOverlay();
+ updateSettings();
}
-
- mContext = context;
- mDisplayAreaOrganizer = displayAreaOrganizer;
- mDisplayController = displayController;
- mTouchHandler = touchHandler;
- mTutorialHandler = tutorialHandler;
- mGestureHandler = gestureHandler;
-
- mOverlayManager = IOverlayManager.Stub.asInterface(
- ServiceManager.getService(Context.OVERLAY_SERVICE));
- mOffSetFraction = SystemProperties.getInt(ONE_HANDED_MODE_OFFSET_PERCENTAGE, 50) / 100.0f;
- mIsOneHandedEnabled = OneHandedSettingsUtil.getSettingsOneHandedModeEnabled(
- context.getContentResolver());
- mIsSwipeToNotificationEnabled = OneHandedSettingsUtil.getSettingsSwipeToNotificationEnabled(
- context.getContentResolver());
- mTimeoutHandler = OneHandedTimeoutHandler.get();
-
- mDisplayController.addDisplayChangingController(mRotationController);
-
- setupCallback();
- setupSettingObservers();
- setupTimeoutListener();
- setupGesturalOverlay();
- updateSettings();
}
/**
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/onehanded/OneHandedTutorialHandler.java b/libs/WindowManager/Shell/src/com/android/wm/shell/onehanded/OneHandedTutorialHandler.java
index b15b5154c2a4..7c0c738644b7 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/onehanded/OneHandedTutorialHandler.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/onehanded/OneHandedTutorialHandler.java
@@ -29,6 +29,8 @@ import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.view.WindowManager;
+import android.view.accessibility.AccessibilityEvent;
+import android.view.accessibility.AccessibilityManager;
import android.widget.FrameLayout;
import androidx.annotation.NonNull;
@@ -50,12 +52,16 @@ public class OneHandedTutorialHandler implements OneHandedTransitionCallback {
private static final int MAX_TUTORIAL_SHOW_COUNT = 2;
private final Rect mLastUpdatedBounds = new Rect();
private final WindowManager mWindowManager;
+ private final AccessibilityManager mAccessibilityManager;
+ private final String mPackageName;
private View mTutorialView;
private Point mDisplaySize = new Point();
private Handler mUpdateHandler;
private ContentResolver mContentResolver;
private boolean mCanShowTutorial;
+ private String mStartOneHandedDescription;
+ private String mStopOneHandedDescription;
/**
* Container of the tutorial panel showing at outside region when one handed starting
@@ -72,9 +78,12 @@ public class OneHandedTutorialHandler implements OneHandedTransitionCallback {
public OneHandedTutorialHandler(Context context) {
context.getDisplay().getRealSize(mDisplaySize);
+ mPackageName = context.getPackageName();
mContentResolver = context.getContentResolver();
mUpdateHandler = new Handler();
mWindowManager = context.getSystemService(WindowManager.class);
+ mAccessibilityManager = (AccessibilityManager)
+ context.getSystemService(Context.ACCESSIBILITY_SERVICE);
mTargetViewContainer = new FrameLayout(context);
mTargetViewContainer.setClipChildren(false);
mTutorialAreaHeight = Math.round(mDisplaySize.y
@@ -84,6 +93,10 @@ public class OneHandedTutorialHandler implements OneHandedTransitionCallback {
mCanShowTutorial = (Settings.Secure.getInt(mContentResolver,
Settings.Secure.ONE_HANDED_TUTORIAL_SHOW_COUNT, 0) >= MAX_TUTORIAL_SHOW_COUNT)
? false : true;
+ mStartOneHandedDescription = context.getResources().getString(
+ R.string.accessibility_action_start_one_handed);
+ mStopOneHandedDescription = context.getResources().getString(
+ R.string.accessibility_action_stop_one_handed);
if (mCanShowTutorial) {
createOrUpdateTutorialTarget();
}
@@ -94,13 +107,16 @@ public class OneHandedTutorialHandler implements OneHandedTransitionCallback {
mUpdateHandler.post(() -> {
updateFinished(View.VISIBLE, 0f);
updateTutorialCount();
+ announcementForScreenReader(true);
});
}
@Override
public void onStopFinished(Rect bounds) {
- mUpdateHandler.post(() -> updateFinished(
- View.INVISIBLE, -mTargetViewContainer.getHeight()));
+ mUpdateHandler.post(() -> {
+ updateFinished(View.INVISIBLE, -mTargetViewContainer.getHeight());
+ announcementForScreenReader(false);
+ });
}
private void updateFinished(int visible, float finalPosition) {
@@ -121,6 +137,17 @@ public class OneHandedTutorialHandler implements OneHandedTransitionCallback {
Settings.Secure.ONE_HANDED_TUTORIAL_SHOW_COUNT, showCount);
}
+ private void announcementForScreenReader(boolean isStartOneHanded) {
+ if (mAccessibilityManager.isTouchExplorationEnabled()) {
+ final AccessibilityEvent event = AccessibilityEvent.obtain();
+ event.setPackageName(mPackageName);
+ event.setEventType(AccessibilityEvent.TYPE_ANNOUNCEMENT);
+ event.getText().add(isStartOneHanded
+ ? mStartOneHandedDescription : mStopOneHandedDescription);
+ mAccessibilityManager.sendAccessibilityEvent(event);
+ }
+ }
+
/**
* Adds the tutorial target view to the WindowManager and update its layout, so it's ready
* to be animated in.
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/onehanded/OneHandedControllerTest.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/onehanded/OneHandedControllerTest.java
index 1ce8b5445b37..3645f1e56f92 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/onehanded/OneHandedControllerTest.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/onehanded/OneHandedControllerTest.java
@@ -18,13 +18,13 @@ package com.android.wm.shell.onehanded;
import static com.google.common.truth.Truth.assertThat;
-import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.anyInt;
import static org.mockito.Mockito.atLeastOnce;
import static org.mockito.Mockito.never;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
+import android.content.om.IOverlayManager;
import android.provider.Settings;
import android.testing.AndroidTestingRunner;
import android.testing.TestableLooper;
@@ -62,6 +62,8 @@ public class OneHandedControllerTest extends OneHandedTestCase {
OneHandedGestureHandler mMockGestureHandler;
@Mock
OneHandedTimeoutHandler mMockTimeoutHandler;
+ @Mock
+ IOverlayManager mMockOverlayManager;
@Before
public void setUp() throws Exception {
@@ -73,7 +75,8 @@ public class OneHandedControllerTest extends OneHandedTestCase {
mMockDisplayAreaOrganizer,
mMockTouchHandler,
mMockTutorialHandler,
- mMockGestureHandler);
+ mMockGestureHandler,
+ mMockOverlayManager);
mOneHandedController = Mockito.spy(oneHandedController);
mTimeoutHandler = Mockito.spy(OneHandedTimeoutHandler.get());
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/onehanded/OneHandedTutorialHandlerTest.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/onehanded/OneHandedTutorialHandlerTest.java
index 4a133d39291a..3341c9cbacb9 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/onehanded/OneHandedTutorialHandlerTest.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/onehanded/OneHandedTutorialHandlerTest.java
@@ -18,6 +18,7 @@ package com.android.wm.shell.onehanded;
import static org.mockito.Mockito.verify;
+import android.content.om.IOverlayManager;
import android.testing.AndroidTestingRunner;
import android.testing.TestableLooper;
@@ -44,6 +45,8 @@ public class OneHandedTutorialHandlerTest extends OneHandedTestCase {
DisplayController mMockDisplayController;
@Mock
OneHandedDisplayAreaOrganizer mMockDisplayAreaOrganizer;
+ @Mock
+ IOverlayManager mMockOverlayManager;
@Before
public void setUp() {
@@ -56,11 +59,12 @@ public class OneHandedTutorialHandlerTest extends OneHandedTestCase {
mMockDisplayAreaOrganizer,
mTouchHandler,
mTutorialHandler,
- mGestureHandler);
+ mGestureHandler,
+ mMockOverlayManager);
}
@Test
- public void testOneHandedManager_registerForDisplayAreaOrganizer() {
+ public void testRegisterForDisplayAreaOrganizer() {
verify(mMockDisplayAreaOrganizer).registerTransitionCallback(mTutorialHandler);
}
}
diff --git a/libs/hwui/FrameInfo.cpp b/libs/hwui/FrameInfo.cpp
index 0698775b0021..30ce5370760d 100644
--- a/libs/hwui/FrameInfo.cpp
+++ b/libs/hwui/FrameInfo.cpp
@@ -22,6 +22,7 @@ namespace uirenderer {
const std::string FrameInfoNames[] = {
"Flags",
+ "FrameTimelineVsyncId",
"IntendedVsync",
"Vsync",
"OldestInputEvent",
@@ -44,7 +45,7 @@ static_assert((sizeof(FrameInfoNames) / sizeof(FrameInfoNames[0])) ==
static_cast<int>(FrameInfoIndex::NumIndexes),
"size mismatch: FrameInfoNames doesn't match the enum!");
-static_assert(static_cast<int>(FrameInfoIndex::NumIndexes) == 17,
+static_assert(static_cast<int>(FrameInfoIndex::NumIndexes) == 18,
"Must update value in FrameMetrics.java#FRAME_STATS_COUNT (and here)");
void FrameInfo::importUiThreadInfo(int64_t* info) {
diff --git a/libs/hwui/FrameInfo.h b/libs/hwui/FrameInfo.h
index dc30617009e7..f5bfedde2f92 100644
--- a/libs/hwui/FrameInfo.h
+++ b/libs/hwui/FrameInfo.h
@@ -27,10 +27,11 @@
namespace android {
namespace uirenderer {
-#define UI_THREAD_FRAME_INFO_SIZE 9
+#define UI_THREAD_FRAME_INFO_SIZE 10
enum class FrameInfoIndex {
Flags = 0,
+ FrameTimelineVsyncId,
IntendedVsync,
Vsync,
OldestInputEvent,
@@ -71,11 +72,15 @@ enum {
class UiFrameInfoBuilder {
public:
+ static constexpr int64_t INVALID_VSYNC_ID = -1;
+
explicit UiFrameInfoBuilder(int64_t* buffer) : mBuffer(buffer) {
memset(mBuffer, 0, UI_THREAD_FRAME_INFO_SIZE * sizeof(int64_t));
+ set(FrameInfoIndex::FrameTimelineVsyncId) = INVALID_VSYNC_ID;
}
- UiFrameInfoBuilder& setVsync(nsecs_t vsyncTime, nsecs_t intendedVsync) {
+ UiFrameInfoBuilder& setVsync(nsecs_t vsyncTime, nsecs_t intendedVsync, int64_t vsyncId) {
+ set(FrameInfoIndex::FrameTimelineVsyncId) = vsyncId;
set(FrameInfoIndex::Vsync) = vsyncTime;
set(FrameInfoIndex::IntendedVsync) = intendedVsync;
// Pretend the other fields are all at vsync, too, so that naive
diff --git a/libs/hwui/OWNERS b/libs/hwui/OWNERS
index c232d1360419..bb93e66968be 100644
--- a/libs/hwui/OWNERS
+++ b/libs/hwui/OWNERS
@@ -5,3 +5,6 @@ njawad@google.com
reed@google.com
scroggo@google.com
stani@google.com
+
+# For text, e.g. Typeface, Font, Minikin, etc.
+nona@google.com
diff --git a/libs/hwui/SkiaCanvas.cpp b/libs/hwui/SkiaCanvas.cpp
index cfba5d4f6aa2..a690840e91a9 100644
--- a/libs/hwui/SkiaCanvas.cpp
+++ b/libs/hwui/SkiaCanvas.cpp
@@ -735,8 +735,7 @@ void SkiaCanvas::drawVectorDrawable(VectorDrawableRoot* vectorDrawable) {
// ----------------------------------------------------------------------------
void SkiaCanvas::drawGlyphs(ReadGlyphFunc glyphFunc, int count, const Paint& paint, float x,
- float y, float boundsLeft, float boundsTop, float boundsRight,
- float boundsBottom, float totalAdvance) {
+ float y, float totalAdvance) {
if (count <= 0 || paint.nothingToDraw()) return;
Paint paintCopy(paint);
if (mPaintFilter) {
diff --git a/libs/hwui/SkiaCanvas.h b/libs/hwui/SkiaCanvas.h
index 1df2b2671659..2cb850c83934 100644
--- a/libs/hwui/SkiaCanvas.h
+++ b/libs/hwui/SkiaCanvas.h
@@ -161,8 +161,7 @@ protected:
void drawDrawable(SkDrawable* drawable) { mCanvas->drawDrawable(drawable); }
virtual void drawGlyphs(ReadGlyphFunc glyphFunc, int count, const Paint& paint, float x,
- float y, float boundsLeft, float boundsTop, float boundsRight,
- float boundsBottom, float totalAdvance) override;
+ float y, float totalAdvance) override;
virtual void drawLayoutOnPath(const minikin::Layout& layout, float hOffset, float vOffset,
const Paint& paint, const SkPath& path, size_t start,
size_t end) override;
diff --git a/libs/hwui/hwui/Canvas.cpp b/libs/hwui/hwui/Canvas.cpp
index 2a377bbb83f2..2001b5620f84 100644
--- a/libs/hwui/hwui/Canvas.cpp
+++ b/libs/hwui/hwui/Canvas.cpp
@@ -84,13 +84,12 @@ static void simplifyPaint(int color, Paint* paint) {
class DrawTextFunctor {
public:
DrawTextFunctor(const minikin::Layout& layout, Canvas* canvas, const Paint& paint, float x,
- float y, minikin::MinikinRect& bounds, float totalAdvance)
+ float y, float totalAdvance)
: layout(layout)
, canvas(canvas)
, paint(paint)
, x(x)
, y(y)
- , bounds(bounds)
, totalAdvance(totalAdvance) {}
void operator()(size_t start, size_t end) {
@@ -114,19 +113,16 @@ public:
Paint outlinePaint(paint);
simplifyPaint(darken ? SK_ColorWHITE : SK_ColorBLACK, &outlinePaint);
outlinePaint.setStyle(SkPaint::kStrokeAndFill_Style);
- canvas->drawGlyphs(glyphFunc, glyphCount, outlinePaint, x, y, bounds.mLeft, bounds.mTop,
- bounds.mRight, bounds.mBottom, totalAdvance);
+ canvas->drawGlyphs(glyphFunc, glyphCount, outlinePaint, x, y, totalAdvance);
// inner
Paint innerPaint(paint);
simplifyPaint(darken ? SK_ColorBLACK : SK_ColorWHITE, &innerPaint);
innerPaint.setStyle(SkPaint::kFill_Style);
- canvas->drawGlyphs(glyphFunc, glyphCount, innerPaint, x, y, bounds.mLeft, bounds.mTop,
- bounds.mRight, bounds.mBottom, totalAdvance);
+ canvas->drawGlyphs(glyphFunc, glyphCount, innerPaint, x, y, totalAdvance);
} else {
// standard draw path
- canvas->drawGlyphs(glyphFunc, glyphCount, paint, x, y, bounds.mLeft, bounds.mTop,
- bounds.mRight, bounds.mBottom, totalAdvance);
+ canvas->drawGlyphs(glyphFunc, glyphCount, paint, x, y, totalAdvance);
}
}
@@ -136,7 +132,6 @@ private:
const Paint& paint;
float x;
float y;
- minikin::MinikinRect& bounds;
float totalAdvance;
};
@@ -156,15 +151,12 @@ void Canvas::drawText(const uint16_t* text, int textSize, int start, int count,
x += MinikinUtils::xOffsetForTextAlign(&paint, layout);
- minikin::MinikinRect bounds;
- layout.getBounds(&bounds);
-
// Set align to left for drawing, as we don't want individual
// glyphs centered or right-aligned; the offset above takes
// care of all alignment.
paint.setTextAlign(Paint::kLeft_Align);
- DrawTextFunctor f(layout, this, paint, x, y, bounds, layout.getAdvance());
+ DrawTextFunctor f(layout, this, paint, x, y, layout.getAdvance());
MinikinUtils::forFontRun(layout, &paint, f);
}
diff --git a/libs/hwui/hwui/Canvas.h b/libs/hwui/hwui/Canvas.h
index 333567b0cf91..817c7ee9077f 100644
--- a/libs/hwui/hwui/Canvas.h
+++ b/libs/hwui/hwui/Canvas.h
@@ -288,8 +288,7 @@ protected:
* totalAdvance: used to define width of text decorations (underlines, strikethroughs).
*/
virtual void drawGlyphs(ReadGlyphFunc glyphFunc, int count, const Paint& paint, float x,
- float y, float boundsLeft, float boundsTop, float boundsRight,
- float boundsBottom, float totalAdvance) = 0;
+ float y,float totalAdvance) = 0;
virtual void drawLayoutOnPath(const minikin::Layout& layout, float hOffset, float vOffset,
const Paint& paint, const SkPath& path, size_t start,
size_t end) = 0;
diff --git a/libs/hwui/hwui/MinikinSkia.cpp b/libs/hwui/hwui/MinikinSkia.cpp
index a6137b073d5a..0e338f35b8e7 100644
--- a/libs/hwui/hwui/MinikinSkia.cpp
+++ b/libs/hwui/hwui/MinikinSkia.cpp
@@ -33,8 +33,7 @@ namespace android {
MinikinFontSkia::MinikinFontSkia(sk_sp<SkTypeface> typeface, const void* fontData, size_t fontSize,
std::string_view filePath, int ttcIndex,
const std::vector<minikin::FontVariation>& axes)
- : minikin::MinikinFont(typeface->uniqueID())
- , mTypeface(std::move(typeface))
+ : mTypeface(std::move(typeface))
, mFontData(fontData)
, mFontSize(fontSize)
, mTtcIndex(ttcIndex)
diff --git a/libs/hwui/jni/Graphics.cpp b/libs/hwui/jni/Graphics.cpp
index ecbb55ec878d..77f46beb2100 100644
--- a/libs/hwui/jni/Graphics.cpp
+++ b/libs/hwui/jni/Graphics.cpp
@@ -9,6 +9,7 @@
#include "GraphicsJNI.h"
#include "SkCanvas.h"
+#include "SkFontMetrics.h"
#include "SkMath.h"
#include "SkRegion.h"
#include <cutils/ashmem.h>
@@ -228,6 +229,20 @@ static jfieldID gColorSpace_Named_LinearExtendedSRGBFieldID;
static jclass gTransferParameters_class;
static jmethodID gTransferParameters_constructorMethodID;
+static jclass gFontMetrics_class;
+static jfieldID gFontMetrics_top;
+static jfieldID gFontMetrics_ascent;
+static jfieldID gFontMetrics_descent;
+static jfieldID gFontMetrics_bottom;
+static jfieldID gFontMetrics_leading;
+
+static jclass gFontMetricsInt_class;
+static jfieldID gFontMetricsInt_top;
+static jfieldID gFontMetricsInt_ascent;
+static jfieldID gFontMetricsInt_descent;
+static jfieldID gFontMetricsInt_bottom;
+static jfieldID gFontMetricsInt_leading;
+
///////////////////////////////////////////////////////////////////////////////
void GraphicsJNI::get_jrect(JNIEnv* env, jobject obj, int* L, int* T, int* R, int* B)
@@ -468,6 +483,32 @@ SkRegion* GraphicsJNI::getNativeRegion(JNIEnv* env, jobject region)
return r;
}
+void GraphicsJNI::set_metrics(JNIEnv* env, jobject metrics, const SkFontMetrics& skmetrics) {
+ if (metrics == nullptr) return;
+ SkASSERT(env->IsInstanceOf(metrics, gFontMetrics_class));
+ env->SetFloatField(metrics, gFontMetrics_top, SkScalarToFloat(skmetrics.fTop));
+ env->SetFloatField(metrics, gFontMetrics_ascent, SkScalarToFloat(skmetrics.fAscent));
+ env->SetFloatField(metrics, gFontMetrics_descent, SkScalarToFloat(skmetrics.fDescent));
+ env->SetFloatField(metrics, gFontMetrics_bottom, SkScalarToFloat(skmetrics.fBottom));
+ env->SetFloatField(metrics, gFontMetrics_leading, SkScalarToFloat(skmetrics.fLeading));
+}
+
+int GraphicsJNI::set_metrics_int(JNIEnv* env, jobject metrics, const SkFontMetrics& skmetrics) {
+ int ascent = SkScalarRoundToInt(skmetrics.fAscent);
+ int descent = SkScalarRoundToInt(skmetrics.fDescent);
+ int leading = SkScalarRoundToInt(skmetrics.fLeading);
+
+ if (metrics) {
+ SkASSERT(env->IsInstanceOf(metrics, gFontMetricsInt_class));
+ env->SetIntField(metrics, gFontMetricsInt_top, SkScalarFloorToInt(skmetrics.fTop));
+ env->SetIntField(metrics, gFontMetricsInt_ascent, ascent);
+ env->SetIntField(metrics, gFontMetricsInt_descent, descent);
+ env->SetIntField(metrics, gFontMetricsInt_bottom, SkScalarCeilToInt(skmetrics.fBottom));
+ env->SetIntField(metrics, gFontMetricsInt_leading, leading);
+ }
+ return descent - ascent + leading;
+}
+
///////////////////////////////////////////////////////////////////////////////////////////
jobject GraphicsJNI::createBitmapRegionDecoder(JNIEnv* env, skia::BitmapRegionDecoder* bitmap)
@@ -764,5 +805,23 @@ int register_android_graphics_Graphics(JNIEnv* env)
gTransferParameters_constructorMethodID = GetMethodIDOrDie(env, gTransferParameters_class,
"<init>", "(DDDDDDD)V");
+ gFontMetrics_class = FindClassOrDie(env, "android/graphics/Paint$FontMetrics");
+ gFontMetrics_class = MakeGlobalRefOrDie(env, gFontMetrics_class);
+
+ gFontMetrics_top = GetFieldIDOrDie(env, gFontMetrics_class, "top", "F");
+ gFontMetrics_ascent = GetFieldIDOrDie(env, gFontMetrics_class, "ascent", "F");
+ gFontMetrics_descent = GetFieldIDOrDie(env, gFontMetrics_class, "descent", "F");
+ gFontMetrics_bottom = GetFieldIDOrDie(env, gFontMetrics_class, "bottom", "F");
+ gFontMetrics_leading = GetFieldIDOrDie(env, gFontMetrics_class, "leading", "F");
+
+ gFontMetricsInt_class = FindClassOrDie(env, "android/graphics/Paint$FontMetricsInt");
+ gFontMetricsInt_class = MakeGlobalRefOrDie(env, gFontMetricsInt_class);
+
+ gFontMetricsInt_top = GetFieldIDOrDie(env, gFontMetricsInt_class, "top", "I");
+ gFontMetricsInt_ascent = GetFieldIDOrDie(env, gFontMetricsInt_class, "ascent", "I");
+ gFontMetricsInt_descent = GetFieldIDOrDie(env, gFontMetricsInt_class, "descent", "I");
+ gFontMetricsInt_bottom = GetFieldIDOrDie(env, gFontMetricsInt_class, "bottom", "I");
+ gFontMetricsInt_leading = GetFieldIDOrDie(env, gFontMetricsInt_class, "leading", "I");
+
return 0;
}
diff --git a/libs/hwui/jni/GraphicsJNI.h b/libs/hwui/jni/GraphicsJNI.h
index 79ab617411e3..541d5a53de07 100644
--- a/libs/hwui/jni/GraphicsJNI.h
+++ b/libs/hwui/jni/GraphicsJNI.h
@@ -18,6 +18,7 @@
#include "graphics_jni_helpers.h"
class SkCanvas;
+struct SkFontMetrics;
namespace android {
namespace skia {
@@ -85,6 +86,17 @@ public:
bool* isHardware);
static SkRegion* getNativeRegion(JNIEnv*, jobject region);
+ /**
+ * Set SkFontMetrics to Java Paint.FontMetrics.
+ * Do nothing if metrics is nullptr.
+ */
+ static void set_metrics(JNIEnv*, jobject metrics, const SkFontMetrics& skmetrics);
+ /**
+ * Set SkFontMetrics to Java Paint.FontMetricsInt and return recommended interline space.
+ * Do nothing if metrics is nullptr.
+ */
+ static int set_metrics_int(JNIEnv*, jobject metrics, const SkFontMetrics& skmetrics);
+
/*
* LegacyBitmapConfig is the old enum in Skia that matched the enum int values
* in Bitmap.Config. Skia no longer supports this config, but has replaced it
diff --git a/libs/hwui/jni/Paint.cpp b/libs/hwui/jni/Paint.cpp
index 554674a331cd..d275659094be 100644
--- a/libs/hwui/jni/Paint.cpp
+++ b/libs/hwui/jni/Paint.cpp
@@ -59,20 +59,6 @@ using namespace android::uirenderer;
namespace android {
-struct JMetricsID {
- jfieldID top;
- jfieldID ascent;
- jfieldID descent;
- jfieldID bottom;
- jfieldID leading;
-};
-
-static jclass gFontMetrics_class;
-static JMetricsID gFontMetrics_fieldID;
-
-static jclass gFontMetricsInt_class;
-static JMetricsID gFontMetricsInt_fieldID;
-
static void getPosTextPath(const SkFont& font, const uint16_t glyphs[], int count,
const SkPoint pos[], SkPath* dst) {
dst->reset();
@@ -618,35 +604,14 @@ namespace PaintGlue {
static jfloat getFontMetrics(JNIEnv* env, jobject, jlong paintHandle, jobject metricsObj) {
SkFontMetrics metrics;
SkScalar spacing = getMetricsInternal(paintHandle, &metrics);
-
- if (metricsObj) {
- SkASSERT(env->IsInstanceOf(metricsObj, gFontMetrics_class));
- env->SetFloatField(metricsObj, gFontMetrics_fieldID.top, SkScalarToFloat(metrics.fTop));
- env->SetFloatField(metricsObj, gFontMetrics_fieldID.ascent, SkScalarToFloat(metrics.fAscent));
- env->SetFloatField(metricsObj, gFontMetrics_fieldID.descent, SkScalarToFloat(metrics.fDescent));
- env->SetFloatField(metricsObj, gFontMetrics_fieldID.bottom, SkScalarToFloat(metrics.fBottom));
- env->SetFloatField(metricsObj, gFontMetrics_fieldID.leading, SkScalarToFloat(metrics.fLeading));
- }
+ GraphicsJNI::set_metrics(env, metricsObj, metrics);
return SkScalarToFloat(spacing);
}
static jint getFontMetricsInt(JNIEnv* env, jobject, jlong paintHandle, jobject metricsObj) {
SkFontMetrics metrics;
-
getMetricsInternal(paintHandle, &metrics);
- int ascent = SkScalarRoundToInt(metrics.fAscent);
- int descent = SkScalarRoundToInt(metrics.fDescent);
- int leading = SkScalarRoundToInt(metrics.fLeading);
-
- if (metricsObj) {
- SkASSERT(env->IsInstanceOf(metricsObj, gFontMetricsInt_class));
- env->SetIntField(metricsObj, gFontMetricsInt_fieldID.top, SkScalarFloorToInt(metrics.fTop));
- env->SetIntField(metricsObj, gFontMetricsInt_fieldID.ascent, ascent);
- env->SetIntField(metricsObj, gFontMetricsInt_fieldID.descent, descent);
- env->SetIntField(metricsObj, gFontMetricsInt_fieldID.bottom, SkScalarCeilToInt(metrics.fBottom));
- env->SetIntField(metricsObj, gFontMetricsInt_fieldID.leading, leading);
- }
- return descent - ascent + leading;
+ return GraphicsJNI::set_metrics_int(env, metricsObj, metrics);
}
@@ -1137,24 +1102,6 @@ static const JNINativeMethod methods[] = {
};
int register_android_graphics_Paint(JNIEnv* env) {
- gFontMetrics_class = FindClassOrDie(env, "android/graphics/Paint$FontMetrics");
- gFontMetrics_class = MakeGlobalRefOrDie(env, gFontMetrics_class);
-
- gFontMetrics_fieldID.top = GetFieldIDOrDie(env, gFontMetrics_class, "top", "F");
- gFontMetrics_fieldID.ascent = GetFieldIDOrDie(env, gFontMetrics_class, "ascent", "F");
- gFontMetrics_fieldID.descent = GetFieldIDOrDie(env, gFontMetrics_class, "descent", "F");
- gFontMetrics_fieldID.bottom = GetFieldIDOrDie(env, gFontMetrics_class, "bottom", "F");
- gFontMetrics_fieldID.leading = GetFieldIDOrDie(env, gFontMetrics_class, "leading", "F");
-
- gFontMetricsInt_class = FindClassOrDie(env, "android/graphics/Paint$FontMetricsInt");
- gFontMetricsInt_class = MakeGlobalRefOrDie(env, gFontMetricsInt_class);
-
- gFontMetricsInt_fieldID.top = GetFieldIDOrDie(env, gFontMetricsInt_class, "top", "I");
- gFontMetricsInt_fieldID.ascent = GetFieldIDOrDie(env, gFontMetricsInt_class, "ascent", "I");
- gFontMetricsInt_fieldID.descent = GetFieldIDOrDie(env, gFontMetricsInt_class, "descent", "I");
- gFontMetricsInt_fieldID.bottom = GetFieldIDOrDie(env, gFontMetricsInt_class, "bottom", "I");
- gFontMetricsInt_fieldID.leading = GetFieldIDOrDie(env, gFontMetricsInt_class, "leading", "I");
-
return RegisterMethodsOrDie(env, "android/graphics/Paint", methods, NELEM(methods));
}
diff --git a/libs/hwui/jni/Shader.cpp b/libs/hwui/jni/Shader.cpp
index 7cb77233846f..0a194f9dd666 100644
--- a/libs/hwui/jni/Shader.cpp
+++ b/libs/hwui/jni/Shader.cpp
@@ -224,7 +224,7 @@ static jlong ComposeShader_create(JNIEnv* env, jobject o, jlong matrixPtr,
///////////////////////////////////////////////////////////////////////////////////////////////
static jlong BlurShader_create(JNIEnv* env , jobject o, jlong matrixPtr, jfloat sigmaX,
- jfloat sigmaY, jlong shaderHandle) {
+ jfloat sigmaY, jlong shaderHandle, jint edgeTreatment) {
auto* matrix = reinterpret_cast<const SkMatrix*>(matrixPtr);
auto* inputShader = reinterpret_cast<Shader*>(shaderHandle);
@@ -232,6 +232,7 @@ static jlong BlurShader_create(JNIEnv* env , jobject o, jlong matrixPtr, jfloat
sigmaX,
sigmaY,
inputShader,
+ static_cast<SkTileMode>(edgeTreatment),
matrix
);
return reinterpret_cast<jlong>(blurShader);
@@ -291,7 +292,7 @@ static const JNINativeMethod gBitmapShaderMethods[] = {
};
static const JNINativeMethod gBlurShaderMethods[] = {
- { "nativeCreate", "(JFFJ)J", (void*)BlurShader_create }
+ { "nativeCreate", "(JFFJI)J", (void*)BlurShader_create }
};
static const JNINativeMethod gLinearGradientMethods[] = {
diff --git a/libs/hwui/jni/android_graphics_HardwareRenderer.cpp b/libs/hwui/jni/android_graphics_HardwareRenderer.cpp
index e817ca744c58..c89463bf3ab4 100644
--- a/libs/hwui/jni/android_graphics_HardwareRenderer.cpp
+++ b/libs/hwui/jni/android_graphics_HardwareRenderer.cpp
@@ -514,7 +514,7 @@ static jobject android_view_ThreadedRenderer_createHardwareBitmapFromRenderNode(
proxy.setLightGeometry((Vector3){0, 0, 0}, 0);
nsecs_t vsync = systemTime(SYSTEM_TIME_MONOTONIC);
UiFrameInfoBuilder(proxy.frameInfo())
- .setVsync(vsync, vsync)
+ .setVsync(vsync, vsync, UiFrameInfoBuilder::INVALID_VSYNC_ID)
.addFlag(FrameInfoFlags::SurfaceCanvas);
proxy.syncAndDrawFrame();
}
diff --git a/libs/hwui/jni/fonts/Font.cpp b/libs/hwui/jni/fonts/Font.cpp
index 996cdceed8a7..bcdb4f5f1d13 100644
--- a/libs/hwui/jni/fonts/Font.cpp
+++ b/libs/hwui/jni/fonts/Font.cpp
@@ -18,6 +18,8 @@
#define LOG_TAG "Minikin"
#include "SkData.h"
+#include "SkFont.h"
+#include "SkFontMetrics.h"
#include "SkFontMgr.h"
#include "SkRefCnt.h"
#include "SkTypeface.h"
@@ -27,6 +29,7 @@
#include "FontUtils.h"
#include <hwui/MinikinSkia.h>
+#include <hwui/Paint.h>
#include <hwui/Typeface.h>
#include <minikin/FontFamily.h>
#include <ui/FatVector.h>
@@ -120,6 +123,36 @@ static jlong Font_Builder_build(JNIEnv* env, jobject clazz, jlong builderPtr, jo
return reinterpret_cast<jlong>(new FontWrapper(std::move(font)));
}
+// Fast Native
+static jlong Font_Builder_clone(JNIEnv* env, jobject clazz, jlong fontPtr, jlong builderPtr,
+ jint weight, jboolean italic, jint ttcIndex) {
+ FontWrapper* font = reinterpret_cast<FontWrapper*>(fontPtr);
+ MinikinFontSkia* minikinSkia = static_cast<MinikinFontSkia*>(font->font.typeface().get());
+ std::unique_ptr<NativeFontBuilder> builder(toBuilder(builderPtr));
+
+ // Reconstruct SkTypeface with different arguments from existing SkTypeface.
+ FatVector<SkFontArguments::VariationPosition::Coordinate, 2> skVariation;
+ for (const auto& axis : builder->axes) {
+ skVariation.push_back({axis.axisTag, axis.value});
+ }
+ SkFontArguments args;
+ args.setCollectionIndex(ttcIndex);
+ args.setVariationDesignPosition({skVariation.data(), static_cast<int>(skVariation.size())});
+
+ sk_sp<SkTypeface> newTypeface = minikinSkia->GetSkTypeface()->makeClone(args);
+
+ std::shared_ptr<minikin::MinikinFont> newMinikinFont = std::make_shared<MinikinFontSkia>(
+ std::move(newTypeface),
+ minikinSkia->GetFontData(),
+ minikinSkia->GetFontSize(),
+ minikinSkia->getFilePath(),
+ minikinSkia->GetFontIndex(),
+ builder->axes);
+ minikin::Font newFont = minikin::Font::Builder(newMinikinFont).setWeight(weight)
+ .setSlant(static_cast<minikin::FontStyle::Slant>(italic)).build();
+ return reinterpret_cast<jlong>(new FontWrapper(std::move(newFont)));
+}
+
// Critical Native
static jlong Font_Builder_getReleaseNativeFont(CRITICAL_JNI_PARAMS) {
return reinterpret_cast<jlong>(releaseFont);
@@ -127,16 +160,64 @@ static jlong Font_Builder_getReleaseNativeFont(CRITICAL_JNI_PARAMS) {
///////////////////////////////////////////////////////////////////////////////
+// Fast Native
+static jfloat Font_getGlyphBounds(JNIEnv* env, jobject, jlong fontHandle, jint glyphId,
+ jlong paintHandle, jobject rect) {
+ FontWrapper* font = reinterpret_cast<FontWrapper*>(fontHandle);
+ MinikinFontSkia* minikinSkia = static_cast<MinikinFontSkia*>(font->font.typeface().get());
+ Paint* paint = reinterpret_cast<Paint*>(paintHandle);
+
+ SkFont* skFont = &paint->getSkFont();
+ // We don't use populateSkFont since it is designed to be used for layout result with addressing
+ // auto fake-bolding.
+ skFont->setTypeface(minikinSkia->RefSkTypeface());
+
+ uint16_t glyph16 = glyphId;
+ SkRect skBounds;
+ SkScalar skWidth;
+ skFont->getWidthsBounds(&glyph16, 1, &skWidth, &skBounds, nullptr);
+ GraphicsJNI::rect_to_jrectf(skBounds, env, rect);
+ return SkScalarToFloat(skWidth);
+}
+
+// Fast Native
+static jfloat Font_getFontMetrics(JNIEnv* env, jobject, jlong fontHandle, jlong paintHandle,
+ jobject metricsObj) {
+ FontWrapper* font = reinterpret_cast<FontWrapper*>(fontHandle);
+ MinikinFontSkia* minikinSkia = static_cast<MinikinFontSkia*>(font->font.typeface().get());
+ Paint* paint = reinterpret_cast<Paint*>(paintHandle);
+
+ SkFont* skFont = &paint->getSkFont();
+ // We don't use populateSkFont since it is designed to be used for layout result with addressing
+ // auto fake-bolding.
+ skFont->setTypeface(minikinSkia->RefSkTypeface());
+
+ SkFontMetrics metrics;
+ SkScalar spacing = skFont->getMetrics(&metrics);
+ GraphicsJNI::set_metrics(env, metricsObj, metrics);
+ return spacing;
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
static const JNINativeMethod gFontBuilderMethods[] = {
{ "nInitBuilder", "()J", (void*) Font_Builder_initBuilder },
{ "nAddAxis", "(JIF)V", (void*) Font_Builder_addAxis },
{ "nBuild", "(JLjava/nio/ByteBuffer;Ljava/lang/String;IZI)J", (void*) Font_Builder_build },
+ { "nClone", "(JJIZI)J", (void*) Font_Builder_clone },
{ "nGetReleaseNativeFont", "()J", (void*) Font_Builder_getReleaseNativeFont },
};
+static const JNINativeMethod gFontMethods[] = {
+ { "nGetGlyphBounds", "(JIJLandroid/graphics/RectF;)F", (void*) Font_getGlyphBounds },
+ { "nGetFontMetrics", "(JJLandroid/graphics/Paint$FontMetrics;)F", (void*) Font_getFontMetrics },
+};
+
int register_android_graphics_fonts_Font(JNIEnv* env) {
return RegisterMethodsOrDie(env, "android/graphics/fonts/Font$Builder", gFontBuilderMethods,
- NELEM(gFontBuilderMethods));
+ NELEM(gFontBuilderMethods)) +
+ RegisterMethodsOrDie(env, "android/graphics/fonts/Font", gFontMethods,
+ NELEM(gFontMethods));
}
}
diff --git a/libs/hwui/pipeline/skia/GLFunctorDrawable.cpp b/libs/hwui/pipeline/skia/GLFunctorDrawable.cpp
index dd0fc695c246..bfbdc5c009c0 100644
--- a/libs/hwui/pipeline/skia/GLFunctorDrawable.cpp
+++ b/libs/hwui/pipeline/skia/GLFunctorDrawable.cpp
@@ -56,7 +56,8 @@ static void GetFboDetails(SkCanvas* canvas, GLuint* outFboID, SkISize* outFboSiz
}
void GLFunctorDrawable::onDraw(SkCanvas* canvas) {
- if (canvas->getGrContext() == nullptr) {
+ GrDirectContext* directContext = GrAsDirectContext(canvas->recordingContext());
+ if (directContext == nullptr) {
// We're dumping a picture, render a light-blue rectangle instead
// TODO: Draw the WebView text on top? Seemingly complicated as SkPaint doesn't
// seem to have a default typeface that works. We only ever use drawGlyphs, which
@@ -87,7 +88,7 @@ void GLFunctorDrawable::onDraw(SkCanvas* canvas) {
SkImageInfo surfaceInfo =
canvas->imageInfo().makeWH(clipBounds.width(), clipBounds.height());
tmpSurface =
- SkSurface::MakeRenderTarget(canvas->getGrContext(), SkBudgeted::kYes, surfaceInfo);
+ SkSurface::MakeRenderTarget(directContext, SkBudgeted::kYes, surfaceInfo);
tmpSurface->getCanvas()->clear(SK_ColorTRANSPARENT);
GrGLFramebufferInfo fboInfo;
@@ -141,7 +142,7 @@ void GLFunctorDrawable::onDraw(SkCanvas* canvas) {
// notify Skia that we just updated the FBO and stencil
const uint32_t grState = kStencil_GrGLBackendState | kRenderTarget_GrGLBackendState;
- canvas->getGrContext()->resetContext(grState);
+ directContext->resetContext(grState);
SkCanvas* tmpCanvas = canvas;
if (tmpSurface) {
@@ -188,7 +189,7 @@ void GLFunctorDrawable::onDraw(SkCanvas* canvas) {
glClear(GL_STENCIL_BUFFER_BIT);
}
- canvas->getGrContext()->resetContext();
+ directContext->resetContext();
// if there were unclipped save layers involved we draw our offscreen surface to the canvas
if (tmpSurface) {
diff --git a/libs/hwui/pipeline/skia/SkiaPipeline.cpp b/libs/hwui/pipeline/skia/SkiaPipeline.cpp
index 6dd36981e8aa..6e7493cb443d 100644
--- a/libs/hwui/pipeline/skia/SkiaPipeline.cpp
+++ b/libs/hwui/pipeline/skia/SkiaPipeline.cpp
@@ -86,7 +86,7 @@ void SkiaPipeline::renderLayers(const LightGeometry& lightGeometry,
}
void SkiaPipeline::renderLayersImpl(const LayerUpdateQueue& layers, bool opaque) {
- sk_sp<GrContext> cachedContext;
+ sk_sp<GrDirectContext> cachedContext;
// Render all layers that need to be updated, in order.
for (size_t i = 0; i < layers.entries().size(); i++) {
@@ -142,7 +142,8 @@ void SkiaPipeline::renderLayersImpl(const LayerUpdateQueue& layers, bool opaque)
// cache the current context so that we can defer flushing it until
// either all the layers have been rendered or the context changes
- GrContext* currentContext = layerNode->getLayerSurface()->getCanvas()->getGrContext();
+ GrDirectContext* currentContext =
+ GrAsDirectContext(layerNode->getLayerSurface()->getCanvas()->recordingContext());
if (cachedContext.get() != currentContext) {
if (cachedContext.get()) {
ATRACE_NAME("flush layers (context changed)");
@@ -201,7 +202,7 @@ bool SkiaPipeline::createOrUpdateLayer(RenderNode* node, const DamageAccumulator
}
void SkiaPipeline::prepareToDraw(const RenderThread& thread, Bitmap* bitmap) {
- GrContext* context = thread.getGrContext();
+ GrDirectContext* context = thread.getGrContext();
if (context) {
ATRACE_FORMAT("Bitmap#prepareToDraw %dx%d", bitmap->width(), bitmap->height());
auto image = bitmap->makeImage();
diff --git a/libs/hwui/pipeline/skia/VkFunctorDrawable.cpp b/libs/hwui/pipeline/skia/VkFunctorDrawable.cpp
index 50b45e6eb7ec..6efe1762976b 100644
--- a/libs/hwui/pipeline/skia/VkFunctorDrawable.cpp
+++ b/libs/hwui/pipeline/skia/VkFunctorDrawable.cpp
@@ -96,7 +96,7 @@ void VkFunctorDrawable::onDraw(SkCanvas* canvas) {
// "VkFunctorDrawable::onDraw" is not invoked for the most common case, when drawing in a GPU
// canvas.
- if (canvas->getGrContext() == nullptr) {
+ if (canvas->recordingContext() == nullptr) {
// We're dumping a picture, render a light-blue rectangle instead
SkPaint paint;
paint.setColor(0xFF81D4FA);
diff --git a/libs/hwui/pipeline/skia/VkInteropFunctorDrawable.cpp b/libs/hwui/pipeline/skia/VkInteropFunctorDrawable.cpp
index 403d9075dbd1..bc8ce428ce2e 100644
--- a/libs/hwui/pipeline/skia/VkInteropFunctorDrawable.cpp
+++ b/libs/hwui/pipeline/skia/VkInteropFunctorDrawable.cpp
@@ -67,7 +67,7 @@ void VkInteropFunctorDrawable::vkInvokeFunctor(Functor* functor) {
void VkInteropFunctorDrawable::onDraw(SkCanvas* canvas) {
ATRACE_CALL();
- if (canvas->getGrContext() == nullptr) {
+ if (canvas->recordingContext() == nullptr) {
SkDEBUGF(("Attempting to draw VkInteropFunctor into an unsupported surface"));
return;
}
diff --git a/libs/hwui/renderthread/CanvasContext.cpp b/libs/hwui/renderthread/CanvasContext.cpp
index 13d544c68e95..04359818e1b7 100644
--- a/libs/hwui/renderthread/CanvasContext.cpp
+++ b/libs/hwui/renderthread/CanvasContext.cpp
@@ -139,7 +139,7 @@ void CanvasContext::destroy() {
mAnimationContext->destroy();
}
-static void setBufferCount(ANativeWindow* window, uint32_t extraBuffers) {
+static void setBufferCount(ANativeWindow* window) {
int query_value;
int err = window->query(window, NATIVE_WINDOW_MIN_UNDEQUEUED_BUFFERS, &query_value);
if (err != 0 || query_value < 0) {
@@ -148,7 +148,9 @@ static void setBufferCount(ANativeWindow* window, uint32_t extraBuffers) {
}
auto min_undequeued_buffers = static_cast<uint32_t>(query_value);
- int bufferCount = min_undequeued_buffers + 2 + extraBuffers;
+ // We only need to set min_undequeued + 2 because the renderahead amount was already factored into the
+ // query for min_undequeued
+ int bufferCount = min_undequeued_buffers + 2;
native_window_set_buffer_count(window, bufferCount);
}
@@ -182,7 +184,8 @@ void CanvasContext::setupPipelineSurface() {
mNativeSurface ? mNativeSurface->getNativeWindow() : nullptr, mSwapBehavior);
if (mNativeSurface && !mNativeSurface->didSetExtraBuffers()) {
- setBufferCount(mNativeSurface->getNativeWindow(), mRenderAheadCapacity);
+ setBufferCount(mNativeSurface->getNativeWindow());
+
}
mFrameNumber = -1;
@@ -484,6 +487,14 @@ void CanvasContext::draw() {
waitOnFences();
+ if (mNativeSurface) {
+ // TODO(b/165985262): measure performance impact
+ if (const auto vsyncId = mCurrentFrameInfo->get(FrameInfoIndex::FrameTimelineVsyncId);
+ vsyncId != UiFrameInfoBuilder::INVALID_VSYNC_ID) {
+ native_window_set_frame_timeline_vsync(mNativeSurface->getNativeWindow(), vsyncId);
+ }
+ }
+
bool requireSwap = false;
int error = OK;
bool didSwap =
@@ -617,8 +628,11 @@ void CanvasContext::prepareAndDraw(RenderNode* node) {
ATRACE_CALL();
nsecs_t vsync = mRenderThread.timeLord().computeFrameTimeNanos();
+ int64_t vsyncId = mRenderThread.timeLord().lastVsyncId();
int64_t frameInfo[UI_THREAD_FRAME_INFO_SIZE];
- UiFrameInfoBuilder(frameInfo).addFlag(FrameInfoFlags::RTAnimation).setVsync(vsync, vsync);
+ UiFrameInfoBuilder(frameInfo)
+ .addFlag(FrameInfoFlags::RTAnimation)
+ .setVsync(vsync, vsync, vsyncId);
TreeInfo info(TreeInfo::MODE_RT_ONLY, *this);
prepareTree(info, frameInfo, systemTime(SYSTEM_TIME_MONOTONIC), node);
diff --git a/libs/hwui/renderthread/CanvasContext.h b/libs/hwui/renderthread/CanvasContext.h
index cba710f01063..cc4eb3285bbe 100644
--- a/libs/hwui/renderthread/CanvasContext.h
+++ b/libs/hwui/renderthread/CanvasContext.h
@@ -106,7 +106,7 @@ public:
* If Properties::isSkiaEnabled() is true then this will return the Skia
* grContext associated with the current RenderPipeline.
*/
- GrContext* getGrContext() const { return mRenderThread.getGrContext(); }
+ GrDirectContext* getGrContext() const { return mRenderThread.getGrContext(); }
// Won't take effect until next EGLSurface creation
void setSwapBehavior(SwapBehavior swapBehavior);
diff --git a/libs/hwui/renderthread/DrawFrameTask.cpp b/libs/hwui/renderthread/DrawFrameTask.cpp
index 1e593388d063..1ea595d6a30a 100644
--- a/libs/hwui/renderthread/DrawFrameTask.cpp
+++ b/libs/hwui/renderthread/DrawFrameTask.cpp
@@ -128,7 +128,9 @@ void DrawFrameTask::run() {
bool DrawFrameTask::syncFrameState(TreeInfo& info) {
ATRACE_CALL();
int64_t vsync = mFrameInfo[static_cast<int>(FrameInfoIndex::Vsync)];
- mRenderThread->timeLord().vsyncReceived(vsync);
+ int64_t intendedVsync = mFrameInfo[static_cast<int>(FrameInfoIndex::IntendedVsync)];
+ int64_t vsyncId = mFrameInfo[static_cast<int>(FrameInfoIndex::FrameTimelineVsyncId)];
+ mRenderThread->timeLord().vsyncReceived(vsync, intendedVsync, vsyncId);
bool canDraw = mContext->makeCurrent();
mContext->unpinImages();
diff --git a/libs/hwui/renderthread/RenderThread.cpp b/libs/hwui/renderthread/RenderThread.cpp
index 4dcbc4458e97..9371656eda7f 100644
--- a/libs/hwui/renderthread/RenderThread.cpp
+++ b/libs/hwui/renderthread/RenderThread.cpp
@@ -51,8 +51,10 @@ static JVMAttachHook gOnStartHook = nullptr;
void RenderThread::frameCallback(int64_t frameTimeNanos, void* data) {
RenderThread* rt = reinterpret_cast<RenderThread*>(data);
+ int64_t vsyncId = AChoreographer_getVsyncId(rt->mChoreographer);
rt->mVsyncRequested = false;
- if (rt->timeLord().vsyncReceived(frameTimeNanos) && !rt->mFrameCallbackTaskPending) {
+ if (rt->timeLord().vsyncReceived(frameTimeNanos, frameTimeNanos, vsyncId) &&
+ !rt->mFrameCallbackTaskPending) {
ATRACE_NAME("queue mFrameCallbackTask");
rt->mFrameCallbackTaskPending = true;
nsecs_t runAt = (frameTimeNanos + rt->mDispatchFrameDelay);
diff --git a/libs/hwui/renderthread/RenderThread.h b/libs/hwui/renderthread/RenderThread.h
index d7dc00b8a5c1..4fbb07168ac0 100644
--- a/libs/hwui/renderthread/RenderThread.h
+++ b/libs/hwui/renderthread/RenderThread.h
@@ -19,8 +19,8 @@
#include <GrDirectContext.h>
#include <SkBitmap.h>
-#include <apex/choreographer.h>
#include <cutils/compiler.h>
+#include <private/android/choreographer.h>
#include <thread/ThreadBase.h>
#include <utils/Looper.h>
#include <utils/Thread.h>
diff --git a/libs/hwui/renderthread/TimeLord.cpp b/libs/hwui/renderthread/TimeLord.cpp
index 784068f1d877..7dc36c449568 100644
--- a/libs/hwui/renderthread/TimeLord.cpp
+++ b/libs/hwui/renderthread/TimeLord.cpp
@@ -19,9 +19,17 @@ namespace android {
namespace uirenderer {
namespace renderthread {
-TimeLord::TimeLord() : mFrameIntervalNanos(milliseconds_to_nanoseconds(16)), mFrameTimeNanos(0) {}
+TimeLord::TimeLord() : mFrameIntervalNanos(milliseconds_to_nanoseconds(16)),
+ mFrameTimeNanos(0),
+ mFrameIntendedTimeNanos(0),
+ mFrameVsyncId(-1) {}
+
+bool TimeLord::vsyncReceived(nsecs_t vsync, nsecs_t intendedVsync, int64_t vsyncId) {
+ if (intendedVsync > mFrameIntendedTimeNanos) {
+ mFrameIntendedTimeNanos = intendedVsync;
+ mFrameVsyncId = vsyncId;
+ }
-bool TimeLord::vsyncReceived(nsecs_t vsync) {
if (vsync > mFrameTimeNanos) {
mFrameTimeNanos = vsync;
return true;
@@ -36,6 +44,8 @@ nsecs_t TimeLord::computeFrameTimeNanos() {
if (jitterNanos >= mFrameIntervalNanos) {
nsecs_t lastFrameOffset = jitterNanos % mFrameIntervalNanos;
mFrameTimeNanos = now - lastFrameOffset;
+ // mFrameVsyncId is not adjusted here as we still want to send
+ // the vsync id that started this frame to the Surface Composer
}
return mFrameTimeNanos;
}
diff --git a/libs/hwui/renderthread/TimeLord.h b/libs/hwui/renderthread/TimeLord.h
index 68a0f7f971b9..23c1e51c427a 100644
--- a/libs/hwui/renderthread/TimeLord.h
+++ b/libs/hwui/renderthread/TimeLord.h
@@ -32,9 +32,10 @@ public:
nsecs_t frameIntervalNanos() const { return mFrameIntervalNanos; }
// returns true if the vsync is newer, false if it was rejected for staleness
- bool vsyncReceived(nsecs_t vsync);
+ bool vsyncReceived(nsecs_t vsync, nsecs_t indendedVsync, int64_t vsyncId);
nsecs_t latestVsync() { return mFrameTimeNanos; }
nsecs_t computeFrameTimeNanos();
+ int64_t lastVsyncId() const { return mFrameVsyncId; }
private:
friend class RenderThread;
@@ -44,6 +45,8 @@ private:
nsecs_t mFrameIntervalNanos;
nsecs_t mFrameTimeNanos;
+ nsecs_t mFrameIntendedTimeNanos;
+ int64_t mFrameVsyncId;
};
} /* namespace renderthread */
diff --git a/libs/hwui/shader/BlurShader.cpp b/libs/hwui/shader/BlurShader.cpp
index fa10be100bca..2abd8714204b 100644
--- a/libs/hwui/shader/BlurShader.cpp
+++ b/libs/hwui/shader/BlurShader.cpp
@@ -20,13 +20,14 @@
#include "utils/Blur.h"
namespace android::uirenderer {
-BlurShader::BlurShader(float radiusX, float radiusY, Shader* inputShader, const SkMatrix* matrix)
+BlurShader::BlurShader(float radiusX, float radiusY, Shader* inputShader, SkTileMode edgeTreatment,
+ const SkMatrix* matrix)
: Shader(matrix)
, skImageFilter(
SkImageFilters::Blur(
Blur::convertRadiusToSigma(radiusX),
Blur::convertRadiusToSigma(radiusY),
- SkTileMode::kClamp,
+ edgeTreatment,
inputShader ? inputShader->asSkImageFilter() : nullptr,
nullptr)
) { }
diff --git a/libs/hwui/shader/BlurShader.h b/libs/hwui/shader/BlurShader.h
index 9eb22bd11f4a..60a15898893e 100644
--- a/libs/hwui/shader/BlurShader.h
+++ b/libs/hwui/shader/BlurShader.h
@@ -30,8 +30,12 @@ public:
*
* This will blur the contents of the provided input shader if it is non-null, otherwise
* the source bitmap will be blurred instead.
+ *
+ * The edge treatment parameter determines how content near the edges of the source is to
+ * participate in the blur
*/
- BlurShader(float radiusX, float radiusY, Shader* inputShader, const SkMatrix* matrix);
+ BlurShader(float radiusX, float radiusY, Shader* inputShader, SkTileMode edgeTreatment,
+ const SkMatrix* matrix);
~BlurShader() override;
protected:
sk_sp<SkImageFilter> makeSkImageFilter() override;
diff --git a/libs/hwui/tests/macrobench/TestSceneRunner.cpp b/libs/hwui/tests/macrobench/TestSceneRunner.cpp
index 801cb7d9e8c5..ed89c590be10 100644
--- a/libs/hwui/tests/macrobench/TestSceneRunner.cpp
+++ b/libs/hwui/tests/macrobench/TestSceneRunner.cpp
@@ -145,7 +145,8 @@ void run(const TestScene::Info& info, const TestScene::Options& opts,
for (int i = 0; i < warmupFrameCount; i++) {
testContext.waitForVsync();
nsecs_t vsync = systemTime(SYSTEM_TIME_MONOTONIC);
- UiFrameInfoBuilder(proxy->frameInfo()).setVsync(vsync, vsync);
+ UiFrameInfoBuilder(proxy->frameInfo())
+ .setVsync(vsync, vsync, UiFrameInfoBuilder::INVALID_VSYNC_ID);
proxy->syncAndDrawFrame();
}
@@ -165,7 +166,8 @@ void run(const TestScene::Info& info, const TestScene::Options& opts,
nsecs_t vsync = systemTime(SYSTEM_TIME_MONOTONIC);
{
ATRACE_NAME("UI-Draw Frame");
- UiFrameInfoBuilder(proxy->frameInfo()).setVsync(vsync, vsync);
+ UiFrameInfoBuilder(proxy->frameInfo())
+ .setVsync(vsync, vsync, UiFrameInfoBuilder::INVALID_VSYNC_ID);
scene->doFrame(i);
proxy->syncAndDrawFrame();
}
diff --git a/location/java/android/location/ILocationManager.aidl b/location/java/android/location/ILocationManager.aidl
index c1a98eabb2b7..31dcd7e65663 100644
--- a/location/java/android/location/ILocationManager.aidl
+++ b/location/java/android/location/ILocationManager.aidl
@@ -47,7 +47,7 @@ import com.android.internal.location.ProviderProperties;
interface ILocationManager
{
Location getLastLocation(String provider, String packageName, String attributionTag);
- void getCurrentLocation(String provider, in LocationRequest request, in ICancellationSignal cancellationSignal, in ILocationCallback callback, String packageName, String attributionTag, String listenerId);
+ @nullable ICancellationSignal getCurrentLocation(String provider, in LocationRequest request, in ILocationCallback callback, String packageName, String attributionTag, String listenerId);
void registerLocationListener(String provider, in LocationRequest request, in ILocationListener listener, String packageName, String attributionTag, String listenerId);
void unregisterLocationListener(in ILocationListener listener);
diff --git a/location/java/android/location/LocationManager.java b/location/java/android/location/LocationManager.java
index d7ef24170c24..6e597b2c1d63 100644
--- a/location/java/android/location/LocationManager.java
+++ b/location/java/android/location/LocationManager.java
@@ -20,6 +20,8 @@ import static android.Manifest.permission.ACCESS_COARSE_LOCATION;
import static android.Manifest.permission.ACCESS_FINE_LOCATION;
import static android.Manifest.permission.LOCATION_HARDWARE;
import static android.Manifest.permission.WRITE_SECURE_SETTINGS;
+import static android.location.LocationRequest.createFromDeprecatedCriteria;
+import static android.location.LocationRequest.createFromDeprecatedProvider;
import static com.android.internal.util.ConcurrentUtils.DIRECT_EXECUTOR;
@@ -783,22 +785,25 @@ public class LocationManager {
Preconditions.checkArgument(provider != null, "invalid null provider");
Preconditions.checkArgument(locationRequest != null, "invalid null location request");
- ICancellationSignal remoteCancellationSignal = CancellationSignal.createTransport();
- GetCurrentLocationTransport transport = new GetCurrentLocationTransport(executor, consumer,
- remoteCancellationSignal);
-
if (cancellationSignal != null) {
cancellationSignal.throwIfCanceled();
- cancellationSignal.setOnCancelListener(transport::cancel);
}
+ GetCurrentLocationTransport transport = new GetCurrentLocationTransport(executor, consumer,
+ cancellationSignal);
+
+ ICancellationSignal cancelRemote;
try {
- mService.getCurrentLocation(provider, locationRequest, remoteCancellationSignal,
- transport, mContext.getPackageName(), mContext.getAttributionTag(),
- AppOpsManager.toReceiverId(consumer));
+ cancelRemote = mService.getCurrentLocation(provider,
+ locationRequest, transport, mContext.getPackageName(),
+ mContext.getAttributionTag(), AppOpsManager.toReceiverId(consumer));
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
+
+ if (cancellationSignal != null) {
+ cancellationSignal.setRemote(cancelRemote);
+ }
}
/**
@@ -1024,9 +1029,7 @@ public class LocationManager {
requestLocationUpdates(
provider,
- new LocationRequest.Builder(minTimeMs)
- .setMinUpdateDistanceMeters(minDistanceM)
- .build(),
+ createFromDeprecatedProvider(provider, minTimeMs, minDistanceM, false),
executor,
listener);
}
@@ -1085,10 +1088,7 @@ public class LocationManager {
requestLocationUpdates(
FUSED_PROVIDER,
- new LocationRequest.Builder(minTimeMs)
- .setQuality(criteria)
- .setMinUpdateDistanceMeters(minDistanceM)
- .build(),
+ createFromDeprecatedCriteria(criteria, minTimeMs, minDistanceM, false),
executor,
listener);
}
@@ -1116,9 +1116,7 @@ public class LocationManager {
requestLocationUpdates(
provider,
- new LocationRequest.Builder(minTimeMs)
- .setMinUpdateDistanceMeters(minDistanceM)
- .build(),
+ createFromDeprecatedProvider(provider, minTimeMs, minDistanceM, false),
pendingIntent);
}
@@ -1144,10 +1142,7 @@ public class LocationManager {
Preconditions.checkArgument(criteria != null, "invalid null criteria");
requestLocationUpdates(
FUSED_PROVIDER,
- new LocationRequest.Builder(minTimeMs)
- .setQuality(criteria)
- .setMinUpdateDistanceMeters(minDistanceM)
- .build(),
+ createFromDeprecatedCriteria(criteria, minTimeMs, minDistanceM, false),
pendingIntent);
}
@@ -2519,7 +2514,7 @@ public class LocationManager {
}
private static class GetCurrentLocationTransport extends ILocationCallback.Stub implements
- ListenerExecutor {
+ ListenerExecutor, CancellationSignal.OnCancelListener {
private final Executor mExecutor;
@@ -2527,33 +2522,22 @@ public class LocationManager {
@Nullable
private Consumer<Location> mConsumer;
- @GuardedBy("this")
- @Nullable
- private ICancellationSignal mRemoteCancellationSignal;
-
GetCurrentLocationTransport(Executor executor, Consumer<Location> consumer,
- ICancellationSignal remoteCancellationSignal) {
+ @Nullable CancellationSignal cancellationSignal) {
Preconditions.checkArgument(executor != null, "illegal null executor");
Preconditions.checkArgument(consumer != null, "illegal null consumer");
mExecutor = executor;
mConsumer = consumer;
- mRemoteCancellationSignal = remoteCancellationSignal;
+
+ if (cancellationSignal != null) {
+ cancellationSignal.setOnCancelListener(this);
+ }
}
- public void cancel() {
- ICancellationSignal cancellationSignal;
+ @Override
+ public void onCancel() {
synchronized (this) {
- cancellationSignal = mRemoteCancellationSignal;
mConsumer = null;
- mRemoteCancellationSignal = null;
- }
-
- if (cancellationSignal != null) {
- try {
- cancellationSignal.cancel();
- } catch (RemoteException e) {
- throw e.rethrowFromSystemServer();
- }
}
}
@@ -2563,7 +2547,6 @@ public class LocationManager {
synchronized (this) {
consumer = mConsumer;
mConsumer = null;
- mRemoteCancellationSignal = null;
}
executeSafely(mExecutor, () -> consumer, listener -> listener.accept(location));
diff --git a/location/java/android/location/LocationRequest.java b/location/java/android/location/LocationRequest.java
index 911b9ab9c973..0521b10a2530 100644
--- a/location/java/android/location/LocationRequest.java
+++ b/location/java/android/location/LocationRequest.java
@@ -475,7 +475,7 @@ public final class LocationRequest implements Parcelable {
/**
* Returns the minimum update interval. If location updates are available faster than the
- * request interval then locations will only be delivered if the minimum update interval has
+ * request interval then locations will only be updated if the minimum update interval has
* expired since the last location update.
*
* <p class=note><strong>Note:</strong> Some allowance for jitter is already built into the
@@ -951,9 +951,9 @@ public final class LocationRequest implements Parcelable {
/**
* Sets an explicit minimum update interval. If location updates are available faster than
- * the request interval then locations will only be delivered if the minimum update interval
- * has expired since the last location update. Defaults to no explicit minimum update
- * interval set, which means the minimum update interval is the same as the interval.
+ * the request interval then an update will only occur if the minimum update interval has
+ * expired since the last location update. Defaults to no explicit minimum update interval
+ * set, which means the minimum update interval is the same as the interval.
*
* <p class=note><strong>Note:</strong> Some allowance for jitter is already built into the
* minimum update interval, so you need not worry about updates blocked simply because they
@@ -972,7 +972,7 @@ public final class LocationRequest implements Parcelable {
/**
* Clears an explicitly set minimum update interval and reverts to an implicit minimum
- * update interval (ie, the minimum update interval is same value as the interval).
+ * update interval (ie, the minimum update interval is the same value as the interval).
*/
public @NonNull Builder clearMinUpdateIntervalMillis() {
mMinUpdateIntervalMillis = IMPLICIT_MIN_UPDATE_INTERVAL;
@@ -980,8 +980,8 @@ public final class LocationRequest implements Parcelable {
}
/**
- * Sets the minimum update distance between delivered locations. If a potential location
- * update is closer to the last delivered location than the minimum update distance, then
+ * Sets the minimum update distance between location updates. If a potential location
+ * update is closer to the last location update than the minimum update distance, then
* the potential location update will not occur. Defaults to 0, which represents no minimum
* update distance.
*/
diff --git a/media/Android.bp b/media/Android.bp
index 0ed10472561d..828707b70e7b 100644
--- a/media/Android.bp
+++ b/media/Android.bp
@@ -1,65 +1,54 @@
aidl_interface {
name: "audio_common-aidl",
unstable: true,
- local_include_dir: "java",
+ local_include_dir: "aidl",
srcs: [
- "java/android/media/audio/common/AudioChannelMask.aidl",
- "java/android/media/audio/common/AudioConfig.aidl",
- "java/android/media/audio/common/AudioFormat.aidl",
- "java/android/media/audio/common/AudioOffloadInfo.aidl",
- "java/android/media/audio/common/AudioStreamType.aidl",
- "java/android/media/audio/common/AudioUsage.aidl",
+ "aidl/android/media/audio/common/AudioChannelMask.aidl",
+ "aidl/android/media/audio/common/AudioConfig.aidl",
+ "aidl/android/media/audio/common/AudioFormat.aidl",
+ "aidl/android/media/audio/common/AudioOffloadInfo.aidl",
+ "aidl/android/media/audio/common/AudioStreamType.aidl",
+ "aidl/android/media/audio/common/AudioUsage.aidl",
+ ],
+}
+
+aidl_interface {
+ name: "media_permission-aidl",
+ unstable: true,
+ local_include_dir: "aidl",
+ srcs: [
+ "aidl/android/media/permission/Identity.aidl",
],
- backend:
- {
- cpp: {
- enabled: true,
- },
- java: {
- // Already generated as part of the entire media java library.
- enabled: false,
- },
- },
}
aidl_interface {
name: "soundtrigger_middleware-aidl",
unstable: true,
- local_include_dir: "java",
+ local_include_dir: "aidl",
srcs: [
- "java/android/media/soundtrigger_middleware/ConfidenceLevel.aidl",
- "java/android/media/soundtrigger_middleware/ISoundTriggerCallback.aidl",
- "java/android/media/soundtrigger_middleware/ISoundTriggerMiddlewareService.aidl",
- "java/android/media/soundtrigger_middleware/ISoundTriggerModule.aidl",
- "java/android/media/soundtrigger_middleware/ModelParameter.aidl",
- "java/android/media/soundtrigger_middleware/ModelParameterRange.aidl",
- "java/android/media/soundtrigger_middleware/Phrase.aidl",
- "java/android/media/soundtrigger_middleware/PhraseRecognitionEvent.aidl",
- "java/android/media/soundtrigger_middleware/PhraseRecognitionExtra.aidl",
- "java/android/media/soundtrigger_middleware/PhraseSoundModel.aidl",
- "java/android/media/soundtrigger_middleware/RecognitionConfig.aidl",
- "java/android/media/soundtrigger_middleware/RecognitionEvent.aidl",
- "java/android/media/soundtrigger_middleware/RecognitionMode.aidl",
- "java/android/media/soundtrigger_middleware/RecognitionStatus.aidl",
- "java/android/media/soundtrigger_middleware/SoundModel.aidl",
- "java/android/media/soundtrigger_middleware/SoundModelType.aidl",
- "java/android/media/soundtrigger_middleware/SoundTriggerModuleDescriptor.aidl",
- "java/android/media/soundtrigger_middleware/SoundTriggerModuleProperties.aidl",
- "java/android/media/soundtrigger_middleware/Status.aidl",
+ "aidl/android/media/soundtrigger_middleware/AudioCapabilities.aidl",
+ "aidl/android/media/soundtrigger_middleware/ConfidenceLevel.aidl",
+ "aidl/android/media/soundtrigger_middleware/ISoundTriggerCallback.aidl",
+ "aidl/android/media/soundtrigger_middleware/ISoundTriggerMiddlewareService.aidl",
+ "aidl/android/media/soundtrigger_middleware/ISoundTriggerModule.aidl",
+ "aidl/android/media/soundtrigger_middleware/ModelParameter.aidl",
+ "aidl/android/media/soundtrigger_middleware/ModelParameterRange.aidl",
+ "aidl/android/media/soundtrigger_middleware/Phrase.aidl",
+ "aidl/android/media/soundtrigger_middleware/PhraseRecognitionEvent.aidl",
+ "aidl/android/media/soundtrigger_middleware/PhraseRecognitionExtra.aidl",
+ "aidl/android/media/soundtrigger_middleware/PhraseSoundModel.aidl",
+ "aidl/android/media/soundtrigger_middleware/RecognitionConfig.aidl",
+ "aidl/android/media/soundtrigger_middleware/RecognitionEvent.aidl",
+ "aidl/android/media/soundtrigger_middleware/RecognitionMode.aidl",
+ "aidl/android/media/soundtrigger_middleware/RecognitionStatus.aidl",
+ "aidl/android/media/soundtrigger_middleware/SoundModel.aidl",
+ "aidl/android/media/soundtrigger_middleware/SoundModelType.aidl",
+ "aidl/android/media/soundtrigger_middleware/SoundTriggerModuleDescriptor.aidl",
+ "aidl/android/media/soundtrigger_middleware/SoundTriggerModuleProperties.aidl",
+ "aidl/android/media/soundtrigger_middleware/Status.aidl",
+ ],
+ imports: [
+ "audio_common-aidl",
+ "media_permission-aidl",
],
- backend:
- {
- cpp: {
- enabled: true,
- },
- java: {
- // Already generated as part of the entire media java library.
- enabled: false,
- },
- ndk: {
- // Not currently needed, and disabled because of b/146172425
- enabled: false,
- },
- },
- imports: [ "audio_common-aidl" ],
}
diff --git a/media/OWNERS b/media/OWNERS
index c95ac6c210c0..36df3a05e0ee 100644
--- a/media/OWNERS
+++ b/media/OWNERS
@@ -9,6 +9,7 @@ hkuang@google.com
hunga@google.com
insun@google.com
jaewan@google.com
+jinpark@google.com
jmtrivi@google.com
jsharkey@android.com
klhyun@google.com
@@ -17,6 +18,3 @@ marcone@google.com
philburk@google.com
sungsoo@google.com
wonsik@google.com
-
-# For maintaining sync with AndroidX code
-per-file ExifInterface.java = jinpark@google.com, sungsoo@google.com
diff --git a/media/TEST_MAPPING b/media/TEST_MAPPING
index 6dc0c6f5b676..cf2f0f0da2b7 100644
--- a/media/TEST_MAPPING
+++ b/media/TEST_MAPPING
@@ -4,21 +4,13 @@
"name": "GtsMediaTestCases",
"options" : [
{
- "include-annotation": "android.platform.test.annotations.Presubmit"
+ "include-annotation": "android.platform.test.annotations.Presubmit"
},
{
"include-filter": "com.google.android.media.gts.WidevineGenericOpsTests"
- }
- ]
- },
- {
- "name": "GtsExoPlayerTestCases",
- "options" : [
- {
- "include-annotation": "android.platform.test.annotations.SocPresubmit"
},
{
- "include-filter": "com.google.android.exoplayer.gts.DashTest#testWidevine23FpsH264Fixed"
+ "include-filter": "com.google.android.media.gts.WidevineYouTubePerformanceTests"
}
]
}
diff --git a/media/java/android/media/audio/common/AudioChannelMask.aidl b/media/aidl/android/media/audio/common/AudioChannelMask.aidl
index b9b08e6921bc..b9b08e6921bc 100644
--- a/media/java/android/media/audio/common/AudioChannelMask.aidl
+++ b/media/aidl/android/media/audio/common/AudioChannelMask.aidl
diff --git a/media/java/android/media/audio/common/AudioConfig.aidl b/media/aidl/android/media/audio/common/AudioConfig.aidl
index 50dd796e1fa0..50dd796e1fa0 100644
--- a/media/java/android/media/audio/common/AudioConfig.aidl
+++ b/media/aidl/android/media/audio/common/AudioConfig.aidl
diff --git a/media/java/android/media/audio/common/AudioFormat.aidl b/media/aidl/android/media/audio/common/AudioFormat.aidl
index aadc8e26cce3..aadc8e26cce3 100644
--- a/media/java/android/media/audio/common/AudioFormat.aidl
+++ b/media/aidl/android/media/audio/common/AudioFormat.aidl
diff --git a/media/java/android/media/audio/common/AudioOffloadInfo.aidl b/media/aidl/android/media/audio/common/AudioOffloadInfo.aidl
index ec10d71135ae..ec10d71135ae 100644
--- a/media/java/android/media/audio/common/AudioOffloadInfo.aidl
+++ b/media/aidl/android/media/audio/common/AudioOffloadInfo.aidl
diff --git a/media/java/android/media/audio/common/AudioStreamType.aidl b/media/aidl/android/media/audio/common/AudioStreamType.aidl
index c54566726350..c54566726350 100644
--- a/media/java/android/media/audio/common/AudioStreamType.aidl
+++ b/media/aidl/android/media/audio/common/AudioStreamType.aidl
diff --git a/media/java/android/media/audio/common/AudioUsage.aidl b/media/aidl/android/media/audio/common/AudioUsage.aidl
index ef348165b22c..ef348165b22c 100644
--- a/media/java/android/media/audio/common/AudioUsage.aidl
+++ b/media/aidl/android/media/audio/common/AudioUsage.aidl
diff --git a/media/aidl/android/media/permission/Identity.aidl b/media/aidl/android/media/permission/Identity.aidl
new file mode 100644
index 000000000000..361497d59ea9
--- /dev/null
+++ b/media/aidl/android/media/permission/Identity.aidl
@@ -0,0 +1,32 @@
+/*
+ * 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.media.permission;
+
+/**
+ * A collection of identity-related information, required for permission enforcement.
+ *
+ * {@hide}
+ */
+parcelable Identity {
+ /** Linux user ID. */
+ int uid;
+ /** Linux process ID. */
+ int pid;
+ /** Package name. If null, the first package owned by the given uid will be assumed. */
+ @nullable String packageName;
+ /** Attribution tag. Mostly used for diagnostic purposes. */
+ @nullable String attributionTag;
+}
diff --git a/media/java/android/media/soundtrigger_middleware/AudioCapabilities.aidl b/media/aidl/android/media/soundtrigger_middleware/AudioCapabilities.aidl
index 97a8849c7b07..97a8849c7b07 100644
--- a/media/java/android/media/soundtrigger_middleware/AudioCapabilities.aidl
+++ b/media/aidl/android/media/soundtrigger_middleware/AudioCapabilities.aidl
diff --git a/media/java/android/media/soundtrigger_middleware/ConfidenceLevel.aidl b/media/aidl/android/media/soundtrigger_middleware/ConfidenceLevel.aidl
index 3dbc70556bd3..3dbc70556bd3 100644
--- a/media/java/android/media/soundtrigger_middleware/ConfidenceLevel.aidl
+++ b/media/aidl/android/media/soundtrigger_middleware/ConfidenceLevel.aidl
diff --git a/media/java/android/media/soundtrigger_middleware/ISoundTriggerCallback.aidl b/media/aidl/android/media/soundtrigger_middleware/ISoundTriggerCallback.aidl
index 726af7681979..726af7681979 100644
--- a/media/java/android/media/soundtrigger_middleware/ISoundTriggerCallback.aidl
+++ b/media/aidl/android/media/soundtrigger_middleware/ISoundTriggerCallback.aidl
diff --git a/media/aidl/android/media/soundtrigger_middleware/ISoundTriggerMiddlewareService.aidl b/media/aidl/android/media/soundtrigger_middleware/ISoundTriggerMiddlewareService.aidl
new file mode 100644
index 000000000000..d1126b9006e0
--- /dev/null
+++ b/media/aidl/android/media/soundtrigger_middleware/ISoundTriggerMiddlewareService.aidl
@@ -0,0 +1,89 @@
+/*
+ * 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 android.media.soundtrigger_middleware;
+
+import android.media.permission.Identity;
+import android.media.soundtrigger_middleware.ISoundTriggerModule;
+import android.media.soundtrigger_middleware.ISoundTriggerCallback;
+import android.media.soundtrigger_middleware.SoundTriggerModuleDescriptor;
+
+/**
+ * Main entry point into this module.
+ *
+ * Allows the client to enumerate the available soundtrigger devices and their capabilities, then
+ * attach to either one of them in order to use it.
+ *
+ * {@hide}
+ */
+interface ISoundTriggerMiddlewareService {
+ /**
+ * Query the available modules and their capabilities.
+ *
+ * This variant is intended for use by the originator of the operations for permission
+ * enforcement purposes. The provided identity's uid/pid fields will be ignored and overridden
+ * by the ones provided by Binder.getCallingUid() / Binder.getCallingPid().
+ */
+ SoundTriggerModuleDescriptor[] listModulesAsOriginator(in Identity identity);
+
+ /**
+ * Query the available modules and their capabilities.
+ *
+ * This variant is intended for use by a trusted "middleman", acting on behalf of some identity
+ * other than itself. The caller must provide:
+ * - Its own identity, which will be used to establish trust via the
+ * SOUNDTRIGGER_DELEGATE_IDENTITY permission. This identity's uid/pid fields will be ignored
+ * and overridden by the ones provided by Binder.getCallingUid() / Binder.getCallingPid().
+ * This implies that the caller must clear its caller identity to protect from the case where
+ * it resides in the same process as the callee.
+ * - The identity of the entity on behalf of which module operations are to be performed.
+ */
+ SoundTriggerModuleDescriptor[] listModulesAsMiddleman(in Identity middlemanIdentity,
+ in Identity originatorIdentity);
+
+ /**
+ * Attach to one of the available modules.
+ *
+ * This variant is intended for use by the originator of the operations for permission
+ * enforcement purposes. The provided identity's uid/pid fields will be ignored and overridden
+ * by the ones provided by Binder.getCallingUid() / Binder.getCallingPid().
+ *
+ * listModules() must be called prior to calling this method and the provided handle must be
+ * one of the handles from the returned list.
+ */
+ ISoundTriggerModule attachAsOriginator(int handle,
+ in Identity identity,
+ ISoundTriggerCallback callback);
+
+ /**
+ * Attach to one of the available modules.
+ *
+ * This variant is intended for use by a trusted "middleman", acting on behalf of some identity
+ * other than itself. The caller must provide:
+ * - Its own identity, which will be used to establish trust via the
+ * SOUNDTRIGGER_DELEGATE_IDENTITY permission. This identity's uid/pid fields will be ignored
+ * and overridden by the ones provided by Binder.getCallingUid() / Binder.getCallingPid().
+ * This implies that the caller must clear its caller identity to protect from the case where
+ * it resides in the same process as the callee.
+ * - The identity of the entity on behalf of which module operations are to be performed.
+ *
+ * listModules() must be called prior to calling this method and the provided handle must be
+ * one of the handles from the returned list.
+ */
+ ISoundTriggerModule attachAsMiddleman(int handle,
+ in Identity middlemanIdentity,
+ in Identity originatorIdentity,
+ ISoundTriggerCallback callback);
+}
diff --git a/media/java/android/media/soundtrigger_middleware/ISoundTriggerModule.aidl b/media/aidl/android/media/soundtrigger_middleware/ISoundTriggerModule.aidl
index c4a57857dd3d..c4a57857dd3d 100644
--- a/media/java/android/media/soundtrigger_middleware/ISoundTriggerModule.aidl
+++ b/media/aidl/android/media/soundtrigger_middleware/ISoundTriggerModule.aidl
diff --git a/media/java/android/media/soundtrigger_middleware/ModelParameter.aidl b/media/aidl/android/media/soundtrigger_middleware/ModelParameter.aidl
index 09936278e93a..09936278e93a 100644
--- a/media/java/android/media/soundtrigger_middleware/ModelParameter.aidl
+++ b/media/aidl/android/media/soundtrigger_middleware/ModelParameter.aidl
diff --git a/media/java/android/media/soundtrigger_middleware/ModelParameterRange.aidl b/media/aidl/android/media/soundtrigger_middleware/ModelParameterRange.aidl
index d6948a87dc6d..d6948a87dc6d 100644
--- a/media/java/android/media/soundtrigger_middleware/ModelParameterRange.aidl
+++ b/media/aidl/android/media/soundtrigger_middleware/ModelParameterRange.aidl
diff --git a/media/java/android/media/soundtrigger_middleware/OWNERS b/media/aidl/android/media/soundtrigger_middleware/OWNERS
index e5d037003ac4..e5d037003ac4 100644
--- a/media/java/android/media/soundtrigger_middleware/OWNERS
+++ b/media/aidl/android/media/soundtrigger_middleware/OWNERS
diff --git a/media/java/android/media/soundtrigger_middleware/Phrase.aidl b/media/aidl/android/media/soundtrigger_middleware/Phrase.aidl
index 98a489f8a6a9..98a489f8a6a9 100644
--- a/media/java/android/media/soundtrigger_middleware/Phrase.aidl
+++ b/media/aidl/android/media/soundtrigger_middleware/Phrase.aidl
diff --git a/media/java/android/media/soundtrigger_middleware/PhraseRecognitionEvent.aidl b/media/aidl/android/media/soundtrigger_middleware/PhraseRecognitionEvent.aidl
index 6a3ec61d1ebf..6a3ec61d1ebf 100644
--- a/media/java/android/media/soundtrigger_middleware/PhraseRecognitionEvent.aidl
+++ b/media/aidl/android/media/soundtrigger_middleware/PhraseRecognitionEvent.aidl
diff --git a/media/java/android/media/soundtrigger_middleware/PhraseRecognitionExtra.aidl b/media/aidl/android/media/soundtrigger_middleware/PhraseRecognitionExtra.aidl
index cb96bf37a95d..cb96bf37a95d 100644
--- a/media/java/android/media/soundtrigger_middleware/PhraseRecognitionExtra.aidl
+++ b/media/aidl/android/media/soundtrigger_middleware/PhraseRecognitionExtra.aidl
diff --git a/media/java/android/media/soundtrigger_middleware/PhraseSoundModel.aidl b/media/aidl/android/media/soundtrigger_middleware/PhraseSoundModel.aidl
index 81028c1608ea..81028c1608ea 100644
--- a/media/java/android/media/soundtrigger_middleware/PhraseSoundModel.aidl
+++ b/media/aidl/android/media/soundtrigger_middleware/PhraseSoundModel.aidl
diff --git a/media/java/android/media/soundtrigger_middleware/RecognitionConfig.aidl b/media/aidl/android/media/soundtrigger_middleware/RecognitionConfig.aidl
index 5c0eeb1e32b1..5c0eeb1e32b1 100644
--- a/media/java/android/media/soundtrigger_middleware/RecognitionConfig.aidl
+++ b/media/aidl/android/media/soundtrigger_middleware/RecognitionConfig.aidl
diff --git a/media/java/android/media/soundtrigger_middleware/RecognitionEvent.aidl b/media/aidl/android/media/soundtrigger_middleware/RecognitionEvent.aidl
index a237ec1aa3b3..a237ec1aa3b3 100644
--- a/media/java/android/media/soundtrigger_middleware/RecognitionEvent.aidl
+++ b/media/aidl/android/media/soundtrigger_middleware/RecognitionEvent.aidl
diff --git a/media/java/android/media/soundtrigger_middleware/RecognitionMode.aidl b/media/aidl/android/media/soundtrigger_middleware/RecognitionMode.aidl
index d8bfff4bec6f..d8bfff4bec6f 100644
--- a/media/java/android/media/soundtrigger_middleware/RecognitionMode.aidl
+++ b/media/aidl/android/media/soundtrigger_middleware/RecognitionMode.aidl
diff --git a/media/java/android/media/soundtrigger_middleware/RecognitionStatus.aidl b/media/aidl/android/media/soundtrigger_middleware/RecognitionStatus.aidl
index d563edca547d..d563edca547d 100644
--- a/media/java/android/media/soundtrigger_middleware/RecognitionStatus.aidl
+++ b/media/aidl/android/media/soundtrigger_middleware/RecognitionStatus.aidl
diff --git a/media/java/android/media/soundtrigger_middleware/SoundModel.aidl b/media/aidl/android/media/soundtrigger_middleware/SoundModel.aidl
index 81d8291e85aa..cee3635a1e3b 100644
--- a/media/java/android/media/soundtrigger_middleware/SoundModel.aidl
+++ b/media/aidl/android/media/soundtrigger_middleware/SoundModel.aidl
@@ -16,6 +16,7 @@
package android.media.soundtrigger_middleware;
import android.media.soundtrigger_middleware.SoundModelType;
+import android.os.ParcelFileDescriptor;
/**
* Base sound model descriptor. This struct can be extended for various specific types by way of
@@ -32,7 +33,7 @@ parcelable SoundModel {
* was build for */
String vendorUuid;
/** Opaque data transparent to Android framework */
- FileDescriptor data;
+ ParcelFileDescriptor data;
/** Size of the above data, in bytes. */
int dataSize;
}
diff --git a/media/java/android/media/soundtrigger_middleware/SoundModelType.aidl b/media/aidl/android/media/soundtrigger_middleware/SoundModelType.aidl
index f2abc9af7780..f2abc9af7780 100644
--- a/media/java/android/media/soundtrigger_middleware/SoundModelType.aidl
+++ b/media/aidl/android/media/soundtrigger_middleware/SoundModelType.aidl
diff --git a/media/java/android/media/soundtrigger_middleware/SoundTriggerModuleDescriptor.aidl b/media/aidl/android/media/soundtrigger_middleware/SoundTriggerModuleDescriptor.aidl
index 667135ff61b9..667135ff61b9 100644
--- a/media/java/android/media/soundtrigger_middleware/SoundTriggerModuleDescriptor.aidl
+++ b/media/aidl/android/media/soundtrigger_middleware/SoundTriggerModuleDescriptor.aidl
diff --git a/media/java/android/media/soundtrigger_middleware/SoundTriggerModuleProperties.aidl b/media/aidl/android/media/soundtrigger_middleware/SoundTriggerModuleProperties.aidl
index 9c56e7b98b3f..9c56e7b98b3f 100644
--- a/media/java/android/media/soundtrigger_middleware/SoundTriggerModuleProperties.aidl
+++ b/media/aidl/android/media/soundtrigger_middleware/SoundTriggerModuleProperties.aidl
diff --git a/media/java/android/media/soundtrigger_middleware/Status.aidl b/media/aidl/android/media/soundtrigger_middleware/Status.aidl
index c7623f5bf491..c7623f5bf491 100644
--- a/media/java/android/media/soundtrigger_middleware/Status.aidl
+++ b/media/aidl/android/media/soundtrigger_middleware/Status.aidl
diff --git a/media/java/android/media/AudioAttributes.aidl b/media/java/android/media/AudioAttributes.aidl
index 04587f9d48d4..88a6f50d2d3c 100644
--- a/media/java/android/media/AudioAttributes.aidl
+++ b/media/java/android/media/AudioAttributes.aidl
@@ -15,4 +15,4 @@
package android.media;
-parcelable AudioAttributes;
+@JavaOnlyStableParcelable parcelable AudioAttributes;
diff --git a/media/java/android/media/AudioManager.java b/media/java/android/media/AudioManager.java
index e1e55c25b3fa..22b5ca53e7e9 100644
--- a/media/java/android/media/AudioManager.java
+++ b/media/java/android/media/AudioManager.java
@@ -96,8 +96,8 @@ public class AudioManager {
private Context mOriginalContext;
private Context mApplicationContext;
private long mVolumeKeyUpTime;
- private final boolean mUseVolumeKeySounds;
- private final boolean mUseFixedVolume;
+ private boolean mUseFixedVolumeInitialized;
+ private boolean mUseFixedVolume;
private static final String TAG = "AudioManager";
private static final boolean DEBUG = false;
private static final AudioPortEventHandler sAudioPortEventHandler = new AudioPortEventHandler();
@@ -711,8 +711,6 @@ public class AudioManager {
*/
@UnsupportedAppUsage
public AudioManager() {
- mUseVolumeKeySounds = true;
- mUseFixedVolume = false;
}
/**
@@ -721,10 +719,6 @@ public class AudioManager {
@UnsupportedAppUsage
public AudioManager(Context context) {
setContext(context);
- mUseVolumeKeySounds = getContext().getResources().getBoolean(
- com.android.internal.R.bool.config_useVolumeKeySounds);
- mUseFixedVolume = getContext().getResources().getBoolean(
- com.android.internal.R.bool.config_useFixedVolume);
}
private Context getContext() {
@@ -823,6 +817,18 @@ public class AudioManager {
* </ul>
*/
public boolean isVolumeFixed() {
+ synchronized (this) {
+ try {
+ if (!mUseFixedVolumeInitialized) {
+ mUseFixedVolume = getContext().getResources().getBoolean(
+ com.android.internal.R.bool.config_useFixedVolume);
+ }
+ } catch (Exception e) {
+ } finally {
+ // only ever try once, so always consider initialized even if query failed
+ mUseFixedVolumeInitialized = true;
+ }
+ }
return mUseFixedVolume;
}
diff --git a/media/java/android/media/CamcorderProfile.java b/media/java/android/media/CamcorderProfile.java
index f89893136804..45f1ca044afb 100644
--- a/media/java/android/media/CamcorderProfile.java
+++ b/media/java/android/media/CamcorderProfile.java
@@ -19,6 +19,8 @@ package android.media;
import android.compat.annotation.UnsupportedAppUsage;
import android.hardware.Camera;
import android.hardware.Camera.CameraInfo;
+import android.hardware.camera2.CameraManager;
+import android.hardware.camera2.CameraMetadata;
import android.os.Build;
/**
@@ -418,7 +420,11 @@ public class CamcorderProfile
* resolution and higher audio sampling rate, etc, than those with lower quality
* level.
*
- * @param cameraId the id for the camera
+ * @param cameraId the id for the camera. Integer camera ids parsed from the list received by
+ * invoking {@link CameraManager#getCameraIdList} can be used as long as they
+ * are {@link CameraMetadata#REQUEST_AVAILABLE_CAPABILITIES_BACKWARD_COMPATIBLE}
+ * and not
+ * {@link CameraMetadata#INFO_SUPPORTED_HARDWARE_LEVEL_EXTERNAL EXTERNAL}.
* @param quality the target quality level for the camcorder profile.
* @see #QUALITY_LOW
* @see #QUALITY_HIGH
@@ -512,7 +518,11 @@ public class CamcorderProfile
* @see android.hardware.camera2.CameraManager
* @see android.hardware.camera2.CameraCharacteristics
*
- * @param cameraId the id for the camera
+ * @param cameraId the id for the camera. Integer camera ids parsed from the list received by
+ * invoking {@link CameraManager#getCameraIdList} can be used as long as they
+ * are {@link CameraMetadata#REQUEST_AVAILABLE_CAPABILITIES_BACKWARD_COMPATIBLE}
+ * and not
+ * {@link CameraMetadata#INFO_SUPPORTED_HARDWARE_LEVEL_EXTERNAL EXTERNAL}.
* @param quality the target quality level for the camcorder profile
*/
public static boolean hasProfile(int cameraId, int quality) {
diff --git a/media/java/android/media/DrmInitData.java b/media/java/android/media/DrmInitData.java
index d803311fdae3..85b4ba54de63 100644
--- a/media/java/android/media/DrmInitData.java
+++ b/media/java/android/media/DrmInitData.java
@@ -87,13 +87,13 @@ public abstract class DrmInitData {
public final byte[] data;
/**
+ * Creates a new instance with the given values.
+ *
* @param uuid The UUID associated with this scheme initialization data.
* @param mimeType The mimeType of the initialization data.
* @param data The initialization data.
- *
- * @hide
*/
- public SchemeInitData(UUID uuid, String mimeType, byte[] data) {
+ public SchemeInitData(@NonNull UUID uuid, @NonNull String mimeType, @NonNull byte[] data) {
this.uuid = uuid;
this.mimeType = mimeType;
this.data = data;
diff --git a/media/java/android/media/ExifInterface.java b/media/java/android/media/ExifInterface.java
index 57e8e438d592..8845d6954db2 100644
--- a/media/java/android/media/ExifInterface.java
+++ b/media/java/android/media/ExifInterface.java
@@ -17,6 +17,7 @@
package android.media;
import static android.media.ExifInterfaceUtils.byteArrayToHexString;
+import static android.media.ExifInterfaceUtils.closeFileDescriptor;
import static android.media.ExifInterfaceUtils.closeQuietly;
import static android.media.ExifInterfaceUtils.convertToLongArray;
import static android.media.ExifInterfaceUtils.copy;
@@ -30,7 +31,6 @@ import android.compat.annotation.UnsupportedAppUsage;
import android.content.res.AssetManager;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
-import android.os.Build;
import android.system.ErrnoException;
import android.system.Os;
import android.system.OsConstants;
@@ -598,7 +598,6 @@ public class ExifInterface {
private static final int WEBP_CHUNK_TYPE_BYTE_LENGTH = 4;
private static final int WEBP_CHUNK_SIZE_BYTE_LENGTH = 4;
- @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P, trackingBug = 115609023)
@GuardedBy("sFormatter")
private static SimpleDateFormat sFormatter;
@GuardedBy("sFormatterTz")
@@ -1445,18 +1444,17 @@ public class ExifInterface {
sExifPointerTagMap.put(EXIF_POINTER_TAGS[5].number, IFD_TYPE_ORF_IMAGE_PROCESSING); // 8256
}
- @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P, trackingBug = 115609023)
private String mFilename;
private FileDescriptor mSeekableFileDescriptor;
private AssetManager.AssetInputStream mAssetInputStream;
private boolean mIsInputStream;
private int mMimeType;
private boolean mIsExifDataOnly;
- @UnsupportedAppUsage
+ @UnsupportedAppUsage(publicAlternatives = "Use {@link #getAttribute(java.lang.String)} "
+ + "instead.")
private final HashMap[] mAttributes = new HashMap[EXIF_TAGS.length];
private Set<Integer> mHandledIfdOffsets = new HashSet<>(EXIF_TAGS.length);
private ByteOrder mExifByteOrder = ByteOrder.BIG_ENDIAN;
- @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P, trackingBug = 115609023)
private boolean mHasThumbnail;
private boolean mHasThumbnailStrips;
private boolean mAreThumbnailStripsConsecutive;
@@ -1529,9 +1527,8 @@ public class ExifInterface {
mAssetInputStream = null;
mFilename = null;
- // When FileDescriptor is duplicated and set to FileInputStream, ownership needs to be
- // clarified in order for garbage collection to take place.
- boolean isFdOwner = false;
+
+ boolean isFdDuped = false;
if (isSeekableFD(fileDescriptor)) {
mSeekableFileDescriptor = fileDescriptor;
// Keep the original file descriptor in order to save attributes when it's seekable.
@@ -1539,7 +1536,7 @@ public class ExifInterface {
// feature won't be working.
try {
fileDescriptor = Os.dup(fileDescriptor);
- isFdOwner = true;
+ isFdDuped = true;
} catch (ErrnoException e) {
throw e.rethrowAsIOException();
}
@@ -1549,10 +1546,13 @@ public class ExifInterface {
mIsInputStream = false;
FileInputStream in = null;
try {
- in = new FileInputStream(fileDescriptor, isFdOwner);
+ in = new FileInputStream(fileDescriptor);
loadAttributes(in);
} finally {
closeQuietly(in);
+ if (isFdDuped) {
+ closeFileDescriptor(fileDescriptor);
+ }
}
}
@@ -2199,6 +2199,7 @@ public class ExifInterface {
// Read the thumbnail.
InputStream in = null;
+ FileDescriptor newFileDescriptor = null;
try {
if (mAssetInputStream != null) {
in = mAssetInputStream;
@@ -2211,9 +2212,9 @@ public class ExifInterface {
} else if (mFilename != null) {
in = new FileInputStream(mFilename);
} else if (mSeekableFileDescriptor != null) {
- FileDescriptor fileDescriptor = Os.dup(mSeekableFileDescriptor);
- Os.lseek(fileDescriptor, 0, OsConstants.SEEK_SET);
- in = new FileInputStream(fileDescriptor, true);
+ newFileDescriptor = Os.dup(mSeekableFileDescriptor);
+ Os.lseek(newFileDescriptor, 0, OsConstants.SEEK_SET);
+ in = new FileInputStream(newFileDescriptor);
}
if (in == null) {
// Should not be reached this.
@@ -2234,6 +2235,9 @@ public class ExifInterface {
Log.d(TAG, "Encountered exception while getting thumbnail", e);
} finally {
closeQuietly(in);
+ if (newFileDescriptor != null) {
+ closeFileDescriptor(newFileDescriptor);
+ }
}
return null;
}
@@ -2402,11 +2406,8 @@ public class ExifInterface {
}
/**
- * Returns parsed {@code DateTime} value, or -1 if unavailable or invalid.
- *
- * @hide
+ * Returns parsed {@link #TAG_DATETIME} value, or -1 if unavailable or invalid.
*/
- @UnsupportedAppUsage
public @CurrentTimeMillisLong long getDateTime() {
return parseDateTime(getAttribute(TAG_DATETIME),
getAttribute(TAG_SUBSEC_TIME),
@@ -2414,10 +2415,7 @@ public class ExifInterface {
}
/**
- * Returns parsed {@code DateTimeDigitized} value, or -1 if unavailable or
- * invalid.
- *
- * @hide
+ * Returns parsed {@link #TAG_DATETIME_DIGITIZED} value, or -1 if unavailable or invalid.
*/
public @CurrentTimeMillisLong long getDateTimeDigitized() {
return parseDateTime(getAttribute(TAG_DATETIME_DIGITIZED),
@@ -2426,12 +2424,8 @@ public class ExifInterface {
}
/**
- * Returns parsed {@code DateTimeOriginal} value, or -1 if unavailable or
- * invalid.
- *
- * @hide
+ * Returns parsed {@link #TAG_DATETIME_ORIGINAL} value, or -1 if unavailable or invalid.
*/
- @UnsupportedAppUsage
public @CurrentTimeMillisLong long getDateTimeOriginal() {
return parseDateTime(getAttribute(TAG_DATETIME_ORIGINAL),
getAttribute(TAG_SUBSEC_TIME_ORIGINAL),
@@ -2483,9 +2477,7 @@ public class ExifInterface {
/**
* Returns number of milliseconds since Jan. 1, 1970, midnight UTC.
* Returns -1 if the date time information if not available.
- * @hide
*/
- @UnsupportedAppUsage
public long getGpsDateTime() {
String date = getAttribute(TAG_GPS_DATESTAMP);
String time = getAttribute(TAG_GPS_TIMESTAMP);
@@ -2511,7 +2503,6 @@ public class ExifInterface {
}
/** {@hide} */
- @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P, trackingBug = 115609023)
public static float convertRationalLatLonToFloat(String rationalString, String ref) {
try {
String [] parts = rationalString.split(",");
diff --git a/media/java/android/media/ExifInterfaceUtils.java b/media/java/android/media/ExifInterfaceUtils.java
index 6ff706e8041f..491fe1d64ab9 100644
--- a/media/java/android/media/ExifInterfaceUtils.java
+++ b/media/java/android/media/ExifInterfaceUtils.java
@@ -16,7 +16,12 @@
package android.media;
+import android.system.ErrnoException;
+import android.system.Os;
+import android.util.Log;
+
import java.io.Closeable;
+import java.io.FileDescriptor;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
@@ -25,6 +30,8 @@ import java.io.OutputStream;
* Package private utility class for ExifInterface.
*/
class ExifInterfaceUtils {
+ private static final String TAG = "ExifInterface";
+
/**
* Copies all of the bytes from {@code in} to {@code out}. Neither stream is closed.
* Returns the total number of bytes transferred.
@@ -114,4 +121,15 @@ class ExifInterfaceUtils {
}
}
}
+
+ /**
+ * Closes a file descriptor that has been duplicated.
+ */
+ public static void closeFileDescriptor(FileDescriptor fd) {
+ try {
+ Os.close(fd);
+ } catch (ErrnoException ex) {
+ Log.e(TAG, "Error closing fd.", ex);
+ }
+ }
}
diff --git a/media/java/android/media/MediaExtractor.java b/media/java/android/media/MediaExtractor.java
index 6ef871383a58..8f603300dc11 100644
--- a/media/java/android/media/MediaExtractor.java
+++ b/media/java/android/media/MediaExtractor.java
@@ -22,10 +22,6 @@ import android.annotation.Nullable;
import android.content.ContentResolver;
import android.content.Context;
import android.content.res.AssetFileDescriptor;
-import android.media.AudioPresentation;
-import android.media.MediaCodec;
-import android.media.MediaFormat;
-import android.media.MediaHTTPService;
import android.net.Uri;
import android.os.IBinder;
import android.os.IHwBinder;
diff --git a/media/java/android/media/MediaFormat.java b/media/java/android/media/MediaFormat.java
index 755bbfb94362..1a49b85403e4 100644
--- a/media/java/android/media/MediaFormat.java
+++ b/media/java/android/media/MediaFormat.java
@@ -20,10 +20,12 @@ import android.annotation.IntDef;
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.compat.annotation.UnsupportedAppUsage;
+import android.util.Log;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.nio.ByteBuffer;
+import java.nio.ByteOrder;
import java.util.AbstractSet;
import java.util.HashMap;
import java.util.Iterator;
@@ -436,6 +438,52 @@ public final class MediaFormat {
public static final String KEY_CAPTURE_RATE = "capture-rate";
/**
+ * A key for retrieving the slow-motion marker information associated with a video track.
+ * <p>
+ * The associated value is a ByteBuffer in {@link ByteOrder#BIG_ENDIAN}
+ * (networking order) of the following format:
+ * </p>
+ * <pre class="prettyprint">
+ * float(32) playbackRate;
+ * unsigned int(32) numMarkers;
+ * for (i = 0;i < numMarkers; i++) {
+ * int(64) timestampUs;
+ * float(32) speedRatio;
+ * }</pre>
+ * The meaning of each field is as follows:
+ * <table border="1" width="90%" align="center" cellpadding="5">
+ * <tbody>
+ * <tr>
+ * <td>playbackRate</td>
+ * <td>The frame rate at which the playback should happen (or the flattened
+ * clip should be).</td>
+ * </tr>
+ * <tr>
+ * <td>numMarkers</td>
+ * <td>The number of slow-motion markers that follows.</td>
+ * </tr>
+ * <tr>
+ * <td>timestampUs</td>
+ * <td>The starting point of a new segment.</td>
+ * </tr>
+ * <tr>
+ * <td>speedRatio</td>
+ * <td>The playback speed for that segment. The playback speed is a floating
+ * point number, indicating how fast the time progresses relative to that
+ * written in the container. (Eg. 4.0 means time goes 4x as fast, which
+ * makes 30fps become 120fps.)</td>
+ * </tr>
+ * </table>
+ * <p>
+ * The following constraints apply to the timestampUs of the markers:
+ * </p>
+ * <li>The timestampUs shall be monotonically increasing.</li>
+ * <li>The timestampUs shall fall within the time span of the video track.</li>
+ * <li>The first timestampUs should match that of the first video sample.</li>
+ */
+ public static final String KEY_SLOW_MOTION_MARKERS = "slow-motion-markers";
+
+ /**
* A key describing the frequency of key frames expressed in seconds between key frames.
* <p>
* This key is used by video encoders.
diff --git a/media/java/android/media/MediaMetadata.aidl b/media/java/android/media/MediaMetadata.aidl
index 66ee48304168..1d78da2a1b8e 100644
--- a/media/java/android/media/MediaMetadata.aidl
+++ b/media/java/android/media/MediaMetadata.aidl
@@ -15,4 +15,4 @@
package android.media;
-parcelable MediaMetadata;
+@JavaOnlyStableParcelable parcelable MediaMetadata;
diff --git a/media/java/android/media/MediaTranscodeManager.java b/media/java/android/media/MediaTranscodeManager.java
index 451677f6a8bc..21376bb0fecb 100644
--- a/media/java/android/media/MediaTranscodeManager.java
+++ b/media/java/android/media/MediaTranscodeManager.java
@@ -83,13 +83,13 @@ import java.util.concurrent.Executors;
<pre class=prettyprint>
TranscodingRequest request =
- new TranscodingRequest.Builder()
- .setSourceUri(srcUri)
- .setDestinationUri(dstUri)
- .setType(MediaTranscodeManager.TRANSCODING_TYPE_VIDEO)
- .setPriority(REALTIME)
- .setVideoTrackFormat(videoFormat)
- .build();
+ new TranscodingRequest.Builder()
+ .setSourceUri(srcUri)
+ .setDestinationUri(dstUri)
+ .setType(MediaTranscodeManager.TRANSCODING_TYPE_VIDEO)
+ .setPriority(REALTIME)
+ .setVideoTrackFormat(videoFormat)
+ .build();
}</pre>
TODO(hkuang): Add architecture diagram showing the transcoding service and api.
@@ -274,7 +274,7 @@ public final class MediaTranscodeManager {
private static IMediaTranscodingService getService(boolean retry) {
int retryCount = !retry ? 1 : CONNECT_SERVICE_RETRY_COUNT;
- Log.i(TAG, "get service with rety " + retryCount);
+ Log.i(TAG, "get service with retry " + retryCount);
for (int count = 1; count <= retryCount; count++) {
Log.d(TAG, "Trying to connect to service. Try count: " + count);
IMediaTranscodingService service = IMediaTranscodingService.Stub.asInterface(
@@ -356,7 +356,15 @@ public final class MediaTranscodeManager {
for (TranscodingJob job : retryJobs) {
// Notify the job failure if we fails to connect to the service or fail
// to retry the job.
- if (!haveTranscodingClient || !job.retry()) {
+ if (!haveTranscodingClient) {
+ // TODO(hkuang): Return correct error code to the client.
+ handleTranscodingFailed(job.getJobId(), 0 /*unused */);
+ }
+
+ try {
+ // Do not set hasRetried for retry initiated by MediaTranscodeManager.
+ job.retryInternal(false /*setHasRetried*/);
+ } catch (Exception re) {
// TODO(hkuang): Return correct error code to the client.
handleTranscodingFailed(job.getJobId(), 0 /*unused */);
}
@@ -490,6 +498,20 @@ public final class MediaTranscodeManager {
/** Uri of the destination media file. */
private @NonNull Uri mDestinationUri;
+ /**
+ * The UID of the client that the TranscodingRequest is for. Only privileged caller could
+ * set this Uid as only they could do the transcoding on behalf of the client.
+ * -1 means not available.
+ */
+ private int mClientUid = -1;
+
+ /**
+ * The Pid of the client that the TranscodingRequest is for. Only privileged caller could
+ * set this Uid as only they could do the transcoding on behalf of the client.
+ * -1 means not available.
+ */
+ private int mClientPid = -1;
+
/** Type of the transcoding. */
private @TranscodingType int mType = TRANSCODING_TYPE_UNKNOWN;
@@ -526,6 +548,8 @@ public final class MediaTranscodeManager {
private TranscodingRequest(Builder b) {
mSourceUri = b.mSourceUri;
mDestinationUri = b.mDestinationUri;
+ mClientUid = b.mClientUid;
+ mClientPid = b.mClientPid;
mPriority = b.mPriority;
mType = b.mType;
mVideoTrackFormat = b.mVideoTrackFormat;
@@ -546,6 +570,16 @@ public final class MediaTranscodeManager {
return mSourceUri;
}
+ /** Return the UID of the client that this request is for. -1 means not available. */
+ public int getClientUid() {
+ return mClientUid;
+ }
+
+ /** Return the PID of the client that this request is for. -1 means not available. */
+ public int getClientPid() {
+ return mClientPid;
+ }
+
/** Return destination uri of the transcoding. */
@NonNull
public Uri getDestinationUri() {
@@ -584,6 +618,8 @@ public final class MediaTranscodeManager {
parcel.transcodingType = mType;
parcel.sourceFilePath = mSourceUri.toString();
parcel.destinationFilePath = mDestinationUri.toString();
+ parcel.clientUid = mClientUid;
+ parcel.clientPid = mClientPid;
parcel.requestedVideoTrackFormat = convertToVideoTrackFormat(mVideoTrackFormat);
if (mTestConfig != null) {
parcel.isForTesting = true;
@@ -659,6 +695,8 @@ public final class MediaTranscodeManager {
public static final class Builder {
private @NonNull Uri mSourceUri;
private @NonNull Uri mDestinationUri;
+ private int mClientUid = -1;
+ private int mClientPid = -1;
private @TranscodingType int mType = TRANSCODING_TYPE_UNKNOWN;
private @TranscodingPriority int mPriority = PRIORITY_UNKNOWN;
private @Nullable MediaFormat mVideoTrackFormat;
@@ -702,6 +740,38 @@ public final class MediaTranscodeManager {
}
/**
+ * Specify the UID of the client that this request is for.
+ * @param uid client Uid.
+ * @return The same builder instance.
+ * @throws IllegalArgumentException if uid is invalid.
+ * TODO(hkuang): Check the permission if it is allowed.
+ */
+ @NonNull
+ public Builder setClientUid(int uid) {
+ if (uid <= 0) {
+ throw new IllegalArgumentException("Invalid Uid");
+ }
+ mClientUid = uid;
+ return this;
+ }
+
+ /**
+ * Specify the PID of the client that this request is for.
+ * @param pid client Pid.
+ * @return The same builder instance.
+ * @throws IllegalArgumentException if pid is invalid.
+ * TODO(hkuang): Check the permission if it is allowed.
+ */
+ @NonNull
+ public Builder setClientPid(int pid) {
+ if (pid <= 0) {
+ throw new IllegalArgumentException("Invalid pid");
+ }
+ mClientPid = pid;
+ return this;
+ }
+
+ /**
* Specifies the priority of the transcoding.
*
* @param priority Must be one of the {@code PRIORITY_*}
@@ -1010,9 +1080,9 @@ public final class MediaTranscodeManager {
@IntRange(from = 0, to = 100) int progress);
}
- private final ITranscodingClient mJobOwner;
- private final Executor mListenerExecutor;
- private final OnTranscodingFinishedListener mListener;
+ private final MediaTranscodeManager mManager;
+ private Executor mListenerExecutor;
+ private OnTranscodingFinishedListener mListener;
private int mJobId = -1;
// Lock for internal state.
private final Object mLock = new Object();
@@ -1028,20 +1098,26 @@ public final class MediaTranscodeManager {
private @Status int mStatus = STATUS_PENDING;
@GuardedBy("mLock")
private @Result int mResult = RESULT_NONE;
+ @GuardedBy("mLock")
+ private boolean mHasRetried = false;
+ // The original request that associated with this job.
+ private final TranscodingRequest mRequest;
private TranscodingJob(
- @NonNull ITranscodingClient jobOwner,
+ @NonNull MediaTranscodeManager manager,
+ @NonNull TranscodingRequest request,
@NonNull TranscodingJobParcel parcel,
@NonNull @CallbackExecutor Executor executor,
@NonNull OnTranscodingFinishedListener listener) {
- Objects.requireNonNull(jobOwner, "JobOwner must not be null");
- Objects.requireNonNull(parcel, "TranscodingJobParcel must not be null");
+ Objects.requireNonNull(manager, "manager must not be null");
+ Objects.requireNonNull(parcel, "parcel must not be null");
Objects.requireNonNull(executor, "listenerExecutor must not be null");
Objects.requireNonNull(listener, "listener must not be null");
- mJobOwner = jobOwner;
+ mManager = manager;
mJobId = parcel.jobId;
mListenerExecutor = executor;
mListener = listener;
+ mRequest = request;
}
/**
@@ -1085,14 +1161,61 @@ public final class MediaTranscodeManager {
/**
* Resubmit the transcoding job to the service.
+ * Note that only the job that fails or gets cancelled could be retried and each job could
+ * be retried only once. After that, Client need to enqueue a new request if they want to
+ * try again.
*
- * @return true if successfully resubmit the job to the service. False otherwise.
+ * @throws MediaTranscodingException.ServiceNotAvailableException if the service
+ * is temporarily unavailable due to internal service rebooting. Client could retry
+ * again after receiving this exception.
+ * @throws UnsupportedOperationException if the retry could not be fulfilled.
+ * @hide
*/
- public boolean retry() {
+ public void retry() throws MediaTranscodingException.ServiceNotAvailableException {
+ retryInternal(true /*setHasRetried*/);
+ }
+
+ // TODO(hkuang): Add more test for it.
+ private void retryInternal(boolean setHasRetried)
+ throws MediaTranscodingException.ServiceNotAvailableException {
synchronized (mLock) {
- // TODO(hkuang): Implement this.
+ if (mStatus == STATUS_PENDING || mStatus == STATUS_RUNNING) {
+ throw new UnsupportedOperationException(
+ "Failed to retry as job is in processing");
+ }
+
+ if (mHasRetried) {
+ throw new UnsupportedOperationException("Job has been retried already");
+ }
+
+ // Get the client interface.
+ ITranscodingClient client = mManager.getTranscodingClient();
+ if (client == null) {
+ throw new MediaTranscodingException.ServiceNotAvailableException(
+ "Service rebooting. Try again later");
+ }
+
+ synchronized (mManager.mPendingTranscodingJobs) {
+ try {
+ // Submits the request to MediaTranscoding service.
+ TranscodingJobParcel jobParcel = new TranscodingJobParcel();
+ if (!client.submitRequest(mRequest.writeToParcel(), jobParcel)) {
+ mHasRetried = true;
+ throw new UnsupportedOperationException("Failed to enqueue request");
+ }
+
+ // Replace the old job id wit the new one.
+ mJobId = jobParcel.jobId;
+ // Adds the new job back into pending jobs.
+ mManager.mPendingTranscodingJobs.put(mJobId, this);
+ } catch (RemoteException re) {
+ throw new MediaTranscodingException.ServiceNotAvailableException(
+ "Failed to resubmit request to Transcoding service");
+ }
+ mStatus = STATUS_PENDING;
+ mHasRetried = setHasRetried ? true : false;
+ }
}
- return true;
}
/**
@@ -1105,7 +1228,11 @@ public final class MediaTranscodeManager {
// Check if the job is finished already.
if (mStatus != STATUS_FINISHED) {
try {
- mJobOwner.cancelJob(mJobId);
+ ITranscodingClient client = mManager.getTranscodingClient();
+ // The client may be gone.
+ if (client != null) {
+ client.cancelJob(mJobId);
+ }
} catch (RemoteException re) {
//TODO(hkuang): Find out what to do if failing to cancel the job.
Log.e(TAG, "Failed to cancel the job due to exception: " + re);
@@ -1160,6 +1287,50 @@ public final class MediaTranscodeManager {
}
}
+ @Override
+ public String toString() {
+ String result;
+ String status;
+
+ switch (mResult) {
+ case RESULT_NONE:
+ result = "RESULT_NONE";
+ break;
+ case RESULT_SUCCESS:
+ result = "RESULT_SUCCESS";
+ break;
+ case RESULT_ERROR:
+ result = "RESULT_ERROR";
+ break;
+ case RESULT_CANCELED:
+ result = "RESULT_CANCELED";
+ break;
+ default:
+ result = String.valueOf(mResult);
+ break;
+ }
+
+ switch (mStatus) {
+ case STATUS_PENDING:
+ status = "STATUS_PENDING";
+ break;
+ case STATUS_PAUSED:
+ status = "STATUS_PAUSED";
+ break;
+ case STATUS_RUNNING:
+ status = "STATUS_RUNNING";
+ break;
+ case STATUS_FINISHED:
+ status = "STATUS_FINISHED";
+ break;
+ default:
+ status = String.valueOf(mStatus);
+ break;
+ }
+ return String.format(" Job: {id: %d, status: %s, result: %s, progress: %d}",
+ mJobId, status, result, mProgress);
+ }
+
private void updateProgress(int newProgress) {
synchronized (mLock) {
mProgress = newProgress;
@@ -1173,6 +1344,12 @@ public final class MediaTranscodeManager {
}
}
+ private ITranscodingClient getTranscodingClient() {
+ synchronized (mLock) {
+ return mTranscodingClient;
+ }
+ }
+
/**
* Enqueues a TranscodingRequest for execution.
* <p> Upon successfully accepting the request, MediaTranscodeManager will return a
@@ -1185,13 +1362,17 @@ public final class MediaTranscodeManager {
* @return A TranscodingJob for this operation.
* @throws FileNotFoundException if the source Uri or destination Uri could not be opened.
* @throws UnsupportedOperationException if the request could not be fulfilled.
+ * @throws MediaTranscodingException.ServiceNotAvailableException if the service
+ * is temporarily unavailable due to internal service rebooting. Client could retry
+ * again after receiving this exception.
*/
@NonNull
public TranscodingJob enqueueRequest(
@NonNull TranscodingRequest transcodingRequest,
@NonNull @CallbackExecutor Executor listenerExecutor,
@NonNull OnTranscodingFinishedListener listener)
- throws FileNotFoundException {
+ throws FileNotFoundException,
+ MediaTranscodingException.ServiceNotAvailableException {
Log.i(TAG, "enqueueRequest called.");
Objects.requireNonNull(transcodingRequest, "transcodingRequest must not be null");
Objects.requireNonNull(listenerExecutor, "listenerExecutor must not be null");
@@ -1200,6 +1381,8 @@ public final class MediaTranscodeManager {
// Converts the request to TranscodingRequestParcel.
TranscodingRequestParcel requestParcel = transcodingRequest.writeToParcel();
+ Log.i(TAG, "Getting transcoding request " + transcodingRequest.getSourceUri());
+
// Submits the request to MediaTranscoding service.
try {
TranscodingJobParcel jobParcel = new TranscodingJobParcel();
@@ -1208,7 +1391,14 @@ public final class MediaTranscodeManager {
synchronized (mPendingTranscodingJobs) {
synchronized (mLock) {
if (mTranscodingClient == null) {
- // TODO(hkuang): Handle the case if client is temporarily unavailable.
+ // Try to register with the service again.
+ IMediaTranscodingService service = getService(false /*retry*/);
+ mTranscodingClient = registerClient(service);
+ // If still fails, throws an exception to tell client to try later.
+ if (mTranscodingClient == null) {
+ throw new MediaTranscodingException.ServiceNotAvailableException(
+ "Service rebooting. Try again later");
+ }
}
if (!mTranscodingClient.submitRequest(requestParcel, jobParcel)) {
@@ -1218,7 +1408,8 @@ public final class MediaTranscodeManager {
// Wraps the TranscodingJobParcel into a TranscodingJob and returns it to client for
// tracking.
- TranscodingJob job = new TranscodingJob(mTranscodingClient, jobParcel,
+ TranscodingJob job = new TranscodingJob(this, transcodingRequest,
+ jobParcel,
listenerExecutor,
listener);
diff --git a/media/java/android/media/MediaTranscodingException.java b/media/java/android/media/MediaTranscodingException.java
new file mode 100644
index 000000000000..50cc9c4bae24
--- /dev/null
+++ b/media/java/android/media/MediaTranscodingException.java
@@ -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 android.media;
+
+/**
+ * Base class for MediaTranscoding exceptions
+ */
+public class MediaTranscodingException extends Exception {
+ private MediaTranscodingException(String detailMessage) {
+ super(detailMessage);
+ }
+
+ /**
+ * Exception thrown when the service is rebooting and MediaTranscodeManager is temporarily
+ * unavailable for accepting new request. It's likely that retrying will be successful.
+ */
+ public static final class ServiceNotAvailableException extends
+ MediaTranscodingException {
+ /** @hide */
+ public ServiceNotAvailableException(String detailMessage) {
+ super(detailMessage);
+ }
+ }
+}
diff --git a/media/java/android/media/RemoteController.java b/media/java/android/media/RemoteController.java
index 9e48f1e05391..35cfaca8562a 100644
--- a/media/java/android/media/RemoteController.java
+++ b/media/java/android/media/RemoteController.java
@@ -538,7 +538,7 @@ import java.util.List;
handler = new Handler(Looper.getMainLooper());
}
mSessionManager.addOnActiveSessionsChangedListener(mSessionListener, listenerComponent,
- UserHandle.myUserId(), handler);
+ handler);
mSessionListener.onActiveSessionsChanged(mSessionManager
.getActiveSessions(listenerComponent));
if (DEBUG) {
diff --git a/media/java/android/media/browse/MediaBrowser.java b/media/java/android/media/browse/MediaBrowser.java
index 011e835e02ec..b662901176e6 100644
--- a/media/java/android/media/browse/MediaBrowser.java
+++ b/media/java/android/media/browse/MediaBrowser.java
@@ -184,7 +184,7 @@ public final class MediaBrowser {
boolean bound = false;
try {
bound = mContext.bindService(intent, mServiceConnection,
- Context.BIND_AUTO_CREATE);
+ Context.BIND_AUTO_CREATE | Context.BIND_INCLUDE_CAPABILITIES);
} catch (Exception ex) {
Log.e(TAG, "Failed binding to service " + mServiceComponent);
}
diff --git a/media/java/android/media/permission/ClearCallingIdentityContext.java b/media/java/android/media/permission/ClearCallingIdentityContext.java
new file mode 100644
index 000000000000..364a2e800afe
--- /dev/null
+++ b/media/java/android/media/permission/ClearCallingIdentityContext.java
@@ -0,0 +1,58 @@
+/*
+ * 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.media.permission;
+
+import android.annotation.NonNull;
+import android.os.Binder;
+
+/**
+ * An RAII-style object, used to establish a scope in which the binder calling identity is cleared.
+ *
+ * <p>
+ * Intended usage:
+ * <pre>
+ * void caller() {
+ * try (SafeCloseable ignored = ClearCallingIdentityContext.create()) {
+ * // Within this scope the binder calling identity is cleared.
+ * ...
+ * }
+ * // Outside the scope the calling identity is restored to its prior state.
+ * </pre>
+ *
+ * @hide
+ */
+public class ClearCallingIdentityContext implements SafeCloseable {
+ private final long mRestoreKey;
+
+ /**
+ * Creates a new instance.
+ * @return A {@link SafeCloseable}, intended to be used in a try-with-resource block.
+ */
+ public static @NonNull
+ SafeCloseable create() {
+ return new ClearCallingIdentityContext();
+ }
+
+ private ClearCallingIdentityContext() {
+ mRestoreKey = Binder.clearCallingIdentity();
+ }
+
+ @Override
+ public void close() {
+ Binder.restoreCallingIdentity(mRestoreKey);
+ }
+}
diff --git a/media/java/android/media/permission/CompositeSafeCloseable.java b/media/java/android/media/permission/CompositeSafeCloseable.java
new file mode 100644
index 000000000000..08990ebd5057
--- /dev/null
+++ b/media/java/android/media/permission/CompositeSafeCloseable.java
@@ -0,0 +1,40 @@
+/*
+ * 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.media.permission;
+
+import android.annotation.NonNull;
+
+/**
+ * A composite {@link SafeCloseable}. Will close its children in reverse order.
+ *
+ * @hide
+ */
+class CompositeSafeCloseable implements SafeCloseable {
+ private final @NonNull SafeCloseable[] mChildren;
+
+ CompositeSafeCloseable(@NonNull SafeCloseable... children) {
+ mChildren = children;
+ }
+
+ @Override
+ public void close() {
+ // Close in reverse order.
+ for (int i = mChildren.length - 1; i >= 0; --i) {
+ mChildren[i].close();
+ }
+ }
+}
diff --git a/media/java/android/media/permission/IdentityContext.java b/media/java/android/media/permission/IdentityContext.java
new file mode 100644
index 000000000000..d10654fbe684
--- /dev/null
+++ b/media/java/android/media/permission/IdentityContext.java
@@ -0,0 +1,100 @@
+/*
+ * 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.media.permission;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+
+/**
+ * An RAII-style object, used to establish a scope in which a single identity is part of the
+ * context. This is used in order to avoid having to explicitly pass identity information through
+ * deep call-stacks.
+ * <p>
+ * Intended usage:
+ * <pre>
+ * void caller() {
+ * Identity originator = ...;
+ * try (SafeCloseable ignored = IdentityContext.create(originator)) {
+ * // Within this scope the context is established.
+ * callee();
+ * }
+ * // Outside the scope the context is restored to its prior state.
+ *
+ * void callee() {
+ * // Here we can access the identity without having to explicitly take it as an argument.
+ * // This is true even if this were a deeply nested call.
+ * Identity originator = IdentityContext.getNonNull();
+ * ...
+ * }
+ * </pre>
+ *
+ * @hide
+ */
+public class IdentityContext implements SafeCloseable {
+ private static ThreadLocal<Identity> sThreadLocalIdentity = new ThreadLocal<>();
+ private @Nullable Identity mPrior = get();
+
+ /**
+ * Create a scoped identity context.
+ *
+ * @param identity The identity to establish with the scope.
+ * @return A {@link SafeCloseable}, to be used in a try-with-resources block to establish a
+ * scope.
+ */
+ public static @NonNull
+ SafeCloseable create(@Nullable Identity identity) {
+ return new IdentityContext(identity);
+ }
+
+ /**
+ * Get the current identity context.
+ *
+ * @return The identity, or null if it has not been established.
+ */
+ public static @Nullable
+ Identity get() {
+ return sThreadLocalIdentity.get();
+ }
+
+ /**
+ * Get the current identity context. Throws a {@link NullPointerException} if it has not been
+ * established.
+ *
+ * @return The identity.
+ */
+ public static @NonNull
+ Identity getNonNull() {
+ Identity result = get();
+ if (result == null) {
+ throw new NullPointerException("Identity context is not set");
+ }
+ return result;
+ }
+
+ private IdentityContext(@Nullable Identity identity) {
+ set(identity);
+ }
+
+ @Override
+ public void close() {
+ set(mPrior);
+ }
+
+ private static void set(@Nullable Identity identity) {
+ sThreadLocalIdentity.set(identity);
+ }
+}
diff --git a/media/java/android/media/permission/PermissionUtil.java b/media/java/android/media/permission/PermissionUtil.java
new file mode 100644
index 000000000000..458f11243f68
--- /dev/null
+++ b/media/java/android/media/permission/PermissionUtil.java
@@ -0,0 +1,250 @@
+/*
+ * 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.media.permission;
+
+import android.annotation.NonNull;
+import android.app.AppOpsManager;
+import android.content.Context;
+import android.content.PermissionChecker;
+import android.content.pm.PackageManager;
+import android.os.Binder;
+
+import java.util.Objects;
+
+/**
+ * This module provides some utility methods for facilitating our permission enforcement patterns.
+ * <p>
+ * <h1>Intended usage:</h1>
+ * Close to the client-facing edge of the server, first authenticate the client, using {@link
+ * #establishIdentityDirect(Identity)}, or {@link #establishIdentityIndirect(Context, String,
+ * Identity, Identity)}, depending on whether the client is trying to authenticate as the
+ * originator or a middleman. Those methods will establish a scope with the originator in the
+ * {@link android.media.permission.IdentityContext} and a cleared binder calling identity.
+ * Typically there would be two distinct API methods for the two different options, and typically
+ * those API methods would be used to establish a client session which is associated with the
+ * originator for the lifetime of the session.
+ * <p>
+ * When performing an operation that requires permissions, use {@link
+ * #checkPermissionForPreflight(Context, Identity, String)} or {@link
+ * #checkPermissionForDataDelivery(Context, Identity, String, String)} on the originator
+ * identity. Note that this won't typically be the identity pulled from the {@link
+ * android.media.permission.IdentityContext}, since we are working with a session-based approach,
+ * the originator identity will be established once upon creation of a session, and then all
+ * interactions with this session will using the identity attached to the session. This also covers
+ * performing checks prior to invoking client callbacks for data delivery.
+ *
+ * @hide
+ */
+public class PermissionUtil {
+ /**
+ * Authenticate an originator, where the binder call is coming from a middleman.
+ *
+ * The middleman is expected to hold a special permission to act as such, or else a
+ * {@link SecurityException} will be thrown. If the call succeeds:
+ * <ul>
+ * <li>The passed middlemanIdentity argument will have its uid/pid fields overridden with
+ * those provided by binder.
+ * <li>An {@link SafeCloseable} is returned, used to established a scope in which the
+ * originator identity is available via {@link android.media.permission.IdentityContext}
+ * and in which the binder
+ * calling ID is cleared.
+ * </ul>
+ * Example usage:
+ * <pre>
+ * try (SafeCloseable ignored = PermissionUtil.establishIdentityIndirect(...)) {
+ * // Within this scope we have the identity context established, and the binder calling
+ * // identity cleared.
+ * ...
+ * Identity originator = IdentityContext.getNonNull();
+ * ...
+ * }
+ * // outside the scope, everything is back to the prior state.
+ * </pre>
+ * <p>
+ * <b>Important note:</b> The binder calling ID will be used to securely establish the identity
+ * of the middleman. However, if the middleman is on the same process as the server,
+ * the middleman must remember to clear the binder calling identity, or else the binder calling
+ * ID will reflect the process calling into the middleman, not the middleman process itself. If
+ * the middleman itself is using this API, this is typically not an issue, since this method
+ * will take care of that.
+ *
+ * @param context A {@link Context}, used for permission checks.
+ * @param middlemanPermission The permission that will be checked in order to authorize the
+ * middleman to act as such (i.e. be trusted to convey the
+ * originator
+ * identity reliably).
+ * @param middlemanIdentity The identity of the middleman.
+ * @param originatorIdentity The identity of the originator.
+ * @return A {@link SafeCloseable}, used to establish a scope, as mentioned above.
+ */
+ public static @NonNull
+ SafeCloseable establishIdentityIndirect(
+ @NonNull Context context,
+ @NonNull String middlemanPermission,
+ @NonNull Identity middlemanIdentity,
+ @NonNull Identity originatorIdentity) {
+ Objects.requireNonNull(context);
+ Objects.requireNonNull(middlemanPermission);
+ Objects.requireNonNull(middlemanIdentity);
+ Objects.requireNonNull(originatorIdentity);
+
+ // Override uid/pid with the secure values provided by binder.
+ middlemanIdentity.pid = Binder.getCallingPid();
+ middlemanIdentity.uid = Binder.getCallingUid();
+
+ // Authorize middleman to delegate identity.
+ context.enforcePermission(middlemanPermission, middlemanIdentity.pid,
+ middlemanIdentity.uid,
+ String.format("Middleman must have the %s permision.", middlemanPermission));
+ return new CompositeSafeCloseable(IdentityContext.create(originatorIdentity),
+ ClearCallingIdentityContext.create());
+ }
+
+ /**
+ * Authenticate an originator, where the binder call is coming directly from the originator.
+ *
+ * If the call succeeds:
+ * <ul>
+ * <li>The passed originatorIdentity argument will have its uid/pid fields overridden with
+ * those provided by binder.
+ * <li>A {@link SafeCloseable} is returned, used to established a scope in which the
+ * originator identity is available via {@link IdentityContext} and in which the binder
+ * calling ID is cleared.
+ * </ul>
+ * Example usage:
+ * <pre>
+ * try (AutoClosable ignored = PermissionUtil.establishIdentityDirect(...)) {
+ * // Within this scope we have the identity context established, and the binder calling
+ * // identity cleared.
+ * ...
+ * Identity originator = IdentityContext.getNonNull();
+ * ...
+ * }
+ * // outside the scope, everything is back to the prior state.
+ * </pre>
+ * <p>
+ * <b>Important note:</b> The binder calling ID will be used to securely establish the identity
+ * of the client. However, if the client is on the same process as the server, and is itself a
+ * binder server, it must remember to clear the binder calling identity, or else the binder
+ * calling ID will reflect the process calling into the client, not the client process itself.
+ * If the client itself is using this API, this is typically not an issue, since this method
+ * will take care of that.
+ *
+ * @param originatorIdentity The identity of the originator.
+ * @return A {@link SafeCloseable}, used to establish a scope, as mentioned above.
+ */
+ public static @NonNull
+ SafeCloseable establishIdentityDirect(@NonNull Identity originatorIdentity) {
+ Objects.requireNonNull(originatorIdentity);
+
+ originatorIdentity.uid = Binder.getCallingUid();
+ originatorIdentity.pid = Binder.getCallingPid();
+ return new CompositeSafeCloseable(
+ IdentityContext.create(originatorIdentity),
+ ClearCallingIdentityContext.create());
+ }
+
+ /**
+ * Checks whether the given identity has the given permission to receive data.
+ *
+ * @param context A {@link Context}, used for permission checks.
+ * @param identity The identity to check.
+ * @param permission The identifier of the permission we want to check.
+ * @param reason The reason why we're requesting the permission, for auditing purposes.
+ * @return The permission check result which is either
+ * {@link PermissionChecker#PERMISSION_GRANTED}
+ * or {@link PermissionChecker#PERMISSION_SOFT_DENIED} or
+ * {@link PermissionChecker#PERMISSION_HARD_DENIED}.
+ */
+ public static int checkPermissionForDataDelivery(@NonNull Context context,
+ @NonNull Identity identity,
+ @NonNull String permission,
+ @NonNull String reason) {
+ return PermissionChecker.checkPermissionForDataDelivery(context, permission,
+ identity.pid, identity.uid, identity.packageName, identity.attributionTag,
+ reason);
+ }
+
+ /**
+ * Checks whether the given identity has the given permission to receive data.
+ *
+ * This variant ignores the proc-state for the sake of the check, i.e. overrides any
+ * restrictions that apply specifically to apps running in the background.
+ *
+ * TODO(ytai): This is a temporary hack until we have permissions that are specifically intended
+ * for background microphone access.
+ *
+ * @param context A {@link Context}, used for permission checks.
+ * @param identity The identity to check.
+ * @param permission The identifier of the permission we want to check.
+ * @param reason The reason why we're requesting the permission, for auditing purposes.
+ * @return The permission check result which is either
+ * {@link PermissionChecker#PERMISSION_GRANTED}
+ * or {@link PermissionChecker#PERMISSION_SOFT_DENIED} or
+ * {@link PermissionChecker#PERMISSION_HARD_DENIED}.
+ */
+ public static int checkPermissionForDataDeliveryIgnoreProcState(@NonNull Context context,
+ @NonNull Identity identity,
+ @NonNull String permission,
+ @NonNull String reason) {
+ if (context.checkPermission(permission, identity.pid, identity.uid)
+ != PackageManager.PERMISSION_GRANTED) {
+ return PermissionChecker.PERMISSION_HARD_DENIED;
+ }
+
+ String appOpOfPermission = AppOpsManager.permissionToOp(permission);
+ if (appOpOfPermission == null) {
+ // not platform defined
+ return PermissionChecker.PERMISSION_GRANTED;
+ }
+
+ String packageName = identity.packageName;
+ if (packageName == null) {
+ String[] packageNames = context.getPackageManager().getPackagesForUid(identity.uid);
+ if (packageNames != null && packageNames.length > 0) {
+ packageName = packageNames[0];
+ }
+ }
+
+ final AppOpsManager appOpsManager = context.getSystemService(AppOpsManager.class);
+
+ int appOpMode = appOpsManager.unsafeCheckOpRawNoThrow(appOpOfPermission, identity.uid,
+ packageName);
+ if (appOpMode == AppOpsManager.MODE_ALLOWED) {
+ return PermissionChecker.PERMISSION_GRANTED;
+ }
+ return PermissionChecker.PERMISSION_SOFT_DENIED;
+ }
+
+ /**
+ * Checks whether the given identity has the given permission.
+ *
+ * @param context A {@link Context}, used for permission checks.
+ * @param identity The identity to check.
+ * @param permission The identifier of the permission we want to check.
+ * @return The permission check result which is either
+ * {@link PermissionChecker#PERMISSION_GRANTED}
+ * or {@link PermissionChecker#PERMISSION_SOFT_DENIED} or
+ * {@link PermissionChecker#PERMISSION_HARD_DENIED}.
+ */
+ public static int checkPermissionForPreflight(@NonNull Context context,
+ @NonNull Identity identity,
+ @NonNull String permission) {
+ return PermissionChecker.checkPermissionForPreflight(context, permission,
+ identity.pid, identity.uid, identity.packageName);
+ }
+}
diff --git a/tests/utils/testutils/java/android/view/test/InsetsModeSession.java b/media/java/android/media/permission/SafeCloseable.java
index e05fdce0ca0c..8ec15b18bd37 100644
--- a/tests/utils/testutils/java/android/view/test/InsetsModeSession.java
+++ b/media/java/android/media/permission/SafeCloseable.java
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2019 The Android Open Source Project
+ * 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.
@@ -14,24 +14,14 @@
* limitations under the License.
*/
-package android.view.test;
-
-import android.view.ViewRootImpl;
+package android.media.permission;
/**
- * Session to set insets mode for {@link ViewRootImpl#sNewInsetsMode}.
+ * An {@link AutoCloseable} that doesn't throw on {@link #close()}.
+ *
+ * @hide
*/
-public class InsetsModeSession implements AutoCloseable {
-
- private int mOldMode;
-
- public InsetsModeSession(int flag) {
- mOldMode = ViewRootImpl.sNewInsetsMode;
- ViewRootImpl.sNewInsetsMode = flag;
- }
-
+public interface SafeCloseable extends AutoCloseable {
@Override
- public void close() {
- ViewRootImpl.sNewInsetsMode = mOldMode;
- }
+ void close();
}
diff --git a/media/java/android/media/session/MediaSessionManager.java b/media/java/android/media/session/MediaSessionManager.java
index 9494295e4bd9..3acb9516054e 100644
--- a/media/java/android/media/session/MediaSessionManager.java
+++ b/media/java/android/media/session/MediaSessionManager.java
@@ -20,6 +20,7 @@ import android.annotation.CallbackExecutor;
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.annotation.RequiresPermission;
+import android.annotation.SuppressLint;
import android.annotation.SystemApi;
import android.annotation.SystemService;
import android.compat.annotation.UnsupportedAppUsage;
@@ -262,18 +263,14 @@ public final class MediaSessionManager {
}
/**
- * Add a listener to be notified when the list of active sessions
- * changes.This requires the
- * android.Manifest.permission.MEDIA_CONTENT_CONTROL permission be held by
- * the calling app. You may also retrieve this list if your app is an
- * enabled notification listener using the
- * {@link NotificationListenerService} APIs, in which case you must pass the
- * {@link ComponentName} of your enabled listener. Updates will be posted to
- * the thread that registered the listener.
+ * Add a listener to be notified when the list of active sessions changes. This requires the
+ * {@link android.Manifest.permission#MEDIA_CONTENT_CONTROL} permission be held by the calling
+ * app. You may also retrieve this list if your app is an enabled notification listener using
+ * the {@link NotificationListenerService} APIs, in which case you must pass the
+ * {@link ComponentName} of your enabled listener.
*
* @param sessionListener The listener to add.
- * @param notificationListener The enabled notification listener component.
- * May be null.
+ * @param notificationListener The enabled notification listener component. May be null.
*/
public void addOnActiveSessionsChangedListener(
@NonNull OnActiveSessionsChangedListener sessionListener,
@@ -282,18 +279,15 @@ public final class MediaSessionManager {
}
/**
- * Add a listener to be notified when the list of active sessions
- * changes.This requires the
- * android.Manifest.permission.MEDIA_CONTENT_CONTROL permission be held by
- * the calling app. You may also retrieve this list if your app is an
- * enabled notification listener using the
- * {@link NotificationListenerService} APIs, in which case you must pass the
- * {@link ComponentName} of your enabled listener. Updates will be posted to
- * the handler specified or to the caller's thread if the handler is null.
+ * Add a listener to be notified when the list of active sessions changes. This requires the
+ * {@link android.Manifest.permission#MEDIA_CONTENT_CONTROL} permission be held by the calling
+ * app. You may also retrieve this list if your app is an enabled notification listener using
+ * the {@link NotificationListenerService} APIs, in which case you must pass the
+ * {@link ComponentName} of your enabled listener. Updates will be posted to the handler
+ * specified or to the caller's thread if the handler is null.
*
* @param sessionListener The listener to add.
- * @param notificationListener The enabled notification listener component.
- * May be null.
+ * @param notificationListener The enabled notification listener component. May be null.
* @param handler The handler to post events to.
*/
public void addOnActiveSessionsChangedListener(
@@ -304,21 +298,24 @@ public final class MediaSessionManager {
}
/**
- * Add a listener to be notified when the list of active sessions
- * changes.This requires the
- * android.Manifest.permission.MEDIA_CONTENT_CONTROL permission be held by
- * the calling app. You may also retrieve this list if your app is an
- * enabled notification listener using the
- * {@link NotificationListenerService} APIs, in which case you must pass the
- * {@link ComponentName} of your enabled listener.
+ * Add a listener to be notified when the list of active sessions changes for the given user.
+ * The calling app must have the {@link android.Manifest.permission#INTERACT_ACROSS_USERS_FULL}
+ * permission if it wants to call this method for a user that is not running the app.
+ * <p>
+ * This requires the {@link android.Manifest.permission#MEDIA_CONTENT_CONTROL} permission be
+ * held by the calling app. You may also retrieve this list if your app is an enabled
+ * notification listener using the {@link NotificationListenerService} APIs, in which case you
+ * must pass the {@link ComponentName} of your enabled listener. Updates will be posted to the
+ * handler specified or to the caller's thread if the handler is null.
*
* @param sessionListener The listener to add.
- * @param notificationListener The enabled notification listener component.
- * May be null.
+ * @param notificationListener The enabled notification listener component. May be null.
* @param userId The userId to listen for changes on.
* @param handler The handler to post updates on.
* @hide
*/
+ @SuppressLint({"ExecutorRegistration", "SamShouldBeLast"})
+ @SystemApi(client = SystemApi.Client.MODULE_LIBRARIES)
public void addOnActiveSessionsChangedListener(
@NonNull OnActiveSessionsChangedListener sessionListener,
@Nullable ComponentName notificationListener, int userId, @Nullable Handler handler) {
diff --git a/media/java/android/media/soundtrigger/SoundTriggerDetector.java b/media/java/android/media/soundtrigger/SoundTriggerDetector.java
index 08953392ca18..16a517b72119 100644
--- a/media/java/android/media/soundtrigger/SoundTriggerDetector.java
+++ b/media/java/android/media/soundtrigger/SoundTriggerDetector.java
@@ -35,7 +35,7 @@ import android.os.ParcelUuid;
import android.os.RemoteException;
import android.util.Slog;
-import com.android.internal.app.ISoundTriggerService;
+import com.android.internal.app.ISoundTriggerSession;
import java.io.PrintWriter;
import java.lang.annotation.Retention;
@@ -64,7 +64,7 @@ public final class SoundTriggerDetector {
private final Object mLock = new Object();
- private final ISoundTriggerService mSoundTriggerService;
+ private final ISoundTriggerSession mSoundTriggerSession;
private final UUID mSoundModelId;
private final Callback mCallback;
private final Handler mHandler;
@@ -266,9 +266,9 @@ public final class SoundTriggerDetector {
* This class should be constructed by the {@link SoundTriggerManager}.
* @hide
*/
- SoundTriggerDetector(ISoundTriggerService soundTriggerService, UUID soundModelId,
+ SoundTriggerDetector(ISoundTriggerSession soundTriggerSession, UUID soundModelId,
@NonNull Callback callback, @Nullable Handler handler) {
- mSoundTriggerService = soundTriggerService;
+ mSoundTriggerSession = soundTriggerSession;
mSoundModelId = soundModelId;
mCallback = callback;
if (handler == null) {
@@ -305,7 +305,7 @@ public final class SoundTriggerDetector {
int status;
try {
- status = mSoundTriggerService.startRecognition(new ParcelUuid(mSoundModelId),
+ status = mSoundTriggerSession.startRecognition(new ParcelUuid(mSoundModelId),
mRecognitionCallback, new RecognitionConfig(captureTriggerAudio,
allowMultipleTriggers, null, null, audioCapabilities));
} catch (RemoteException e) {
@@ -321,7 +321,7 @@ public final class SoundTriggerDetector {
public boolean stopRecognition() {
int status = STATUS_OK;
try {
- status = mSoundTriggerService.stopRecognition(new ParcelUuid(mSoundModelId),
+ status = mSoundTriggerSession.stopRecognition(new ParcelUuid(mSoundModelId),
mRecognitionCallback);
} catch (RemoteException e) {
return false;
diff --git a/media/java/android/media/soundtrigger/SoundTriggerManager.java b/media/java/android/media/soundtrigger/SoundTriggerManager.java
index 7d51b104a47d..0ff8d9e96220 100644
--- a/media/java/android/media/soundtrigger/SoundTriggerManager.java
+++ b/media/java/android/media/soundtrigger/SoundTriggerManager.java
@@ -23,6 +23,7 @@ import android.annotation.Nullable;
import android.annotation.RequiresPermission;
import android.annotation.SystemApi;
import android.annotation.SystemService;
+import android.app.ActivityThread;
import android.compat.annotation.UnsupportedAppUsage;
import android.content.ComponentName;
import android.content.Context;
@@ -33,6 +34,10 @@ import android.hardware.soundtrigger.SoundTrigger.KeyphraseSoundModel;
import android.hardware.soundtrigger.SoundTrigger.ModelParamRange;
import android.hardware.soundtrigger.SoundTrigger.RecognitionConfig;
import android.hardware.soundtrigger.SoundTrigger.SoundModel;
+import android.media.permission.ClearCallingIdentityContext;
+import android.media.permission.Identity;
+import android.media.permission.SafeCloseable;
+import android.os.Binder;
import android.os.Bundle;
import android.os.Handler;
import android.os.ParcelUuid;
@@ -41,6 +46,7 @@ import android.provider.Settings;
import android.util.Slog;
import com.android.internal.app.ISoundTriggerService;
+import com.android.internal.app.ISoundTriggerSession;
import com.android.internal.util.Preconditions;
import java.util.HashMap;
@@ -61,7 +67,7 @@ public final class SoundTriggerManager {
private static final String TAG = "SoundTriggerManager";
private final Context mContext;
- private final ISoundTriggerService mSoundTriggerService;
+ private final ISoundTriggerSession mSoundTriggerSession;
// Stores a mapping from the sound model UUID to the SoundTriggerInstance created by
// the createSoundTriggerDetector() call.
@@ -74,7 +80,20 @@ public final class SoundTriggerManager {
if (DBG) {
Slog.i(TAG, "SoundTriggerManager created.");
}
- mSoundTriggerService = soundTriggerService;
+ try {
+ // This assumes that whoever is calling this ctor is the originator of the operations,
+ // as opposed to a service acting on behalf of a separate identity.
+ // Services acting on behalf of some other identity should not be using this class at
+ // all, but rather directly connect to the server and attach with explicit credentials.
+ Identity originatorIdentity = new Identity();
+ originatorIdentity.packageName = ActivityThread.currentOpPackageName();
+
+ try (SafeCloseable ignored = ClearCallingIdentityContext.create()) {
+ mSoundTriggerSession = soundTriggerService.attachAsOriginator(originatorIdentity);
+ }
+ } catch (RemoteException e) {
+ throw e.rethrowAsRuntimeException();
+ }
mContext = context;
mReceiverInstanceMap = new HashMap<UUID, SoundTriggerDetector>();
}
@@ -85,7 +104,7 @@ public final class SoundTriggerManager {
@RequiresPermission(android.Manifest.permission.MANAGE_SOUND_TRIGGER)
public void updateModel(Model model) {
try {
- mSoundTriggerService.updateSoundModel(model.getGenericSoundModel());
+ mSoundTriggerSession.updateSoundModel(model.getGenericSoundModel());
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
@@ -102,7 +121,7 @@ public final class SoundTriggerManager {
public Model getModel(UUID soundModelId) {
try {
GenericSoundModel model =
- mSoundTriggerService.getSoundModel(new ParcelUuid(soundModelId));
+ mSoundTriggerSession.getSoundModel(new ParcelUuid(soundModelId));
if (model == null) {
return null;
}
@@ -119,7 +138,7 @@ public final class SoundTriggerManager {
@RequiresPermission(android.Manifest.permission.MANAGE_SOUND_TRIGGER)
public void deleteModel(UUID soundModelId) {
try {
- mSoundTriggerService.deleteSoundModel(new ParcelUuid(soundModelId));
+ mSoundTriggerSession.deleteSoundModel(new ParcelUuid(soundModelId));
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
@@ -149,7 +168,7 @@ public final class SoundTriggerManager {
if (oldInstance != null) {
// Shutdown old instance.
}
- SoundTriggerDetector newInstance = new SoundTriggerDetector(mSoundTriggerService,
+ SoundTriggerDetector newInstance = new SoundTriggerDetector(mSoundTriggerSession,
soundModelId, callback, handler);
mReceiverInstanceMap.put(soundModelId, newInstance);
return newInstance;
@@ -309,10 +328,10 @@ public final class SoundTriggerManager {
try {
switch (soundModel.getType()) {
case SoundModel.TYPE_GENERIC_SOUND:
- return mSoundTriggerService.loadGenericSoundModel(
+ return mSoundTriggerSession.loadGenericSoundModel(
(GenericSoundModel) soundModel);
case SoundModel.TYPE_KEYPHRASE:
- return mSoundTriggerService.loadKeyphraseSoundModel(
+ return mSoundTriggerSession.loadKeyphraseSoundModel(
(KeyphraseSoundModel) soundModel);
default:
Slog.e(TAG, "Unkown model type");
@@ -351,7 +370,7 @@ public final class SoundTriggerManager {
Preconditions.checkNotNull(config);
try {
- return mSoundTriggerService.startRecognitionForService(new ParcelUuid(soundModelId),
+ return mSoundTriggerSession.startRecognitionForService(new ParcelUuid(soundModelId),
params, detectionService, config);
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
@@ -369,7 +388,7 @@ public final class SoundTriggerManager {
return STATUS_ERROR;
}
try {
- return mSoundTriggerService.stopRecognitionForService(new ParcelUuid(soundModelId));
+ return mSoundTriggerSession.stopRecognitionForService(new ParcelUuid(soundModelId));
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
@@ -386,7 +405,7 @@ public final class SoundTriggerManager {
return STATUS_ERROR;
}
try {
- return mSoundTriggerService.unloadSoundModel(
+ return mSoundTriggerSession.unloadSoundModel(
new ParcelUuid(soundModelId));
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
@@ -404,7 +423,7 @@ public final class SoundTriggerManager {
return false;
}
try {
- return mSoundTriggerService.isRecognitionActive(
+ return mSoundTriggerSession.isRecognitionActive(
new ParcelUuid(soundModelId));
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
@@ -439,7 +458,7 @@ public final class SoundTriggerManager {
return STATUS_ERROR;
}
try {
- return mSoundTriggerService.getModelState(new ParcelUuid(soundModelId));
+ return mSoundTriggerSession.getModelState(new ParcelUuid(soundModelId));
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
@@ -455,7 +474,7 @@ public final class SoundTriggerManager {
public SoundTrigger.ModuleProperties getModuleProperties() {
try {
- return mSoundTriggerService.getModuleProperties();
+ return mSoundTriggerSession.getModuleProperties();
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
@@ -481,7 +500,7 @@ public final class SoundTriggerManager {
public int setParameter(@Nullable UUID soundModelId,
@ModelParams int modelParam, int value) {
try {
- return mSoundTriggerService.setParameter(new ParcelUuid(soundModelId), modelParam,
+ return mSoundTriggerSession.setParameter(new ParcelUuid(soundModelId), modelParam,
value);
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
@@ -504,7 +523,7 @@ public final class SoundTriggerManager {
public int getParameter(@NonNull UUID soundModelId,
@ModelParams int modelParam) {
try {
- return mSoundTriggerService.getParameter(new ParcelUuid(soundModelId), modelParam);
+ return mSoundTriggerSession.getParameter(new ParcelUuid(soundModelId), modelParam);
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
@@ -524,7 +543,7 @@ public final class SoundTriggerManager {
public ModelParamRange queryParameter(@Nullable UUID soundModelId,
@ModelParams int modelParam) {
try {
- return mSoundTriggerService.queryParameter(new ParcelUuid(soundModelId), modelParam);
+ return mSoundTriggerSession.queryParameter(new ParcelUuid(soundModelId), modelParam);
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
diff --git a/media/java/android/media/soundtrigger_middleware/ISoundTriggerMiddlewareService.aidl b/media/java/android/media/soundtrigger_middleware/ISoundTriggerMiddlewareService.aidl
deleted file mode 100644
index 06c39071cdf5..000000000000
--- a/media/java/android/media/soundtrigger_middleware/ISoundTriggerMiddlewareService.aidl
+++ /dev/null
@@ -1,42 +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 android.media.soundtrigger_middleware;
-
-import android.media.soundtrigger_middleware.ISoundTriggerModule;
-import android.media.soundtrigger_middleware.ISoundTriggerCallback;
-import android.media.soundtrigger_middleware.SoundTriggerModuleDescriptor;
-
-/**
- * Main entry point into this module.
- *
- * Allows the client to enumerate the available soundtrigger devices and their capabilities, then
- * attach to either one of them in order to use it.
- *
- * {@hide}
- */
-interface ISoundTriggerMiddlewareService {
- /**
- * Query the available modules and their capabilities.
- */
- SoundTriggerModuleDescriptor[] listModules();
-
- /**
- * Attach to one of the available modules.
- * listModules() must be called prior to calling this method and the provided handle must be
- * one of the handles from the returned list.
- */
- ISoundTriggerModule attach(int handle, ISoundTriggerCallback callback);
-} \ No newline at end of file
diff --git a/media/jni/android_media_tv_Tuner.cpp b/media/jni/android_media_tv_Tuner.cpp
index 5db672993df1..5daf8b0f88f8 100644
--- a/media/jni/android_media_tv_Tuner.cpp
+++ b/media/jni/android_media_tv_Tuner.cpp
@@ -171,6 +171,12 @@ static int IP_V6_LENGTH = 16;
void DestroyCallback(const C2Buffer * /* buf */, void *arg) {
android::sp<android::MediaEvent> event = (android::MediaEvent *)arg;
+ if (event->mLinearBlockObj != NULL) {
+ JNIEnv *env = android::AndroidRuntime::getJNIEnv();
+ env->DeleteWeakGlobalRef(event->mLinearBlockObj);
+ event->mLinearBlockObj = NULL;
+ }
+
event->mAvHandleRefCnt--;
event->finalize();
}
@@ -182,6 +188,12 @@ LnbCallback::LnbCallback(jobject lnbObj, LnbId id) : mId(id) {
mLnb = env->NewWeakGlobalRef(lnbObj);
}
+LnbCallback::~LnbCallback() {
+ JNIEnv *env = AndroidRuntime::getJNIEnv();
+ env->DeleteWeakGlobalRef(mLnb);
+ mLnb = NULL;
+}
+
Return<void> LnbCallback::onEvent(LnbEventType lnbEventType) {
ALOGD("LnbCallback::onEvent, type=%d", lnbEventType);
JNIEnv *env = AndroidRuntime::getJNIEnv();
@@ -305,6 +317,7 @@ MediaEvent::MediaEvent(sp<IFilter> iFilter, hidl_handle avHandle,
JNIEnv *env = AndroidRuntime::getJNIEnv();
mMediaEventObj = env->NewWeakGlobalRef(obj);
mAvHandle = native_handle_clone(avHandle.getNativeHandle());
+ mLinearBlockObj = NULL;
}
MediaEvent::~MediaEvent() {
@@ -367,7 +380,7 @@ jobject MediaEvent::getLinearBlock() {
true);
mLinearBlockObj = env->NewWeakGlobalRef(linearBlock);
mAvHandleRefCnt++;
- return mLinearBlockObj;
+ return linearBlock;
} else {
native_handle_close(const_cast<native_handle_t*>(
reinterpret_cast<const native_handle_t*>(mIonHandle)));
diff --git a/media/jni/android_media_tv_Tuner.h b/media/jni/android_media_tv_Tuner.h
index fd2995917475..c4deeaf887bb 100644
--- a/media/jni/android_media_tv_Tuner.h
+++ b/media/jni/android_media_tv_Tuner.h
@@ -73,6 +73,7 @@ namespace android {
struct LnbCallback : public ILnbCallback {
LnbCallback(jweak tunerObj, LnbId id);
+ ~LnbCallback();
virtual Return<void> onEvent(LnbEventType lnbEventType);
virtual Return<void> onDiseqcMessage(const hidl_vec<uint8_t>& diseqcMessage);
jweak mLnb;
diff --git a/media/tests/MediaTranscodingTest/src/com/android/mediatranscodingtest/MediaTranscodeManagerDiedTest.java b/media/tests/MediaTranscodingTest/src/com/android/mediatranscodingtest/MediaTranscodeManagerDiedTest.java
index f00c14d79bec..6b5abccccb8b 100644
--- a/media/tests/MediaTranscodingTest/src/com/android/mediatranscodingtest/MediaTranscodeManagerDiedTest.java
+++ b/media/tests/MediaTranscodingTest/src/com/android/mediatranscodingtest/MediaTranscodeManagerDiedTest.java
@@ -22,6 +22,7 @@ import android.media.MediaFormat;
import android.media.MediaTranscodeManager;
import android.media.MediaTranscodeManager.TranscodingJob;
import android.media.MediaTranscodeManager.TranscodingRequest;
+import android.media.MediaTranscodingException;
import android.net.Uri;
import android.os.Bundle;
import android.os.FileUtils;
@@ -250,6 +251,27 @@ public class MediaTranscodeManagerDiedTest
TRANSCODE_TIMEOUT_SECONDS, TimeUnit.SECONDS);
assertTrue("Invalid job status", job.getStatus() == TranscodingJob.STATUS_FINISHED);
assertTrue("Invalid job result", job.getResult()== TranscodingJob.RESULT_ERROR);
+
+
+ boolean retryJob = false;
+ // Wait till service is available again.
+ Log.d(TAG, "Retry the failed transcoding job");
+ while (!retryJob) {
+ try {
+ job.retry();
+ // Break out when job retry succeeds.
+ break;
+ } catch (MediaTranscodingException.ServiceNotAvailableException ex) {
+ // Sleep for 10 milliseconds to wait.
+ Thread.sleep(10);
+ }
+ }
+
+ finishedOnTime = transcodeCompleteSemaphore.tryAcquire(
+ TRANSCODE_TIMEOUT_SECONDS, TimeUnit.SECONDS);
+ // Check the make sure job is successfully finished after retry.
+ assertTrue("Invalid job status", job.getStatus() == TranscodingJob.STATUS_FINISHED);
+ assertTrue("Invalid job result", job.getResult() == TranscodingJob.RESULT_SUCCESS);
}
}
diff --git a/media/tests/MediaTranscodingTest/src/com/android/mediatranscodingtest/MediaTranscodeManagerTest.java b/media/tests/MediaTranscodingTest/src/com/android/mediatranscodingtest/MediaTranscodeManagerTest.java
index 33d6d64c7f37..21ed840c7313 100644
--- a/media/tests/MediaTranscodingTest/src/com/android/mediatranscodingtest/MediaTranscodeManagerTest.java
+++ b/media/tests/MediaTranscodingTest/src/com/android/mediatranscodingtest/MediaTranscodeManagerTest.java
@@ -203,6 +203,42 @@ public class MediaTranscodeManagerTest
}
/**
+ * Verify that setting invalid pid will throw exception.
+ */
+ @Test
+ public void testCreateTranscodingWithInvalidClientPid() throws Exception {
+ assertThrows(IllegalArgumentException.class, () -> {
+ TranscodingRequest request =
+ new TranscodingRequest.Builder()
+ .setSourceUri(mSourceHEVCVideoUri)
+ .setDestinationUri(mDestinationUri)
+ .setType(MediaTranscodeManager.TRANSCODING_TYPE_VIDEO)
+ .setPriority(MediaTranscodeManager.PRIORITY_REALTIME)
+ .setClientPid(-1)
+ .setVideoTrackFormat(createMediaFormat())
+ .build();
+ });
+ }
+
+ /**
+ * Verify that setting invalid uid will throw exception.
+ */
+ @Test
+ public void testCreateTranscodingWithInvalidClientUid() throws Exception {
+ assertThrows(IllegalArgumentException.class, () -> {
+ TranscodingRequest request =
+ new TranscodingRequest.Builder()
+ .setSourceUri(mSourceHEVCVideoUri)
+ .setDestinationUri(mDestinationUri)
+ .setType(MediaTranscodeManager.TRANSCODING_TYPE_VIDEO)
+ .setPriority(MediaTranscodeManager.PRIORITY_REALTIME)
+ .setClientUid(-1)
+ .setVideoTrackFormat(createMediaFormat())
+ .build();
+ });
+ }
+
+ /**
* Verify that setting null source uri will throw exception.
*/
@Test
@@ -425,16 +461,24 @@ public class MediaTranscodeManagerTest
MediaFormat videoTrackFormat = resolver.resolveVideoFormat();
assertNotNull(videoTrackFormat);
+ int pid = android.os.Process.myPid();
+ int uid = android.os.Process.myUid();
+
TranscodingRequest request =
new TranscodingRequest.Builder()
.setSourceUri(mSourceHEVCVideoUri)
.setDestinationUri(destinationUri)
.setType(MediaTranscodeManager.TRANSCODING_TYPE_VIDEO)
+ .setClientPid(pid)
+ .setClientUid(uid)
.setPriority(MediaTranscodeManager.PRIORITY_REALTIME)
.setVideoTrackFormat(videoTrackFormat)
.build();
Executor listenerExecutor = Executors.newSingleThreadExecutor();
+ assertEquals(pid, request.getClientPid());
+ assertEquals(uid, request.getClientUid());
+
Log.i(TAG, "transcoding to " + videoTrackFormat);
TranscodingJob job = mMediaTranscodeManager.enqueueRequest(request, listenerExecutor,
diff --git a/media/tests/MediaTranscodingTest/src/com/android/mediatranscodingtest/MediaTranscodingBenchmark.java b/media/tests/MediaTranscodingTest/src/com/android/mediatranscodingtest/MediaTranscodingBenchmark.java
index 73e1d98cb2e2..10262d98d617 100644
--- a/media/tests/MediaTranscodingTest/src/com/android/mediatranscodingtest/MediaTranscodingBenchmark.java
+++ b/media/tests/MediaTranscodingTest/src/com/android/mediatranscodingtest/MediaTranscodingBenchmark.java
@@ -22,6 +22,7 @@ import android.media.MediaFormat;
import android.media.MediaTranscodeManager;
import android.media.MediaTranscodeManager.TranscodingJob;
import android.media.MediaTranscodeManager.TranscodingRequest;
+import android.media.MediaTranscodingException;
import android.net.Uri;
import android.test.ActivityInstrumentationTestCase2;
import android.util.Log;
@@ -101,7 +102,8 @@ public class MediaTranscodingBenchmark
* Transcode the sourceFileName to destinationFileName with LOOP_COUNT.
*/
private void transcode(final String sourceFileName, final String destinationFileName)
- throws IOException, InterruptedException {
+ throws IOException, InterruptedException,
+ MediaTranscodingException.ServiceNotAvailableException {
AtomicLong totalTimeMs = new AtomicLong();
AtomicLong transcodingTime = new AtomicLong();
Uri srcUri = getUri(sourceFileName);
diff --git a/non-updatable-api/Android.bp b/non-updatable-api/Android.bp
new file mode 100644
index 000000000000..4037781c1844
--- /dev/null
+++ b/non-updatable-api/Android.bp
@@ -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 {
+ default_visibility: ["//visibility:private"],
+}
+
+filegroup {
+ name: "non-updatable-current.txt",
+ srcs: ["current.txt"],
+ visibility: ["//frameworks/base/api"],
+}
+
+filegroup {
+ name: "non-updatable-system-current.txt",
+ srcs: ["system-current.txt"],
+ visibility: ["//frameworks/base/api"],
+}
+
+filegroup {
+ name: "non-updatable-module-lib-current.txt",
+ srcs: ["module-lib-current.txt"],
+ visibility: ["//frameworks/base/api"],
+}
diff --git a/non-updatable-api/current.txt b/non-updatable-api/current.txt
index 8a2976fb9e85..d46f1d1606ba 100644
--- a/non-updatable-api/current.txt
+++ b/non-updatable-api/current.txt
@@ -11359,6 +11359,7 @@ package android.content.pm {
field public static final int CONFIG_COLOR_MODE = 16384; // 0x4000
field public static final int CONFIG_DENSITY = 4096; // 0x1000
field public static final int CONFIG_FONT_SCALE = 1073741824; // 0x40000000
+ field public static final int CONFIG_FORCE_BOLD_TEXT = 268435456; // 0x10000000
field public static final int CONFIG_KEYBOARD = 16; // 0x10
field public static final int CONFIG_KEYBOARD_HIDDEN = 32; // 0x20
field public static final int CONFIG_LAYOUT_DIRECTION = 8192; // 0x2000
@@ -11451,6 +11452,7 @@ package android.content.pm {
method public int describeContents();
method public int getKind();
method @Nullable public java.security.cert.Certificate getSourceCertificate() throws java.security.cert.CertificateException;
+ method @Nullable public String getSourcePackageName();
method @Nullable public String getSplitName();
method @NonNull public byte[] getValue();
method public void writeToParcel(@NonNull android.os.Parcel, int);
@@ -11559,6 +11561,7 @@ package android.content.pm {
}
public final class Checksum implements android.os.Parcelable {
+ ctor public Checksum(int, @NonNull byte[]);
method public int describeContents();
method public int getKind();
method @NonNull public byte[] getValue();
@@ -11566,9 +11569,9 @@ package android.content.pm {
field @NonNull public static final android.os.Parcelable.Creator<android.content.pm.Checksum> CREATOR;
field public static final int PARTIAL_MERKLE_ROOT_1M_SHA256 = 32; // 0x20
field public static final int PARTIAL_MERKLE_ROOT_1M_SHA512 = 64; // 0x40
- field public static final int WHOLE_MD5 = 2; // 0x2
+ field @Deprecated public static final int WHOLE_MD5 = 2; // 0x2
field public static final int WHOLE_MERKLE_ROOT_4K_SHA256 = 1; // 0x1
- field public static final int WHOLE_SHA1 = 4; // 0x4
+ field @Deprecated public static final int WHOLE_SHA1 = 4; // 0x4
field public static final int WHOLE_SHA256 = 8; // 0x8
field public static final int WHOLE_SHA512 = 16; // 0x10
}
@@ -11864,7 +11867,7 @@ package android.content.pm {
public static class PackageInstaller.Session implements java.io.Closeable {
method public void abandon();
- method public void addChecksums(@NonNull String, @NonNull java.util.List<android.content.pm.Checksum>) throws java.io.IOException;
+ method @Deprecated public void addChecksums(@NonNull String, @NonNull java.util.List<android.content.pm.Checksum>) throws java.io.IOException;
method public void addChildSessionId(int);
method public void close();
method public void commit(@NonNull android.content.IntentSender);
@@ -12061,6 +12064,7 @@ package android.content.pm {
method public boolean getSyntheticAppDetailsActivityEnabled(@NonNull String);
method @NonNull public abstract android.content.pm.FeatureInfo[] getSystemAvailableFeatures();
method @Nullable public abstract String[] getSystemSharedLibraryNames();
+ method @IntRange(from=0) public int getTargetSdkVersion(@NonNull String) throws android.content.pm.PackageManager.NameNotFoundException;
method @Nullable public abstract CharSequence getText(@NonNull String, @StringRes int, @Nullable android.content.pm.ApplicationInfo);
method @NonNull public abstract android.graphics.drawable.Drawable getUserBadgedDrawableForDensity(@NonNull android.graphics.drawable.Drawable, @NonNull android.os.UserHandle, @Nullable android.graphics.Rect, int);
method @NonNull public abstract android.graphics.drawable.Drawable getUserBadgedIcon(@NonNull android.graphics.drawable.Drawable, @NonNull android.os.UserHandle);
@@ -12679,6 +12683,9 @@ package android.content.res {
field public static final int COLOR_MODE_WIDE_COLOR_GAMUT_YES = 2; // 0x2
field @NonNull public static final android.os.Parcelable.Creator<android.content.res.Configuration> CREATOR;
field public static final int DENSITY_DPI_UNDEFINED = 0; // 0x0
+ field public static final int FORCE_BOLD_TEXT_NO = 1; // 0x1
+ field public static final int FORCE_BOLD_TEXT_UNDEFINED = 0; // 0x0
+ field public static final int FORCE_BOLD_TEXT_YES = 2; // 0x2
field public static final int HARDKEYBOARDHIDDEN_NO = 1; // 0x1
field public static final int HARDKEYBOARDHIDDEN_UNDEFINED = 0; // 0x0
field public static final int HARDKEYBOARDHIDDEN_YES = 2; // 0x2
@@ -12745,6 +12752,7 @@ package android.content.res {
field public int colorMode;
field public int densityDpi;
field public float fontScale;
+ field public int forceBoldText;
field public int hardKeyboardHidden;
field public int keyboard;
field public int keyboardHidden;
@@ -14284,6 +14292,7 @@ package android.graphics {
public final class BlurShader extends android.graphics.Shader {
ctor public BlurShader(float, float, @Nullable android.graphics.Shader);
+ ctor public BlurShader(float, float, @Nullable android.graphics.Shader, @NonNull android.graphics.Shader.TileMode);
}
public class Camera {
@@ -15169,6 +15178,20 @@ package android.graphics {
ctor public PaintFlagsDrawFilter(int, int);
}
+ public final class ParcelableColorSpace extends android.graphics.ColorSpace implements android.os.Parcelable {
+ ctor public ParcelableColorSpace(@NonNull android.graphics.ColorSpace);
+ method public int describeContents();
+ method @NonNull public float[] fromXyz(@NonNull float[]);
+ method @NonNull public android.graphics.ColorSpace getColorSpace();
+ method public float getMaxValue(int);
+ method public float getMinValue(int);
+ method public static boolean isParcelable(@NonNull android.graphics.ColorSpace);
+ method public boolean isWideGamut();
+ method @NonNull public float[] toXyz(@NonNull float[]);
+ method public void writeToParcel(@NonNull android.os.Parcel, int);
+ field @NonNull public static final android.os.Parcelable.Creator<android.graphics.ParcelableColorSpace> CREATOR;
+ }
+
public class Path {
ctor public Path();
ctor public Path(@Nullable android.graphics.Path);
@@ -15604,6 +15627,7 @@ package android.graphics {
public enum Shader.TileMode {
enum_constant public static final android.graphics.Shader.TileMode CLAMP;
+ enum_constant public static final android.graphics.Shader.TileMode DECAL;
enum_constant public static final android.graphics.Shader.TileMode MIRROR;
enum_constant public static final android.graphics.Shader.TileMode REPEAT;
}
@@ -16347,7 +16371,9 @@ package android.graphics.fonts {
method @Nullable public android.graphics.fonts.FontVariationAxis[] getAxes();
method @NonNull public java.nio.ByteBuffer getBuffer();
method @Nullable public java.io.File getFile();
+ method public float getGlyphBounds(@IntRange(from=0) int, @NonNull android.graphics.Paint, @Nullable android.graphics.RectF);
method @NonNull public android.os.LocaleList getLocaleList();
+ method public void getMetrics(@NonNull android.graphics.Paint, @Nullable android.graphics.Paint.FontMetrics);
method @NonNull public android.graphics.fonts.FontStyle getStyle();
method @IntRange(from=0) public int getTtcIndex();
}
@@ -16359,6 +16385,7 @@ package android.graphics.fonts {
ctor public Font.Builder(@NonNull android.os.ParcelFileDescriptor, @IntRange(from=0) long, @IntRange(from=0xffffffff) long);
ctor public Font.Builder(@NonNull android.content.res.AssetManager, @NonNull String);
ctor public Font.Builder(@NonNull android.content.res.Resources, int);
+ ctor public Font.Builder(@NonNull android.graphics.fonts.Font);
method @NonNull public android.graphics.fonts.Font build() throws java.io.IOException;
method @NonNull public android.graphics.fonts.Font.Builder setFontVariationSettings(@Nullable String);
method @NonNull public android.graphics.fonts.Font.Builder setFontVariationSettings(@Nullable android.graphics.fonts.FontVariationAxis[]);
@@ -24868,6 +24895,7 @@ package android.media {
}
public static final class DrmInitData.SchemeInitData {
+ ctor public DrmInitData.SchemeInitData(@NonNull java.util.UUID, @NonNull String, @NonNull byte[]);
field @NonNull public static final java.util.UUID UUID_NIL;
field public final byte[] data;
field public final String mimeType;
@@ -24886,6 +24914,10 @@ package android.media {
method public double getAttributeDouble(@NonNull String, double);
method public int getAttributeInt(@NonNull String, int);
method @Nullable public long[] getAttributeRange(@NonNull String);
+ method public long getDateTime();
+ method public long getDateTimeDigitized();
+ method public long getDateTimeOriginal();
+ method public long getGpsDateTime();
method public boolean getLatLong(float[]);
method public byte[] getThumbnail();
method public android.graphics.Bitmap getThumbnailBitmap();
@@ -26232,6 +26264,7 @@ package android.media {
field public static final String KEY_ROTATION = "rotation-degrees";
field public static final String KEY_SAMPLE_RATE = "sample-rate";
field public static final String KEY_SLICE_HEIGHT = "slice-height";
+ field public static final String KEY_SLOW_MOTION_MARKERS = "slow-motion-markers";
field public static final String KEY_STRIDE = "stride";
field public static final String KEY_TEMPORAL_LAYERING = "ts-schema";
field public static final String KEY_TILE_HEIGHT = "tile-height";
@@ -27138,6 +27171,12 @@ package android.media {
field public static final android.media.MediaTimestamp TIMESTAMP_UNKNOWN;
}
+ public class MediaTranscodingException extends java.lang.Exception {
+ }
+
+ public static final class MediaTranscodingException.ServiceNotAvailableException extends android.media.MediaTranscodingException {
+ }
+
public interface MicrophoneDirection {
method public boolean setPreferredMicrophoneDirection(int);
method public boolean setPreferredMicrophoneFieldDimension(@FloatRange(from=-1.0, to=1.0) float);
@@ -42422,54 +42461,8 @@ package android.service.textservice {
package android.service.voice {
- public class AlwaysOnHotwordDetector {
- method public android.content.Intent createEnrollIntent();
- method public android.content.Intent createReEnrollIntent();
- method public android.content.Intent createUnEnrollIntent();
- method public int getParameter(int);
- method public int getSupportedAudioCapabilities();
- method public int getSupportedRecognitionModes();
- method @Nullable public android.service.voice.AlwaysOnHotwordDetector.ModelParamRange queryParameter(int);
- method public int setParameter(int, int);
- method public boolean startRecognition(int);
- method public boolean stopRecognition();
- field public static final int AUDIO_CAPABILITY_ECHO_CANCELLATION = 1; // 0x1
- field public static final int AUDIO_CAPABILITY_NOISE_SUPPRESSION = 2; // 0x2
- field public static final int MODEL_PARAM_THRESHOLD_FACTOR = 0; // 0x0
- field public static final int RECOGNITION_FLAG_ALLOW_MULTIPLE_TRIGGERS = 2; // 0x2
- field public static final int RECOGNITION_FLAG_CAPTURE_TRIGGER_AUDIO = 1; // 0x1
- field public static final int RECOGNITION_FLAG_ENABLE_AUDIO_ECHO_CANCELLATION = 4; // 0x4
- field public static final int RECOGNITION_FLAG_ENABLE_AUDIO_NOISE_SUPPRESSION = 8; // 0x8
- field public static final int RECOGNITION_MODE_USER_IDENTIFICATION = 2; // 0x2
- field public static final int RECOGNITION_MODE_VOICE_TRIGGER = 1; // 0x1
- field public static final int STATE_HARDWARE_UNAVAILABLE = -2; // 0xfffffffe
- field public static final int STATE_KEYPHRASE_ENROLLED = 2; // 0x2
- field public static final int STATE_KEYPHRASE_UNENROLLED = 1; // 0x1
- field @Deprecated public static final int STATE_KEYPHRASE_UNSUPPORTED = -1; // 0xffffffff
- }
-
- public abstract static class AlwaysOnHotwordDetector.Callback {
- ctor public AlwaysOnHotwordDetector.Callback();
- method public abstract void onAvailabilityChanged(int);
- method public abstract void onDetected(@NonNull android.service.voice.AlwaysOnHotwordDetector.EventPayload);
- method public abstract void onError();
- method public abstract void onRecognitionPaused();
- method public abstract void onRecognitionResumed();
- }
-
- public static class AlwaysOnHotwordDetector.EventPayload {
- method @Nullable public android.media.AudioFormat getCaptureAudioFormat();
- method @Nullable public byte[] getTriggerAudio();
- }
-
- public static final class AlwaysOnHotwordDetector.ModelParamRange {
- method public int getEnd();
- method public int getStart();
- }
-
public class VoiceInteractionService extends android.app.Service {
ctor public VoiceInteractionService();
- method public final android.service.voice.AlwaysOnHotwordDetector createAlwaysOnHotwordDetector(String, java.util.Locale, android.service.voice.AlwaysOnHotwordDetector.Callback);
method public int getDisabledShowContext();
method public static boolean isActiveService(android.content.Context, android.content.ComponentName);
method public android.os.IBinder onBind(android.content.Intent);
@@ -44903,6 +44896,7 @@ package android.telephony {
field public static final String KEY_APN_EXPAND_BOOL = "apn_expand_bool";
field public static final String KEY_APN_SETTINGS_DEFAULT_APN_TYPES_STRING_ARRAY = "apn_settings_default_apn_types_string_array";
field public static final String KEY_AUTO_RETRY_ENABLED_BOOL = "auto_retry_enabled_bool";
+ field public static final String KEY_CALL_BARRING_DEFAULT_SERVICE_CLASS_INT = "call_barring_default_service_class_int";
field public static final String KEY_CALL_BARRING_SUPPORTS_DEACTIVATE_ALL_BOOL = "call_barring_supports_deactivate_all_bool";
field public static final String KEY_CALL_BARRING_SUPPORTS_PASSWORD_CHANGE_BOOL = "call_barring_supports_password_change_bool";
field public static final String KEY_CALL_BARRING_VISIBILITY_BOOL = "call_barring_visibility_bool";
@@ -45130,6 +45124,8 @@ package android.telephony {
field public static final String KEY_WFC_EMERGENCY_ADDRESS_CARRIER_APP_STRING = "wfc_emergency_address_carrier_app_string";
field public static final String KEY_WORLD_MODE_ENABLED_BOOL = "world_mode_enabled_bool";
field public static final String KEY_WORLD_PHONE_BOOL = "world_phone_bool";
+ field public static final int SERVICE_CLASS_NONE = 0; // 0x0
+ field public static final int SERVICE_CLASS_VOICE = 1; // 0x1
}
public static final class CarrierConfigManager.Apn {
diff --git a/non-updatable-api/module-lib-current.txt b/non-updatable-api/module-lib-current.txt
index 63b98cb5bd6a..85136df0d1ad 100644
--- a/non-updatable-api/module-lib-current.txt
+++ b/non-updatable-api/module-lib-current.txt
@@ -10,6 +10,10 @@ package android.app {
field public static final String ACTION_NOTIFICATION_LISTENER_ENABLED_CHANGED = "android.app.action.NOTIFICATION_LISTENER_ENABLED_CHANGED";
}
+ public class StatusBarManager {
+ method @RequiresPermission(android.Manifest.permission.STATUS_BAR) public void setDisabledForSimNetworkLock(boolean);
+ }
+
}
package android.content.rollback {
@@ -62,6 +66,7 @@ package android.media.session {
}
public final class MediaSessionManager {
+ method public void addOnActiveSessionsChangedListener(@NonNull android.media.session.MediaSessionManager.OnActiveSessionsChangedListener, @Nullable android.content.ComponentName, int, @Nullable android.os.Handler);
method public void dispatchMediaKeyEventAsSystemService(@NonNull android.view.KeyEvent);
method public boolean dispatchMediaKeyEventAsSystemService(@NonNull android.media.session.MediaSession.Token, @NonNull android.view.KeyEvent);
method public void dispatchVolumeKeyEventAsSystemService(@NonNull android.view.KeyEvent, int);
@@ -104,6 +109,7 @@ package android.os {
package android.provider {
public final class DeviceConfig {
+ field public static final String NAMESPACE_ALARM_MANAGER = "alarm_manager";
field public static final String NAMESPACE_DEVICE_IDLE = "device_idle";
}
diff --git a/non-updatable-api/system-current.txt b/non-updatable-api/system-current.txt
index 38f745e95d22..e27ca09f8e86 100644
--- a/non-updatable-api/system-current.txt
+++ b/non-updatable-api/system-current.txt
@@ -1532,12 +1532,16 @@ package android.bluetooth {
method @RequiresPermission(android.Manifest.permission.BLUETOOTH_PRIVILEGED) public void setBluetoothTethering(boolean);
method @RequiresPermission(android.Manifest.permission.BLUETOOTH_PRIVILEGED) public boolean setConnectionPolicy(@NonNull android.bluetooth.BluetoothDevice, int);
field public static final String ACTION_CONNECTION_STATE_CHANGED = "android.bluetooth.pan.profile.action.CONNECTION_STATE_CHANGED";
+ field public static final String ACTION_TETHERING_STATE_CHANGED = "android.bluetooth.action.TETHERING_STATE_CHANGED";
field public static final String EXTRA_LOCAL_ROLE = "android.bluetooth.pan.extra.LOCAL_ROLE";
+ field public static final String EXTRA_TETHERING_STATE = "android.bluetooth.extra.TETHERING_STATE";
field public static final int LOCAL_NAP_ROLE = 1; // 0x1
field public static final int LOCAL_PANU_ROLE = 2; // 0x2
field public static final int PAN_ROLE_NONE = 0; // 0x0
field public static final int REMOTE_NAP_ROLE = 1; // 0x1
field public static final int REMOTE_PANU_ROLE = 2; // 0x2
+ field public static final int TETHERING_STATE_OFF = 1; // 0x1
+ field public static final int TETHERING_STATE_ON = 2; // 0x2
}
public class BluetoothPbap implements android.bluetooth.BluetoothProfile {
@@ -4303,7 +4307,7 @@ package android.media {
}
public final class MediaTranscodeManager {
- method @NonNull public android.media.MediaTranscodeManager.TranscodingJob enqueueRequest(@NonNull android.media.MediaTranscodeManager.TranscodingRequest, @NonNull java.util.concurrent.Executor, @NonNull android.media.MediaTranscodeManager.OnTranscodingFinishedListener) throws java.io.FileNotFoundException;
+ method @NonNull public android.media.MediaTranscodeManager.TranscodingJob enqueueRequest(@NonNull android.media.MediaTranscodeManager.TranscodingRequest, @NonNull java.util.concurrent.Executor, @NonNull android.media.MediaTranscodeManager.OnTranscodingFinishedListener) throws java.io.FileNotFoundException, android.media.MediaTranscodingException.ServiceNotAvailableException;
field public static final int PRIORITY_REALTIME = 1; // 0x1
field public static final int TRANSCODING_TYPE_VIDEO = 1; // 0x1
}
@@ -4318,7 +4322,6 @@ package android.media {
method @IntRange(from=0, to=100) public int getProgress();
method public int getResult();
method public int getStatus();
- method public boolean retry();
method public void setOnProgressUpdateListener(@NonNull java.util.concurrent.Executor, @Nullable android.media.MediaTranscodeManager.TranscodingJob.OnProgressUpdateListener);
method public void setOnProgressUpdateListener(int, @NonNull java.util.concurrent.Executor, @Nullable android.media.MediaTranscodeManager.TranscodingJob.OnProgressUpdateListener);
field public static final int RESULT_CANCELED = 4; // 0x4
@@ -4336,6 +4339,8 @@ package android.media {
}
public static final class MediaTranscodeManager.TranscodingRequest {
+ method public int getClientPid();
+ method public int getClientUid();
method @NonNull public android.net.Uri getDestinationUri();
method public int getPriority();
method @NonNull public android.net.Uri getSourceUri();
@@ -4346,6 +4351,8 @@ package android.media {
public static final class MediaTranscodeManager.TranscodingRequest.Builder {
ctor public MediaTranscodeManager.TranscodingRequest.Builder();
method @NonNull public android.media.MediaTranscodeManager.TranscodingRequest build();
+ method @NonNull public android.media.MediaTranscodeManager.TranscodingRequest.Builder setClientPid(int);
+ method @NonNull public android.media.MediaTranscodeManager.TranscodingRequest.Builder setClientUid(int);
method @NonNull public android.media.MediaTranscodeManager.TranscodingRequest.Builder setDestinationUri(@NonNull android.net.Uri);
method @NonNull public android.media.MediaTranscodeManager.TranscodingRequest.Builder setPriority(int);
method @NonNull public android.media.MediaTranscodeManager.TranscodingRequest.Builder setSourceUri(@NonNull android.net.Uri);
@@ -8238,6 +8245,7 @@ package android.provider {
field public static final String APN_SET_ID = "apn_set_id";
field public static final int CARRIER_EDITED = 4; // 0x4
field public static final String EDITED_STATUS = "edited";
+ field public static final int MATCH_ALL_APN_SET_ID = -1; // 0xffffffff
field public static final String MAX_CONNECTIONS = "max_conns";
field public static final String MODEM_PERSIST = "modem_cognitive";
field public static final String MTU = "mtu";
@@ -9131,7 +9139,53 @@ package android.service.trust {
package android.service.voice {
+ public class AlwaysOnHotwordDetector {
+ method @Nullable public android.content.Intent createEnrollIntent();
+ method @Nullable public android.content.Intent createReEnrollIntent();
+ method @Nullable public android.content.Intent createUnEnrollIntent();
+ method @RequiresPermission(allOf={android.Manifest.permission.RECORD_AUDIO, android.Manifest.permission.CAPTURE_AUDIO_HOTWORD}) public int getParameter(int);
+ method public int getSupportedAudioCapabilities();
+ method public int getSupportedRecognitionModes();
+ method @Nullable @RequiresPermission(allOf={android.Manifest.permission.RECORD_AUDIO, android.Manifest.permission.CAPTURE_AUDIO_HOTWORD}) public android.service.voice.AlwaysOnHotwordDetector.ModelParamRange queryParameter(int);
+ method @RequiresPermission(allOf={android.Manifest.permission.RECORD_AUDIO, android.Manifest.permission.CAPTURE_AUDIO_HOTWORD}) public int setParameter(int, int);
+ method @RequiresPermission(allOf={android.Manifest.permission.RECORD_AUDIO, android.Manifest.permission.CAPTURE_AUDIO_HOTWORD}) public boolean startRecognition(int);
+ method @RequiresPermission(allOf={android.Manifest.permission.RECORD_AUDIO, android.Manifest.permission.CAPTURE_AUDIO_HOTWORD}) public boolean stopRecognition();
+ field public static final int AUDIO_CAPABILITY_ECHO_CANCELLATION = 1; // 0x1
+ field public static final int AUDIO_CAPABILITY_NOISE_SUPPRESSION = 2; // 0x2
+ field public static final int MODEL_PARAM_THRESHOLD_FACTOR = 0; // 0x0
+ field public static final int RECOGNITION_FLAG_ALLOW_MULTIPLE_TRIGGERS = 2; // 0x2
+ field public static final int RECOGNITION_FLAG_CAPTURE_TRIGGER_AUDIO = 1; // 0x1
+ field public static final int RECOGNITION_FLAG_ENABLE_AUDIO_ECHO_CANCELLATION = 4; // 0x4
+ field public static final int RECOGNITION_FLAG_ENABLE_AUDIO_NOISE_SUPPRESSION = 8; // 0x8
+ field public static final int RECOGNITION_MODE_USER_IDENTIFICATION = 2; // 0x2
+ field public static final int RECOGNITION_MODE_VOICE_TRIGGER = 1; // 0x1
+ field public static final int STATE_HARDWARE_UNAVAILABLE = -2; // 0xfffffffe
+ field public static final int STATE_KEYPHRASE_ENROLLED = 2; // 0x2
+ field public static final int STATE_KEYPHRASE_UNENROLLED = 1; // 0x1
+ field @Deprecated public static final int STATE_KEYPHRASE_UNSUPPORTED = -1; // 0xffffffff
+ }
+
+ public abstract static class AlwaysOnHotwordDetector.Callback {
+ ctor public AlwaysOnHotwordDetector.Callback();
+ method public abstract void onAvailabilityChanged(int);
+ method public abstract void onDetected(@NonNull android.service.voice.AlwaysOnHotwordDetector.EventPayload);
+ method public abstract void onError();
+ method public abstract void onRecognitionPaused();
+ method public abstract void onRecognitionResumed();
+ }
+
+ public static class AlwaysOnHotwordDetector.EventPayload {
+ method @Nullable public android.media.AudioFormat getCaptureAudioFormat();
+ method @Nullable public byte[] getTriggerAudio();
+ }
+
+ public static final class AlwaysOnHotwordDetector.ModelParamRange {
+ method public int getEnd();
+ method public int getStart();
+ }
+
public class VoiceInteractionService extends android.app.Service {
+ method @NonNull public final android.service.voice.AlwaysOnHotwordDetector createAlwaysOnHotwordDetector(String, java.util.Locale, android.service.voice.AlwaysOnHotwordDetector.Callback);
method @NonNull @RequiresPermission("android.permission.MANAGE_VOICE_KEYPHRASES") public final android.media.voice.KeyphraseModelManager createKeyphraseModelManager();
}
@@ -9522,6 +9576,26 @@ package android.telephony {
field @NonNull public static final android.os.Parcelable.Creator<android.telephony.CallAttributes> CREATOR;
}
+ public final class CallForwardingInfo implements android.os.Parcelable {
+ ctor public CallForwardingInfo(boolean, int, @Nullable String, int);
+ method public int describeContents();
+ method @Nullable public String getNumber();
+ method public int getReason();
+ method public int getTimeoutSeconds();
+ method public boolean isEnabled();
+ field @NonNull public static final android.os.Parcelable.Creator<android.telephony.CallForwardingInfo> CREATOR;
+ field public static final int ERROR_FDN_CHECK_FAILURE = 2; // 0x2
+ field public static final int ERROR_NOT_SUPPORTED = 3; // 0x3
+ field public static final int ERROR_UNKNOWN = 1; // 0x1
+ field public static final int REASON_ALL = 4; // 0x4
+ field public static final int REASON_ALL_CONDITIONAL = 5; // 0x5
+ field public static final int REASON_BUSY = 1; // 0x1
+ field public static final int REASON_NOT_REACHABLE = 3; // 0x3
+ field public static final int REASON_NO_REPLY = 2; // 0x2
+ field public static final int REASON_UNCONDITIONAL = 0; // 0x0
+ field public static final int SUCCESS = 0; // 0x0
+ }
+
public final class CallQuality implements android.os.Parcelable {
ctor public CallQuality(int, int, int, int, int, int, int, int, int, int, int);
ctor public CallQuality(int, int, int, int, int, int, int, int, int, int, int, boolean, boolean, boolean);
@@ -9795,7 +9869,8 @@ package android.telephony {
method public void onCallAttributesChanged(@NonNull android.telephony.CallAttributes);
method @Deprecated public void onOutgoingEmergencyCall(@NonNull android.telephony.emergency.EmergencyNumber);
method public void onOutgoingEmergencyCall(@NonNull android.telephony.emergency.EmergencyNumber, int);
- method public void onOutgoingEmergencySms(@NonNull android.telephony.emergency.EmergencyNumber);
+ method @Deprecated public void onOutgoingEmergencySms(@NonNull android.telephony.emergency.EmergencyNumber);
+ method public void onOutgoingEmergencySms(@NonNull android.telephony.emergency.EmergencyNumber, int);
method public void onPhysicalChannelConfigurationChanged(@NonNull java.util.List<android.telephony.PhysicalChannelConfig>);
method @RequiresPermission("android.permission.READ_PRECISE_PHONE_STATE") public void onPreciseCallStateChanged(@NonNull android.telephony.PreciseCallState);
method public void onRadioPowerStateChanged(int);
@@ -10182,6 +10257,8 @@ package android.telephony {
method @Deprecated @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public java.util.List<android.service.carrier.CarrierIdentifier> getAllowedCarriers(int);
method @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public long getAllowedNetworkTypes();
method @Nullable @RequiresPermission(android.Manifest.permission.INTERACT_ACROSS_USERS) public android.content.ComponentName getAndUpdateDefaultRespondViaMessageApplication();
+ method @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public void getCallForwarding(int, @NonNull java.util.concurrent.Executor, @NonNull android.telephony.TelephonyManager.CallForwardingInfoCallback);
+ method @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public void getCallWaitingStatus(@NonNull java.util.concurrent.Executor, @NonNull java.util.function.Consumer<java.lang.Integer>);
method @Nullable @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public android.telephony.ImsiEncryptionInfo getCarrierInfoForImsiEncryption(int);
method public java.util.List<java.lang.String> getCarrierPackageNamesForIntent(android.content.Intent);
method public java.util.List<java.lang.String> getCarrierPackageNamesForIntentAndPhone(android.content.Intent, int);
@@ -10258,6 +10335,8 @@ package android.telephony {
method @RequiresPermission(android.Manifest.permission.CONNECTIVITY_INTERNAL) public void resetSettings();
method @Deprecated @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public int setAllowedCarriers(int, java.util.List<android.service.carrier.CarrierIdentifier>);
method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public boolean setAllowedNetworkTypes(long);
+ method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public void setCallForwarding(@NonNull android.telephony.CallForwardingInfo, @Nullable java.util.concurrent.Executor, @Nullable java.util.function.Consumer<java.lang.Integer>);
+ method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public void setCallWaitingStatus(boolean, @Nullable java.util.concurrent.Executor, @Nullable java.util.function.Consumer<java.lang.Integer>);
method @Deprecated @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public void setCarrierDataEnabled(boolean);
method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public int setCarrierRestrictionRules(@NonNull android.telephony.CarrierRestrictionRules);
method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public void setDataActivationState(int);
@@ -10295,6 +10374,10 @@ package android.telephony {
field public static final String ACTION_SIM_APPLICATION_STATE_CHANGED = "android.telephony.action.SIM_APPLICATION_STATE_CHANGED";
field public static final String ACTION_SIM_CARD_STATE_CHANGED = "android.telephony.action.SIM_CARD_STATE_CHANGED";
field public static final String ACTION_SIM_SLOT_STATUS_CHANGED = "android.telephony.action.SIM_SLOT_STATUS_CHANGED";
+ field public static final int CALL_WAITING_STATUS_DISABLED = 2; // 0x2
+ field public static final int CALL_WAITING_STATUS_ENABLED = 1; // 0x1
+ field public static final int CALL_WAITING_STATUS_NOT_SUPPORTED = 4; // 0x4
+ field public static final int CALL_WAITING_STATUS_UNKNOWN_ERROR = 3; // 0x3
field public static final int CARRIER_PRIVILEGE_STATUS_ERROR_LOADING_RULES = -2; // 0xfffffffe
field public static final int CARRIER_PRIVILEGE_STATUS_HAS_ACCESS = 1; // 0x1
field public static final int CARRIER_PRIVILEGE_STATUS_NO_ACCESS = 0; // 0x0
@@ -10349,6 +10432,11 @@ package android.telephony {
field public static final int SRVCC_STATE_HANDOVER_STARTED = 0; // 0x0
}
+ public static interface TelephonyManager.CallForwardingInfoCallback {
+ method public void onCallForwardingInfoAvailable(@NonNull android.telephony.CallForwardingInfo);
+ method public void onError(int);
+ }
+
public final class UiccAccessRule implements android.os.Parcelable {
ctor public UiccAccessRule(byte[], @Nullable String, long);
method public int describeContents();
diff --git a/packages/CarSystemUI/res-keyguard/layout/keyguard_password_view.xml b/packages/CarSystemUI/res-keyguard/layout/keyguard_password_view.xml
index 8b235e6f246e..d57875f8daba 100644
--- a/packages/CarSystemUI/res-keyguard/layout/keyguard_password_view.xml
+++ b/packages/CarSystemUI/res-keyguard/layout/keyguard_password_view.xml
@@ -53,7 +53,7 @@
android:textColor="?attr/wallpaperTextColor"
android:textAppearance="?android:attr/textAppearanceMedium"
android:imeOptions="flagForceAscii|actionDone"
- android:maxLength="@integer/password_text_view_scale"
+ android:maxLength="@integer/password_max_length"
/>
<TextView
diff --git a/packages/CarSystemUI/res/layout/sysui_overlay_window.xml b/packages/CarSystemUI/res/layout/sysui_overlay_window.xml
index 2c9788955bfa..e7295aa6383d 100644
--- a/packages/CarSystemUI/res/layout/sysui_overlay_window.xml
+++ b/packages/CarSystemUI/res/layout/sysui_overlay_window.xml
@@ -25,7 +25,8 @@
<ViewStub android:id="@+id/notification_panel_stub"
android:layout_width="match_parent"
android:layout_height="match_parent"
- android:layout="@layout/notification_panel_container"/>
+ android:layout="@layout/notification_panel_container"
+ android:layout_marginBottom="@dimen/car_bottom_navigation_bar_height"/>
<ViewStub android:id="@+id/keyguard_stub"
android:layout_width="match_parent"
diff --git a/packages/CarSystemUI/res/values/dimens.xml b/packages/CarSystemUI/res/values/dimens.xml
index 28b8eadf9750..fe060ac8e707 100644
--- a/packages/CarSystemUI/res/values/dimens.xml
+++ b/packages/CarSystemUI/res/values/dimens.xml
@@ -207,6 +207,12 @@
<dimen name="car_navigation_bar_width">760dp</dimen>
<dimen name="car_left_navigation_bar_width">96dp</dimen>
<dimen name="car_right_navigation_bar_width">96dp</dimen>
+ <!-- In order to change the height of the bottom nav bar, overlay navigation_bar_height in
+ frameworks/base/core/res/res instead. -->
+ <dimen name="car_bottom_navigation_bar_height">@*android:dimen/navigation_bar_height</dimen>
+ <!-- In order to change the height of the top nav bar, overlay status_bar_height in
+ frameworks/base/core/res/res instead. -->
+ <dimen name="car_top_navigation_bar_height">@*android:dimen/status_bar_height</dimen>
<dimen name="car_user_switcher_container_height">420dp</dimen>
<!-- This must be the negative of car_user_switcher_container_height for the animation. -->
diff --git a/packages/CarSystemUI/res/values/strings.xml b/packages/CarSystemUI/res/values/strings.xml
index fbdb5167fade..06ae7cfd6d1b 100644
--- a/packages/CarSystemUI/res/values/strings.xml
+++ b/packages/CarSystemUI/res/values/strings.xml
@@ -22,6 +22,8 @@
<string name="hvac_min_text">Min</string>
<!-- String to represent largest setting of an HVAC system [CHAR LIMIT=10]-->
<string name="hvac_max_text">Max</string>
+ <!-- String to display when no HVAC temperature is available -->
+ <string name="hvac_null_temp_text" translatable="false">--</string>
<!-- Text for voice recognition toast. [CHAR LIMIT=60] -->
<string name="voice_recognition_toast">Voice recognition now handled by connected Bluetooth device</string>
<!-- Name of Guest Profile. [CHAR LIMIT=35] -->
diff --git a/packages/CarSystemUI/src/com/android/systemui/car/hvac/AdjustableTemperatureView.java b/packages/CarSystemUI/src/com/android/systemui/car/hvac/AdjustableTemperatureView.java
index 85d4ceb81eeb..af2a1d36bbd7 100644
--- a/packages/CarSystemUI/src/com/android/systemui/car/hvac/AdjustableTemperatureView.java
+++ b/packages/CarSystemUI/src/com/android/systemui/car/hvac/AdjustableTemperatureView.java
@@ -40,6 +40,9 @@ public class AdjustableTemperatureView extends LinearLayout implements Temperatu
private float mMinTempC;
private float mMaxTempC;
private String mTempFormat;
+ private String mNullTempText;
+ private String mMinTempText;
+ private String mMaxTempText;
private boolean mDisplayInFahrenheit = false;
private HvacController mHvacController;
@@ -59,6 +62,9 @@ public class AdjustableTemperatureView extends LinearLayout implements Temperatu
mTempFormat = getResources().getString(R.string.hvac_temperature_format);
mMinTempC = getResources().getFloat(R.dimen.hvac_min_value_celsius);
mMaxTempC = getResources().getFloat(R.dimen.hvac_max_value_celsius);
+ mNullTempText = getResources().getString(R.string.hvac_null_temp_text);
+ mMinTempText = getResources().getString(R.string.hvac_min_text);
+ mMaxTempText = getResources().getString(R.string.hvac_max_text);
initializeButtons();
}
@@ -69,12 +75,23 @@ public class AdjustableTemperatureView extends LinearLayout implements Temperatu
@Override
public void setTemp(float tempC) {
- if (tempC > mMaxTempC || tempC < mMinTempC) {
- return;
- }
if (mTempTextView == null) {
mTempTextView = findViewById(R.id.hvac_temperature_text);
}
+ if (Float.isNaN(tempC)) {
+ mTempTextView.setText(mNullTempText);
+ return;
+ }
+ if (tempC <= mMinTempC) {
+ mTempTextView.setText(mMinTempText);
+ mCurrentTempC = mMinTempC;
+ return;
+ }
+ if (tempC >= mMaxTempC) {
+ mTempTextView.setText(mMaxTempText);
+ mCurrentTempC = mMaxTempC;
+ return;
+ }
mTempTextView.setText(String.format(mTempFormat,
mDisplayInFahrenheit ? convertToFahrenheit(tempC) : tempC));
mCurrentTempC = tempC;
diff --git a/packages/CarSystemUI/src/com/android/systemui/car/keyguard/CarKeyguardViewController.java b/packages/CarSystemUI/src/com/android/systemui/car/keyguard/CarKeyguardViewController.java
index 3af7507ae2b4..c190ae54b1cf 100644
--- a/packages/CarSystemUI/src/com/android/systemui/car/keyguard/CarKeyguardViewController.java
+++ b/packages/CarSystemUI/src/com/android/systemui/car/keyguard/CarKeyguardViewController.java
@@ -124,7 +124,7 @@ public class CarKeyguardViewController extends OverlayViewController implements
}
@Override
- protected boolean shouldShowNavigationBar() {
+ protected boolean shouldShowNavigationBarInsets() {
return true;
}
@@ -157,7 +157,6 @@ public class CarKeyguardViewController extends OverlayViewController implements
mKeyguardStateController.notifyKeyguardState(mShowing, /* occluded= */ false);
mCarNavigationBarController.showAllKeyguardButtons(/* isSetUp= */ true);
start();
- getOverlayViewGlobalStateController().setWindowFocusable(/* focusable= */ true);
reset(/* hideBouncerWhenShowing= */ false);
notifyKeyguardUpdateMonitor();
}
@@ -172,7 +171,6 @@ public class CarKeyguardViewController extends OverlayViewController implements
mBouncer.hide(/* destroyView= */ true);
mCarNavigationBarController.hideAllKeyguardButtons(/* isSetUp= */ true);
stop();
- getOverlayViewGlobalStateController().setWindowFocusable(/* focusable= */ false);
mKeyguardStateController.notifyKeyguardDoneFading();
mHandler.post(mViewMediatorCallback::keyguardGone);
notifyKeyguardUpdateMonitor();
@@ -217,7 +215,6 @@ public class CarKeyguardViewController extends OverlayViewController implements
public void onCancelClicked() {
if (mBouncer == null) return;
- getOverlayViewGlobalStateController().setWindowFocusable(/* focusable= */ false);
getOverlayViewGlobalStateController().setWindowNeedsInput(/* needsInput= */ false);
mBouncer.hide(/* destroyView= */ true);
diff --git a/packages/CarSystemUI/src/com/android/systemui/car/navigationbar/SystemBarConfigs.java b/packages/CarSystemUI/src/com/android/systemui/car/navigationbar/SystemBarConfigs.java
index 078196e18b88..dfda4e6b2635 100644
--- a/packages/CarSystemUI/src/com/android/systemui/car/navigationbar/SystemBarConfigs.java
+++ b/packages/CarSystemUI/src/com/android/systemui/car/navigationbar/SystemBarConfigs.java
@@ -216,7 +216,7 @@ public class SystemBarConfigs {
new SystemBarConfigBuilder()
.setSide(TOP)
.setGirth(mResources.getDimensionPixelSize(
- com.android.internal.R.dimen.status_bar_height))
+ R.dimen.car_top_navigation_bar_height))
.setBarType(mResources.getInteger(R.integer.config_topSystemBarType))
.setZOrder(mResources.getInteger(R.integer.config_topSystemBarZOrder))
.setHideForKeyboard(mResources.getBoolean(
@@ -230,7 +230,7 @@ public class SystemBarConfigs {
new SystemBarConfigBuilder()
.setSide(BOTTOM)
.setGirth(mResources.getDimensionPixelSize(
- com.android.internal.R.dimen.navigation_bar_height))
+ R.dimen.car_bottom_navigation_bar_height))
.setBarType(mResources.getInteger(R.integer.config_bottomSystemBarType))
.setZOrder(
mResources.getInteger(R.integer.config_bottomSystemBarZOrder))
diff --git a/packages/CarSystemUI/src/com/android/systemui/car/notification/NotificationPanelViewController.java b/packages/CarSystemUI/src/com/android/systemui/car/notification/NotificationPanelViewController.java
index 8d91e7eae91a..b647f13cbe1f 100644
--- a/packages/CarSystemUI/src/com/android/systemui/car/notification/NotificationPanelViewController.java
+++ b/packages/CarSystemUI/src/com/android/systemui/car/notification/NotificationPanelViewController.java
@@ -16,8 +16,6 @@
package com.android.systemui.car.notification;
-import static android.view.WindowInsets.Type.navigationBars;
-
import android.app.ActivityManager;
import android.car.Car;
import android.car.drivingstate.CarUxRestrictionsManager;
@@ -25,6 +23,8 @@ import android.content.Context;
import android.content.res.Resources;
import android.graphics.Rect;
import android.graphics.drawable.Drawable;
+import android.inputmethodservice.InputMethodService;
+import android.os.IBinder;
import android.os.RemoteException;
import android.util.Log;
import android.view.GestureDetector;
@@ -82,6 +82,7 @@ public class NotificationPanelViewController extends OverlayPanelViewController
private final StatusBarStateController mStatusBarStateController;
private final boolean mEnableHeadsUpNotificationWhenNotificationShadeOpen;
private final NotificationVisibilityLogger mNotificationVisibilityLogger;
+ private final int mNavBarHeight;
private float mInitialBackgroundAlpha;
private float mBackgroundAlphaDiff;
@@ -138,7 +139,10 @@ public class NotificationPanelViewController extends OverlayPanelViewController
mStatusBarStateController = statusBarStateController;
mNotificationVisibilityLogger = notificationVisibilityLogger;
+ mNavBarHeight = mResources.getDimensionPixelSize(R.dimen.car_bottom_navigation_bar_height);
+
mCommandQueue.addCallback(this);
+
// Notification background setup.
mInitialBackgroundAlpha = (float) mResources.getInteger(
R.integer.config_initialNotificationBackgroundAlpha) / 100;
@@ -179,6 +183,27 @@ public class NotificationPanelViewController extends OverlayPanelViewController
}
}
+ @Override
+ public void setImeWindowStatus(int displayId, IBinder token, int vis, int backDisposition,
+ boolean showImeSwitcher) {
+ if (mContext.getDisplayId() != displayId) {
+ return;
+ }
+ boolean isKeyboardVisible = (vis & InputMethodService.IME_VISIBLE) != 0;
+ int bottomMargin = isKeyboardVisible ? 0 : mNavBarHeight;
+ ViewGroup container = (ViewGroup) getLayout();
+ if (container == null) {
+ // Notification panel hasn't been inflated before. We shouldn't try to update the layout
+ // params.
+ return;
+ }
+
+ ViewGroup.MarginLayoutParams params =
+ (ViewGroup.MarginLayoutParams) container.getLayoutParams();
+ params.setMargins(params.leftMargin, params.topMargin, params.rightMargin, bottomMargin);
+ container.setLayoutParams(params);
+ }
+
// OverlayViewController
@Override
@@ -193,18 +218,18 @@ public class NotificationPanelViewController extends OverlayPanelViewController
}
@Override
- protected boolean shouldShowNavigationBar() {
+ protected boolean shouldShowNavigationBarInsets() {
return true;
}
@Override
- protected boolean shouldShowStatusBar() {
+ protected boolean shouldShowStatusBarInsets() {
return true;
}
@Override
protected int getInsetTypesToFit() {
- return navigationBars();
+ return 0;
}
@Override
diff --git a/packages/CarSystemUI/src/com/android/systemui/car/userswitcher/FullScreenUserSwitcherViewController.java b/packages/CarSystemUI/src/com/android/systemui/car/userswitcher/FullScreenUserSwitcherViewController.java
index 66bfb2d316ae..3a7fac9c0e79 100644
--- a/packages/CarSystemUI/src/com/android/systemui/car/userswitcher/FullScreenUserSwitcherViewController.java
+++ b/packages/CarSystemUI/src/com/android/systemui/car/userswitcher/FullScreenUserSwitcherViewController.java
@@ -78,6 +78,11 @@ public class FullScreenUserSwitcherViewController extends OverlayViewController
}
@Override
+ protected boolean shouldFocusWindow() {
+ return false;
+ }
+
+ @Override
protected void showInternal() {
getLayout().setVisibility(View.VISIBLE);
}
diff --git a/packages/CarSystemUI/src/com/android/systemui/car/window/OverlayPanelViewController.java b/packages/CarSystemUI/src/com/android/systemui/car/window/OverlayPanelViewController.java
index d928ad0f42c9..44cb5cf9cb54 100644
--- a/packages/CarSystemUI/src/com/android/systemui/car/window/OverlayPanelViewController.java
+++ b/packages/CarSystemUI/src/com/android/systemui/car/window/OverlayPanelViewController.java
@@ -238,7 +238,6 @@ public abstract class OverlayPanelViewController extends OverlayViewController {
}
onAnimateCollapsePanel();
- getOverlayViewGlobalStateController().setWindowFocusable(false);
animatePanel(mClosingVelocity, /* isClosing= */ true);
}
@@ -415,7 +414,6 @@ public abstract class OverlayPanelViewController extends OverlayViewController {
getOverlayViewGlobalStateController().hideView(/* panelViewController= */ this);
}
getLayout().setVisibility(visible ? View.VISIBLE : View.INVISIBLE);
- getOverlayViewGlobalStateController().setWindowFocusable(visible);
}
/* ***************************************************************************************** *
diff --git a/packages/CarSystemUI/src/com/android/systemui/car/window/OverlayViewController.java b/packages/CarSystemUI/src/com/android/systemui/car/window/OverlayViewController.java
index 53deb9d9dc5d..8adc1adcc41c 100644
--- a/packages/CarSystemUI/src/com/android/systemui/car/window/OverlayViewController.java
+++ b/packages/CarSystemUI/src/com/android/systemui/car/window/OverlayViewController.java
@@ -136,16 +136,18 @@ public class OverlayViewController {
}
/**
- * Returns {@code true} if navigation bar should be displayed over this view.
+ * Returns {@code true} if navigation bar insets should be displayed over this view. Has no
+ * effect if {@link #shouldFocusWindow} returns {@code false}.
*/
- protected boolean shouldShowNavigationBar() {
+ protected boolean shouldShowNavigationBarInsets() {
return false;
}
/**
- * Returns {@code true} if status bar should be displayed over this view.
+ * Returns {@code true} if status bar insets should be displayed over this view. Has no
+ * effect if {@link #shouldFocusWindow} returns {@code false}.
*/
- protected boolean shouldShowStatusBar() {
+ protected boolean shouldShowStatusBarInsets() {
return false;
}
@@ -157,6 +159,15 @@ public class OverlayViewController {
}
/**
+ * Returns {@code true} if the window should be focued when this view is visible. Note that
+ * returning {@code false} here means that {@link #shouldShowStatusBarInsets} and
+ * {@link #shouldShowNavigationBarInsets} will have no effect.
+ */
+ protected boolean shouldFocusWindow() {
+ return true;
+ }
+
+ /**
* Returns the insets types to fit to the sysui overlay window when this
* {@link OverlayViewController} is in the foreground.
*/
diff --git a/packages/CarSystemUI/src/com/android/systemui/car/window/OverlayViewGlobalStateController.java b/packages/CarSystemUI/src/com/android/systemui/car/window/OverlayViewGlobalStateController.java
index 22b6455dbe2b..c13e486f1c0e 100644
--- a/packages/CarSystemUI/src/com/android/systemui/car/window/OverlayViewGlobalStateController.java
+++ b/packages/CarSystemUI/src/com/android/systemui/car/window/OverlayViewGlobalStateController.java
@@ -18,7 +18,6 @@ package com.android.systemui.car.window;
import static android.view.WindowInsets.Type.navigationBars;
import static android.view.WindowInsets.Type.statusBars;
-import static android.view.WindowInsetsController.BEHAVIOR_SHOW_TRANSIENT_BARS_BY_SWIPE;
import android.annotation.Nullable;
import android.util.Log;
@@ -119,6 +118,7 @@ public class OverlayViewGlobalStateController {
updateInternalsWhenShowingView(viewController);
refreshInsetTypesToFit();
+ refreshWindowFocus();
refreshNavigationBarVisibility();
refreshStatusBarVisibility();
@@ -191,6 +191,7 @@ public class OverlayViewGlobalStateController {
mZOrderVisibleSortedMap.remove(mZOrderMap.get(viewController));
refreshHighestZOrderWhenHidingView(viewController);
refreshInsetTypesToFit();
+ refreshWindowFocus();
refreshNavigationBarVisibility();
refreshStatusBarVisibility();
@@ -215,23 +216,37 @@ public class OverlayViewGlobalStateController {
}
private void refreshNavigationBarVisibility() {
- mWindowInsetsController.setSystemBarsBehavior(BEHAVIOR_SHOW_TRANSIENT_BARS_BY_SWIPE);
- if (mZOrderVisibleSortedMap.isEmpty() || mHighestZOrder.shouldShowNavigationBar()) {
+ if (mZOrderVisibleSortedMap.isEmpty()) {
mWindowInsetsController.show(navigationBars());
- } else {
+ return;
+ }
+
+ // Do not hide navigation bar insets if the window is not focusable.
+ if (mHighestZOrder.shouldFocusWindow() && !mHighestZOrder.shouldShowNavigationBarInsets()) {
mWindowInsetsController.hide(navigationBars());
+ } else {
+ mWindowInsetsController.show(navigationBars());
}
}
private void refreshStatusBarVisibility() {
- mWindowInsetsController.setSystemBarsBehavior(BEHAVIOR_SHOW_TRANSIENT_BARS_BY_SWIPE);
- if (mZOrderVisibleSortedMap.isEmpty() || mHighestZOrder.shouldShowStatusBar()) {
+ if (mZOrderVisibleSortedMap.isEmpty()) {
mWindowInsetsController.show(statusBars());
- } else {
+ return;
+ }
+
+ // Do not hide status bar insets if the window is not focusable.
+ if (mHighestZOrder.shouldFocusWindow() && !mHighestZOrder.shouldShowStatusBarInsets()) {
mWindowInsetsController.hide(statusBars());
+ } else {
+ mWindowInsetsController.show(statusBars());
}
}
+ private void refreshWindowFocus() {
+ setWindowFocusable(mHighestZOrder == null ? false : mHighestZOrder.shouldFocusWindow());
+ }
+
private void refreshInsetTypesToFit() {
if (mZOrderVisibleSortedMap.isEmpty()) {
setFitInsetsTypes(statusBars());
diff --git a/packages/CarSystemUI/src/com/android/systemui/car/window/SystemUIOverlayWindowController.java b/packages/CarSystemUI/src/com/android/systemui/car/window/SystemUIOverlayWindowController.java
index 81b1bf930e7e..887329359b05 100644
--- a/packages/CarSystemUI/src/com/android/systemui/car/window/SystemUIOverlayWindowController.java
+++ b/packages/CarSystemUI/src/com/android/systemui/car/window/SystemUIOverlayWindowController.java
@@ -16,6 +16,7 @@
package com.android.systemui.car.window;
+import static android.view.WindowInsetsController.BEHAVIOR_SHOW_TRANSIENT_BARS_BY_SWIPE;
import static android.view.WindowManager.LayoutParams.LAYOUT_IN_DISPLAY_CUTOUT_MODE_ALWAYS;
import android.content.Context;
@@ -104,6 +105,7 @@ public class SystemUIOverlayWindowController implements
mLp.setTitle("SystemUIOverlayWindow");
mLp.packageName = mContext.getPackageName();
mLp.layoutInDisplayCutoutMode = LAYOUT_IN_DISPLAY_CUTOUT_MODE_ALWAYS;
+ mLp.insetsFlags.behavior = BEHAVIOR_SHOW_TRANSIENT_BARS_BY_SWIPE;
mWindowManager.addView(mBaseLayout, mLp);
mLpChanged.copyFrom(mLp);
@@ -160,6 +162,7 @@ public class SystemUIOverlayWindowController implements
private void updateWindow() {
if (mLp != null && mLp.copyFrom(mLpChanged) != 0) {
if (isAttached()) {
+ mLp.insetsFlags.behavior = BEHAVIOR_SHOW_TRANSIENT_BARS_BY_SWIPE;
mWindowManager.updateViewLayout(mBaseLayout, mLp);
}
}
diff --git a/packages/CarSystemUI/src/com/android/systemui/wm/DisplaySystemBarsController.java b/packages/CarSystemUI/src/com/android/systemui/wm/DisplaySystemBarsController.java
index c7354ed5b459..b113d29f00e6 100644
--- a/packages/CarSystemUI/src/com/android/systemui/wm/DisplaySystemBarsController.java
+++ b/packages/CarSystemUI/src/com/android/systemui/wm/DisplaySystemBarsController.java
@@ -36,6 +36,8 @@ import com.android.wm.shell.common.DisplayController;
import com.android.wm.shell.common.DisplayImeController;
import com.android.wm.shell.common.TransactionPool;
+import java.util.Objects;
+
/**
* Controller that maps between displays and {@link IDisplayWindowInsetsController} in order to
* give system bar control to SystemUI.
@@ -47,6 +49,7 @@ public class DisplaySystemBarsController extends DisplayImeController {
private static final String TAG = "DisplaySystemBarsController";
private final Context mContext;
+ private final DisplayController mDisplayController;
private SparseArray<PerDisplay> mPerDisplaySparseArray;
public DisplaySystemBarsController(
@@ -57,6 +60,7 @@ public class DisplaySystemBarsController extends DisplayImeController {
TransactionPool transactionPool) {
super(wmService, displayController, mainHandler, transactionPool);
mContext = context;
+ mDisplayController = displayController;
}
@Override
@@ -92,7 +96,7 @@ public class DisplaySystemBarsController extends DisplayImeController {
}
@VisibleForTesting
- class PerDisplay extends IDisplayWindowInsetsController.Stub {
+ class PerDisplay extends DisplayImeController.PerDisplay {
int mDisplayId;
InsetsController mInsetsController;
@@ -100,6 +104,7 @@ public class DisplaySystemBarsController extends DisplayImeController {
String mPackageName;
PerDisplay(int displayId) {
+ super(displayId, mDisplayController.getDisplayLayout(displayId).rotation());
mDisplayId = displayId;
mInsetsController = new InsetsController(
new DisplaySystemBarsInsetsControllerHost(mHandler, this));
@@ -107,6 +112,7 @@ public class DisplaySystemBarsController extends DisplayImeController {
@Override
public void insetsChanged(InsetsState insetsState) {
+ super.insetsChanged(insetsState);
if (mInsetsState.equals(insetsState)) {
return;
}
@@ -120,24 +126,33 @@ public class DisplaySystemBarsController extends DisplayImeController {
@Override
public void insetsControlChanged(InsetsState insetsState,
InsetsSourceControl[] activeControls) {
+ super.insetsControlChanged(insetsState, activeControls);
mInsetsController.onControlsChanged(activeControls);
}
@Override
public void hideInsets(@WindowInsets.Type.InsetsType int types, boolean fromIme) {
- mInsetsController.hide(types);
+ if ((types & WindowInsets.Type.ime()) == 0) {
+ mInsetsController.hide(types);
+ } else {
+ super.hideInsets(types, fromIme);
+ }
+
}
@Override
public void showInsets(@WindowInsets.Type.InsetsType int types, boolean fromIme) {
- mInsetsController.show(types);
+ if ((types & WindowInsets.Type.ime()) == 0) {
+ mInsetsController.show(types);
+ } else {
+ super.showInsets(types, fromIme);
+ }
+
}
@Override
public void topFocusedWindowChanged(String packageName) {
- // If both package names are null or both package names are equal, return.
- if (mPackageName == packageName
- || (mPackageName != null && mPackageName.equals(packageName))) {
+ if (Objects.equals(mPackageName, packageName)) {
return;
}
mPackageName = packageName;
diff --git a/packages/CarSystemUI/tests/src/com/android/systemui/car/hvac/AdjustableTemperatureViewTest.java b/packages/CarSystemUI/tests/src/com/android/systemui/car/hvac/AdjustableTemperatureViewTest.java
index a3a55aae5f18..fe071d54fb10 100644
--- a/packages/CarSystemUI/tests/src/com/android/systemui/car/hvac/AdjustableTemperatureViewTest.java
+++ b/packages/CarSystemUI/tests/src/com/android/systemui/car/hvac/AdjustableTemperatureViewTest.java
@@ -98,6 +98,48 @@ public class AdjustableTemperatureViewTest extends SysuiTestCase {
}
@Test
+ public void setTemp_tempNaN_setsTextToNaNText() {
+ when(mCarPropertyManager.isPropertyAvailable(eq(HVAC_TEMPERATURE_SET),
+ anyInt())).thenReturn(true);
+ when(mCarPropertyManager.getFloatProperty(eq(HVAC_TEMPERATURE_SET), anyInt())).thenReturn(
+ Float.NaN);
+
+ mHvacController.addTemperatureViewToController(mAdjustableTemperatureView);
+
+ TextView tempText = mAdjustableTemperatureView.findViewById(R.id.hvac_temperature_text);
+ assertEquals(tempText.getText(),
+ getContext().getResources().getString(R.string.hvac_null_temp_text));
+ }
+
+ @Test
+ public void setTemp_tempBelowMin_setsTextToMinTempText() {
+ when(mCarPropertyManager.isPropertyAvailable(eq(HVAC_TEMPERATURE_SET),
+ anyInt())).thenReturn(true);
+ when(mCarPropertyManager.getFloatProperty(eq(HVAC_TEMPERATURE_SET), anyInt())).thenReturn(
+ getContext().getResources().getFloat(R.dimen.hvac_min_value_celsius));
+
+ mHvacController.addTemperatureViewToController(mAdjustableTemperatureView);
+
+ TextView tempText = mAdjustableTemperatureView.findViewById(R.id.hvac_temperature_text);
+ assertEquals(tempText.getText(),
+ getContext().getResources().getString(R.string.hvac_min_text));
+ }
+
+ @Test
+ public void setTemp_tempAboveMax_setsTextToMaxTempText() {
+ when(mCarPropertyManager.isPropertyAvailable(eq(HVAC_TEMPERATURE_SET),
+ anyInt())).thenReturn(true);
+ when(mCarPropertyManager.getFloatProperty(eq(HVAC_TEMPERATURE_SET), anyInt())).thenReturn(
+ getContext().getResources().getFloat(R.dimen.hvac_max_value_celsius));
+
+ mHvacController.addTemperatureViewToController(mAdjustableTemperatureView);
+
+ TextView tempText = mAdjustableTemperatureView.findViewById(R.id.hvac_temperature_text);
+ assertEquals(tempText.getText(),
+ getContext().getResources().getString(R.string.hvac_max_text));
+ }
+
+ @Test
public void setTemperatureToFahrenheit_callsViewSetDisplayInFahrenheit() {
when(mCarPropertyManager.isPropertyAvailable(eq(HVAC_TEMPERATURE_SET),
anyInt())).thenReturn(true);
diff --git a/packages/CarSystemUI/tests/src/com/android/systemui/car/window/OverlayPanelViewControllerTest.java b/packages/CarSystemUI/tests/src/com/android/systemui/car/window/OverlayPanelViewControllerTest.java
index bed803eedc22..f5f3ea3030da 100644
--- a/packages/CarSystemUI/tests/src/com/android/systemui/car/window/OverlayPanelViewControllerTest.java
+++ b/packages/CarSystemUI/tests/src/com/android/systemui/car/window/OverlayPanelViewControllerTest.java
@@ -224,18 +224,6 @@ public class OverlayPanelViewControllerTest extends SysuiTestCase {
}
@Test
- public void animateCollapsePanel_removesWindowFocus() {
- mOverlayPanelViewController.inflate(mBaseLayout);
- mOverlayPanelViewController.setShouldAnimateCollapsePanel(true);
- mOverlayPanelViewController.setPanelExpanded(true);
- mOverlayPanelViewController.setPanelVisible(true);
-
- mOverlayPanelViewController.animateCollapsePanel();
-
- verify(mOverlayViewGlobalStateController).setWindowFocusable(false);
- }
-
- @Test
public void animateExpandPanel_shouldNotAnimateExpandPanel_doesNotExpand() {
mOverlayPanelViewController.inflate(mBaseLayout);
mOverlayPanelViewController.setShouldAnimateExpandPanel(false);
@@ -365,14 +353,6 @@ public class OverlayPanelViewControllerTest extends SysuiTestCase {
}
@Test
- public void setPanelVisible_setTrue_setWindowFocusable() {
- mOverlayPanelViewController.inflate(mBaseLayout);
- mOverlayPanelViewController.setPanelVisible(true);
-
- verify(mOverlayViewGlobalStateController).setWindowFocusable(true);
- }
-
- @Test
public void setPanelVisible_setFalse_windowVisible_setsWindowNotVisible() {
mOverlayPanelViewController.inflate(mBaseLayout);
when(mOverlayViewGlobalStateController.isWindowVisible()).thenReturn(true);
@@ -404,15 +384,6 @@ public class OverlayPanelViewControllerTest extends SysuiTestCase {
}
@Test
- public void setPanelVisible_setFalse_setWindowNotFocusable() {
- mOverlayPanelViewController.inflate(mBaseLayout);
-
- mOverlayPanelViewController.setPanelVisible(false);
-
- verify(mOverlayViewGlobalStateController).setWindowFocusable(false);
- }
-
- @Test
public void dragOpenTouchListener_isNotInflated_inflatesView() {
when(mCarDeviceProvisionedController.isCurrentUserFullySetup()).thenReturn(true);
assertThat(mOverlayPanelViewController.isInflated()).isFalse();
diff --git a/packages/CarSystemUI/tests/src/com/android/systemui/car/window/OverlayViewGlobalStateControllerTest.java b/packages/CarSystemUI/tests/src/com/android/systemui/car/window/OverlayViewGlobalStateControllerTest.java
index ff286650ea50..294aa0d3cf9b 100644
--- a/packages/CarSystemUI/tests/src/com/android/systemui/car/window/OverlayViewGlobalStateControllerTest.java
+++ b/packages/CarSystemUI/tests/src/com/android/systemui/car/window/OverlayViewGlobalStateControllerTest.java
@@ -108,9 +108,54 @@ public class OverlayViewGlobalStateControllerTest extends SysuiTestCase {
}
@Test
+ public void showView_nothingVisible_windowNotFocusable_shouldShowNavBar_navBarsVisible() {
+ setupOverlayViewController1();
+ when(mOverlayViewController1.shouldFocusWindow()).thenReturn(false);
+ when(mOverlayViewController1.shouldShowNavigationBarInsets()).thenReturn(true);
+
+ mOverlayViewGlobalStateController.showView(mOverlayViewController1, mRunnable);
+
+ verify(mWindowInsetsController).show(navigationBars());
+ }
+
+ @Test
+ public void showView_nothingVisible_windowNotFocusable_shouldHideNavBar_notHidden() {
+ setupOverlayViewController1();
+ when(mOverlayViewController1.shouldFocusWindow()).thenReturn(false);
+ when(mOverlayViewController1.shouldShowNavigationBarInsets()).thenReturn(false);
+
+ mOverlayViewGlobalStateController.showView(mOverlayViewController1, mRunnable);
+
+ verify(mWindowInsetsController, never()).hide(navigationBars());
+ }
+
+ @Test
+ public void showView_nothingVisible_windowNotFocusable_shouldShowStatusBar_statusBarsVisible() {
+ setupOverlayViewController1();
+ when(mOverlayViewController1.shouldFocusWindow()).thenReturn(false);
+ when(mOverlayViewController1.shouldShowStatusBarInsets()).thenReturn(true);
+
+ mOverlayViewGlobalStateController.showView(mOverlayViewController1, mRunnable);
+
+ verify(mWindowInsetsController).show(statusBars());
+ }
+
+ @Test
+ public void showView_nothingVisible_windowNotFocusable_shouldHideStatusBar_notHidden() {
+ setupOverlayViewController1();
+ when(mOverlayViewController1.shouldFocusWindow()).thenReturn(false);
+ when(mOverlayViewController1.shouldShowStatusBarInsets()).thenReturn(false);
+
+ mOverlayViewGlobalStateController.showView(mOverlayViewController1, mRunnable);
+
+ verify(mWindowInsetsController, never()).hide(statusBars());
+ }
+
+ @Test
public void showView_nothingAlreadyShown_shouldShowNavBarFalse_navigationBarsHidden() {
setupOverlayViewController1();
- when(mOverlayViewController1.shouldShowNavigationBar()).thenReturn(false);
+ when(mOverlayViewController1.shouldFocusWindow()).thenReturn(true);
+ when(mOverlayViewController1.shouldShowNavigationBarInsets()).thenReturn(false);
mOverlayViewGlobalStateController.showView(mOverlayViewController1, mRunnable);
@@ -120,7 +165,8 @@ public class OverlayViewGlobalStateControllerTest extends SysuiTestCase {
@Test
public void showView_nothingAlreadyShown_shouldShowNavBarTrue_navigationBarsShown() {
setupOverlayViewController1();
- when(mOverlayViewController1.shouldShowNavigationBar()).thenReturn(true);
+ when(mOverlayViewController1.shouldFocusWindow()).thenReturn(true);
+ when(mOverlayViewController1.shouldShowNavigationBarInsets()).thenReturn(true);
mOverlayViewGlobalStateController.showView(mOverlayViewController1, mRunnable);
@@ -130,7 +176,8 @@ public class OverlayViewGlobalStateControllerTest extends SysuiTestCase {
@Test
public void showView_nothingAlreadyShown_shouldShowStatusBarFalse_statusBarsHidden() {
setupOverlayViewController1();
- when(mOverlayViewController1.shouldShowStatusBar()).thenReturn(false);
+ when(mOverlayViewController1.shouldFocusWindow()).thenReturn(true);
+ when(mOverlayViewController1.shouldShowStatusBarInsets()).thenReturn(false);
mOverlayViewGlobalStateController.showView(mOverlayViewController1, mRunnable);
@@ -140,7 +187,8 @@ public class OverlayViewGlobalStateControllerTest extends SysuiTestCase {
@Test
public void showView_nothingAlreadyShown_shouldShowStatusBarTrue_statusBarsShown() {
setupOverlayViewController1();
- when(mOverlayViewController1.shouldShowStatusBar()).thenReturn(true);
+ when(mOverlayViewController1.shouldFocusWindow()).thenReturn(true);
+ when(mOverlayViewController1.shouldShowStatusBarInsets()).thenReturn(true);
mOverlayViewGlobalStateController.showView(mOverlayViewController1, mRunnable);
@@ -201,9 +249,11 @@ public class OverlayViewGlobalStateControllerTest extends SysuiTestCase {
@Test
public void showView_newHighestZOrder_shouldShowNavBarFalse_navigationBarsHidden() {
setupOverlayViewController1();
- setOverlayViewControllerAsShowing(mOverlayViewController1);
setupOverlayViewController2();
- when(mOverlayViewController2.shouldShowNavigationBar()).thenReturn(false);
+ when(mOverlayViewController1.shouldFocusWindow()).thenReturn(true);
+ when(mOverlayViewController2.shouldFocusWindow()).thenReturn(true);
+ setOverlayViewControllerAsShowing(mOverlayViewController1);
+ when(mOverlayViewController2.shouldShowNavigationBarInsets()).thenReturn(false);
reset(mWindowInsetsController);
mOverlayViewGlobalStateController.showView(mOverlayViewController2, mRunnable);
@@ -214,9 +264,11 @@ public class OverlayViewGlobalStateControllerTest extends SysuiTestCase {
@Test
public void showView_newHighestZOrder_shouldShowNavBarTrue_navigationBarsShown() {
setupOverlayViewController1();
- setOverlayViewControllerAsShowing(mOverlayViewController1);
setupOverlayViewController2();
- when(mOverlayViewController2.shouldShowNavigationBar()).thenReturn(true);
+ when(mOverlayViewController1.shouldFocusWindow()).thenReturn(true);
+ when(mOverlayViewController2.shouldFocusWindow()).thenReturn(true);
+ setOverlayViewControllerAsShowing(mOverlayViewController1);
+ when(mOverlayViewController2.shouldShowNavigationBarInsets()).thenReturn(true);
mOverlayViewGlobalStateController.showView(mOverlayViewController2, mRunnable);
@@ -226,9 +278,11 @@ public class OverlayViewGlobalStateControllerTest extends SysuiTestCase {
@Test
public void showView_newHighestZOrder_shouldShowStatusBarFalse_statusBarsHidden() {
setupOverlayViewController1();
- setOverlayViewControllerAsShowing(mOverlayViewController1);
setupOverlayViewController2();
- when(mOverlayViewController2.shouldShowStatusBar()).thenReturn(false);
+ when(mOverlayViewController1.shouldFocusWindow()).thenReturn(true);
+ when(mOverlayViewController2.shouldFocusWindow()).thenReturn(true);
+ setOverlayViewControllerAsShowing(mOverlayViewController1);
+ when(mOverlayViewController2.shouldShowStatusBarInsets()).thenReturn(false);
reset(mWindowInsetsController);
mOverlayViewGlobalStateController.showView(mOverlayViewController2, mRunnable);
@@ -239,9 +293,11 @@ public class OverlayViewGlobalStateControllerTest extends SysuiTestCase {
@Test
public void showView_newHighestZOrder_shouldShowStatusBarTrue_statusBarsShown() {
setupOverlayViewController1();
- setOverlayViewControllerAsShowing(mOverlayViewController1);
setupOverlayViewController2();
- when(mOverlayViewController2.shouldShowStatusBar()).thenReturn(true);
+ when(mOverlayViewController1.shouldFocusWindow()).thenReturn(true);
+ when(mOverlayViewController2.shouldFocusWindow()).thenReturn(true);
+ setOverlayViewControllerAsShowing(mOverlayViewController1);
+ when(mOverlayViewController2.shouldShowStatusBarInsets()).thenReturn(true);
mOverlayViewGlobalStateController.showView(mOverlayViewController2, mRunnable);
@@ -289,9 +345,11 @@ public class OverlayViewGlobalStateControllerTest extends SysuiTestCase {
@Test
public void showView_oldHighestZOrder_shouldShowNavBarFalse_navigationBarsHidden() {
setupOverlayViewController2();
+ when(mOverlayViewController1.shouldFocusWindow()).thenReturn(true);
+ when(mOverlayViewController2.shouldFocusWindow()).thenReturn(true);
setOverlayViewControllerAsShowing(mOverlayViewController2);
- when(mOverlayViewController1.shouldShowNavigationBar()).thenReturn(true);
- when(mOverlayViewController2.shouldShowNavigationBar()).thenReturn(false);
+ when(mOverlayViewController1.shouldShowNavigationBarInsets()).thenReturn(true);
+ when(mOverlayViewController2.shouldShowNavigationBarInsets()).thenReturn(false);
reset(mWindowInsetsController);
mOverlayViewGlobalStateController.showView(mOverlayViewController1, mRunnable);
@@ -302,9 +360,11 @@ public class OverlayViewGlobalStateControllerTest extends SysuiTestCase {
@Test
public void showView_oldHighestZOrder_shouldShowNavBarTrue_navigationBarsShown() {
setupOverlayViewController2();
+ when(mOverlayViewController1.shouldFocusWindow()).thenReturn(true);
+ when(mOverlayViewController2.shouldFocusWindow()).thenReturn(true);
setOverlayViewControllerAsShowing(mOverlayViewController2);
- when(mOverlayViewController1.shouldShowNavigationBar()).thenReturn(false);
- when(mOverlayViewController2.shouldShowNavigationBar()).thenReturn(true);
+ when(mOverlayViewController1.shouldShowNavigationBarInsets()).thenReturn(false);
+ when(mOverlayViewController2.shouldShowNavigationBarInsets()).thenReturn(true);
mOverlayViewGlobalStateController.showView(mOverlayViewController1, mRunnable);
@@ -314,9 +374,11 @@ public class OverlayViewGlobalStateControllerTest extends SysuiTestCase {
@Test
public void showView_oldHighestZOrder_shouldShowStatusBarFalse_statusBarsHidden() {
setupOverlayViewController2();
+ when(mOverlayViewController1.shouldFocusWindow()).thenReturn(true);
+ when(mOverlayViewController2.shouldFocusWindow()).thenReturn(true);
setOverlayViewControllerAsShowing(mOverlayViewController2);
- when(mOverlayViewController1.shouldShowStatusBar()).thenReturn(true);
- when(mOverlayViewController2.shouldShowStatusBar()).thenReturn(false);
+ when(mOverlayViewController1.shouldShowStatusBarInsets()).thenReturn(true);
+ when(mOverlayViewController2.shouldShowStatusBarInsets()).thenReturn(false);
reset(mWindowInsetsController);
mOverlayViewGlobalStateController.showView(mOverlayViewController1, mRunnable);
@@ -327,9 +389,11 @@ public class OverlayViewGlobalStateControllerTest extends SysuiTestCase {
@Test
public void showView_oldHighestZOrder_shouldShowStatusBarTrue_statusBarsShown() {
setupOverlayViewController2();
+ when(mOverlayViewController1.shouldFocusWindow()).thenReturn(true);
+ when(mOverlayViewController2.shouldFocusWindow()).thenReturn(true);
setOverlayViewControllerAsShowing(mOverlayViewController2);
- when(mOverlayViewController1.shouldShowStatusBar()).thenReturn(false);
- when(mOverlayViewController2.shouldShowStatusBar()).thenReturn(true);
+ when(mOverlayViewController1.shouldShowStatusBarInsets()).thenReturn(false);
+ when(mOverlayViewController2.shouldShowStatusBarInsets()).thenReturn(true);
mOverlayViewGlobalStateController.showView(mOverlayViewController1, mRunnable);
@@ -512,10 +576,12 @@ public class OverlayViewGlobalStateControllerTest extends SysuiTestCase {
@Test
public void hideView_newHighestZOrder_shouldShowNavBarFalse_navigationBarHidden() {
setupOverlayViewController1();
- setOverlayViewControllerAsShowing(mOverlayViewController1);
setupOverlayViewController2();
+ when(mOverlayViewController1.shouldFocusWindow()).thenReturn(true);
+ when(mOverlayViewController2.shouldFocusWindow()).thenReturn(true);
+ setOverlayViewControllerAsShowing(mOverlayViewController1);
setOverlayViewControllerAsShowing(mOverlayViewController2);
- when(mOverlayViewController1.shouldShowNavigationBar()).thenReturn(false);
+ when(mOverlayViewController1.shouldShowNavigationBarInsets()).thenReturn(false);
reset(mWindowInsetsController);
mOverlayViewGlobalStateController.hideView(mOverlayViewController2, mRunnable);
@@ -526,10 +592,13 @@ public class OverlayViewGlobalStateControllerTest extends SysuiTestCase {
@Test
public void hideView_newHighestZOrder_shouldShowNavBarTrue_navigationBarShown() {
setupOverlayViewController1();
- setOverlayViewControllerAsShowing(mOverlayViewController1);
setupOverlayViewController2();
+ when(mOverlayViewController1.shouldFocusWindow()).thenReturn(true);
+ when(mOverlayViewController2.shouldFocusWindow()).thenReturn(true);
+ setOverlayViewControllerAsShowing(mOverlayViewController1);
setOverlayViewControllerAsShowing(mOverlayViewController2);
- when(mOverlayViewController1.shouldShowNavigationBar()).thenReturn(true);
+ when(mOverlayViewController1.shouldShowNavigationBarInsets()).thenReturn(true);
+ reset(mWindowInsetsController);
mOverlayViewGlobalStateController.hideView(mOverlayViewController2, mRunnable);
@@ -539,10 +608,12 @@ public class OverlayViewGlobalStateControllerTest extends SysuiTestCase {
@Test
public void hideView_newHighestZOrder_shouldShowStatusBarFalse_statusBarHidden() {
setupOverlayViewController1();
- setOverlayViewControllerAsShowing(mOverlayViewController1);
setupOverlayViewController2();
+ when(mOverlayViewController1.shouldFocusWindow()).thenReturn(true);
+ when(mOverlayViewController2.shouldFocusWindow()).thenReturn(true);
+ setOverlayViewControllerAsShowing(mOverlayViewController1);
setOverlayViewControllerAsShowing(mOverlayViewController2);
- when(mOverlayViewController1.shouldShowStatusBar()).thenReturn(false);
+ when(mOverlayViewController1.shouldShowStatusBarInsets()).thenReturn(false);
reset(mWindowInsetsController);
mOverlayViewGlobalStateController.hideView(mOverlayViewController2, mRunnable);
@@ -553,10 +624,13 @@ public class OverlayViewGlobalStateControllerTest extends SysuiTestCase {
@Test
public void hideView_newHighestZOrder_shouldShowStatusBarTrue_statusBarShown() {
setupOverlayViewController1();
- setOverlayViewControllerAsShowing(mOverlayViewController1);
setupOverlayViewController2();
+ when(mOverlayViewController1.shouldFocusWindow()).thenReturn(true);
+ when(mOverlayViewController2.shouldFocusWindow()).thenReturn(true);
+ setOverlayViewControllerAsShowing(mOverlayViewController1);
setOverlayViewControllerAsShowing(mOverlayViewController2);
- when(mOverlayViewController1.shouldShowStatusBar()).thenReturn(true);
+ when(mOverlayViewController1.shouldShowStatusBarInsets()).thenReturn(true);
+ reset(mWindowInsetsController);
mOverlayViewGlobalStateController.hideView(mOverlayViewController2, mRunnable);
@@ -593,10 +667,12 @@ public class OverlayViewGlobalStateControllerTest extends SysuiTestCase {
@Test
public void hideView_oldHighestZOrder_shouldShowNavBarFalse_navigationBarHidden() {
setupOverlayViewController1();
- setOverlayViewControllerAsShowing(mOverlayViewController1);
setupOverlayViewController2();
+ when(mOverlayViewController1.shouldFocusWindow()).thenReturn(true);
+ when(mOverlayViewController2.shouldFocusWindow()).thenReturn(true);
+ setOverlayViewControllerAsShowing(mOverlayViewController1);
setOverlayViewControllerAsShowing(mOverlayViewController2);
- when(mOverlayViewController2.shouldShowNavigationBar()).thenReturn(false);
+ when(mOverlayViewController2.shouldShowNavigationBarInsets()).thenReturn(false);
reset(mWindowInsetsController);
mOverlayViewGlobalStateController.hideView(mOverlayViewController1, mRunnable);
@@ -607,10 +683,12 @@ public class OverlayViewGlobalStateControllerTest extends SysuiTestCase {
@Test
public void hideView_oldHighestZOrder_shouldShowNavBarTrue_navigationBarShown() {
setupOverlayViewController1();
- setOverlayViewControllerAsShowing(mOverlayViewController1);
setupOverlayViewController2();
+ when(mOverlayViewController1.shouldFocusWindow()).thenReturn(true);
+ when(mOverlayViewController2.shouldFocusWindow()).thenReturn(true);
+ setOverlayViewControllerAsShowing(mOverlayViewController1);
setOverlayViewControllerAsShowing(mOverlayViewController2);
- when(mOverlayViewController2.shouldShowNavigationBar()).thenReturn(true);
+ when(mOverlayViewController2.shouldShowNavigationBarInsets()).thenReturn(true);
mOverlayViewGlobalStateController.hideView(mOverlayViewController1, mRunnable);
@@ -620,10 +698,12 @@ public class OverlayViewGlobalStateControllerTest extends SysuiTestCase {
@Test
public void hideView_oldHighestZOrder_shouldShowStatusBarFalse_statusBarHidden() {
setupOverlayViewController1();
- setOverlayViewControllerAsShowing(mOverlayViewController1);
setupOverlayViewController2();
+ when(mOverlayViewController1.shouldFocusWindow()).thenReturn(true);
+ when(mOverlayViewController2.shouldFocusWindow()).thenReturn(true);
+ setOverlayViewControllerAsShowing(mOverlayViewController1);
setOverlayViewControllerAsShowing(mOverlayViewController2);
- when(mOverlayViewController2.shouldShowStatusBar()).thenReturn(false);
+ when(mOverlayViewController2.shouldShowStatusBarInsets()).thenReturn(false);
reset(mWindowInsetsController);
mOverlayViewGlobalStateController.hideView(mOverlayViewController1, mRunnable);
@@ -634,10 +714,12 @@ public class OverlayViewGlobalStateControllerTest extends SysuiTestCase {
@Test
public void hideView_oldHighestZOrder_shouldShowStatusBarTrue_statusBarShown() {
setupOverlayViewController1();
- setOverlayViewControllerAsShowing(mOverlayViewController1);
setupOverlayViewController2();
+ when(mOverlayViewController1.shouldFocusWindow()).thenReturn(true);
+ when(mOverlayViewController2.shouldFocusWindow()).thenReturn(true);
+ setOverlayViewControllerAsShowing(mOverlayViewController1);
setOverlayViewControllerAsShowing(mOverlayViewController2);
- when(mOverlayViewController2.shouldShowStatusBar()).thenReturn(true);
+ when(mOverlayViewController2.shouldShowStatusBarInsets()).thenReturn(true);
mOverlayViewGlobalStateController.hideView(mOverlayViewController1, mRunnable);
@@ -673,6 +755,7 @@ public class OverlayViewGlobalStateControllerTest extends SysuiTestCase {
@Test
public void hideView_viewControllerOnlyShown_navigationBarShown() {
setupOverlayViewController1();
+ when(mOverlayViewController1.shouldFocusWindow()).thenReturn(true);
setOverlayViewControllerAsShowing(mOverlayViewController1);
mOverlayViewGlobalStateController.hideView(mOverlayViewController1, mRunnable);
@@ -683,6 +766,7 @@ public class OverlayViewGlobalStateControllerTest extends SysuiTestCase {
@Test
public void hideView_viewControllerOnlyShown_statusBarShown() {
setupOverlayViewController1();
+ when(mOverlayViewController1.shouldFocusWindow()).thenReturn(true);
setOverlayViewControllerAsShowing(mOverlayViewController1);
mOverlayViewGlobalStateController.hideView(mOverlayViewController1, mRunnable);
diff --git a/packages/InputDevices/res/values-af/strings.xml b/packages/InputDevices/res/values-af/strings.xml
index eb89ee56345a..462a6a9bb7ac 100644
--- a/packages/InputDevices/res/values-af/strings.xml
+++ b/packages/InputDevices/res/values-af/strings.xml
@@ -19,6 +19,7 @@
<string name="keyboard_layout_swiss_german_label" msgid="2305520941993314258">"Switserse Duits"</string>
<string name="keyboard_layout_belgian" msgid="2011984572838651558">"Belgies"</string>
<string name="keyboard_layout_bulgarian" msgid="8951224309972028398">"Bulgaars"</string>
+ <string name="keyboard_layout_bulgarian_phonetic" msgid="7568914730360106653">"Bulgaars, foneties"</string>
<string name="keyboard_layout_italian" msgid="6497079660449781213">"Italiaans"</string>
<string name="keyboard_layout_danish" msgid="8036432066627127851">"Deens"</string>
<string name="keyboard_layout_norwegian" msgid="9090097917011040937">"Noors"</string>
diff --git a/packages/InputDevices/res/values-am/strings.xml b/packages/InputDevices/res/values-am/strings.xml
index 2970bfe722d8..1559fa890bcd 100644
--- a/packages/InputDevices/res/values-am/strings.xml
+++ b/packages/InputDevices/res/values-am/strings.xml
@@ -19,6 +19,7 @@
<string name="keyboard_layout_swiss_german_label" msgid="2305520941993314258">"የስዊዝ ጀርመን"</string>
<string name="keyboard_layout_belgian" msgid="2011984572838651558">"ቤልጂየም"</string>
<string name="keyboard_layout_bulgarian" msgid="8951224309972028398">"ቡልጋሪያ"</string>
+ <string name="keyboard_layout_bulgarian_phonetic" msgid="7568914730360106653">"ቡልጋሪያኛ፣ ፎነቲክ"</string>
<string name="keyboard_layout_italian" msgid="6497079660449781213">"ጣሊያንኛ"</string>
<string name="keyboard_layout_danish" msgid="8036432066627127851">"ዴኒሽ"</string>
<string name="keyboard_layout_norwegian" msgid="9090097917011040937">"ኖርዌጂያ"</string>
diff --git a/packages/InputDevices/res/values-ar/strings.xml b/packages/InputDevices/res/values-ar/strings.xml
index 12751dbceb5b..3b00576afd89 100644
--- a/packages/InputDevices/res/values-ar/strings.xml
+++ b/packages/InputDevices/res/values-ar/strings.xml
@@ -19,6 +19,8 @@
<string name="keyboard_layout_swiss_german_label" msgid="2305520941993314258">"الألمانية السويسرية"</string>
<string name="keyboard_layout_belgian" msgid="2011984572838651558">"البلجيكية"</string>
<string name="keyboard_layout_bulgarian" msgid="8951224309972028398">"البلغارية"</string>
+ <!-- no translation found for keyboard_layout_bulgarian_phonetic (7568914730360106653) -->
+ <skip />
<string name="keyboard_layout_italian" msgid="6497079660449781213">"الإيطالية"</string>
<string name="keyboard_layout_danish" msgid="8036432066627127851">"الدانماركية"</string>
<string name="keyboard_layout_norwegian" msgid="9090097917011040937">"النرويجية"</string>
@@ -26,8 +28,7 @@
<string name="keyboard_layout_finnish" msgid="5585659438924315466">"الفنلندية"</string>
<string name="keyboard_layout_croatian" msgid="4172229471079281138">"الكرواتية"</string>
<string name="keyboard_layout_czech" msgid="1349256901452975343">"التشيكية"</string>
- <!-- no translation found for keyboard_layout_czech_qwerty (3331402534128515501) -->
- <skip />
+ <string name="keyboard_layout_czech_qwerty" msgid="3331402534128515501">"‏لوحة المفاتيح التشيكية بنمط QWERTY"</string>
<string name="keyboard_layout_estonian" msgid="8775830985185665274">"الإستونية"</string>
<string name="keyboard_layout_hungarian" msgid="4154963661406035109">"المجرية"</string>
<string name="keyboard_layout_icelandic" msgid="5836645650912489642">"الأيسلندية"</string>
diff --git a/packages/InputDevices/res/values-as/strings.xml b/packages/InputDevices/res/values-as/strings.xml
index a2df66cb1995..49fbef92c114 100644
--- a/packages/InputDevices/res/values-as/strings.xml
+++ b/packages/InputDevices/res/values-as/strings.xml
@@ -19,6 +19,7 @@
<string name="keyboard_layout_swiss_german_label" msgid="2305520941993314258">"ছুইছ জাৰ্মান"</string>
<string name="keyboard_layout_belgian" msgid="2011984572838651558">"বেলজিয়ান"</string>
<string name="keyboard_layout_bulgarian" msgid="8951224309972028398">"বুলগেৰিয়ান"</string>
+ <string name="keyboard_layout_bulgarian_phonetic" msgid="7568914730360106653">"বুলগেৰিয়ান ফ’নেটিক"</string>
<string name="keyboard_layout_italian" msgid="6497079660449781213">"ইটালিয়ান"</string>
<string name="keyboard_layout_danish" msgid="8036432066627127851">"ডেনিশ্ব"</string>
<string name="keyboard_layout_norwegian" msgid="9090097917011040937">"ন\'ৰৱেয়ান"</string>
@@ -26,8 +27,7 @@
<string name="keyboard_layout_finnish" msgid="5585659438924315466">"ফিনিশ্ব"</string>
<string name="keyboard_layout_croatian" msgid="4172229471079281138">"ক্ৰ\'ৱেশ্বিয়ান"</string>
<string name="keyboard_layout_czech" msgid="1349256901452975343">"চ্চেক"</string>
- <!-- no translation found for keyboard_layout_czech_qwerty (3331402534128515501) -->
- <skip />
+ <string name="keyboard_layout_czech_qwerty" msgid="3331402534128515501">"Czech QWERTY কীব’ৰ্ড"</string>
<string name="keyboard_layout_estonian" msgid="8775830985185665274">"ইষ্ট\'নিয়া"</string>
<string name="keyboard_layout_hungarian" msgid="4154963661406035109">"হাংগেৰিয়ান"</string>
<string name="keyboard_layout_icelandic" msgid="5836645650912489642">"আইচলেণ্ডিক"</string>
diff --git a/packages/InputDevices/res/values-az/strings.xml b/packages/InputDevices/res/values-az/strings.xml
index 1d9eb0687c7d..c5a1e1ee2e2e 100644
--- a/packages/InputDevices/res/values-az/strings.xml
+++ b/packages/InputDevices/res/values-az/strings.xml
@@ -19,6 +19,7 @@
<string name="keyboard_layout_swiss_german_label" msgid="2305520941993314258">"İsveçrə Almanı"</string>
<string name="keyboard_layout_belgian" msgid="2011984572838651558">"Belçikalı"</string>
<string name="keyboard_layout_bulgarian" msgid="8951224309972028398">"Bolqar"</string>
+ <string name="keyboard_layout_bulgarian_phonetic" msgid="7568914730360106653">"Bolqar dili, Fonetika"</string>
<string name="keyboard_layout_italian" msgid="6497079660449781213">"İtalyan"</string>
<string name="keyboard_layout_danish" msgid="8036432066627127851">"Danimarkalı"</string>
<string name="keyboard_layout_norwegian" msgid="9090097917011040937">"Norveçli"</string>
@@ -26,8 +27,7 @@
<string name="keyboard_layout_finnish" msgid="5585659438924315466">"Fin"</string>
<string name="keyboard_layout_croatian" msgid="4172229471079281138">"Xorvat"</string>
<string name="keyboard_layout_czech" msgid="1349256901452975343">"Çex"</string>
- <!-- no translation found for keyboard_layout_czech_qwerty (3331402534128515501) -->
- <skip />
+ <string name="keyboard_layout_czech_qwerty" msgid="3331402534128515501">"Çex QWERTY üslubu"</string>
<string name="keyboard_layout_estonian" msgid="8775830985185665274">"Eston"</string>
<string name="keyboard_layout_hungarian" msgid="4154963661406035109">"Macar"</string>
<string name="keyboard_layout_icelandic" msgid="5836645650912489642">"İslandiyalı"</string>
diff --git a/packages/InputDevices/res/values-b+sr+Latn/strings.xml b/packages/InputDevices/res/values-b+sr+Latn/strings.xml
index 7952c22caa67..16f1cb2fc591 100644
--- a/packages/InputDevices/res/values-b+sr+Latn/strings.xml
+++ b/packages/InputDevices/res/values-b+sr+Latn/strings.xml
@@ -19,6 +19,7 @@
<string name="keyboard_layout_swiss_german_label" msgid="2305520941993314258">"švajcarsko nemačka"</string>
<string name="keyboard_layout_belgian" msgid="2011984572838651558">"belgijska"</string>
<string name="keyboard_layout_bulgarian" msgid="8951224309972028398">"bugarska"</string>
+ <string name="keyboard_layout_bulgarian_phonetic" msgid="7568914730360106653">"bugarska fonetska"</string>
<string name="keyboard_layout_italian" msgid="6497079660449781213">"italijanska"</string>
<string name="keyboard_layout_danish" msgid="8036432066627127851">"danska"</string>
<string name="keyboard_layout_norwegian" msgid="9090097917011040937">"norveška"</string>
@@ -26,8 +27,7 @@
<string name="keyboard_layout_finnish" msgid="5585659438924315466">"finska"</string>
<string name="keyboard_layout_croatian" msgid="4172229471079281138">"hrvatska"</string>
<string name="keyboard_layout_czech" msgid="1349256901452975343">"češka"</string>
- <!-- no translation found for keyboard_layout_czech_qwerty (3331402534128515501) -->
- <skip />
+ <string name="keyboard_layout_czech_qwerty" msgid="3331402534128515501">"Stil češke QWERTY tastature"</string>
<string name="keyboard_layout_estonian" msgid="8775830985185665274">"estonska"</string>
<string name="keyboard_layout_hungarian" msgid="4154963661406035109">"mađarska"</string>
<string name="keyboard_layout_icelandic" msgid="5836645650912489642">"islandska"</string>
diff --git a/packages/InputDevices/res/values-be/strings.xml b/packages/InputDevices/res/values-be/strings.xml
index 4e2d5ba9216f..6b0523f2bdf8 100644
--- a/packages/InputDevices/res/values-be/strings.xml
+++ b/packages/InputDevices/res/values-be/strings.xml
@@ -19,6 +19,7 @@
<string name="keyboard_layout_swiss_german_label" msgid="2305520941993314258">"Нямецкая (Швейцарыя)"</string>
<string name="keyboard_layout_belgian" msgid="2011984572838651558">"Бельгійская"</string>
<string name="keyboard_layout_bulgarian" msgid="8951224309972028398">"Балгарская"</string>
+ <string name="keyboard_layout_bulgarian_phonetic" msgid="7568914730360106653">"Балгарская фанетычная"</string>
<string name="keyboard_layout_italian" msgid="6497079660449781213">"Італьянская"</string>
<string name="keyboard_layout_danish" msgid="8036432066627127851">"Дацкая"</string>
<string name="keyboard_layout_norwegian" msgid="9090097917011040937">"Нарвежская"</string>
@@ -26,8 +27,7 @@
<string name="keyboard_layout_finnish" msgid="5585659438924315466">"Фінская"</string>
<string name="keyboard_layout_croatian" msgid="4172229471079281138">"Харвацкая"</string>
<string name="keyboard_layout_czech" msgid="1349256901452975343">"Чэшская"</string>
- <!-- no translation found for keyboard_layout_czech_qwerty (3331402534128515501) -->
- <skip />
+ <string name="keyboard_layout_czech_qwerty" msgid="3331402534128515501">"Чэшская раскладка клавіятуры QWERTY"</string>
<string name="keyboard_layout_estonian" msgid="8775830985185665274">"Эстонская"</string>
<string name="keyboard_layout_hungarian" msgid="4154963661406035109">"Венгерская"</string>
<string name="keyboard_layout_icelandic" msgid="5836645650912489642">"Ісландская"</string>
diff --git a/packages/InputDevices/res/values-bg/strings.xml b/packages/InputDevices/res/values-bg/strings.xml
index a1edb21d4b5a..a7088c930395 100644
--- a/packages/InputDevices/res/values-bg/strings.xml
+++ b/packages/InputDevices/res/values-bg/strings.xml
@@ -19,6 +19,7 @@
<string name="keyboard_layout_swiss_german_label" msgid="2305520941993314258">"швейцарски немски"</string>
<string name="keyboard_layout_belgian" msgid="2011984572838651558">"белгийски"</string>
<string name="keyboard_layout_bulgarian" msgid="8951224309972028398">"български"</string>
+ <string name="keyboard_layout_bulgarian_phonetic" msgid="7568914730360106653">"Българска фонетична клавиатура"</string>
<string name="keyboard_layout_italian" msgid="6497079660449781213">"италиански"</string>
<string name="keyboard_layout_danish" msgid="8036432066627127851">"датски"</string>
<string name="keyboard_layout_norwegian" msgid="9090097917011040937">"норвежки"</string>
diff --git a/packages/InputDevices/res/values-bn/strings.xml b/packages/InputDevices/res/values-bn/strings.xml
index d3b47bc3c76b..19292780875e 100644
--- a/packages/InputDevices/res/values-bn/strings.xml
+++ b/packages/InputDevices/res/values-bn/strings.xml
@@ -19,6 +19,8 @@
<string name="keyboard_layout_swiss_german_label" msgid="2305520941993314258">"সুইস জার্মান"</string>
<string name="keyboard_layout_belgian" msgid="2011984572838651558">"বেলজিয়ান"</string>
<string name="keyboard_layout_bulgarian" msgid="8951224309972028398">"বুলগেরীয়"</string>
+ <!-- no translation found for keyboard_layout_bulgarian_phonetic (7568914730360106653) -->
+ <skip />
<string name="keyboard_layout_italian" msgid="6497079660449781213">"ইতালীয়"</string>
<string name="keyboard_layout_danish" msgid="8036432066627127851">"ডেনিশ"</string>
<string name="keyboard_layout_norwegian" msgid="9090097917011040937">"নরওয়েজীয়"</string>
@@ -26,8 +28,7 @@
<string name="keyboard_layout_finnish" msgid="5585659438924315466">"ফিনিশ"</string>
<string name="keyboard_layout_croatian" msgid="4172229471079281138">"ক্রোয়েশিয়"</string>
<string name="keyboard_layout_czech" msgid="1349256901452975343">"চেক"</string>
- <!-- no translation found for keyboard_layout_czech_qwerty (3331402534128515501) -->
- <skip />
+ <string name="keyboard_layout_czech_qwerty" msgid="3331402534128515501">"চেক QWERTY স্টাইল"</string>
<string name="keyboard_layout_estonian" msgid="8775830985185665274">"এস্তোনীয়"</string>
<string name="keyboard_layout_hungarian" msgid="4154963661406035109">"হাঙ্গেরিয়"</string>
<string name="keyboard_layout_icelandic" msgid="5836645650912489642">"আইসল্যান্ডিক"</string>
diff --git a/packages/InputDevices/res/values-bs/strings.xml b/packages/InputDevices/res/values-bs/strings.xml
index 436a3b02eb54..b92ac8cad498 100644
--- a/packages/InputDevices/res/values-bs/strings.xml
+++ b/packages/InputDevices/res/values-bs/strings.xml
@@ -19,6 +19,7 @@
<string name="keyboard_layout_swiss_german_label" msgid="2305520941993314258">"švicarski njemački"</string>
<string name="keyboard_layout_belgian" msgid="2011984572838651558">"belgijski"</string>
<string name="keyboard_layout_bulgarian" msgid="8951224309972028398">"bugarski"</string>
+ <string name="keyboard_layout_bulgarian_phonetic" msgid="7568914730360106653">"bugarski, fonetski"</string>
<string name="keyboard_layout_italian" msgid="6497079660449781213">"italijanski"</string>
<string name="keyboard_layout_danish" msgid="8036432066627127851">"danski"</string>
<string name="keyboard_layout_norwegian" msgid="9090097917011040937">"norveški"</string>
diff --git a/packages/InputDevices/res/values-ca/strings.xml b/packages/InputDevices/res/values-ca/strings.xml
index 1f089e18d5cb..a5b5e10129d4 100644
--- a/packages/InputDevices/res/values-ca/strings.xml
+++ b/packages/InputDevices/res/values-ca/strings.xml
@@ -19,6 +19,7 @@
<string name="keyboard_layout_swiss_german_label" msgid="2305520941993314258">"Alemany suís"</string>
<string name="keyboard_layout_belgian" msgid="2011984572838651558">"Belga"</string>
<string name="keyboard_layout_bulgarian" msgid="8951224309972028398">"Búlgar"</string>
+ <string name="keyboard_layout_bulgarian_phonetic" msgid="7568914730360106653">"Búlgar, fonètic"</string>
<string name="keyboard_layout_italian" msgid="6497079660449781213">"Italià"</string>
<string name="keyboard_layout_danish" msgid="8036432066627127851">"Danès"</string>
<string name="keyboard_layout_norwegian" msgid="9090097917011040937">"Noruec"</string>
diff --git a/packages/InputDevices/res/values-cs/strings.xml b/packages/InputDevices/res/values-cs/strings.xml
index b36861a99cc0..99aed5e6ea0e 100644
--- a/packages/InputDevices/res/values-cs/strings.xml
+++ b/packages/InputDevices/res/values-cs/strings.xml
@@ -19,6 +19,8 @@
<string name="keyboard_layout_swiss_german_label" msgid="2305520941993314258">"švýcarské (němčina)"</string>
<string name="keyboard_layout_belgian" msgid="2011984572838651558">"belgické"</string>
<string name="keyboard_layout_bulgarian" msgid="8951224309972028398">"bulharské"</string>
+ <!-- no translation found for keyboard_layout_bulgarian_phonetic (7568914730360106653) -->
+ <skip />
<string name="keyboard_layout_italian" msgid="6497079660449781213">"italské"</string>
<string name="keyboard_layout_danish" msgid="8036432066627127851">"dánské"</string>
<string name="keyboard_layout_norwegian" msgid="9090097917011040937">"norské"</string>
diff --git a/packages/InputDevices/res/values-da/strings.xml b/packages/InputDevices/res/values-da/strings.xml
index 7e446fd44bc4..cf2aecf1783a 100644
--- a/packages/InputDevices/res/values-da/strings.xml
+++ b/packages/InputDevices/res/values-da/strings.xml
@@ -19,6 +19,7 @@
<string name="keyboard_layout_swiss_german_label" msgid="2305520941993314258">"Schweizertysk"</string>
<string name="keyboard_layout_belgian" msgid="2011984572838651558">"Belgisk"</string>
<string name="keyboard_layout_bulgarian" msgid="8951224309972028398">"Bulgarsk"</string>
+ <string name="keyboard_layout_bulgarian_phonetic" msgid="7568914730360106653">"Bulgarsk, fonetisk"</string>
<string name="keyboard_layout_italian" msgid="6497079660449781213">"Italiensk"</string>
<string name="keyboard_layout_danish" msgid="8036432066627127851">"Dansk"</string>
<string name="keyboard_layout_norwegian" msgid="9090097917011040937">"Norsk"</string>
diff --git a/packages/InputDevices/res/values-de/strings.xml b/packages/InputDevices/res/values-de/strings.xml
index c8a7ab1f34fe..1e786855ac25 100644
--- a/packages/InputDevices/res/values-de/strings.xml
+++ b/packages/InputDevices/res/values-de/strings.xml
@@ -19,6 +19,7 @@
<string name="keyboard_layout_swiss_german_label" msgid="2305520941993314258">"Schweizerdeutsch"</string>
<string name="keyboard_layout_belgian" msgid="2011984572838651558">"Belgisch"</string>
<string name="keyboard_layout_bulgarian" msgid="8951224309972028398">"Bulgarisch"</string>
+ <string name="keyboard_layout_bulgarian_phonetic" msgid="7568914730360106653">"Bulgarisch – phonetisch"</string>
<string name="keyboard_layout_italian" msgid="6497079660449781213">"Italienisch"</string>
<string name="keyboard_layout_danish" msgid="8036432066627127851">"Dänisch"</string>
<string name="keyboard_layout_norwegian" msgid="9090097917011040937">"Norwegisch"</string>
diff --git a/packages/InputDevices/res/values-el/strings.xml b/packages/InputDevices/res/values-el/strings.xml
index ff7cd022cee7..eb2cc9b09a09 100644
--- a/packages/InputDevices/res/values-el/strings.xml
+++ b/packages/InputDevices/res/values-el/strings.xml
@@ -19,6 +19,7 @@
<string name="keyboard_layout_swiss_german_label" msgid="2305520941993314258">"Γερμανικά Ελβετίας"</string>
<string name="keyboard_layout_belgian" msgid="2011984572838651558">"Βελγικά"</string>
<string name="keyboard_layout_bulgarian" msgid="8951224309972028398">"Βουλγαρικά"</string>
+ <string name="keyboard_layout_bulgarian_phonetic" msgid="7568914730360106653">"Βουλγαρικά (Φωνητικό)"</string>
<string name="keyboard_layout_italian" msgid="6497079660449781213">"Ιταλικά"</string>
<string name="keyboard_layout_danish" msgid="8036432066627127851">"Δανικά"</string>
<string name="keyboard_layout_norwegian" msgid="9090097917011040937">"Νορβηγικά"</string>
diff --git a/packages/InputDevices/res/values-en-rAU/strings.xml b/packages/InputDevices/res/values-en-rAU/strings.xml
index 02a8e6d4138f..f79c3daf25ef 100644
--- a/packages/InputDevices/res/values-en-rAU/strings.xml
+++ b/packages/InputDevices/res/values-en-rAU/strings.xml
@@ -19,6 +19,7 @@
<string name="keyboard_layout_swiss_german_label" msgid="2305520941993314258">"Swiss German"</string>
<string name="keyboard_layout_belgian" msgid="2011984572838651558">"Belgian"</string>
<string name="keyboard_layout_bulgarian" msgid="8951224309972028398">"Bulgarian"</string>
+ <string name="keyboard_layout_bulgarian_phonetic" msgid="7568914730360106653">"Bulgarian, phonetic"</string>
<string name="keyboard_layout_italian" msgid="6497079660449781213">"Italian"</string>
<string name="keyboard_layout_danish" msgid="8036432066627127851">"Danish"</string>
<string name="keyboard_layout_norwegian" msgid="9090097917011040937">"Norwegian"</string>
diff --git a/packages/InputDevices/res/values-en-rCA/strings.xml b/packages/InputDevices/res/values-en-rCA/strings.xml
index 02a8e6d4138f..f79c3daf25ef 100644
--- a/packages/InputDevices/res/values-en-rCA/strings.xml
+++ b/packages/InputDevices/res/values-en-rCA/strings.xml
@@ -19,6 +19,7 @@
<string name="keyboard_layout_swiss_german_label" msgid="2305520941993314258">"Swiss German"</string>
<string name="keyboard_layout_belgian" msgid="2011984572838651558">"Belgian"</string>
<string name="keyboard_layout_bulgarian" msgid="8951224309972028398">"Bulgarian"</string>
+ <string name="keyboard_layout_bulgarian_phonetic" msgid="7568914730360106653">"Bulgarian, phonetic"</string>
<string name="keyboard_layout_italian" msgid="6497079660449781213">"Italian"</string>
<string name="keyboard_layout_danish" msgid="8036432066627127851">"Danish"</string>
<string name="keyboard_layout_norwegian" msgid="9090097917011040937">"Norwegian"</string>
diff --git a/packages/InputDevices/res/values-en-rGB/strings.xml b/packages/InputDevices/res/values-en-rGB/strings.xml
index 02a8e6d4138f..f79c3daf25ef 100644
--- a/packages/InputDevices/res/values-en-rGB/strings.xml
+++ b/packages/InputDevices/res/values-en-rGB/strings.xml
@@ -19,6 +19,7 @@
<string name="keyboard_layout_swiss_german_label" msgid="2305520941993314258">"Swiss German"</string>
<string name="keyboard_layout_belgian" msgid="2011984572838651558">"Belgian"</string>
<string name="keyboard_layout_bulgarian" msgid="8951224309972028398">"Bulgarian"</string>
+ <string name="keyboard_layout_bulgarian_phonetic" msgid="7568914730360106653">"Bulgarian, phonetic"</string>
<string name="keyboard_layout_italian" msgid="6497079660449781213">"Italian"</string>
<string name="keyboard_layout_danish" msgid="8036432066627127851">"Danish"</string>
<string name="keyboard_layout_norwegian" msgid="9090097917011040937">"Norwegian"</string>
diff --git a/packages/InputDevices/res/values-en-rIN/strings.xml b/packages/InputDevices/res/values-en-rIN/strings.xml
index 02a8e6d4138f..f79c3daf25ef 100644
--- a/packages/InputDevices/res/values-en-rIN/strings.xml
+++ b/packages/InputDevices/res/values-en-rIN/strings.xml
@@ -19,6 +19,7 @@
<string name="keyboard_layout_swiss_german_label" msgid="2305520941993314258">"Swiss German"</string>
<string name="keyboard_layout_belgian" msgid="2011984572838651558">"Belgian"</string>
<string name="keyboard_layout_bulgarian" msgid="8951224309972028398">"Bulgarian"</string>
+ <string name="keyboard_layout_bulgarian_phonetic" msgid="7568914730360106653">"Bulgarian, phonetic"</string>
<string name="keyboard_layout_italian" msgid="6497079660449781213">"Italian"</string>
<string name="keyboard_layout_danish" msgid="8036432066627127851">"Danish"</string>
<string name="keyboard_layout_norwegian" msgid="9090097917011040937">"Norwegian"</string>
diff --git a/packages/InputDevices/res/values-en-rXC/strings.xml b/packages/InputDevices/res/values-en-rXC/strings.xml
index a8d342f04258..d0881864efef 100644
--- a/packages/InputDevices/res/values-en-rXC/strings.xml
+++ b/packages/InputDevices/res/values-en-rXC/strings.xml
@@ -19,6 +19,7 @@
<string name="keyboard_layout_swiss_german_label" msgid="2305520941993314258">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‎‏‏‏‏‏‏‏‏‏‏‏‏‏‎‏‏‎‏‏‎‏‏‎‎‎‏‎‏‎‎‏‏‏‎‎‎‎‏‏‎‏‏‏‏‎‎‎‏‎‎‎‏‏‏‏‏‎‏‎‎‏‎‎Swiss German‎‏‎‎‏‎"</string>
<string name="keyboard_layout_belgian" msgid="2011984572838651558">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‎‏‏‏‎‏‏‏‏‏‎‏‏‎‎‎‎‎‎‎‎‎‏‎‏‎‎‏‏‏‏‎‎‎‏‏‏‎‏‏‎‎‏‏‏‎‏‎‎‏‎‎‏‏‎‏‎‏‎‎‏‏‎‎Belgian‎‏‎‎‏‎"</string>
<string name="keyboard_layout_bulgarian" msgid="8951224309972028398">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‏‏‏‏‎‎‎‎‏‏‏‎‎‏‎‎‏‎‎‎‏‏‎‎‎‏‎‏‎‏‏‎‎‏‎‏‎‎‏‎‏‏‏‎‎‏‎‏‎‎‏‎‏‏‏‏‏‎‏‏‏‎‎Bulgarian‎‏‎‎‏‎"</string>
+ <string name="keyboard_layout_bulgarian_phonetic" msgid="7568914730360106653">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‏‎‏‎‎‏‎‎‎‎‏‎‏‎‎‎‏‎‏‏‏‏‏‏‎‏‏‎‎‎‎‏‏‎‏‏‏‎‎‏‎‏‎‏‎‏‏‏‏‎‏‏‏‎‏‎‎‏‏‏‎‏‎Bulgarian, Phonetic‎‏‎‎‏‎"</string>
<string name="keyboard_layout_italian" msgid="6497079660449781213">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‎‏‏‎‏‎‎‎‏‎‏‎‏‎‎‏‎‎‎‎‏‏‏‎‎‎‎‏‏‏‎‎‏‏‎‏‏‎‏‎‎‎‎‏‏‎‎‏‎‏‎‏‎‏‏‏‎‏‏‏‎‏‎Italian‎‏‎‎‏‎"</string>
<string name="keyboard_layout_danish" msgid="8036432066627127851">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‏‎‏‏‏‏‏‎‎‎‎‏‏‏‎‎‏‎‎‏‎‎‎‏‏‎‎‎‏‎‏‏‎‎‎‎‎‎‏‏‎‏‏‏‏‎‎‏‎‎‏‎‏‎‎‎‏‎‏‎‏‏‎Danish‎‏‎‎‏‎"</string>
<string name="keyboard_layout_norwegian" msgid="9090097917011040937">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‏‏‏‏‏‎‎‎‏‎‎‏‏‎‏‎‎‎‎‎‏‏‏‏‏‎‎‏‎‎‏‏‏‎‎‏‎‎‏‏‏‏‎‏‏‎‎‎‎‏‏‎‏‎‏‎‏‎‏‎‎‏‎Norwegian‎‏‎‎‏‎"</string>
diff --git a/packages/InputDevices/res/values-es-rUS/strings.xml b/packages/InputDevices/res/values-es-rUS/strings.xml
index 9090cb7b02b2..e8d6597f0e57 100644
--- a/packages/InputDevices/res/values-es-rUS/strings.xml
+++ b/packages/InputDevices/res/values-es-rUS/strings.xml
@@ -19,6 +19,7 @@
<string name="keyboard_layout_swiss_german_label" msgid="2305520941993314258">"Alemán de Suiza"</string>
<string name="keyboard_layout_belgian" msgid="2011984572838651558">"Belga"</string>
<string name="keyboard_layout_bulgarian" msgid="8951224309972028398">"Búlgaro"</string>
+ <string name="keyboard_layout_bulgarian_phonetic" msgid="7568914730360106653">"Búlgaro fonético"</string>
<string name="keyboard_layout_italian" msgid="6497079660449781213">"Italiano"</string>
<string name="keyboard_layout_danish" msgid="8036432066627127851">"Danés"</string>
<string name="keyboard_layout_norwegian" msgid="9090097917011040937">"Noruego"</string>
diff --git a/packages/InputDevices/res/values-es/strings.xml b/packages/InputDevices/res/values-es/strings.xml
index 66ea65557538..9396e460eba1 100644
--- a/packages/InputDevices/res/values-es/strings.xml
+++ b/packages/InputDevices/res/values-es/strings.xml
@@ -19,6 +19,7 @@
<string name="keyboard_layout_swiss_german_label" msgid="2305520941993314258">"Alemán suizo"</string>
<string name="keyboard_layout_belgian" msgid="2011984572838651558">"Belga"</string>
<string name="keyboard_layout_bulgarian" msgid="8951224309972028398">"Búlgaro"</string>
+ <string name="keyboard_layout_bulgarian_phonetic" msgid="7568914730360106653">"Búlgaro (fonético)"</string>
<string name="keyboard_layout_italian" msgid="6497079660449781213">"Italiano"</string>
<string name="keyboard_layout_danish" msgid="8036432066627127851">"Danés"</string>
<string name="keyboard_layout_norwegian" msgid="9090097917011040937">"Noruego"</string>
diff --git a/packages/InputDevices/res/values-et/strings.xml b/packages/InputDevices/res/values-et/strings.xml
index 9674a0d1d616..cf28e9f66dae 100644
--- a/packages/InputDevices/res/values-et/strings.xml
+++ b/packages/InputDevices/res/values-et/strings.xml
@@ -19,6 +19,7 @@
<string name="keyboard_layout_swiss_german_label" msgid="2305520941993314258">"Šveitsisaksa"</string>
<string name="keyboard_layout_belgian" msgid="2011984572838651558">"Belgia"</string>
<string name="keyboard_layout_bulgarian" msgid="8951224309972028398">"Bulgaaria"</string>
+ <string name="keyboard_layout_bulgarian_phonetic" msgid="7568914730360106653">"Bulgaaria, foneetiline"</string>
<string name="keyboard_layout_italian" msgid="6497079660449781213">"Itaalia"</string>
<string name="keyboard_layout_danish" msgid="8036432066627127851">"Taani"</string>
<string name="keyboard_layout_norwegian" msgid="9090097917011040937">"Norra"</string>
diff --git a/packages/InputDevices/res/values-eu/strings.xml b/packages/InputDevices/res/values-eu/strings.xml
index 357b6184648f..1e080fc83141 100644
--- a/packages/InputDevices/res/values-eu/strings.xml
+++ b/packages/InputDevices/res/values-eu/strings.xml
@@ -19,6 +19,7 @@
<string name="keyboard_layout_swiss_german_label" msgid="2305520941993314258">"Alemana (Suitza)"</string>
<string name="keyboard_layout_belgian" msgid="2011984572838651558">"Belgikarra"</string>
<string name="keyboard_layout_bulgarian" msgid="8951224309972028398">"Bulgariarra"</string>
+ <string name="keyboard_layout_bulgarian_phonetic" msgid="7568914730360106653">"Bulgariarra, fonetikoa"</string>
<string name="keyboard_layout_italian" msgid="6497079660449781213">"Italiarra"</string>
<string name="keyboard_layout_danish" msgid="8036432066627127851">"Daniarra"</string>
<string name="keyboard_layout_norwegian" msgid="9090097917011040937">"Norvegiarra"</string>
diff --git a/packages/InputDevices/res/values-fa/strings.xml b/packages/InputDevices/res/values-fa/strings.xml
index 5395d27c2cef..5cb237e35053 100644
--- a/packages/InputDevices/res/values-fa/strings.xml
+++ b/packages/InputDevices/res/values-fa/strings.xml
@@ -19,6 +19,7 @@
<string name="keyboard_layout_swiss_german_label" msgid="2305520941993314258">"آلمانی سوئیسی"</string>
<string name="keyboard_layout_belgian" msgid="2011984572838651558">"بلژیکی"</string>
<string name="keyboard_layout_bulgarian" msgid="8951224309972028398">"بلغاری"</string>
+ <string name="keyboard_layout_bulgarian_phonetic" msgid="7568914730360106653">"بلغاری، آوایی"</string>
<string name="keyboard_layout_italian" msgid="6497079660449781213">"ایتالیایی"</string>
<string name="keyboard_layout_danish" msgid="8036432066627127851">"دانمارکی"</string>
<string name="keyboard_layout_norwegian" msgid="9090097917011040937">"نروژی"</string>
diff --git a/packages/InputDevices/res/values-fi/strings.xml b/packages/InputDevices/res/values-fi/strings.xml
index d4b0580dac69..da7210657381 100644
--- a/packages/InputDevices/res/values-fi/strings.xml
+++ b/packages/InputDevices/res/values-fi/strings.xml
@@ -19,6 +19,7 @@
<string name="keyboard_layout_swiss_german_label" msgid="2305520941993314258">"sveitsinsaksa"</string>
<string name="keyboard_layout_belgian" msgid="2011984572838651558">"belgialainen"</string>
<string name="keyboard_layout_bulgarian" msgid="8951224309972028398">"bulgaria"</string>
+ <string name="keyboard_layout_bulgarian_phonetic" msgid="7568914730360106653">"bulgaria, foneettinen"</string>
<string name="keyboard_layout_italian" msgid="6497079660449781213">"italia"</string>
<string name="keyboard_layout_danish" msgid="8036432066627127851">"tanska"</string>
<string name="keyboard_layout_norwegian" msgid="9090097917011040937">"norja"</string>
@@ -26,8 +27,7 @@
<string name="keyboard_layout_finnish" msgid="5585659438924315466">"suomi"</string>
<string name="keyboard_layout_croatian" msgid="4172229471079281138">"kroaatti"</string>
<string name="keyboard_layout_czech" msgid="1349256901452975343">"tšekki"</string>
- <!-- no translation found for keyboard_layout_czech_qwerty (3331402534128515501) -->
- <skip />
+ <string name="keyboard_layout_czech_qwerty" msgid="3331402534128515501">"tšekki (QWERTY-tyyli)"</string>
<string name="keyboard_layout_estonian" msgid="8775830985185665274">"viro"</string>
<string name="keyboard_layout_hungarian" msgid="4154963661406035109">"unkari"</string>
<string name="keyboard_layout_icelandic" msgid="5836645650912489642">"islanti"</string>
diff --git a/packages/InputDevices/res/values-fr-rCA/strings.xml b/packages/InputDevices/res/values-fr-rCA/strings.xml
index e714e8380800..697fff6f1339 100644
--- a/packages/InputDevices/res/values-fr-rCA/strings.xml
+++ b/packages/InputDevices/res/values-fr-rCA/strings.xml
@@ -19,6 +19,8 @@
<string name="keyboard_layout_swiss_german_label" msgid="2305520941993314258">"Allemand (Suisse)"</string>
<string name="keyboard_layout_belgian" msgid="2011984572838651558">"Belge"</string>
<string name="keyboard_layout_bulgarian" msgid="8951224309972028398">"Bulgare"</string>
+ <!-- no translation found for keyboard_layout_bulgarian_phonetic (7568914730360106653) -->
+ <skip />
<string name="keyboard_layout_italian" msgid="6497079660449781213">"Italien"</string>
<string name="keyboard_layout_danish" msgid="8036432066627127851">"Danois"</string>
<string name="keyboard_layout_norwegian" msgid="9090097917011040937">"Norvégien"</string>
diff --git a/packages/InputDevices/res/values-fr/strings.xml b/packages/InputDevices/res/values-fr/strings.xml
index 0a022f192ea2..2e14019340fc 100644
--- a/packages/InputDevices/res/values-fr/strings.xml
+++ b/packages/InputDevices/res/values-fr/strings.xml
@@ -19,6 +19,8 @@
<string name="keyboard_layout_swiss_german_label" msgid="2305520941993314258">"Allemand (Suisse)"</string>
<string name="keyboard_layout_belgian" msgid="2011984572838651558">"Belge"</string>
<string name="keyboard_layout_bulgarian" msgid="8951224309972028398">"Bulgare"</string>
+ <!-- no translation found for keyboard_layout_bulgarian_phonetic (7568914730360106653) -->
+ <skip />
<string name="keyboard_layout_italian" msgid="6497079660449781213">"Italien"</string>
<string name="keyboard_layout_danish" msgid="8036432066627127851">"Danois"</string>
<string name="keyboard_layout_norwegian" msgid="9090097917011040937">"Norvégien"</string>
diff --git a/packages/InputDevices/res/values-gl/strings.xml b/packages/InputDevices/res/values-gl/strings.xml
index 0c86f816fa85..40ede04c8e05 100644
--- a/packages/InputDevices/res/values-gl/strings.xml
+++ b/packages/InputDevices/res/values-gl/strings.xml
@@ -19,6 +19,7 @@
<string name="keyboard_layout_swiss_german_label" msgid="2305520941993314258">"Alemán suízo"</string>
<string name="keyboard_layout_belgian" msgid="2011984572838651558">"Belga"</string>
<string name="keyboard_layout_bulgarian" msgid="8951224309972028398">"Búlgaro"</string>
+ <string name="keyboard_layout_bulgarian_phonetic" msgid="7568914730360106653">"Búlgaro, fonético"</string>
<string name="keyboard_layout_italian" msgid="6497079660449781213">"Italiano"</string>
<string name="keyboard_layout_danish" msgid="8036432066627127851">"Dinamarqués"</string>
<string name="keyboard_layout_norwegian" msgid="9090097917011040937">"Noruegués"</string>
diff --git a/packages/InputDevices/res/values-gu/strings.xml b/packages/InputDevices/res/values-gu/strings.xml
index 8648389db862..631fc1497ce4 100644
--- a/packages/InputDevices/res/values-gu/strings.xml
+++ b/packages/InputDevices/res/values-gu/strings.xml
@@ -19,6 +19,7 @@
<string name="keyboard_layout_swiss_german_label" msgid="2305520941993314258">"સ્વિસ જર્મન"</string>
<string name="keyboard_layout_belgian" msgid="2011984572838651558">"બેલ્જિયન"</string>
<string name="keyboard_layout_bulgarian" msgid="8951224309972028398">"બલ્ગેરિયન"</string>
+ <string name="keyboard_layout_bulgarian_phonetic" msgid="7568914730360106653">"બલ્ગેરિયન ફોનેટિક"</string>
<string name="keyboard_layout_italian" msgid="6497079660449781213">"ઇટાલિયન"</string>
<string name="keyboard_layout_danish" msgid="8036432066627127851">"ડેનિશ"</string>
<string name="keyboard_layout_norwegian" msgid="9090097917011040937">"નોર્વેજીયન"</string>
diff --git a/packages/InputDevices/res/values-hi/strings.xml b/packages/InputDevices/res/values-hi/strings.xml
index 6e674edbedc1..2ffebdd2c1ff 100644
--- a/packages/InputDevices/res/values-hi/strings.xml
+++ b/packages/InputDevices/res/values-hi/strings.xml
@@ -19,6 +19,8 @@
<string name="keyboard_layout_swiss_german_label" msgid="2305520941993314258">"स्विस जर्मन"</string>
<string name="keyboard_layout_belgian" msgid="2011984572838651558">"बेल्जियाई"</string>
<string name="keyboard_layout_bulgarian" msgid="8951224309972028398">"बुल्‍गारियाई"</string>
+ <!-- no translation found for keyboard_layout_bulgarian_phonetic (7568914730360106653) -->
+ <skip />
<string name="keyboard_layout_italian" msgid="6497079660449781213">"इटैलियन"</string>
<string name="keyboard_layout_danish" msgid="8036432066627127851">"डैनिश"</string>
<string name="keyboard_layout_norwegian" msgid="9090097917011040937">"नार्वेजियाई"</string>
diff --git a/packages/InputDevices/res/values-hr/strings.xml b/packages/InputDevices/res/values-hr/strings.xml
index cd2dcc183058..aff2a37c2b7f 100644
--- a/packages/InputDevices/res/values-hr/strings.xml
+++ b/packages/InputDevices/res/values-hr/strings.xml
@@ -19,6 +19,7 @@
<string name="keyboard_layout_swiss_german_label" msgid="2305520941993314258">"švicarsko-njemačka"</string>
<string name="keyboard_layout_belgian" msgid="2011984572838651558">"belgijska"</string>
<string name="keyboard_layout_bulgarian" msgid="8951224309972028398">"bugarska"</string>
+ <string name="keyboard_layout_bulgarian_phonetic" msgid="7568914730360106653">"bugarska (fonetska)"</string>
<string name="keyboard_layout_italian" msgid="6497079660449781213">"talijanska"</string>
<string name="keyboard_layout_danish" msgid="8036432066627127851">"danska"</string>
<string name="keyboard_layout_norwegian" msgid="9090097917011040937">"norveška"</string>
diff --git a/packages/InputDevices/res/values-hu/strings.xml b/packages/InputDevices/res/values-hu/strings.xml
index 1c7a89a06059..50d667b40b62 100644
--- a/packages/InputDevices/res/values-hu/strings.xml
+++ b/packages/InputDevices/res/values-hu/strings.xml
@@ -19,6 +19,7 @@
<string name="keyboard_layout_swiss_german_label" msgid="2305520941993314258">"svájci német"</string>
<string name="keyboard_layout_belgian" msgid="2011984572838651558">"belga"</string>
<string name="keyboard_layout_bulgarian" msgid="8951224309972028398">"bolgár"</string>
+ <string name="keyboard_layout_bulgarian_phonetic" msgid="7568914730360106653">"bolgár fonetikus"</string>
<string name="keyboard_layout_italian" msgid="6497079660449781213">"olasz"</string>
<string name="keyboard_layout_danish" msgid="8036432066627127851">"dán"</string>
<string name="keyboard_layout_norwegian" msgid="9090097917011040937">"norvég"</string>
diff --git a/packages/InputDevices/res/values-hy/strings.xml b/packages/InputDevices/res/values-hy/strings.xml
index fff9fbc5bfd8..4a6fe2b3be4f 100644
--- a/packages/InputDevices/res/values-hy/strings.xml
+++ b/packages/InputDevices/res/values-hy/strings.xml
@@ -19,6 +19,7 @@
<string name="keyboard_layout_swiss_german_label" msgid="2305520941993314258">"Շվեյցարական գերմաներեն"</string>
<string name="keyboard_layout_belgian" msgid="2011984572838651558">"Բելգիական"</string>
<string name="keyboard_layout_bulgarian" msgid="8951224309972028398">"Բուլղարերեն"</string>
+ <string name="keyboard_layout_bulgarian_phonetic" msgid="7568914730360106653">"բուլղարերեն (հնչյունային)"</string>
<string name="keyboard_layout_italian" msgid="6497079660449781213">"Իտալերեն"</string>
<string name="keyboard_layout_danish" msgid="8036432066627127851">"Դանիերեն"</string>
<string name="keyboard_layout_norwegian" msgid="9090097917011040937">"Նորվեգերեն"</string>
diff --git a/packages/InputDevices/res/values-in/strings.xml b/packages/InputDevices/res/values-in/strings.xml
index 04a6dfa38bdd..90ba97d16f6a 100644
--- a/packages/InputDevices/res/values-in/strings.xml
+++ b/packages/InputDevices/res/values-in/strings.xml
@@ -19,6 +19,7 @@
<string name="keyboard_layout_swiss_german_label" msgid="2305520941993314258">"Jerman Swiss"</string>
<string name="keyboard_layout_belgian" msgid="2011984572838651558">"Belgia"</string>
<string name="keyboard_layout_bulgarian" msgid="8951224309972028398">"Bulgaria"</string>
+ <string name="keyboard_layout_bulgarian_phonetic" msgid="7568914730360106653">"Bulgaria, Fonetik"</string>
<string name="keyboard_layout_italian" msgid="6497079660449781213">"Italia"</string>
<string name="keyboard_layout_danish" msgid="8036432066627127851">"Denmark"</string>
<string name="keyboard_layout_norwegian" msgid="9090097917011040937">"Norwegia"</string>
diff --git a/packages/InputDevices/res/values-is/strings.xml b/packages/InputDevices/res/values-is/strings.xml
index a60332a3f979..0889b21f3a64 100644
--- a/packages/InputDevices/res/values-is/strings.xml
+++ b/packages/InputDevices/res/values-is/strings.xml
@@ -19,6 +19,7 @@
<string name="keyboard_layout_swiss_german_label" msgid="2305520941993314258">"Svissneskt-þýskt"</string>
<string name="keyboard_layout_belgian" msgid="2011984572838651558">"Belgískt"</string>
<string name="keyboard_layout_bulgarian" msgid="8951224309972028398">"Búlgarskt"</string>
+ <string name="keyboard_layout_bulgarian_phonetic" msgid="7568914730360106653">"Búlgarskt hljóðritunarlyklaborð"</string>
<string name="keyboard_layout_italian" msgid="6497079660449781213">"Ítalskt"</string>
<string name="keyboard_layout_danish" msgid="8036432066627127851">"Danskt"</string>
<string name="keyboard_layout_norwegian" msgid="9090097917011040937">"Norskt"</string>
diff --git a/packages/InputDevices/res/values-it/strings.xml b/packages/InputDevices/res/values-it/strings.xml
index ac137e41b945..77f78c6f49d0 100644
--- a/packages/InputDevices/res/values-it/strings.xml
+++ b/packages/InputDevices/res/values-it/strings.xml
@@ -19,6 +19,7 @@
<string name="keyboard_layout_swiss_german_label" msgid="2305520941993314258">"Tedesco svizzero"</string>
<string name="keyboard_layout_belgian" msgid="2011984572838651558">"Belga"</string>
<string name="keyboard_layout_bulgarian" msgid="8951224309972028398">"Bulgaro"</string>
+ <string name="keyboard_layout_bulgarian_phonetic" msgid="7568914730360106653">"Bulgara, fonetica"</string>
<string name="keyboard_layout_italian" msgid="6497079660449781213">"Italiano"</string>
<string name="keyboard_layout_danish" msgid="8036432066627127851">"Danese"</string>
<string name="keyboard_layout_norwegian" msgid="9090097917011040937">"Norvegese"</string>
diff --git a/packages/InputDevices/res/values-iw/strings.xml b/packages/InputDevices/res/values-iw/strings.xml
index 544dde22c2cb..52641b2ba1ff 100644
--- a/packages/InputDevices/res/values-iw/strings.xml
+++ b/packages/InputDevices/res/values-iw/strings.xml
@@ -19,6 +19,7 @@
<string name="keyboard_layout_swiss_german_label" msgid="2305520941993314258">"גרמנית שוויצרית"</string>
<string name="keyboard_layout_belgian" msgid="2011984572838651558">"בלגית"</string>
<string name="keyboard_layout_bulgarian" msgid="8951224309972028398">"בולגרית"</string>
+ <string name="keyboard_layout_bulgarian_phonetic" msgid="7568914730360106653">"בולגרית פונטית"</string>
<string name="keyboard_layout_italian" msgid="6497079660449781213">"איטלקית"</string>
<string name="keyboard_layout_danish" msgid="8036432066627127851">"דנית"</string>
<string name="keyboard_layout_norwegian" msgid="9090097917011040937">"נורווגית"</string>
diff --git a/packages/InputDevices/res/values-ja/strings.xml b/packages/InputDevices/res/values-ja/strings.xml
index 717cbb985c74..2961548599a6 100644
--- a/packages/InputDevices/res/values-ja/strings.xml
+++ b/packages/InputDevices/res/values-ja/strings.xml
@@ -19,6 +19,7 @@
<string name="keyboard_layout_swiss_german_label" msgid="2305520941993314258">"ドイツ語(スイス)"</string>
<string name="keyboard_layout_belgian" msgid="2011984572838651558">"ベルギー語"</string>
<string name="keyboard_layout_bulgarian" msgid="8951224309972028398">"ブルガリア語"</string>
+ <string name="keyboard_layout_bulgarian_phonetic" msgid="7568914730360106653">"ブルガリア語(表音)"</string>
<string name="keyboard_layout_italian" msgid="6497079660449781213">"イタリア語"</string>
<string name="keyboard_layout_danish" msgid="8036432066627127851">"デンマーク語"</string>
<string name="keyboard_layout_norwegian" msgid="9090097917011040937">"ノルウェー語"</string>
diff --git a/packages/InputDevices/res/values-ka/strings.xml b/packages/InputDevices/res/values-ka/strings.xml
index ee42b35440b3..2ccfeb2f635f 100644
--- a/packages/InputDevices/res/values-ka/strings.xml
+++ b/packages/InputDevices/res/values-ka/strings.xml
@@ -19,6 +19,7 @@
<string name="keyboard_layout_swiss_german_label" msgid="2305520941993314258">"შვეიცარიული გერმანული"</string>
<string name="keyboard_layout_belgian" msgid="2011984572838651558">"ბელგიური"</string>
<string name="keyboard_layout_bulgarian" msgid="8951224309972028398">"ბულგარული"</string>
+ <string name="keyboard_layout_bulgarian_phonetic" msgid="7568914730360106653">"ბულგარული ფონეტიკური"</string>
<string name="keyboard_layout_italian" msgid="6497079660449781213">"იტალიური"</string>
<string name="keyboard_layout_danish" msgid="8036432066627127851">"დანიური"</string>
<string name="keyboard_layout_norwegian" msgid="9090097917011040937">"ნორვეგიული"</string>
diff --git a/packages/InputDevices/res/values-kk/strings.xml b/packages/InputDevices/res/values-kk/strings.xml
index e4ad73e2efd4..cfdc3f85de4a 100644
--- a/packages/InputDevices/res/values-kk/strings.xml
+++ b/packages/InputDevices/res/values-kk/strings.xml
@@ -19,6 +19,8 @@
<string name="keyboard_layout_swiss_german_label" msgid="2305520941993314258">"Швейцариялық неміс"</string>
<string name="keyboard_layout_belgian" msgid="2011984572838651558">"Бельгиялық"</string>
<string name="keyboard_layout_bulgarian" msgid="8951224309972028398">"Болгар"</string>
+ <!-- no translation found for keyboard_layout_bulgarian_phonetic (7568914730360106653) -->
+ <skip />
<string name="keyboard_layout_italian" msgid="6497079660449781213">"Италиян"</string>
<string name="keyboard_layout_danish" msgid="8036432066627127851">"Дат"</string>
<string name="keyboard_layout_norwegian" msgid="9090097917011040937">"Норвег"</string>
@@ -26,8 +28,7 @@
<string name="keyboard_layout_finnish" msgid="5585659438924315466">"Фин"</string>
<string name="keyboard_layout_croatian" msgid="4172229471079281138">"Хорват"</string>
<string name="keyboard_layout_czech" msgid="1349256901452975343">"Чех"</string>
- <!-- no translation found for keyboard_layout_czech_qwerty (3331402534128515501) -->
- <skip />
+ <string name="keyboard_layout_czech_qwerty" msgid="3331402534128515501">"Чех (QWERTY)"</string>
<string name="keyboard_layout_estonian" msgid="8775830985185665274">"Эстон"</string>
<string name="keyboard_layout_hungarian" msgid="4154963661406035109">"Мадияр"</string>
<string name="keyboard_layout_icelandic" msgid="5836645650912489642">"Исланд"</string>
diff --git a/packages/InputDevices/res/values-km/strings.xml b/packages/InputDevices/res/values-km/strings.xml
index a2c3262559c9..2aaf816c7deb 100644
--- a/packages/InputDevices/res/values-km/strings.xml
+++ b/packages/InputDevices/res/values-km/strings.xml
@@ -19,6 +19,8 @@
<string name="keyboard_layout_swiss_german_label" msgid="2305520941993314258">"អាល្លឺម៉ង់ ស្វីស"</string>
<string name="keyboard_layout_belgian" msgid="2011984572838651558">"បែលហ្ស៊ិក"</string>
<string name="keyboard_layout_bulgarian" msgid="8951224309972028398">"ប៊ុលហ្ការី"</string>
+ <!-- no translation found for keyboard_layout_bulgarian_phonetic (7568914730360106653) -->
+ <skip />
<string name="keyboard_layout_italian" msgid="6497079660449781213">"អ៊ីតាលី"</string>
<string name="keyboard_layout_danish" msgid="8036432066627127851">"ដាណឺម៉ាក"</string>
<string name="keyboard_layout_norwegian" msgid="9090097917011040937">"ន័រវែស"</string>
diff --git a/packages/InputDevices/res/values-kn/strings.xml b/packages/InputDevices/res/values-kn/strings.xml
index 846101c57c4a..1e3c6932c3d4 100644
--- a/packages/InputDevices/res/values-kn/strings.xml
+++ b/packages/InputDevices/res/values-kn/strings.xml
@@ -19,6 +19,7 @@
<string name="keyboard_layout_swiss_german_label" msgid="2305520941993314258">"ಸ್ವಿಸ್ ಜರ್ಮನ್"</string>
<string name="keyboard_layout_belgian" msgid="2011984572838651558">"ಬೆಲ್ಜಿಯನ್"</string>
<string name="keyboard_layout_bulgarian" msgid="8951224309972028398">"ಬಲ್ಗೇರಿಯನ್‌"</string>
+ <string name="keyboard_layout_bulgarian_phonetic" msgid="7568914730360106653">"ಬಲ್ಗೇರಿಯನ್ ಫೋನೆಟಿಕ್‌‌"</string>
<string name="keyboard_layout_italian" msgid="6497079660449781213">"ಇಟಾಲಿಯನ್"</string>
<string name="keyboard_layout_danish" msgid="8036432066627127851">"ಡ್ಯಾನಿಶ್"</string>
<string name="keyboard_layout_norwegian" msgid="9090097917011040937">"ನಾರ್ವೇಜಿಯನ್"</string>
@@ -26,8 +27,7 @@
<string name="keyboard_layout_finnish" msgid="5585659438924315466">"ಫಿನ್ನಿಷ್"</string>
<string name="keyboard_layout_croatian" msgid="4172229471079281138">"ಕ್ರೊಯೇಶಿಯನ್"</string>
<string name="keyboard_layout_czech" msgid="1349256901452975343">"ಜೆಕ್"</string>
- <!-- no translation found for keyboard_layout_czech_qwerty (3331402534128515501) -->
- <skip />
+ <string name="keyboard_layout_czech_qwerty" msgid="3331402534128515501">"ಝೆಕ್ QWERTY ಶೈಲಿ"</string>
<string name="keyboard_layout_estonian" msgid="8775830985185665274">"ಎಸ್ಟೋನಿಯನ್"</string>
<string name="keyboard_layout_hungarian" msgid="4154963661406035109">"ಹಂಗೇರಿಯನ್"</string>
<string name="keyboard_layout_icelandic" msgid="5836645650912489642">"ಐಸ್‌ಲ್ಯಾಂಡಿಕ್"</string>
diff --git a/packages/InputDevices/res/values-ko/strings.xml b/packages/InputDevices/res/values-ko/strings.xml
index 2677432970da..147050462eab 100644
--- a/packages/InputDevices/res/values-ko/strings.xml
+++ b/packages/InputDevices/res/values-ko/strings.xml
@@ -19,6 +19,7 @@
<string name="keyboard_layout_swiss_german_label" msgid="2305520941993314258">"독일어(스위스)"</string>
<string name="keyboard_layout_belgian" msgid="2011984572838651558">"벨기에어"</string>
<string name="keyboard_layout_bulgarian" msgid="8951224309972028398">"불가리아어"</string>
+ <string name="keyboard_layout_bulgarian_phonetic" msgid="7568914730360106653">"불가리아어, 표음"</string>
<string name="keyboard_layout_italian" msgid="6497079660449781213">"이탈리아어"</string>
<string name="keyboard_layout_danish" msgid="8036432066627127851">"덴마크어"</string>
<string name="keyboard_layout_norwegian" msgid="9090097917011040937">"노르웨이어"</string>
@@ -26,8 +27,7 @@
<string name="keyboard_layout_finnish" msgid="5585659438924315466">"핀란드어"</string>
<string name="keyboard_layout_croatian" msgid="4172229471079281138">"크로아티아어"</string>
<string name="keyboard_layout_czech" msgid="1349256901452975343">"체코어"</string>
- <!-- no translation found for keyboard_layout_czech_qwerty (3331402534128515501) -->
- <skip />
+ <string name="keyboard_layout_czech_qwerty" msgid="3331402534128515501">"체코어 QWERTY 키보드"</string>
<string name="keyboard_layout_estonian" msgid="8775830985185665274">"에스토니아어"</string>
<string name="keyboard_layout_hungarian" msgid="4154963661406035109">"헝가리어"</string>
<string name="keyboard_layout_icelandic" msgid="5836645650912489642">"아이슬란드어"</string>
diff --git a/packages/InputDevices/res/values-ky/strings.xml b/packages/InputDevices/res/values-ky/strings.xml
index 15a504ea4f59..cb9dbb2f0abb 100644
--- a/packages/InputDevices/res/values-ky/strings.xml
+++ b/packages/InputDevices/res/values-ky/strings.xml
@@ -19,6 +19,7 @@
<string name="keyboard_layout_swiss_german_label" msgid="2305520941993314258">"Немис (Швейцария)"</string>
<string name="keyboard_layout_belgian" msgid="2011984572838651558">"Белгия"</string>
<string name="keyboard_layout_bulgarian" msgid="8951224309972028398">"Болгар"</string>
+ <string name="keyboard_layout_bulgarian_phonetic" msgid="7568914730360106653">"Болгарча, фонетикалык"</string>
<string name="keyboard_layout_italian" msgid="6497079660449781213">"Италия"</string>
<string name="keyboard_layout_danish" msgid="8036432066627127851">"Дания"</string>
<string name="keyboard_layout_norwegian" msgid="9090097917011040937">"Норвег"</string>
@@ -26,8 +27,7 @@
<string name="keyboard_layout_finnish" msgid="5585659438924315466">"Фин"</string>
<string name="keyboard_layout_croatian" msgid="4172229471079281138">"Хорват"</string>
<string name="keyboard_layout_czech" msgid="1349256901452975343">"Чех"</string>
- <!-- no translation found for keyboard_layout_czech_qwerty (3331402534128515501) -->
- <skip />
+ <string name="keyboard_layout_czech_qwerty" msgid="3331402534128515501">"Чехиялык QWERTY стили"</string>
<string name="keyboard_layout_estonian" msgid="8775830985185665274">"Эстон"</string>
<string name="keyboard_layout_hungarian" msgid="4154963661406035109">"Венгр"</string>
<string name="keyboard_layout_icelandic" msgid="5836645650912489642">"Исландия"</string>
diff --git a/packages/InputDevices/res/values-lo/strings.xml b/packages/InputDevices/res/values-lo/strings.xml
index 26e7ad471bad..4ae4b7d9c58b 100644
--- a/packages/InputDevices/res/values-lo/strings.xml
+++ b/packages/InputDevices/res/values-lo/strings.xml
@@ -19,6 +19,7 @@
<string name="keyboard_layout_swiss_german_label" msgid="2305520941993314258">"ສະວິສ ເຢຍລະມັນ"</string>
<string name="keyboard_layout_belgian" msgid="2011984572838651558">"ເບວຢ້ຽນ"</string>
<string name="keyboard_layout_bulgarian" msgid="8951224309972028398">"ຮັງກາຣຽນ"</string>
+ <string name="keyboard_layout_bulgarian_phonetic" msgid="7568914730360106653">"ບັງກາຣຽນ, ການອອກສຽງ"</string>
<string name="keyboard_layout_italian" msgid="6497079660449781213">"ອິຕາລຽນ"</string>
<string name="keyboard_layout_danish" msgid="8036432066627127851">"ເດັນນິຊ"</string>
<string name="keyboard_layout_norwegian" msgid="9090097917011040937">"ນໍເວກຽນ"</string>
diff --git a/packages/InputDevices/res/values-lt/strings.xml b/packages/InputDevices/res/values-lt/strings.xml
index d0b855d10822..d2aef7f057db 100644
--- a/packages/InputDevices/res/values-lt/strings.xml
+++ b/packages/InputDevices/res/values-lt/strings.xml
@@ -19,6 +19,7 @@
<string name="keyboard_layout_swiss_german_label" msgid="2305520941993314258">"Šveicarijos vokiečių k."</string>
<string name="keyboard_layout_belgian" msgid="2011984572838651558">"Belgų k."</string>
<string name="keyboard_layout_bulgarian" msgid="8951224309972028398">"Bulgarų k."</string>
+ <string name="keyboard_layout_bulgarian_phonetic" msgid="7568914730360106653">"Fonetinė bulgarų"</string>
<string name="keyboard_layout_italian" msgid="6497079660449781213">"Italų k."</string>
<string name="keyboard_layout_danish" msgid="8036432066627127851">"Danų k."</string>
<string name="keyboard_layout_norwegian" msgid="9090097917011040937">"Norvegų k."</string>
diff --git a/packages/InputDevices/res/values-lv/strings.xml b/packages/InputDevices/res/values-lv/strings.xml
index f3758390380d..8f3ff0ab2264 100644
--- a/packages/InputDevices/res/values-lv/strings.xml
+++ b/packages/InputDevices/res/values-lv/strings.xml
@@ -19,6 +19,7 @@
<string name="keyboard_layout_swiss_german_label" msgid="2305520941993314258">"Vācu (Šveice)"</string>
<string name="keyboard_layout_belgian" msgid="2011984572838651558">"Beļģu"</string>
<string name="keyboard_layout_bulgarian" msgid="8951224309972028398">"Bulgāru"</string>
+ <string name="keyboard_layout_bulgarian_phonetic" msgid="7568914730360106653">"Bulgāru, fonētiskā"</string>
<string name="keyboard_layout_italian" msgid="6497079660449781213">"Itāļu"</string>
<string name="keyboard_layout_danish" msgid="8036432066627127851">"Dāņu"</string>
<string name="keyboard_layout_norwegian" msgid="9090097917011040937">"Norvēģu"</string>
@@ -26,8 +27,7 @@
<string name="keyboard_layout_finnish" msgid="5585659438924315466">"Somu"</string>
<string name="keyboard_layout_croatian" msgid="4172229471079281138">"Horvātu"</string>
<string name="keyboard_layout_czech" msgid="1349256901452975343">"Čehu"</string>
- <!-- no translation found for keyboard_layout_czech_qwerty (3331402534128515501) -->
- <skip />
+ <string name="keyboard_layout_czech_qwerty" msgid="3331402534128515501">"Čehu (QWERTY)"</string>
<string name="keyboard_layout_estonian" msgid="8775830985185665274">"Igauņu"</string>
<string name="keyboard_layout_hungarian" msgid="4154963661406035109">"Ungāru"</string>
<string name="keyboard_layout_icelandic" msgid="5836645650912489642">"Īslandiešu"</string>
diff --git a/packages/InputDevices/res/values-mk/strings.xml b/packages/InputDevices/res/values-mk/strings.xml
index ce5e8f22f8ef..c036409c9c62 100644
--- a/packages/InputDevices/res/values-mk/strings.xml
+++ b/packages/InputDevices/res/values-mk/strings.xml
@@ -19,6 +19,8 @@
<string name="keyboard_layout_swiss_german_label" msgid="2305520941993314258">"Германски (Швајцарија)"</string>
<string name="keyboard_layout_belgian" msgid="2011984572838651558">"Белгиски"</string>
<string name="keyboard_layout_bulgarian" msgid="8951224309972028398">"Бугарски"</string>
+ <!-- no translation found for keyboard_layout_bulgarian_phonetic (7568914730360106653) -->
+ <skip />
<string name="keyboard_layout_italian" msgid="6497079660449781213">"Италијански"</string>
<string name="keyboard_layout_danish" msgid="8036432066627127851">"Дански"</string>
<string name="keyboard_layout_norwegian" msgid="9090097917011040937">"Норвешки"</string>
diff --git a/packages/InputDevices/res/values-ml/strings.xml b/packages/InputDevices/res/values-ml/strings.xml
index cb65e9c40822..9e5344367eae 100644
--- a/packages/InputDevices/res/values-ml/strings.xml
+++ b/packages/InputDevices/res/values-ml/strings.xml
@@ -19,6 +19,7 @@
<string name="keyboard_layout_swiss_german_label" msgid="2305520941993314258">"സ്വിസ് ജര്‍മന്‍"</string>
<string name="keyboard_layout_belgian" msgid="2011984572838651558">"ബെൽജിയൻ"</string>
<string name="keyboard_layout_bulgarian" msgid="8951224309972028398">"ബൾഗേറിയൻ"</string>
+ <string name="keyboard_layout_bulgarian_phonetic" msgid="7568914730360106653">"ബൾഗേറിയൻ, ഉച്ചാരണശബ്‌ദം"</string>
<string name="keyboard_layout_italian" msgid="6497079660449781213">"ഇറ്റാലിയൻ"</string>
<string name="keyboard_layout_danish" msgid="8036432066627127851">"ഡാനിഷ്"</string>
<string name="keyboard_layout_norwegian" msgid="9090097917011040937">"നോർവീജിയൻ"</string>
@@ -26,8 +27,7 @@
<string name="keyboard_layout_finnish" msgid="5585659438924315466">"ഫിന്നിഷ്"</string>
<string name="keyboard_layout_croatian" msgid="4172229471079281138">"ക്രൊയേഷ്യൻ"</string>
<string name="keyboard_layout_czech" msgid="1349256901452975343">"ചെക്ക്"</string>
- <!-- no translation found for keyboard_layout_czech_qwerty (3331402534128515501) -->
- <skip />
+ <string name="keyboard_layout_czech_qwerty" msgid="3331402534128515501">"ചെക്ക് QWERTY ശെെലി"</string>
<string name="keyboard_layout_estonian" msgid="8775830985185665274">"എസ്റ്റോണിയൻ"</string>
<string name="keyboard_layout_hungarian" msgid="4154963661406035109">"ഹംഗേറിയൻ"</string>
<string name="keyboard_layout_icelandic" msgid="5836645650912489642">"ഐസ്‌ലാന്‍ഡിക്"</string>
diff --git a/packages/InputDevices/res/values-mn/strings.xml b/packages/InputDevices/res/values-mn/strings.xml
index f405750e941d..18c2faf7e464 100644
--- a/packages/InputDevices/res/values-mn/strings.xml
+++ b/packages/InputDevices/res/values-mn/strings.xml
@@ -19,6 +19,7 @@
<string name="keyboard_layout_swiss_german_label" msgid="2305520941993314258">"Швейцарийн Герман"</string>
<string name="keyboard_layout_belgian" msgid="2011984572838651558">"Бельги"</string>
<string name="keyboard_layout_bulgarian" msgid="8951224309972028398">"Болгар"</string>
+ <string name="keyboard_layout_bulgarian_phonetic" msgid="7568914730360106653">"Болгар хэл, Авиа зүй"</string>
<string name="keyboard_layout_italian" msgid="6497079660449781213">"Итали"</string>
<string name="keyboard_layout_danish" msgid="8036432066627127851">"Дани"</string>
<string name="keyboard_layout_norwegian" msgid="9090097917011040937">"Норвеги"</string>
@@ -26,8 +27,7 @@
<string name="keyboard_layout_finnish" msgid="5585659438924315466">"Финлянд"</string>
<string name="keyboard_layout_croatian" msgid="4172229471079281138">"Хорват"</string>
<string name="keyboard_layout_czech" msgid="1349256901452975343">"Чех"</string>
- <!-- no translation found for keyboard_layout_czech_qwerty (3331402534128515501) -->
- <skip />
+ <string name="keyboard_layout_czech_qwerty" msgid="3331402534128515501">"Чех хэлний QWERTY загвар"</string>
<string name="keyboard_layout_estonian" msgid="8775830985185665274">"Эстон"</string>
<string name="keyboard_layout_hungarian" msgid="4154963661406035109">"Унгар"</string>
<string name="keyboard_layout_icelandic" msgid="5836645650912489642">"Исланд"</string>
diff --git a/packages/InputDevices/res/values-mr/strings.xml b/packages/InputDevices/res/values-mr/strings.xml
index ba31233e0006..d8788c97b90c 100644
--- a/packages/InputDevices/res/values-mr/strings.xml
+++ b/packages/InputDevices/res/values-mr/strings.xml
@@ -19,6 +19,7 @@
<string name="keyboard_layout_swiss_german_label" msgid="2305520941993314258">"स्विस जर्मन"</string>
<string name="keyboard_layout_belgian" msgid="2011984572838651558">"बेल्जियन"</string>
<string name="keyboard_layout_bulgarian" msgid="8951224309972028398">"बल्गेरियन"</string>
+ <string name="keyboard_layout_bulgarian_phonetic" msgid="7568914730360106653">"बल्गेरियन, फोनेटिक"</string>
<string name="keyboard_layout_italian" msgid="6497079660449781213">"इटालियन"</string>
<string name="keyboard_layout_danish" msgid="8036432066627127851">"डॅनिश"</string>
<string name="keyboard_layout_norwegian" msgid="9090097917011040937">"नॉर्वेजियन"</string>
@@ -26,8 +27,7 @@
<string name="keyboard_layout_finnish" msgid="5585659438924315466">"फिन्निश"</string>
<string name="keyboard_layout_croatian" msgid="4172229471079281138">"क्रोएशियन"</string>
<string name="keyboard_layout_czech" msgid="1349256901452975343">"झेक"</string>
- <!-- no translation found for keyboard_layout_czech_qwerty (3331402534128515501) -->
- <skip />
+ <string name="keyboard_layout_czech_qwerty" msgid="3331402534128515501">"Czech QWERTY शैली"</string>
<string name="keyboard_layout_estonian" msgid="8775830985185665274">"एस्टोनियन"</string>
<string name="keyboard_layout_hungarian" msgid="4154963661406035109">"हंगेरियन"</string>
<string name="keyboard_layout_icelandic" msgid="5836645650912489642">"आइसलँडिक"</string>
diff --git a/packages/InputDevices/res/values-ms/strings.xml b/packages/InputDevices/res/values-ms/strings.xml
index 09afb200773f..8bc9b2a73cb2 100644
--- a/packages/InputDevices/res/values-ms/strings.xml
+++ b/packages/InputDevices/res/values-ms/strings.xml
@@ -19,6 +19,7 @@
<string name="keyboard_layout_swiss_german_label" msgid="2305520941993314258">"Jerman Switzerland"</string>
<string name="keyboard_layout_belgian" msgid="2011984572838651558">"Bahasa Belgium"</string>
<string name="keyboard_layout_bulgarian" msgid="8951224309972028398">"Bahasa Bulgaria"</string>
+ <string name="keyboard_layout_bulgarian_phonetic" msgid="7568914730360106653">"Bahasa Bulgaria, Fonetik"</string>
<string name="keyboard_layout_italian" msgid="6497079660449781213">"Bahasa Itali"</string>
<string name="keyboard_layout_danish" msgid="8036432066627127851">"Bahasa Denmark"</string>
<string name="keyboard_layout_norwegian" msgid="9090097917011040937">"Bahasa Norway"</string>
@@ -26,8 +27,7 @@
<string name="keyboard_layout_finnish" msgid="5585659438924315466">"Bahasa Finland"</string>
<string name="keyboard_layout_croatian" msgid="4172229471079281138">"Bahasa Croatia"</string>
<string name="keyboard_layout_czech" msgid="1349256901452975343">"Bahasa Czech"</string>
- <!-- no translation found for keyboard_layout_czech_qwerty (3331402534128515501) -->
- <skip />
+ <string name="keyboard_layout_czech_qwerty" msgid="3331402534128515501">"Gaya QWERTY Czech"</string>
<string name="keyboard_layout_estonian" msgid="8775830985185665274">"Bahasa Estonia"</string>
<string name="keyboard_layout_hungarian" msgid="4154963661406035109">"Bahasa Hungary"</string>
<string name="keyboard_layout_icelandic" msgid="5836645650912489642">"Bahasa Iceland"</string>
diff --git a/packages/InputDevices/res/values-my/strings.xml b/packages/InputDevices/res/values-my/strings.xml
index 57748dbfa4e1..26720576ee91 100644
--- a/packages/InputDevices/res/values-my/strings.xml
+++ b/packages/InputDevices/res/values-my/strings.xml
@@ -19,6 +19,7 @@
<string name="keyboard_layout_swiss_german_label" msgid="2305520941993314258">"ဆွစ် ဂျာမန်"</string>
<string name="keyboard_layout_belgian" msgid="2011984572838651558">"ဘယ်လ်ဂျီယန်"</string>
<string name="keyboard_layout_bulgarian" msgid="8951224309972028398">"ဘူဂေးရီယန်း"</string>
+ <string name="keyboard_layout_bulgarian_phonetic" msgid="7568914730360106653">"ဘူလ်ဂေးရီးယား အသံထွက်"</string>
<string name="keyboard_layout_italian" msgid="6497079660449781213">"အီတာလီယန်"</string>
<string name="keyboard_layout_danish" msgid="8036432066627127851">"ဒိန်းမတ်"</string>
<string name="keyboard_layout_norwegian" msgid="9090097917011040937">"နောဝေဂျီယန်"</string>
diff --git a/packages/InputDevices/res/values-nb/strings.xml b/packages/InputDevices/res/values-nb/strings.xml
index 6059b4c8a822..83b87e5dd45e 100644
--- a/packages/InputDevices/res/values-nb/strings.xml
+++ b/packages/InputDevices/res/values-nb/strings.xml
@@ -19,6 +19,7 @@
<string name="keyboard_layout_swiss_german_label" msgid="2305520941993314258">"Sveitsisk standardtysk"</string>
<string name="keyboard_layout_belgian" msgid="2011984572838651558">"Belgisk"</string>
<string name="keyboard_layout_bulgarian" msgid="8951224309972028398">"Bulgarsk"</string>
+ <string name="keyboard_layout_bulgarian_phonetic" msgid="7568914730360106653">"Bulgarsk, fonetisk"</string>
<string name="keyboard_layout_italian" msgid="6497079660449781213">"Italiensk"</string>
<string name="keyboard_layout_danish" msgid="8036432066627127851">"Dansk"</string>
<string name="keyboard_layout_norwegian" msgid="9090097917011040937">"Norsk"</string>
diff --git a/packages/InputDevices/res/values-ne/strings.xml b/packages/InputDevices/res/values-ne/strings.xml
index 731c88fe4077..24816c1126e3 100644
--- a/packages/InputDevices/res/values-ne/strings.xml
+++ b/packages/InputDevices/res/values-ne/strings.xml
@@ -19,6 +19,8 @@
<string name="keyboard_layout_swiss_german_label" msgid="2305520941993314258">"स्विस-जर्मन"</string>
<string name="keyboard_layout_belgian" msgid="2011984572838651558">"बेल्जियन"</string>
<string name="keyboard_layout_bulgarian" msgid="8951224309972028398">"बुल्गेरियन"</string>
+ <!-- no translation found for keyboard_layout_bulgarian_phonetic (7568914730360106653) -->
+ <skip />
<string name="keyboard_layout_italian" msgid="6497079660449781213">"इटालियन"</string>
<string name="keyboard_layout_danish" msgid="8036432066627127851">"डेनिश"</string>
<string name="keyboard_layout_norwegian" msgid="9090097917011040937">"नर्वेजियन"</string>
@@ -26,8 +28,7 @@
<string name="keyboard_layout_finnish" msgid="5585659438924315466">"फिनिश"</string>
<string name="keyboard_layout_croatian" msgid="4172229471079281138">"क्रोशीयाली"</string>
<string name="keyboard_layout_czech" msgid="1349256901452975343">"चेक"</string>
- <!-- no translation found for keyboard_layout_czech_qwerty (3331402534128515501) -->
- <skip />
+ <string name="keyboard_layout_czech_qwerty" msgid="3331402534128515501">"चेक भाषामा QWERTY शैली"</string>
<string name="keyboard_layout_estonian" msgid="8775830985185665274">"एस्तोनीयाली"</string>
<string name="keyboard_layout_hungarian" msgid="4154963661406035109">"हंगेरियन"</string>
<string name="keyboard_layout_icelandic" msgid="5836645650912489642">"आइसल्याण्डिक"</string>
diff --git a/packages/InputDevices/res/values-nl/strings.xml b/packages/InputDevices/res/values-nl/strings.xml
index 9767f92d72e4..6e5849013fe7 100644
--- a/packages/InputDevices/res/values-nl/strings.xml
+++ b/packages/InputDevices/res/values-nl/strings.xml
@@ -19,6 +19,7 @@
<string name="keyboard_layout_swiss_german_label" msgid="2305520941993314258">"Zwitsers Duits"</string>
<string name="keyboard_layout_belgian" msgid="2011984572838651558">"Belgisch"</string>
<string name="keyboard_layout_bulgarian" msgid="8951224309972028398">"Bulgaars"</string>
+ <string name="keyboard_layout_bulgarian_phonetic" msgid="7568914730360106653">"Bulgaars, fonetisch"</string>
<string name="keyboard_layout_italian" msgid="6497079660449781213">"Italiaans"</string>
<string name="keyboard_layout_danish" msgid="8036432066627127851">"Deens"</string>
<string name="keyboard_layout_norwegian" msgid="9090097917011040937">"Noors"</string>
@@ -26,7 +27,7 @@
<string name="keyboard_layout_finnish" msgid="5585659438924315466">"Fins"</string>
<string name="keyboard_layout_croatian" msgid="4172229471079281138">"Kroatisch"</string>
<string name="keyboard_layout_czech" msgid="1349256901452975343">"Tsjechisch"</string>
- <string name="keyboard_layout_czech_qwerty" msgid="3331402534128515501">"Tsjechisch - QWERTY-stijl"</string>
+ <string name="keyboard_layout_czech_qwerty" msgid="3331402534128515501">"Tsjechisch - QWERTY-toetsenbord"</string>
<string name="keyboard_layout_estonian" msgid="8775830985185665274">"Estlands"</string>
<string name="keyboard_layout_hungarian" msgid="4154963661406035109">"Hongaars"</string>
<string name="keyboard_layout_icelandic" msgid="5836645650912489642">"IJslands"</string>
diff --git a/packages/InputDevices/res/values-or/strings.xml b/packages/InputDevices/res/values-or/strings.xml
index 04d44dcac860..aa16151752e6 100644
--- a/packages/InputDevices/res/values-or/strings.xml
+++ b/packages/InputDevices/res/values-or/strings.xml
@@ -19,6 +19,7 @@
<string name="keyboard_layout_swiss_german_label" msgid="2305520941993314258">"ସୁଇସ୍ ଜର୍ମାନ୍‍"</string>
<string name="keyboard_layout_belgian" msgid="2011984572838651558">"ବେଲ୍‍ଜିଆନ୍‍"</string>
<string name="keyboard_layout_bulgarian" msgid="8951224309972028398">"ବୁଲଗାରିଆନ୍‍"</string>
+ <string name="keyboard_layout_bulgarian_phonetic" msgid="7568914730360106653">"ବୁଲଗେରିଆନ୍, ଫୋନେଟିକ୍"</string>
<string name="keyboard_layout_italian" msgid="6497079660449781213">"ଇଟାଲିୟାନ୍‌"</string>
<string name="keyboard_layout_danish" msgid="8036432066627127851">"ଡାନିଶ୍‍"</string>
<string name="keyboard_layout_norwegian" msgid="9090097917011040937">"ନରୱେଜିଆନ୍"</string>
@@ -26,8 +27,7 @@
<string name="keyboard_layout_finnish" msgid="5585659438924315466">"ଫିନ୍ନିଶ୍‍"</string>
<string name="keyboard_layout_croatian" msgid="4172229471079281138">"କ୍ରୋଆଶିଆନ୍"</string>
<string name="keyboard_layout_czech" msgid="1349256901452975343">"ଚେକ୍"</string>
- <!-- no translation found for keyboard_layout_czech_qwerty (3331402534128515501) -->
- <skip />
+ <string name="keyboard_layout_czech_qwerty" msgid="3331402534128515501">"ଚେକ୍ QWERTY ଷ୍ଟାଇଲ୍"</string>
<string name="keyboard_layout_estonian" msgid="8775830985185665274">"ଇଷ୍ଟୋନିଆନ୍"</string>
<string name="keyboard_layout_hungarian" msgid="4154963661406035109">"ହଙ୍ଗେରିଆନ୍"</string>
<string name="keyboard_layout_icelandic" msgid="5836645650912489642">"ଆଇସଲାଣ୍ଡିକ୍"</string>
diff --git a/packages/InputDevices/res/values-pa/strings.xml b/packages/InputDevices/res/values-pa/strings.xml
index 2eb30c90dd42..7e5a03cde986 100644
--- a/packages/InputDevices/res/values-pa/strings.xml
+++ b/packages/InputDevices/res/values-pa/strings.xml
@@ -19,6 +19,7 @@
<string name="keyboard_layout_swiss_german_label" msgid="2305520941993314258">"ਸਵਿਸ ਜਰਮਨ"</string>
<string name="keyboard_layout_belgian" msgid="2011984572838651558">"ਬੈਲਜੀਅਨ"</string>
<string name="keyboard_layout_bulgarian" msgid="8951224309972028398">"ਬਲਗੇਰੀਅਨ"</string>
+ <string name="keyboard_layout_bulgarian_phonetic" msgid="7568914730360106653">"ਬਲਗੇਰੀਅਨ, ਧੁਨੀਆਤਮਿਕ"</string>
<string name="keyboard_layout_italian" msgid="6497079660449781213">"ਇਤਾਲਵੀ"</string>
<string name="keyboard_layout_danish" msgid="8036432066627127851">"ਡੈਨਿਸ਼"</string>
<string name="keyboard_layout_norwegian" msgid="9090097917011040937">"ਨਾਰਵੇਜੀਅਨ"</string>
@@ -26,8 +27,7 @@
<string name="keyboard_layout_finnish" msgid="5585659438924315466">"ਫਿਨਿਸ਼"</string>
<string name="keyboard_layout_croatian" msgid="4172229471079281138">"ਕਰੋਆਟੀਆਈ"</string>
<string name="keyboard_layout_czech" msgid="1349256901452975343">"ਚੈਕ"</string>
- <!-- no translation found for keyboard_layout_czech_qwerty (3331402534128515501) -->
- <skip />
+ <string name="keyboard_layout_czech_qwerty" msgid="3331402534128515501">"ਚੈੱਕ QWERTY ਸਟਾਈਲ"</string>
<string name="keyboard_layout_estonian" msgid="8775830985185665274">"ਇਸਟੋਨੀਅਨ"</string>
<string name="keyboard_layout_hungarian" msgid="4154963661406035109">"ਹੰਗੇਰੀਅਨ"</string>
<string name="keyboard_layout_icelandic" msgid="5836645650912489642">"ਆਈਸਲੈਂਡੀ"</string>
diff --git a/packages/InputDevices/res/values-pl/strings.xml b/packages/InputDevices/res/values-pl/strings.xml
index c3751197b7d2..51755e2b2b52 100644
--- a/packages/InputDevices/res/values-pl/strings.xml
+++ b/packages/InputDevices/res/values-pl/strings.xml
@@ -19,6 +19,7 @@
<string name="keyboard_layout_swiss_german_label" msgid="2305520941993314258">"Niemiecki (Szwajcaria)"</string>
<string name="keyboard_layout_belgian" msgid="2011984572838651558">"Belgijski"</string>
<string name="keyboard_layout_bulgarian" msgid="8951224309972028398">"Bułgarski"</string>
+ <string name="keyboard_layout_bulgarian_phonetic" msgid="7568914730360106653">"Bułgarski (znaki fonetyczne)"</string>
<string name="keyboard_layout_italian" msgid="6497079660449781213">"Włoski"</string>
<string name="keyboard_layout_danish" msgid="8036432066627127851">"Duński"</string>
<string name="keyboard_layout_norwegian" msgid="9090097917011040937">"Norweski"</string>
diff --git a/packages/InputDevices/res/values-pt-rBR/strings.xml b/packages/InputDevices/res/values-pt-rBR/strings.xml
index d6687c811a5b..128868856f5a 100644
--- a/packages/InputDevices/res/values-pt-rBR/strings.xml
+++ b/packages/InputDevices/res/values-pt-rBR/strings.xml
@@ -19,6 +19,7 @@
<string name="keyboard_layout_swiss_german_label" msgid="2305520941993314258">"Alemão suíço"</string>
<string name="keyboard_layout_belgian" msgid="2011984572838651558">"Belga"</string>
<string name="keyboard_layout_bulgarian" msgid="8951224309972028398">"Búlgaro"</string>
+ <string name="keyboard_layout_bulgarian_phonetic" msgid="7568914730360106653">"Búlgaro, fonético"</string>
<string name="keyboard_layout_italian" msgid="6497079660449781213">"Italiano"</string>
<string name="keyboard_layout_danish" msgid="8036432066627127851">"Dinamarquês"</string>
<string name="keyboard_layout_norwegian" msgid="9090097917011040937">"Norueguês"</string>
diff --git a/packages/InputDevices/res/values-pt-rPT/strings.xml b/packages/InputDevices/res/values-pt-rPT/strings.xml
index 4f8a43279565..89bb3e376678 100644
--- a/packages/InputDevices/res/values-pt-rPT/strings.xml
+++ b/packages/InputDevices/res/values-pt-rPT/strings.xml
@@ -19,6 +19,7 @@
<string name="keyboard_layout_swiss_german_label" msgid="2305520941993314258">"Alemão (Suíça)"</string>
<string name="keyboard_layout_belgian" msgid="2011984572838651558">"Belga"</string>
<string name="keyboard_layout_bulgarian" msgid="8951224309972028398">"Búlgaro"</string>
+ <string name="keyboard_layout_bulgarian_phonetic" msgid="7568914730360106653">"Búlgaro, fonético"</string>
<string name="keyboard_layout_italian" msgid="6497079660449781213">"Italiano"</string>
<string name="keyboard_layout_danish" msgid="8036432066627127851">"Dinamarquês"</string>
<string name="keyboard_layout_norwegian" msgid="9090097917011040937">"Norueguês"</string>
diff --git a/packages/InputDevices/res/values-pt/strings.xml b/packages/InputDevices/res/values-pt/strings.xml
index d6687c811a5b..128868856f5a 100644
--- a/packages/InputDevices/res/values-pt/strings.xml
+++ b/packages/InputDevices/res/values-pt/strings.xml
@@ -19,6 +19,7 @@
<string name="keyboard_layout_swiss_german_label" msgid="2305520941993314258">"Alemão suíço"</string>
<string name="keyboard_layout_belgian" msgid="2011984572838651558">"Belga"</string>
<string name="keyboard_layout_bulgarian" msgid="8951224309972028398">"Búlgaro"</string>
+ <string name="keyboard_layout_bulgarian_phonetic" msgid="7568914730360106653">"Búlgaro, fonético"</string>
<string name="keyboard_layout_italian" msgid="6497079660449781213">"Italiano"</string>
<string name="keyboard_layout_danish" msgid="8036432066627127851">"Dinamarquês"</string>
<string name="keyboard_layout_norwegian" msgid="9090097917011040937">"Norueguês"</string>
diff --git a/packages/InputDevices/res/values-ro/strings.xml b/packages/InputDevices/res/values-ro/strings.xml
index 1b38bdddd1a9..f7ff2504f31f 100644
--- a/packages/InputDevices/res/values-ro/strings.xml
+++ b/packages/InputDevices/res/values-ro/strings.xml
@@ -19,6 +19,7 @@
<string name="keyboard_layout_swiss_german_label" msgid="2305520941993314258">"Germană (Elveția)"</string>
<string name="keyboard_layout_belgian" msgid="2011984572838651558">"Belgiană"</string>
<string name="keyboard_layout_bulgarian" msgid="8951224309972028398">"Bulgară"</string>
+ <string name="keyboard_layout_bulgarian_phonetic" msgid="7568914730360106653">"Bulgară fonetică"</string>
<string name="keyboard_layout_italian" msgid="6497079660449781213">"Italiană"</string>
<string name="keyboard_layout_danish" msgid="8036432066627127851">"Daneză"</string>
<string name="keyboard_layout_norwegian" msgid="9090097917011040937">"Norvegiană"</string>
diff --git a/packages/InputDevices/res/values-ru/strings.xml b/packages/InputDevices/res/values-ru/strings.xml
index 501517d34176..0cb4f34d4ab1 100644
--- a/packages/InputDevices/res/values-ru/strings.xml
+++ b/packages/InputDevices/res/values-ru/strings.xml
@@ -19,6 +19,7 @@
<string name="keyboard_layout_swiss_german_label" msgid="2305520941993314258">"немецкий (Швейцария)"</string>
<string name="keyboard_layout_belgian" msgid="2011984572838651558">"нидерландский (Бельгия)"</string>
<string name="keyboard_layout_bulgarian" msgid="8951224309972028398">"болгарский"</string>
+ <string name="keyboard_layout_bulgarian_phonetic" msgid="7568914730360106653">"болгарский (фонетическая)"</string>
<string name="keyboard_layout_italian" msgid="6497079660449781213">"итальянский"</string>
<string name="keyboard_layout_danish" msgid="8036432066627127851">"датский"</string>
<string name="keyboard_layout_norwegian" msgid="9090097917011040937">"норвежский"</string>
diff --git a/packages/InputDevices/res/values-si/strings.xml b/packages/InputDevices/res/values-si/strings.xml
index c13661613bd5..eb3c44678db2 100644
--- a/packages/InputDevices/res/values-si/strings.xml
+++ b/packages/InputDevices/res/values-si/strings.xml
@@ -19,6 +19,7 @@
<string name="keyboard_layout_swiss_german_label" msgid="2305520941993314258">"ස්විස් ජර්මන්"</string>
<string name="keyboard_layout_belgian" msgid="2011984572838651558">"බෙල්ගියන්"</string>
<string name="keyboard_layout_bulgarian" msgid="8951224309972028398">"බල්ගේරියානු"</string>
+ <string name="keyboard_layout_bulgarian_phonetic" msgid="7568914730360106653">"බල්ගේරියානු, ශබ්දිම"</string>
<string name="keyboard_layout_italian" msgid="6497079660449781213">"ඉතාලි"</string>
<string name="keyboard_layout_danish" msgid="8036432066627127851">"ඩෙන්මාර්ක"</string>
<string name="keyboard_layout_norwegian" msgid="9090097917011040937">"නෝර්වීජියානු"</string>
diff --git a/packages/InputDevices/res/values-sk/strings.xml b/packages/InputDevices/res/values-sk/strings.xml
index 260a43258818..e7e15b0cbd00 100644
--- a/packages/InputDevices/res/values-sk/strings.xml
+++ b/packages/InputDevices/res/values-sk/strings.xml
@@ -19,6 +19,7 @@
<string name="keyboard_layout_swiss_german_label" msgid="2305520941993314258">"švajčiarske (nemčina)"</string>
<string name="keyboard_layout_belgian" msgid="2011984572838651558">"belgické"</string>
<string name="keyboard_layout_bulgarian" msgid="8951224309972028398">"bulharské"</string>
+ <string name="keyboard_layout_bulgarian_phonetic" msgid="7568914730360106653">"Bulharská fonetická klávesnica"</string>
<string name="keyboard_layout_italian" msgid="6497079660449781213">"talianske"</string>
<string name="keyboard_layout_danish" msgid="8036432066627127851">"dánske"</string>
<string name="keyboard_layout_norwegian" msgid="9090097917011040937">"nórske"</string>
@@ -26,8 +27,7 @@
<string name="keyboard_layout_finnish" msgid="5585659438924315466">"fínske"</string>
<string name="keyboard_layout_croatian" msgid="4172229471079281138">"chorvátske"</string>
<string name="keyboard_layout_czech" msgid="1349256901452975343">"české"</string>
- <!-- no translation found for keyboard_layout_czech_qwerty (3331402534128515501) -->
- <skip />
+ <string name="keyboard_layout_czech_qwerty" msgid="3331402534128515501">"Český štýl QWERTY"</string>
<string name="keyboard_layout_estonian" msgid="8775830985185665274">"estónske"</string>
<string name="keyboard_layout_hungarian" msgid="4154963661406035109">"maďarské"</string>
<string name="keyboard_layout_icelandic" msgid="5836645650912489642">"islandské"</string>
diff --git a/packages/InputDevices/res/values-sl/strings.xml b/packages/InputDevices/res/values-sl/strings.xml
index 91ba472bde1e..f4d1e5732fc5 100644
--- a/packages/InputDevices/res/values-sl/strings.xml
+++ b/packages/InputDevices/res/values-sl/strings.xml
@@ -19,6 +19,7 @@
<string name="keyboard_layout_swiss_german_label" msgid="2305520941993314258">"švicarska nemška"</string>
<string name="keyboard_layout_belgian" msgid="2011984572838651558">"belgijska"</string>
<string name="keyboard_layout_bulgarian" msgid="8951224309972028398">"bolgarska"</string>
+ <string name="keyboard_layout_bulgarian_phonetic" msgid="7568914730360106653">"bolgarščina (fonetična)"</string>
<string name="keyboard_layout_italian" msgid="6497079660449781213">"italijanska"</string>
<string name="keyboard_layout_danish" msgid="8036432066627127851">"danska"</string>
<string name="keyboard_layout_norwegian" msgid="9090097917011040937">"norveška"</string>
diff --git a/packages/InputDevices/res/values-sq/strings.xml b/packages/InputDevices/res/values-sq/strings.xml
index 3128025d6bd0..b3677cd699b4 100644
--- a/packages/InputDevices/res/values-sq/strings.xml
+++ b/packages/InputDevices/res/values-sq/strings.xml
@@ -19,6 +19,8 @@
<string name="keyboard_layout_swiss_german_label" msgid="2305520941993314258">"gjermanishte zvicerane"</string>
<string name="keyboard_layout_belgian" msgid="2011984572838651558">"belge"</string>
<string name="keyboard_layout_bulgarian" msgid="8951224309972028398">"bullgarisht"</string>
+ <!-- no translation found for keyboard_layout_bulgarian_phonetic (7568914730360106653) -->
+ <skip />
<string name="keyboard_layout_italian" msgid="6497079660449781213">"italisht"</string>
<string name="keyboard_layout_danish" msgid="8036432066627127851">"danisht"</string>
<string name="keyboard_layout_norwegian" msgid="9090097917011040937">"norvegjisht"</string>
diff --git a/packages/InputDevices/res/values-sr/strings.xml b/packages/InputDevices/res/values-sr/strings.xml
index 686f6aea8ce1..e3a20438f34a 100644
--- a/packages/InputDevices/res/values-sr/strings.xml
+++ b/packages/InputDevices/res/values-sr/strings.xml
@@ -19,6 +19,7 @@
<string name="keyboard_layout_swiss_german_label" msgid="2305520941993314258">"швајцарско немачка"</string>
<string name="keyboard_layout_belgian" msgid="2011984572838651558">"белгијска"</string>
<string name="keyboard_layout_bulgarian" msgid="8951224309972028398">"бугарска"</string>
+ <string name="keyboard_layout_bulgarian_phonetic" msgid="7568914730360106653">"бугарска фонетска"</string>
<string name="keyboard_layout_italian" msgid="6497079660449781213">"италијанска"</string>
<string name="keyboard_layout_danish" msgid="8036432066627127851">"данска"</string>
<string name="keyboard_layout_norwegian" msgid="9090097917011040937">"норвешка"</string>
@@ -26,8 +27,7 @@
<string name="keyboard_layout_finnish" msgid="5585659438924315466">"финска"</string>
<string name="keyboard_layout_croatian" msgid="4172229471079281138">"хрватска"</string>
<string name="keyboard_layout_czech" msgid="1349256901452975343">"чешка"</string>
- <!-- no translation found for keyboard_layout_czech_qwerty (3331402534128515501) -->
- <skip />
+ <string name="keyboard_layout_czech_qwerty" msgid="3331402534128515501">"Стил чешке QWERTY тастатуре"</string>
<string name="keyboard_layout_estonian" msgid="8775830985185665274">"естонска"</string>
<string name="keyboard_layout_hungarian" msgid="4154963661406035109">"мађарска"</string>
<string name="keyboard_layout_icelandic" msgid="5836645650912489642">"исландска"</string>
diff --git a/packages/InputDevices/res/values-sv/strings.xml b/packages/InputDevices/res/values-sv/strings.xml
index a4c0fcaa86de..097ada40be63 100644
--- a/packages/InputDevices/res/values-sv/strings.xml
+++ b/packages/InputDevices/res/values-sv/strings.xml
@@ -19,6 +19,7 @@
<string name="keyboard_layout_swiss_german_label" msgid="2305520941993314258">"Tyskt (Schweiz)"</string>
<string name="keyboard_layout_belgian" msgid="2011984572838651558">"Belgiskt"</string>
<string name="keyboard_layout_bulgarian" msgid="8951224309972028398">"Bulgariskt"</string>
+ <string name="keyboard_layout_bulgarian_phonetic" msgid="7568914730360106653">"Bulgariska (fonetiskt)"</string>
<string name="keyboard_layout_italian" msgid="6497079660449781213">"Italienskt"</string>
<string name="keyboard_layout_danish" msgid="8036432066627127851">"Danskt"</string>
<string name="keyboard_layout_norwegian" msgid="9090097917011040937">"Norskt"</string>
diff --git a/packages/InputDevices/res/values-sw/strings.xml b/packages/InputDevices/res/values-sw/strings.xml
index 1e5c8d7d30e1..325796232d76 100644
--- a/packages/InputDevices/res/values-sw/strings.xml
+++ b/packages/InputDevices/res/values-sw/strings.xml
@@ -19,6 +19,7 @@
<string name="keyboard_layout_swiss_german_label" msgid="2305520941993314258">"Kijerumani cha Uswisi"</string>
<string name="keyboard_layout_belgian" msgid="2011984572838651558">"Kibelgiji"</string>
<string name="keyboard_layout_bulgarian" msgid="8951224309972028398">"Kibulgaria"</string>
+ <string name="keyboard_layout_bulgarian_phonetic" msgid="7568914730360106653">"Kibulgaria, Fonetiki"</string>
<string name="keyboard_layout_italian" msgid="6497079660449781213">"Kiitaliano"</string>
<string name="keyboard_layout_danish" msgid="8036432066627127851">"Kidenmarki"</string>
<string name="keyboard_layout_norwegian" msgid="9090097917011040937">"Kinorwei"</string>
diff --git a/packages/InputDevices/res/values-ta/strings.xml b/packages/InputDevices/res/values-ta/strings.xml
index e25e5edfda40..b75b57d5f358 100644
--- a/packages/InputDevices/res/values-ta/strings.xml
+++ b/packages/InputDevices/res/values-ta/strings.xml
@@ -19,6 +19,8 @@
<string name="keyboard_layout_swiss_german_label" msgid="2305520941993314258">"ஸ்விஸ் ஜெர்மன்"</string>
<string name="keyboard_layout_belgian" msgid="2011984572838651558">"பெல்ஜியன்"</string>
<string name="keyboard_layout_bulgarian" msgid="8951224309972028398">"பல்கேரியன்"</string>
+ <!-- no translation found for keyboard_layout_bulgarian_phonetic (7568914730360106653) -->
+ <skip />
<string name="keyboard_layout_italian" msgid="6497079660449781213">"இத்தாலியன்"</string>
<string name="keyboard_layout_danish" msgid="8036432066627127851">"டேனிஷ்"</string>
<string name="keyboard_layout_norwegian" msgid="9090097917011040937">"நார்வேஜியன்"</string>
@@ -26,8 +28,7 @@
<string name="keyboard_layout_finnish" msgid="5585659438924315466">"ஃபின்னிஷ்"</string>
<string name="keyboard_layout_croatian" msgid="4172229471079281138">"குரோஷியன்"</string>
<string name="keyboard_layout_czech" msgid="1349256901452975343">"செக்"</string>
- <!-- no translation found for keyboard_layout_czech_qwerty (3331402534128515501) -->
- <skip />
+ <string name="keyboard_layout_czech_qwerty" msgid="3331402534128515501">"செக் QWERTY நடை"</string>
<string name="keyboard_layout_estonian" msgid="8775830985185665274">"எஸ்தோனியன்"</string>
<string name="keyboard_layout_hungarian" msgid="4154963661406035109">"ஹங்கேரியன்"</string>
<string name="keyboard_layout_icelandic" msgid="5836645650912489642">"ஐஸ்லாண்டிக்"</string>
diff --git a/packages/InputDevices/res/values-te/strings.xml b/packages/InputDevices/res/values-te/strings.xml
index b6caafd48790..c0253e528684 100644
--- a/packages/InputDevices/res/values-te/strings.xml
+++ b/packages/InputDevices/res/values-te/strings.xml
@@ -19,6 +19,7 @@
<string name="keyboard_layout_swiss_german_label" msgid="2305520941993314258">"స్విస్ జర్మన్"</string>
<string name="keyboard_layout_belgian" msgid="2011984572838651558">"బెల్జియన్"</string>
<string name="keyboard_layout_bulgarian" msgid="8951224309972028398">"బల్గేరియన్"</string>
+ <string name="keyboard_layout_bulgarian_phonetic" msgid="7568914730360106653">"బల్గేరియన్, ఫోనెటిక్"</string>
<string name="keyboard_layout_italian" msgid="6497079660449781213">"ఇటాలియన్"</string>
<string name="keyboard_layout_danish" msgid="8036432066627127851">"డేనిష్"</string>
<string name="keyboard_layout_norwegian" msgid="9090097917011040937">"నార్వేజియన్"</string>
diff --git a/packages/InputDevices/res/values-th/strings.xml b/packages/InputDevices/res/values-th/strings.xml
index 1170f8620a39..c8e0e62f6e18 100644
--- a/packages/InputDevices/res/values-th/strings.xml
+++ b/packages/InputDevices/res/values-th/strings.xml
@@ -19,6 +19,7 @@
<string name="keyboard_layout_swiss_german_label" msgid="2305520941993314258">"เยอรมันสวิส"</string>
<string name="keyboard_layout_belgian" msgid="2011984572838651558">"เบลเยียม"</string>
<string name="keyboard_layout_bulgarian" msgid="8951224309972028398">"บัลแกเรีย"</string>
+ <string name="keyboard_layout_bulgarian_phonetic" msgid="7568914730360106653">"ภาษาบัลแกเรีย ตามการออกเสียง"</string>
<string name="keyboard_layout_italian" msgid="6497079660449781213">"อิตาลี"</string>
<string name="keyboard_layout_danish" msgid="8036432066627127851">"เดนมาร์ก"</string>
<string name="keyboard_layout_norwegian" msgid="9090097917011040937">"นอร์เวย์"</string>
diff --git a/packages/InputDevices/res/values-tl/strings.xml b/packages/InputDevices/res/values-tl/strings.xml
index 97b878bde52c..21ea5de903bd 100644
--- a/packages/InputDevices/res/values-tl/strings.xml
+++ b/packages/InputDevices/res/values-tl/strings.xml
@@ -19,6 +19,8 @@
<string name="keyboard_layout_swiss_german_label" msgid="2305520941993314258">"Swiss German"</string>
<string name="keyboard_layout_belgian" msgid="2011984572838651558">"Belgian"</string>
<string name="keyboard_layout_bulgarian" msgid="8951224309972028398">"Bulgarian"</string>
+ <!-- no translation found for keyboard_layout_bulgarian_phonetic (7568914730360106653) -->
+ <skip />
<string name="keyboard_layout_italian" msgid="6497079660449781213">"Italian"</string>
<string name="keyboard_layout_danish" msgid="8036432066627127851">"Danish"</string>
<string name="keyboard_layout_norwegian" msgid="9090097917011040937">"Norwegian"</string>
diff --git a/packages/InputDevices/res/values-tr/strings.xml b/packages/InputDevices/res/values-tr/strings.xml
index 7e9b5e50fbc6..f093abb9a708 100644
--- a/packages/InputDevices/res/values-tr/strings.xml
+++ b/packages/InputDevices/res/values-tr/strings.xml
@@ -19,6 +19,7 @@
<string name="keyboard_layout_swiss_german_label" msgid="2305520941993314258">"İsviçre Almancası"</string>
<string name="keyboard_layout_belgian" msgid="2011984572838651558">"Belçika dili"</string>
<string name="keyboard_layout_bulgarian" msgid="8951224309972028398">"Bulgarca"</string>
+ <string name="keyboard_layout_bulgarian_phonetic" msgid="7568914730360106653">"Bulgarca, Fonetik"</string>
<string name="keyboard_layout_italian" msgid="6497079660449781213">"İtalyanca"</string>
<string name="keyboard_layout_danish" msgid="8036432066627127851">"Danca"</string>
<string name="keyboard_layout_norwegian" msgid="9090097917011040937">"Norveççe"</string>
diff --git a/packages/InputDevices/res/values-uk/strings.xml b/packages/InputDevices/res/values-uk/strings.xml
index db5de4cb7a83..d9b58d21c14f 100644
--- a/packages/InputDevices/res/values-uk/strings.xml
+++ b/packages/InputDevices/res/values-uk/strings.xml
@@ -19,6 +19,7 @@
<string name="keyboard_layout_swiss_german_label" msgid="2305520941993314258">"німецька (Швейцарія)"</string>
<string name="keyboard_layout_belgian" msgid="2011984572838651558">"бельгійська"</string>
<string name="keyboard_layout_bulgarian" msgid="8951224309972028398">"болгарська"</string>
+ <string name="keyboard_layout_bulgarian_phonetic" msgid="7568914730360106653">"Болгарська (фонетична)"</string>
<string name="keyboard_layout_italian" msgid="6497079660449781213">"італійська"</string>
<string name="keyboard_layout_danish" msgid="8036432066627127851">"данська"</string>
<string name="keyboard_layout_norwegian" msgid="9090097917011040937">"норвезька"</string>
diff --git a/packages/InputDevices/res/values-ur/strings.xml b/packages/InputDevices/res/values-ur/strings.xml
index bf9e5adca553..2bff7ed2f234 100644
--- a/packages/InputDevices/res/values-ur/strings.xml
+++ b/packages/InputDevices/res/values-ur/strings.xml
@@ -19,6 +19,7 @@
<string name="keyboard_layout_swiss_german_label" msgid="2305520941993314258">"سوئس جرمن"</string>
<string name="keyboard_layout_belgian" msgid="2011984572838651558">"بیلجیئن"</string>
<string name="keyboard_layout_bulgarian" msgid="8951224309972028398">"بلغاریائی"</string>
+ <string name="keyboard_layout_bulgarian_phonetic" msgid="7568914730360106653">"بلغاریائی، فونیٹک"</string>
<string name="keyboard_layout_italian" msgid="6497079660449781213">"اطالوی"</string>
<string name="keyboard_layout_danish" msgid="8036432066627127851">"ڈینش"</string>
<string name="keyboard_layout_norwegian" msgid="9090097917011040937">"نارویجین"</string>
diff --git a/packages/InputDevices/res/values-uz/strings.xml b/packages/InputDevices/res/values-uz/strings.xml
index 9d7f1e43e93f..9245aebd22ba 100644
--- a/packages/InputDevices/res/values-uz/strings.xml
+++ b/packages/InputDevices/res/values-uz/strings.xml
@@ -19,6 +19,7 @@
<string name="keyboard_layout_swiss_german_label" msgid="2305520941993314258">"Nemis (Shveytsariya)"</string>
<string name="keyboard_layout_belgian" msgid="2011984572838651558">"Belgiyancha"</string>
<string name="keyboard_layout_bulgarian" msgid="8951224309972028398">"Bolgarcha"</string>
+ <string name="keyboard_layout_bulgarian_phonetic" msgid="7568914730360106653">"Bolgar, fonetik"</string>
<string name="keyboard_layout_italian" msgid="6497079660449781213">"Italyancha"</string>
<string name="keyboard_layout_danish" msgid="8036432066627127851">"Datcha"</string>
<string name="keyboard_layout_norwegian" msgid="9090097917011040937">"Norvegcha"</string>
@@ -26,8 +27,7 @@
<string name="keyboard_layout_finnish" msgid="5585659438924315466">"Fincha"</string>
<string name="keyboard_layout_croatian" msgid="4172229471079281138">"Xorvatcha"</string>
<string name="keyboard_layout_czech" msgid="1349256901452975343">"Chexcha"</string>
- <!-- no translation found for keyboard_layout_czech_qwerty (3331402534128515501) -->
- <skip />
+ <string name="keyboard_layout_czech_qwerty" msgid="3331402534128515501">"Chex QWERTY uslubi"</string>
<string name="keyboard_layout_estonian" msgid="8775830985185665274">"Estoncha"</string>
<string name="keyboard_layout_hungarian" msgid="4154963661406035109">"Vengercha"</string>
<string name="keyboard_layout_icelandic" msgid="5836645650912489642">"Islandcha"</string>
diff --git a/packages/InputDevices/res/values-vi/strings.xml b/packages/InputDevices/res/values-vi/strings.xml
index eabaa1961946..1b42ece3deed 100644
--- a/packages/InputDevices/res/values-vi/strings.xml
+++ b/packages/InputDevices/res/values-vi/strings.xml
@@ -19,6 +19,7 @@
<string name="keyboard_layout_swiss_german_label" msgid="2305520941993314258">"Tiếng Đức Thụy Sĩ"</string>
<string name="keyboard_layout_belgian" msgid="2011984572838651558">"Tiếng Bỉ"</string>
<string name="keyboard_layout_bulgarian" msgid="8951224309972028398">"Tiếng Bungary"</string>
+ <string name="keyboard_layout_bulgarian_phonetic" msgid="7568914730360106653">"Tiếng Bulgaria, Ngữ âm"</string>
<string name="keyboard_layout_italian" msgid="6497079660449781213">"Tiếng Ý"</string>
<string name="keyboard_layout_danish" msgid="8036432066627127851">"Tiếng Đan Mạch"</string>
<string name="keyboard_layout_norwegian" msgid="9090097917011040937">"Tiếng Na Uy"</string>
diff --git a/packages/InputDevices/res/values-zh-rCN/strings.xml b/packages/InputDevices/res/values-zh-rCN/strings.xml
index 3eb4b2c94a97..aa75605b5abd 100644
--- a/packages/InputDevices/res/values-zh-rCN/strings.xml
+++ b/packages/InputDevices/res/values-zh-rCN/strings.xml
@@ -19,6 +19,7 @@
<string name="keyboard_layout_swiss_german_label" msgid="2305520941993314258">"瑞士德语"</string>
<string name="keyboard_layout_belgian" msgid="2011984572838651558">"比利时语"</string>
<string name="keyboard_layout_bulgarian" msgid="8951224309972028398">"保加利亚语"</string>
+ <string name="keyboard_layout_bulgarian_phonetic" msgid="7568914730360106653">"保加利亚语,注音"</string>
<string name="keyboard_layout_italian" msgid="6497079660449781213">"意大利语"</string>
<string name="keyboard_layout_danish" msgid="8036432066627127851">"丹麦语"</string>
<string name="keyboard_layout_norwegian" msgid="9090097917011040937">"挪威语"</string>
diff --git a/packages/InputDevices/res/values-zh-rHK/strings.xml b/packages/InputDevices/res/values-zh-rHK/strings.xml
index 37cd533ca9b9..dc824b97369d 100644
--- a/packages/InputDevices/res/values-zh-rHK/strings.xml
+++ b/packages/InputDevices/res/values-zh-rHK/strings.xml
@@ -19,6 +19,7 @@
<string name="keyboard_layout_swiss_german_label" msgid="2305520941993314258">"德文(瑞士)"</string>
<string name="keyboard_layout_belgian" msgid="2011984572838651558">"比利時文"</string>
<string name="keyboard_layout_bulgarian" msgid="8951224309972028398">"保加利亞文"</string>
+ <string name="keyboard_layout_bulgarian_phonetic" msgid="7568914730360106653">"保加利亞文 (拼音)"</string>
<string name="keyboard_layout_italian" msgid="6497079660449781213">"意大利文"</string>
<string name="keyboard_layout_danish" msgid="8036432066627127851">"丹麥文"</string>
<string name="keyboard_layout_norwegian" msgid="9090097917011040937">"挪威文"</string>
diff --git a/packages/InputDevices/res/values-zh-rTW/strings.xml b/packages/InputDevices/res/values-zh-rTW/strings.xml
index 8d2de40b2d86..c2714da0c3d9 100644
--- a/packages/InputDevices/res/values-zh-rTW/strings.xml
+++ b/packages/InputDevices/res/values-zh-rTW/strings.xml
@@ -19,6 +19,7 @@
<string name="keyboard_layout_swiss_german_label" msgid="2305520941993314258">"德文 (瑞士)"</string>
<string name="keyboard_layout_belgian" msgid="2011984572838651558">"比利時式"</string>
<string name="keyboard_layout_bulgarian" msgid="8951224309972028398">"保加利亞文"</string>
+ <string name="keyboard_layout_bulgarian_phonetic" msgid="7568914730360106653">"保加利亞文 (拼音)"</string>
<string name="keyboard_layout_italian" msgid="6497079660449781213">"義大利文"</string>
<string name="keyboard_layout_danish" msgid="8036432066627127851">"丹麥文"</string>
<string name="keyboard_layout_norwegian" msgid="9090097917011040937">"挪威文"</string>
diff --git a/packages/InputDevices/res/values-zu/strings.xml b/packages/InputDevices/res/values-zu/strings.xml
index ddb688ad92e9..3af1da1ac694 100644
--- a/packages/InputDevices/res/values-zu/strings.xml
+++ b/packages/InputDevices/res/values-zu/strings.xml
@@ -19,6 +19,7 @@
<string name="keyboard_layout_swiss_german_label" msgid="2305520941993314258">"Isi-Swiss German"</string>
<string name="keyboard_layout_belgian" msgid="2011984572838651558">"Isi-Belgian"</string>
<string name="keyboard_layout_bulgarian" msgid="8951224309972028398">"Isi-Bulgarian"</string>
+ <string name="keyboard_layout_bulgarian_phonetic" msgid="7568914730360106653">"Isi-Bulgarian, Ifonetiki"</string>
<string name="keyboard_layout_italian" msgid="6497079660449781213">"Isi-Italian"</string>
<string name="keyboard_layout_danish" msgid="8036432066627127851">"Isi-Danish"</string>
<string name="keyboard_layout_norwegian" msgid="9090097917011040937">"Isi-Norwegian"</string>
diff --git a/packages/SettingsLib/HelpUtils/res/values-te/strings.xml b/packages/SettingsLib/HelpUtils/res/values-te/strings.xml
index ea66717b1e87..82c8613b5c8c 100644
--- a/packages/SettingsLib/HelpUtils/res/values-te/strings.xml
+++ b/packages/SettingsLib/HelpUtils/res/values-te/strings.xml
@@ -17,5 +17,5 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="help_feedback_label" msgid="7106780063063027882">"సహాయం &amp; అభిప్రాయం"</string>
+ <string name="help_feedback_label" msgid="7106780063063027882">"సహాయం &amp; ఫీడ్‌బ్యాక్"</string>
</resources>
diff --git a/packages/SettingsLib/res/values-ar/strings.xml b/packages/SettingsLib/res/values-ar/strings.xml
index 9ca1814783b5..0b4538097e41 100644
--- a/packages/SettingsLib/res/values-ar/strings.xml
+++ b/packages/SettingsLib/res/values-ar/strings.xml
@@ -153,7 +153,7 @@
<string name="unknown" msgid="3544487229740637809">"غير معروف"</string>
<string name="running_process_item_user_label" msgid="3988506293099805796">"المستخدم: <xliff:g id="USER_NAME">%1$s</xliff:g>"</string>
<string name="launch_defaults_some" msgid="3631650616557252926">"تم ضبط بعض الإعدادات التلقائية"</string>
- <string name="launch_defaults_none" msgid="8049374306261262709">"لم يتم تعيين إعدادات تلقائية"</string>
+ <string name="launch_defaults_none" msgid="8049374306261262709">"لم يتم ضبط إعدادات تلقائية"</string>
<string name="tts_settings" msgid="8130616705989351312">"إعدادات تحويل النص إلى كلام"</string>
<string name="tts_settings_title" msgid="7602210956640483039">"تحويل النص إلى كلام"</string>
<string name="tts_default_rate_title" msgid="3964187817364304022">"معدل سرعة الكلام"</string>
@@ -245,7 +245,7 @@
<string name="confirm_enable_oem_unlock_title" msgid="8249318129774367535">"هل تريد السماح بإلغاء قفل المصنّع الأصلي للجهاز؟"</string>
<string name="confirm_enable_oem_unlock_text" msgid="854131050791011970">"تحذير: لن تعمل ميزات الحماية على هذا الجهاز أثناء تفعيل هذا الإعداد."</string>
<string name="mock_location_app" msgid="6269380172542248304">"اختيار تطبيق الموقع الزائف"</string>
- <string name="mock_location_app_not_set" msgid="6972032787262831155">"لم يتم تعيين تطبيق موقع زائف"</string>
+ <string name="mock_location_app_not_set" msgid="6972032787262831155">"لم يتم ضبط تطبيق موقع زائف"</string>
<string name="mock_location_app_set" msgid="4706722469342913843">"تطبيق الموقع الزائف: <xliff:g id="APP_NAME">%1$s</xliff:g>"</string>
<string name="debug_networking_category" msgid="6829757985772659599">"الشبكات"</string>
<string name="wifi_display_certification" msgid="1805579519992520381">"شهادة عرض شاشة لاسلكي"</string>
@@ -318,7 +318,7 @@
<string name="hdcp_checking_dialog_title" msgid="7691060297616217781">"‏تعيين سلوك التحقق من HDCP"</string>
<string name="debug_debugging_category" msgid="535341063709248842">"تصحيح الأخطاء"</string>
<string name="debug_app" msgid="8903350241392391766">"اختيار التطبيق لتصحيحه"</string>
- <string name="debug_app_not_set" msgid="1934083001283807188">"لم يتم تعيين تطبيق لتصحيحه"</string>
+ <string name="debug_app_not_set" msgid="1934083001283807188">"لم يتم ضبط تطبيق لتصحيحه"</string>
<string name="debug_app_set" msgid="6599535090477753651">"تطبيق التصحيح: <xliff:g id="APP_NAME">%1$s</xliff:g>"</string>
<string name="select_application" msgid="2543228890535466325">"اختيار تطبيق"</string>
<string name="no_application" msgid="9038334538870247690">"لا شيء"</string>
@@ -381,9 +381,9 @@
<string name="local_backup_password_title" msgid="4631017948933578709">"كلمة مرور احتياطية للكمبيوتر"</string>
<string name="local_backup_password_summary_none" msgid="7646898032616361714">"النُسخ الاحتياطية الكاملة لسطح المكتب غير محمية في الوقت الحالي"</string>
<string name="local_backup_password_summary_change" msgid="1707357670383995567">"انقر لتغيير كلمة مرور النسخ الاحتياطية الكاملة لسطح المكتب أو إزالتها."</string>
- <string name="local_backup_password_toast_success" msgid="4891666204428091604">"تم تعيين كلمة مرور احتياطية جديدة"</string>
+ <string name="local_backup_password_toast_success" msgid="4891666204428091604">"تم ضبط كلمة مرور احتياطية جديدة"</string>
<string name="local_backup_password_toast_confirmation_mismatch" msgid="2994718182129097733">"كلمة المرور الجديدة وتأكيدها لا يتطابقان"</string>
- <string name="local_backup_password_toast_validation_failure" msgid="714669442363647122">"تعذّر تعيين كلمة مرور احتياطية"</string>
+ <string name="local_backup_password_toast_validation_failure" msgid="714669442363647122">"تعذّر ضبط كلمة مرور احتياطية"</string>
<string name="loading_injected_setting_summary" msgid="8394446285689070348">"جارٍ التحميل…"</string>
<string-array name="color_mode_names">
<item msgid="3836559907767149216">"نابض بالحياة (تلقائي)"</item>
diff --git a/packages/SettingsLib/res/values-bn/strings.xml b/packages/SettingsLib/res/values-bn/strings.xml
index 87f3b7a4f2e8..1ab88ed8e00e 100644
--- a/packages/SettingsLib/res/values-bn/strings.xml
+++ b/packages/SettingsLib/res/values-bn/strings.xml
@@ -487,8 +487,8 @@
<string name="status_unavailable" msgid="5279036186589861608">"অনুপলভ্য"</string>
<string name="wifi_status_mac_randomized" msgid="466382542497832189">"MAC র‍্যান্ডমাইজ করা হয়েছে"</string>
<plurals name="wifi_tether_connected_summary" formatted="false" msgid="6317236306047306139">
- <item quantity="one">%1$dটি ডিভাইস কানেক্ট</item>
- <item quantity="other">%1$dটি ডিভাইস কানেক্ট</item>
+ <item quantity="one">%1$dটি ডিভাইস কানেক্ট রয়েছে</item>
+ <item quantity="other">%1$dটি ডিভাইস কানেক্ট রয়েছে</item>
</plurals>
<string name="accessibility_manual_zen_more_time" msgid="5141801092071134235">"আরও বেশি।"</string>
<string name="accessibility_manual_zen_less_time" msgid="6828877595848229965">"আরও কম।"</string>
diff --git a/packages/SettingsLib/res/values-fa/strings.xml b/packages/SettingsLib/res/values-fa/strings.xml
index 5fc311af26b2..3373f815ebaf 100644
--- a/packages/SettingsLib/res/values-fa/strings.xml
+++ b/packages/SettingsLib/res/values-fa/strings.xml
@@ -211,8 +211,8 @@
<string name="adb_wireless_error" msgid="721958772149779856">"خطا"</string>
<string name="adb_wireless_settings" msgid="2295017847215680229">"اشکال‌زدایی بی‌سیم"</string>
<string name="adb_wireless_list_empty_off" msgid="1713707973837255490">"برای مشاهده و استفاده از دستگاه‌های در دسترس، اشکال‌زدایی بی‌سیم را روشن کنید"</string>
- <string name="adb_pair_method_qrcode_title" msgid="6982904096137468634">"‏مرتبط کردن دستگاه با کد QR"</string>
- <string name="adb_pair_method_qrcode_summary" msgid="7130694277228970888">"‏دستگاه‌های جدید را بااستفاده از اسکنر کد QR مرتبط کنید"</string>
+ <string name="adb_pair_method_qrcode_title" msgid="6982904096137468634">"مرتبط کردن دستگاه با رمزینه پاسخ‌سریع"</string>
+ <string name="adb_pair_method_qrcode_summary" msgid="7130694277228970888">"دستگاه‌های جدید را بااستفاده از اسکنر رمزینه پاسخ‌سریع مرتبط کنید"</string>
<string name="adb_pair_method_code_title" msgid="1122590300445142904">"مرتبط کردن دستگاه با کد مرتبط‌سازی"</string>
<string name="adb_pair_method_code_summary" msgid="6370414511333685185">"دستگاه‌های جدید را با استفاده از کد شش رقمی مرتبط کنید"</string>
<string name="adb_paired_devices_title" msgid="5268997341526217362">"دستگاه‌های مرتبط‌شده"</string>
@@ -226,12 +226,12 @@
<string name="adb_pairing_device_dialog_pairing_code_label" msgid="3639239786669722731">"‏کد مرتبط‌سازی Wi‑Fi"</string>
<string name="adb_pairing_device_dialog_failed_title" msgid="3426758947882091735">"مرتبط‌سازی ناموفق"</string>
<string name="adb_pairing_device_dialog_failed_msg" msgid="6611097519661997148">"مطمئن شوید که دستگاه به همان شبکه متصل باشد."</string>
- <string name="adb_wireless_qrcode_summary" msgid="8051414549011801917">"‏دستگاه را ازطریق Wi‑Fi و با اسکن کردن کد QR مرتبط کنید"</string>
+ <string name="adb_wireless_qrcode_summary" msgid="8051414549011801917">"‏دستگاه را ازطریق Wi‑Fi و با اسکن کردن رمزینه پاسخ‌سریع مرتبط کنید"</string>
<string name="adb_wireless_verifying_qrcode_text" msgid="6123192424916029207">"مرتبط‌سازی دستگاه…"</string>
- <string name="adb_qrcode_pairing_device_failed_msg" msgid="6936292092592914132">"‏مرتبط کردن دستگاه انجام نشد. یا کد QR اشتباه بوده است، یا دستگاه به همان شبکه متصل نیست."</string>
+ <string name="adb_qrcode_pairing_device_failed_msg" msgid="6936292092592914132">"مرتبط کردن دستگاه انجام نشد. یا رمزینه پاسخ‌سریع اشتباه بوده است، یا دستگاه به همان شبکه متصل نیست."</string>
<string name="adb_wireless_ip_addr_preference_title" msgid="8335132107715311730">"‏نشانی IP و درگاه"</string>
- <string name="adb_wireless_qrcode_pairing_title" msgid="1906409667944674707">"‏اسکن کد QR"</string>
- <string name="adb_wireless_qrcode_pairing_description" msgid="6014121407143607851">"‏دستگاه را ازطریق Wi‑Fi و با اسکن کردن کد QR مرتبط کنید"</string>
+ <string name="adb_wireless_qrcode_pairing_title" msgid="1906409667944674707">"اسکن رمزینه پاسخ‌سریع"</string>
+ <string name="adb_wireless_qrcode_pairing_description" msgid="6014121407143607851">"‏دستگاه را ازطریق Wi‑Fi و با اسکن کردن رمزینه پاسخ‌سریع مرتبط کنید"</string>
<string name="adb_wireless_no_network_msg" msgid="2365795244718494658">"‏لطفاً به شبکه Wi-Fi متصل شوید"</string>
<string name="keywords_adb_wireless" msgid="6507505581882171240">"‏ADB (پل اشکال‌زدایی Android)، اشکال‌زدایی کردن، برنامه‌نویس"</string>
<string name="bugreport_in_power" msgid="8664089072534638709">"میان‌بر گزارش مشکل"</string>
diff --git a/packages/SettingsLib/res/values-gu/strings.xml b/packages/SettingsLib/res/values-gu/strings.xml
index 3261f69b1105..894a14ac3b74 100644
--- a/packages/SettingsLib/res/values-gu/strings.xml
+++ b/packages/SettingsLib/res/values-gu/strings.xml
@@ -487,8 +487,8 @@
<string name="status_unavailable" msgid="5279036186589861608">"અનુપલબ્ધ"</string>
<string name="wifi_status_mac_randomized" msgid="466382542497832189">"MACને રેન્ડમ કરેલ છે"</string>
<plurals name="wifi_tether_connected_summary" formatted="false" msgid="6317236306047306139">
- <item quantity="one">%1$d ઉપકરણ કનેક્ટ કર્યું</item>
- <item quantity="other">%1$d ઉપકરણો કનેક્ટ કર્યા</item>
+ <item quantity="one">%1$d ડિવાઇસ કનેક્ટ કર્યું</item>
+ <item quantity="other">%1$d ડિવાઇસ કનેક્ટ કર્યા</item>
</plurals>
<string name="accessibility_manual_zen_more_time" msgid="5141801092071134235">"વધુ સમય."</string>
<string name="accessibility_manual_zen_less_time" msgid="6828877595848229965">"ઓછો સમય."</string>
diff --git a/packages/SettingsLib/res/values-hi/strings.xml b/packages/SettingsLib/res/values-hi/strings.xml
index 904a70e73958..6e734bc873b8 100644
--- a/packages/SettingsLib/res/values-hi/strings.xml
+++ b/packages/SettingsLib/res/values-hi/strings.xml
@@ -487,7 +487,7 @@
<string name="status_unavailable" msgid="5279036186589861608">"मौजूद नहीं है"</string>
<string name="wifi_status_mac_randomized" msgid="466382542497832189">"एमएसी पता रैंडम पर सेट है"</string>
<plurals name="wifi_tether_connected_summary" formatted="false" msgid="6317236306047306139">
- <item quantity="one">%1$d डिवाइस जुड़े हैं</item>
+ <item quantity="one">%1$d डिवाइस जुड़ा है</item>
<item quantity="other">%1$d डिवाइस जुड़े हैं</item>
</plurals>
<string name="accessibility_manual_zen_more_time" msgid="5141801092071134235">"ज़्यादा समय."</string>
diff --git a/packages/SettingsLib/res/values-hu/strings.xml b/packages/SettingsLib/res/values-hu/strings.xml
index fec2dd6d523f..d86d88b3345d 100644
--- a/packages/SettingsLib/res/values-hu/strings.xml
+++ b/packages/SettingsLib/res/values-hu/strings.xml
@@ -145,7 +145,7 @@
<string name="data_usage_ota" msgid="7984667793701597001">"Rendszerfrissítések"</string>
<string name="tether_settings_title_usb" msgid="3728686573430917722">"USB-megosztás"</string>
<string name="tether_settings_title_wifi" msgid="4803402057533895526">"Hordozható hotspot"</string>
- <string name="tether_settings_title_bluetooth" msgid="916519902721399656">"Bluetooth megosztása"</string>
+ <string name="tether_settings_title_bluetooth" msgid="916519902721399656">"Bluetooth-megosztás"</string>
<string name="tether_settings_title_usb_bluetooth" msgid="1727111807207577322">"Megosztás"</string>
<string name="tether_settings_title_all" msgid="8910259483383010470">"Megosztás és hotspot"</string>
<string name="managed_user_title" msgid="449081789742645723">"Összes munkaalkalmazás"</string>
diff --git a/packages/SettingsLib/res/values-hy/arrays.xml b/packages/SettingsLib/res/values-hy/arrays.xml
index 2696f5a01785..a279872a088e 100644
--- a/packages/SettingsLib/res/values-hy/arrays.xml
+++ b/packages/SettingsLib/res/values-hy/arrays.xml
@@ -55,7 +55,7 @@
</string-array>
<string-array name="hdcp_checking_summaries">
<item msgid="4045840870658484038">"Երբեք չօգտագործել HDCP ստուգումը"</item>
- <item msgid="8254225038262324761">"Օգտագործել HDCP-ը` միայն DRM-ի բովանդակությունը ստուգելու համար"</item>
+ <item msgid="8254225038262324761">"Օգտագործել HDCP-ը՝ միայն DRM-ի բովանդակությունը ստուգելու համար"</item>
<item msgid="6421717003037072581">"Միշտ օգտագործել HDCP ստուգումը"</item>
</string-array>
<string-array name="bt_hci_snoop_log_entries">
diff --git a/packages/SettingsLib/res/values-hy/strings.xml b/packages/SettingsLib/res/values-hy/strings.xml
index 8b92640e9996..e434cac36b34 100644
--- a/packages/SettingsLib/res/values-hy/strings.xml
+++ b/packages/SettingsLib/res/values-hy/strings.xml
@@ -57,7 +57,7 @@
<string name="osu_sign_up_complete" msgid="7640183358878916847">"Գրանցումն ավարտված է: Միացում…"</string>
<string name="speed_label_very_slow" msgid="8526005255731597666">"Շատ դանդաղ"</string>
<string name="speed_label_slow" msgid="6069917670665664161">"Դանդաղ"</string>
- <string name="speed_label_okay" msgid="1253594383880810424">"Հաստատել"</string>
+ <string name="speed_label_okay" msgid="1253594383880810424">"Լավ"</string>
<string name="speed_label_medium" msgid="9078405312828606976">"Միջին"</string>
<string name="speed_label_fast" msgid="2677719134596044051">"Արագ"</string>
<string name="speed_label_very_fast" msgid="8215718029533182439">"Շատ արագ"</string>
@@ -343,7 +343,7 @@
<string name="show_hw_layers_updates_summary" msgid="5850955890493054618">"Թարմացվելիս ընդգծել սարքաշարի ծածկույթները կանաչ գույնով"</string>
<string name="debug_hw_overdraw" msgid="8944851091008756796">"Վրիպազերծել GPU գերազանցումները"</string>
<string name="disable_overlays" msgid="4206590799671557143">"Կասեցնել HW վրադրումները"</string>
- <string name="disable_overlays_summary" msgid="1954852414363338166">"Միշտ օգտագործել GPU-ն` էկրանի կազմման համար"</string>
+ <string name="disable_overlays_summary" msgid="1954852414363338166">"Միշտ օգտագործել GPU-ն՝ էկրանի կազմման համար"</string>
<string name="simulate_color_space" msgid="1206503300335835151">"Նմանակել գունատարածքը"</string>
<string name="enable_opengl_traces_title" msgid="4638773318659125196">"Ակտիվացնել OpenGL հետքերը"</string>
<string name="usb_audio_disable_routing" msgid="3367656923544254975">"Անջատել USB աուդիո երթուղումը"</string>
@@ -493,7 +493,7 @@
<string name="accessibility_manual_zen_more_time" msgid="5141801092071134235">"Ավելացնել ժամանակը:"</string>
<string name="accessibility_manual_zen_less_time" msgid="6828877595848229965">"Պակասեցնել ժամանակը:"</string>
<string name="cancel" msgid="5665114069455378395">"Չեղարկել"</string>
- <string name="okay" msgid="949938843324579502">"Հաստատել"</string>
+ <string name="okay" msgid="949938843324579502">"Եղավ"</string>
<string name="zen_mode_enable_dialog_turn_on" msgid="6418297231575050426">"Միացնել"</string>
<string name="zen_mode_settings_turn_on_dialog_title" msgid="2760567063190790696">"Միացրեք «Չանհանգստացնել» ռեժիմը"</string>
<string name="zen_mode_settings_summary_off" msgid="3832876036123504076">"Երբեք"</string>
diff --git a/packages/SettingsLib/res/values-ne/strings.xml b/packages/SettingsLib/res/values-ne/strings.xml
index 762d8304dd64..ee368fd5bfcf 100644
--- a/packages/SettingsLib/res/values-ne/strings.xml
+++ b/packages/SettingsLib/res/values-ne/strings.xml
@@ -143,9 +143,9 @@
<string name="data_usage_uninstalled_apps" msgid="1933665711856171491">"हटाइएका एपहरू"</string>
<string name="data_usage_uninstalled_apps_users" msgid="5533981546921913295">"एपहरू र प्रयोगकर्ताहरू हटाइयो।"</string>
<string name="data_usage_ota" msgid="7984667793701597001">"प्रणालीसम्बन्धी अद्यावधिकहरू"</string>
- <string name="tether_settings_title_usb" msgid="3728686573430917722">"USB टेथर गर्दै"</string>
+ <string name="tether_settings_title_usb" msgid="3728686573430917722">"USB टेदर गर्दै"</string>
<string name="tether_settings_title_wifi" msgid="4803402057533895526">"पोर्टेबल हटस्पट"</string>
- <string name="tether_settings_title_bluetooth" msgid="916519902721399656">"ब्लुटुथ टेथर गर्दै"</string>
+ <string name="tether_settings_title_bluetooth" msgid="916519902721399656">"ब्लुटुथ टेदर गर्दै"</string>
<string name="tether_settings_title_usb_bluetooth" msgid="1727111807207577322">"टेदर गर्दै"</string>
<string name="tether_settings_title_all" msgid="8910259483383010470">"टेदर गर्ने र पोर्टेबल हटस्पट"</string>
<string name="managed_user_title" msgid="449081789742645723">"कार्य प्रोफाइलका सबै एपहरू"</string>
diff --git a/packages/SettingsLib/res/values-sw/strings.xml b/packages/SettingsLib/res/values-sw/strings.xml
index d2891a06402a..c5d70ac866b1 100644
--- a/packages/SettingsLib/res/values-sw/strings.xml
+++ b/packages/SettingsLib/res/values-sw/strings.xml
@@ -143,11 +143,11 @@
<string name="data_usage_uninstalled_apps" msgid="1933665711856171491">"Programu zilizoondolewa"</string>
<string name="data_usage_uninstalled_apps_users" msgid="5533981546921913295">"Watumiaji na programu ziilizoondolewa"</string>
<string name="data_usage_ota" msgid="7984667793701597001">"Masasisho ya mfumo"</string>
- <string name="tether_settings_title_usb" msgid="3728686573430917722">"Shiriki intaneti kwa USB"</string>
+ <string name="tether_settings_title_usb" msgid="3728686573430917722">"Sambaza mtandao kwa USB"</string>
<string name="tether_settings_title_wifi" msgid="4803402057533895526">"Intaneti ya kusambazwa"</string>
- <string name="tether_settings_title_bluetooth" msgid="916519902721399656">"Shiriki intaneti kwa Bluetooth"</string>
- <string name="tether_settings_title_usb_bluetooth" msgid="1727111807207577322">"Inazuia"</string>
- <string name="tether_settings_title_all" msgid="8910259483383010470">"Kushiriki na kusambaza intaneti"</string>
+ <string name="tether_settings_title_bluetooth" msgid="916519902721399656">"Sambaza mtandao kwa Bluetooth"</string>
+ <string name="tether_settings_title_usb_bluetooth" msgid="1727111807207577322">"Kusambaza mtandao"</string>
+ <string name="tether_settings_title_all" msgid="8910259483383010470">"Kushiriki na kusambaza mtandao"</string>
<string name="managed_user_title" msgid="449081789742645723">"Programu zote za kazini"</string>
<string name="user_guest" msgid="6939192779649870792">"Mgeni"</string>
<string name="unknown" msgid="3544487229740637809">"Haijulikani"</string>
diff --git a/packages/SettingsLib/res/values-te/strings.xml b/packages/SettingsLib/res/values-te/strings.xml
index 60001a0b0ecd..e252eca5d72a 100644
--- a/packages/SettingsLib/res/values-te/strings.xml
+++ b/packages/SettingsLib/res/values-te/strings.xml
@@ -510,7 +510,7 @@
<string name="media_transfer_this_device_name" msgid="2716555073132169240">"ఫోన్ స్పీకర్"</string>
<string name="profile_connect_timeout_subtext" msgid="4043408193005851761">"కనెక్ట్ చేయడంలో సమస్య ఉంది. పరికరాన్ని ఆఫ్ చేసి, ఆపై తిరిగి ఆన్ చేయండి"</string>
<string name="media_transfer_wired_device_name" msgid="4447880899964056007">"వైర్ గల ఆడియో పరికరం"</string>
- <string name="help_label" msgid="3528360748637781274">"సహాయం &amp; అభిప్రాయం"</string>
+ <string name="help_label" msgid="3528360748637781274">"సహాయం &amp; ఫీడ్‌బ్యాక్"</string>
<string name="storage_category" msgid="2287342585424631813">"నిల్వ"</string>
<string name="shared_data_title" msgid="1017034836800864953">"షేర్ చేసిన డేటా"</string>
<string name="shared_data_summary" msgid="5516326713822885652">"షేర్ చేసిన డేటాను చూసి, సవరించండి"</string>
diff --git a/packages/SettingsLib/res/values-th/strings.xml b/packages/SettingsLib/res/values-th/strings.xml
index defc33ee9233..7468d0450a7e 100644
--- a/packages/SettingsLib/res/values-th/strings.xml
+++ b/packages/SettingsLib/res/values-th/strings.xml
@@ -143,7 +143,7 @@
<string name="data_usage_uninstalled_apps" msgid="1933665711856171491">"แอปพลิเคชันที่นำออก"</string>
<string name="data_usage_uninstalled_apps_users" msgid="5533981546921913295">"แอปพลิเคชันและผู้ใช้ที่นำออก"</string>
<string name="data_usage_ota" msgid="7984667793701597001">"การอัปเดตระบบ"</string>
- <string name="tether_settings_title_usb" msgid="3728686573430917722">"ปล่อยสัญญาณผ่าน USB"</string>
+ <string name="tether_settings_title_usb" msgid="3728686573430917722">"เชื่อมต่อเน็ตผ่าน USB"</string>
<string name="tether_settings_title_wifi" msgid="4803402057533895526">"ฮอตสปอตแบบพกพาได้"</string>
<string name="tether_settings_title_bluetooth" msgid="916519902721399656">"ปล่อยสัญญาณบลูทูธ"</string>
<string name="tether_settings_title_usb_bluetooth" msgid="1727111807207577322">"การปล่อยสัญญาณ"</string>
diff --git a/packages/SettingsLib/src/com/android/settingslib/bluetooth/LocalBluetoothProfileManager.java b/packages/SettingsLib/src/com/android/settingslib/bluetooth/LocalBluetoothProfileManager.java
index 35bbbc0e8b39..34fdc1e45567 100644
--- a/packages/SettingsLib/src/com/android/settingslib/bluetooth/LocalBluetoothProfileManager.java
+++ b/packages/SettingsLib/src/com/android/settingslib/bluetooth/LocalBluetoothProfileManager.java
@@ -543,12 +543,13 @@ public class LocalBluetoothProfileManager {
mPbapProfile.setEnabled(device, true);
}
- if (mMapClientProfile != null) {
+ if ((mMapClientProfile != null)
+ && BluetoothUuid.containsAnyUuid(uuids, MapClientProfile.UUIDS)) {
profiles.add(mMapClientProfile);
removedProfiles.remove(mMapClientProfile);
}
- if ((mPbapClientProfile != null) && ArrayUtils.contains(localUuids, BluetoothUuid.PBAP_PCE)
+ if ((mPbapClientProfile != null)
&& BluetoothUuid.containsAnyUuid(uuids, PbapClientProfile.SRC_UUIDS)) {
profiles.add(mPbapClientProfile);
removedProfiles.remove(mPbapClientProfile);
diff --git a/packages/SettingsLib/src/com/android/settingslib/bluetooth/MapClientProfile.java b/packages/SettingsLib/src/com/android/settingslib/bluetooth/MapClientProfile.java
index 19cb2f59f321..4881a86b398a 100644
--- a/packages/SettingsLib/src/com/android/settingslib/bluetooth/MapClientProfile.java
+++ b/packages/SettingsLib/src/com/android/settingslib/bluetooth/MapClientProfile.java
@@ -47,8 +47,6 @@ public final class MapClientProfile implements LocalBluetoothProfile {
private final LocalBluetoothProfileManager mProfileManager;
static final ParcelUuid[] UUIDS = {
- BluetoothUuid.MAP,
- BluetoothUuid.MNS,
BluetoothUuid.MAS,
};
diff --git a/packages/SettingsLib/src/com/android/settingslib/media/MediaOutputSliceConstants.java b/packages/SettingsLib/src/com/android/settingslib/media/MediaOutputSliceConstants.java
index 0d54d7ee9b5c..28e993da7dc4 100644
--- a/packages/SettingsLib/src/com/android/settingslib/media/MediaOutputSliceConstants.java
+++ b/packages/SettingsLib/src/com/android/settingslib/media/MediaOutputSliceConstants.java
@@ -72,6 +72,12 @@ public class MediaOutputSliceConstants {
"com.android.systemui.action.LAUNCH_MEDIA_OUTPUT_DIALOG";
/**
+ * An intent action to dismiss media output dialog.
+ */
+ public static final String ACTION_DISMISS_MEDIA_OUTPUT_DIALOG =
+ "com.android.systemui.action.DISMISS_MEDIA_OUTPUT_DIALOG";
+
+ /**
* Settings package name.
*/
public static final String SETTINGS_PACKAGE_NAME = "com.android.settings";
diff --git a/packages/SettingsLib/src/com/android/settingslib/wifi/OWNERS b/packages/SettingsLib/src/com/android/settingslib/wifi/OWNERS
index f506b7c12d81..0bde5c030eb0 100644
--- a/packages/SettingsLib/src/com/android/settingslib/wifi/OWNERS
+++ b/packages/SettingsLib/src/com/android/settingslib/wifi/OWNERS
@@ -1,7 +1,9 @@
# Default reviewers for this and subdirectories.
-qal@google.com
+andychou@google.com
arcwang@google.com
-govenliu@google.com
asapperstein@google.com
+goldmanj@google.com
+qal@google.com
+wengsu@google.com
-# Emergency approvers in case the above are not available \ No newline at end of file
+# Emergency approvers in case the above are not available
diff --git a/packages/SettingsProvider/src/android/provider/settings/backup/SecureSettings.java b/packages/SettingsProvider/src/android/provider/settings/backup/SecureSettings.java
index bcd2ff71b57f..4db61b02e8e9 100644
--- a/packages/SettingsProvider/src/android/provider/settings/backup/SecureSettings.java
+++ b/packages/SettingsProvider/src/android/provider/settings/backup/SecureSettings.java
@@ -175,6 +175,8 @@ public class SecureSettings {
Settings.Secure.SWIPE_BOTTOM_TO_NOTIFICATION_ENABLED,
Settings.Secure.PANIC_GESTURE_ENABLED,
Settings.Secure.PANIC_SOUND_ENABLED,
- Settings.Secure.ADAPTIVE_CONNECTIVITY_ENABLED
+ Settings.Secure.ADAPTIVE_CONNECTIVITY_ENABLED,
+ Settings.Secure.ASSIST_HANDLES_LEARNING_TIME_ELAPSED_MILLIS,
+ Settings.Secure.ASSIST_HANDLES_LEARNING_EVENT_COUNT
};
}
diff --git a/packages/SettingsProvider/src/android/provider/settings/validators/SecureSettingsValidators.java b/packages/SettingsProvider/src/android/provider/settings/validators/SecureSettingsValidators.java
index 3630f257f583..1fde40c37d9c 100644
--- a/packages/SettingsProvider/src/android/provider/settings/validators/SecureSettingsValidators.java
+++ b/packages/SettingsProvider/src/android/provider/settings/validators/SecureSettingsValidators.java
@@ -264,5 +264,8 @@ public class SecureSettingsValidators {
VALIDATORS.put(Secure.PANIC_GESTURE_ENABLED, BOOLEAN_VALIDATOR);
VALIDATORS.put(Secure.PANIC_SOUND_ENABLED, BOOLEAN_VALIDATOR);
VALIDATORS.put(Secure.ADAPTIVE_CONNECTIVITY_ENABLED, BOOLEAN_VALIDATOR);
+ VALIDATORS.put(
+ Secure.ASSIST_HANDLES_LEARNING_TIME_ELAPSED_MILLIS, NONE_NEGATIVE_LONG_VALIDATOR);
+ VALIDATORS.put(Secure.ASSIST_HANDLES_LEARNING_EVENT_COUNT, NON_NEGATIVE_INTEGER_VALIDATOR);
}
}
diff --git a/packages/SettingsProvider/src/com/android/providers/settings/SettingsProtoDumpUtil.java b/packages/SettingsProvider/src/com/android/providers/settings/SettingsProtoDumpUtil.java
index dd2aa3bbbdf5..bc6660184fe3 100644
--- a/packages/SettingsProvider/src/com/android/providers/settings/SettingsProtoDumpUtil.java
+++ b/packages/SettingsProvider/src/com/android/providers/settings/SettingsProtoDumpUtil.java
@@ -48,6 +48,8 @@ class SettingsProtoDumpUtil {
ConfigSettingsProto.ACTIVITY_MANAGER_SETTINGS);
namespaceToFieldMap.put(DeviceConfig.NAMESPACE_ACTIVITY_MANAGER_NATIVE_BOOT,
ConfigSettingsProto.ACTIVITY_MANAGER_NATIVE_BOOT_SETTINGS);
+ namespaceToFieldMap.put(DeviceConfig.NAMESPACE_ALARM_MANAGER,
+ ConfigSettingsProto.ALARM_MANAGER_SETTINGS);
namespaceToFieldMap.put(DeviceConfig.NAMESPACE_APP_COMPAT,
ConfigSettingsProto.APP_COMPAT_SETTINGS);
namespaceToFieldMap.put(DeviceConfig.NAMESPACE_AUTOFILL,
@@ -183,9 +185,6 @@ class SettingsProtoDumpUtil {
p.end(airplaneModeToken);
dumpSetting(s, p,
- Settings.Global.ALARM_MANAGER_CONSTANTS,
- GlobalSettingsProto.ALARM_MANAGER_CONSTANTS);
- dumpSetting(s, p,
Settings.Global.ALLOW_USER_SWITCHING_WHEN_SYSTEM_USER_LOCKED,
GlobalSettingsProto.ALLOW_USER_SWITCHING_WHEN_SYSTEM_USER_LOCKED);
dumpSetting(s, p,
@@ -1876,6 +1875,15 @@ class SettingsProtoDumpUtil {
SecureSettingsProto.Assist.GESTURE_SETUP_COMPLETE);
p.end(assistToken);
+ final long assistHandlesToken = p.start(SecureSettingsProto.ASSIST_HANDLES);
+ dumpSetting(s, p,
+ Settings.Secure.ASSIST_HANDLES_LEARNING_TIME_ELAPSED_MILLIS,
+ SecureSettingsProto.AssistHandles.LEARNING_TIME_ELAPSED_MILLIS);
+ dumpSetting(s, p,
+ Settings.Secure.ASSIST_HANDLES_LEARNING_EVENT_COUNT,
+ SecureSettingsProto.AssistHandles.LEARNING_EVENT_COUNT);
+ p.end(assistHandlesToken);
+
final long autofillToken = p.start(SecureSettingsProto.AUTOFILL);
dumpSetting(s, p,
Settings.Secure.AUTOFILL_SERVICE,
@@ -2284,6 +2292,18 @@ class SettingsProtoDumpUtil {
SecureSettingsProto.Notification.IN_CALL_NOTIFICATION_ENABLED);
p.end(notificationToken);
+ final long oneHandedToken = p.start(SecureSettingsProto.ONEHANDED);
+ dumpSetting(s, p,
+ Settings.Secure.ONE_HANDED_MODE_ENABLED,
+ SecureSettingsProto.OneHanded.ONE_HANDED_MODE_ENABLED);
+ dumpSetting(s, p,
+ Settings.Secure.ONE_HANDED_MODE_TIMEOUT,
+ SecureSettingsProto.OneHanded.ONE_HANDED_MODE_TIMEOUT);
+ dumpSetting(s, p,
+ Settings.Secure.TAPS_APP_TO_EXIT,
+ SecureSettingsProto.OneHanded.TAPS_APP_TO_EXIT);
+ p.end(oneHandedToken);
+
final long parentalControlToken = p.start(SecureSettingsProto.PARENTAL_CONTROL);
dumpSetting(s, p,
Settings.Secure.PARENTAL_CONTROL_ENABLED,
@@ -2452,6 +2472,9 @@ class SettingsProtoDumpUtil {
p.end(soundsToken);
dumpSetting(s, p,
+ Settings.Secure.SWIPE_BOTTOM_TO_NOTIFICATION_ENABLED,
+ SecureSettingsProto.SWIPE_BOTTOM_TO_NOTIFICATION_ENABLED);
+ dumpSetting(s, p,
Settings.Secure.SYNC_PARENT_SOUNDS,
SecureSettingsProto.SYNC_PARENT_SOUNDS);
dumpSetting(s, p,
@@ -2572,22 +2595,6 @@ class SettingsProtoDumpUtil {
SecureSettingsProto.Zen.SETTINGS_SUGGESTION_VIEWED);
p.end(zenToken);
- dumpSetting(s, p,
- Settings.Secure.ONE_HANDED_MODE_ENABLED,
- SecureSettingsProto.OneHanded.ONE_HANDED_MODE_ENABLED);
-
- dumpSetting(s, p,
- Settings.Secure.ONE_HANDED_MODE_TIMEOUT,
- SecureSettingsProto.OneHanded.ONE_HANDED_MODE_TIMEOUT);
-
- dumpSetting(s, p,
- Settings.Secure.TAPS_APP_TO_EXIT,
- SecureSettingsProto.OneHanded.TAPS_APP_TO_EXIT);
-
- dumpSetting(s, p,
- Settings.Secure.SWIPE_BOTTOM_TO_NOTIFICATION_ENABLED,
- SecureSettingsProto.SWIPE_BOTTOM_TO_NOTIFICATION_ENABLED);
-
// Please insert new settings using the same order as in SecureSettingsProto.
p.end(token);
diff --git a/packages/SettingsProvider/test/src/android/provider/SettingsBackupTest.java b/packages/SettingsProvider/test/src/android/provider/SettingsBackupTest.java
index d8f772d7f440..91510f65316f 100644
--- a/packages/SettingsProvider/test/src/android/provider/SettingsBackupTest.java
+++ b/packages/SettingsProvider/test/src/android/provider/SettingsBackupTest.java
@@ -117,7 +117,6 @@ public class SettingsBackupTest {
Settings.Global.AIRPLANE_MODE_ON,
Settings.Global.AIRPLANE_MODE_RADIOS,
Settings.Global.AIRPLANE_MODE_TOGGLEABLE_RADIOS,
- Settings.Global.ALARM_MANAGER_CONSTANTS,
Settings.Global.ALLOW_USER_SWITCHING_WHEN_SYSTEM_USER_LOCKED,
Settings.Global.ALWAYS_FINISH_ACTIVITIES,
Settings.Global.ALWAYS_ON_DISPLAY_CONSTANTS,
diff --git a/packages/Shell/AndroidManifest.xml b/packages/Shell/AndroidManifest.xml
index 190015cebe30..f5f58efb72e6 100644
--- a/packages/Shell/AndroidManifest.xml
+++ b/packages/Shell/AndroidManifest.xml
@@ -323,6 +323,9 @@
<!-- Permissions required for CTS test - AdbManagerTest -->
<uses-permission android:name="android.permission.MANAGE_DEBUGGING" />
+ <!-- Permission needed for CTS test - DisplayTest -->
+ <uses-permission android:name="android.permission.OVERRIDE_DISPLAY_MODE_REQUESTS" />
+
<application android:label="@string/app_label"
android:theme="@android:style/Theme.DeviceDefault.DayNight"
android:defaultToDeviceProtectedStorage="true"
diff --git a/packages/SoundPicker/src/com/android/soundpicker/RingtonePickerActivity.java b/packages/SoundPicker/src/com/android/soundpicker/RingtonePickerActivity.java
index d69f3d620d48..504e18a1488e 100644
--- a/packages/SoundPicker/src/com/android/soundpicker/RingtonePickerActivity.java
+++ b/packages/SoundPicker/src/com/android/soundpicker/RingtonePickerActivity.java
@@ -587,15 +587,19 @@ public final class RingtonePickerActivity extends AlertActivity implements
}
private Uri getCurrentlySelectedRingtoneUri() {
- if (getCheckedItem() == mDefaultRingtonePos) {
- // Use the default Uri that they originally gave us.
- return mUriForDefaultItem;
- } else if (getCheckedItem() == mSilentPos) {
- // Use a null Uri for the 'Silent' item.
- return null;
- } else {
- return mRingtoneManager.getRingtoneUri(getRingtoneManagerPosition(getCheckedItem()));
- }
+ if (getCheckedItem() == POS_UNKNOWN) {
+ // When the getCheckItem is POS_UNKNOWN, it is not the case we expected.
+ // We return null for this case.
+ return null;
+ } else if (getCheckedItem() == mDefaultRingtonePos) {
+ // Use the default Uri that they originally gave us.
+ return mUriForDefaultItem;
+ } else if (getCheckedItem() == mSilentPos) {
+ // Use a null Uri for the 'Silent' item.
+ return null;
+ } else {
+ return mRingtoneManager.getRingtoneUri(getRingtoneManagerPosition(getCheckedItem()));
+ }
}
private void saveAnyPlayingRingtone() {
diff --git a/packages/SystemUI/AndroidManifest.xml b/packages/SystemUI/AndroidManifest.xml
index 57c15e3211e1..cf78a131d6fb 100644
--- a/packages/SystemUI/AndroidManifest.xml
+++ b/packages/SystemUI/AndroidManifest.xml
@@ -772,10 +772,11 @@
</intent-filter>
</receiver>
- <receiver android:name=".media.dialog.MediaOutDialogReceiver"
+ <receiver android:name=".media.dialog.MediaOutputDialogReceiver"
android:exported="true">
<intent-filter>
<action android:name="com.android.systemui.action.LAUNCH_MEDIA_OUTPUT_DIALOG" />
+ <action android:name="com.android.systemui.action.DISMISS_MEDIA_OUTPUT_DIALOG" />
</intent-filter>
</receiver>
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 e246917842b0..26cead206310 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
@@ -86,6 +86,10 @@ public interface QSTile {
*/
InstanceId getInstanceId();
+ default boolean isTileReady() {
+ return false;
+ }
+
@ProvidesInterface(version = Callback.VERSION)
public interface Callback {
public static final int VERSION = 1;
diff --git a/packages/SystemUI/res-keyguard/values-km/strings.xml b/packages/SystemUI/res-keyguard/values-km/strings.xml
index 24b5c23a6732..52b7fab768c5 100644
--- a/packages/SystemUI/res-keyguard/values-km/strings.xml
+++ b/packages/SystemUI/res-keyguard/values-km/strings.xml
@@ -100,7 +100,7 @@
<string name="kg_pin_accepted" msgid="1625501841604389716">"កូដត្រូវ​បានទទួល​យក!"</string>
<string name="keyguard_carrier_default" msgid="6359808469637388586">"គ្មាន​សេវា​ទេ។"</string>
<string name="accessibility_ime_switch_button" msgid="9082358310194861329">"ប្ដូរ​វិធី​បញ្ចូល"</string>
- <string name="airplane_mode" msgid="2528005343938497866">"មុខងារ​ពេល​ជិះ​យន្តហោះ"</string>
+ <string name="airplane_mode" msgid="2528005343938497866">"​ពេល​ជិះ​យន្តហោះ"</string>
<string name="kg_prompt_reason_restart_pattern" msgid="4720554342633852066">"តម្រូវឲ្យប្រើលំនាំ បន្ទាប់ពីឧបករណ៍ចាប់ផ្តើមឡើងវិញ"</string>
<string name="kg_prompt_reason_restart_pin" msgid="1587671566498057656">"តម្រូវឲ្យបញ្ចូលកូដ PIN បន្ទាប់ពីឧបករណ៍ចាប់ផ្តើមឡើងវិញ"</string>
<string name="kg_prompt_reason_restart_password" msgid="8061279087240952002">"តម្រូវឲ្យបញ្ចូលពាក្យសម្ងាត់ បន្ទាប់ពីឧបករណ៍ចាប់ផ្តើមឡើងវិញ"</string>
diff --git a/packages/SystemUI/res-keyguard/values-te/strings.xml b/packages/SystemUI/res-keyguard/values-te/strings.xml
index 74386bc7a487..d44003bc5b95 100644
--- a/packages/SystemUI/res-keyguard/values-te/strings.xml
+++ b/packages/SystemUI/res-keyguard/values-te/strings.xml
@@ -134,7 +134,7 @@
<item quantity="other">SIM ఇప్పుడు నిలిపివేయబడింది. PUK కోడ్‌ను నమోదు చేయండి. SIM శాశ్వతంగా నిరుపయోగం కాకుండా ఉండటానికి మీకు <xliff:g id="_NUMBER_1">%d</xliff:g> ప్రయత్నాలు మిగిలి ఉన్నాయి. వివరాల కోసం కారియర్‌ను సంప్రదించండి.</item>
<item quantity="one">SIM ఇప్పుడు నిలిపివేయబడింది. PUK కోడ్‌ను నమోదు చేయండి. SIM శాశ్వతంగా నిరుపయోగం కాకుండా ఉండటానికి మీకు <xliff:g id="_NUMBER_0">%d</xliff:g> ప్రయత్నం మిగిలి ఉంది వివరాల కోసం కారియర్‌ను సంప్రదించండి.</item>
</plurals>
- <string name="clock_title_default" msgid="6342735240617459864">"డిఫాల్ట్"</string>
+ <string name="clock_title_default" msgid="6342735240617459864">"ఆటోమేటిక్"</string>
<string name="clock_title_bubble" msgid="2204559396790593213">"బబుల్"</string>
<string name="clock_title_analog" msgid="8409262532900918273">"ఎనలాగ్"</string>
</resources>
diff --git a/packages/SystemUI/res/drawable/ic_move.xml b/packages/SystemUI/res/drawable/ic_move_magnification.xml
index e82c9d0f23a9..ed97d0cc0522 100644
--- a/packages/SystemUI/res/drawable/ic_move.xml
+++ b/packages/SystemUI/res/drawable/ic_move_magnification.xml
@@ -16,7 +16,7 @@
<layer-list xmlns:android="http://schemas.android.com/apk/res/android">
<item>
- <shape android:shape="oval">
+ <shape android:shape="rectangle">
<solid
android:color="@android:color/black" />
<size
diff --git a/packages/SystemUI/res/drawable/tv_rect_dark_left_rounded.xml b/packages/SystemUI/res/drawable/tv_rect_dark_left_rounded.xml
deleted file mode 100644
index 9b48a70d9439..000000000000
--- a/packages/SystemUI/res/drawable/tv_rect_dark_left_rounded.xml
+++ /dev/null
@@ -1,26 +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.
- -->
-
-<shape xmlns:android="http://schemas.android.com/apk/res/android"
- android:shape="rectangle">
-
- <corners
- android:bottomLeftRadius="8dp"
- android:topLeftRadius="8dp" />
- <solid android:color="@color/tv_audio_recording_indicator_background" />
-
-</shape> \ No newline at end of file
diff --git a/packages/SystemUI/res/drawable/tv_rect_dark_right_rounded.xml b/packages/SystemUI/res/drawable/tv_rect_dark_right_rounded.xml
deleted file mode 100644
index 03348756231b..000000000000
--- a/packages/SystemUI/res/drawable/tv_rect_dark_right_rounded.xml
+++ /dev/null
@@ -1,26 +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.
- -->
-
-<shape xmlns:android="http://schemas.android.com/apk/res/android"
- android:shape="rectangle">
-
- <corners
- android:bottomRightRadius="8dp"
- android:topRightRadius="8dp" />
- <solid android:color="@color/tv_audio_recording_indicator_background" />
-
-</shape> \ No newline at end of file
diff --git a/packages/SystemUI/res/layout/media_output_dialog.xml b/packages/SystemUI/res/layout/media_output_dialog.xml
index 0229e6e9d4dd..73beefc9da83 100644
--- a/packages/SystemUI/res/layout/media_output_dialog.xml
+++ b/packages/SystemUI/res/layout/media_output_dialog.xml
@@ -32,7 +32,7 @@
android:id="@+id/header_icon"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
- android:paddingEnd="16dp"/>
+ android:paddingEnd="@dimen/media_output_dialog_header_icon_padding"/>
<LinearLayout
android:layout_width="match_parent"
@@ -70,36 +70,14 @@
android:id="@+id/device_list"
android:layout_width="match_parent"
android:layout_height="wrap_content"
- android:gravity="start|center_vertical"
android:orientation="vertical">
- <View
- android:layout_width="match_parent"
- android:layout_height="12dp"/>
-
- <include
- layout="@layout/media_output_list_item"
- android:id="@+id/group_item_controller"
- android:visibility="gone"/>
-
- <View
- android:id="@+id/group_item_divider"
- android:layout_width="match_parent"
- android:layout_height="1dp"
- android:background="?android:attr/listDivider"
- android:visibility="gone"/>
-
<androidx.recyclerview.widget.RecyclerView
android:id="@+id/list_result"
android:scrollbars="vertical"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:overScrollMode="never"/>
-
- <View
- android:id="@+id/list_bottom_padding"
- android:layout_width="match_parent"
- android:layout_height="12dp"/>
</LinearLayout>
<View
diff --git a/packages/SystemUI/res/layout/notification_conversation_info.xml b/packages/SystemUI/res/layout/notification_conversation_info.xml
index fd89c0bf39dd..10ad8291636e 100644
--- a/packages/SystemUI/res/layout/notification_conversation_info.xml
+++ b/packages/SystemUI/res/layout/notification_conversation_info.xml
@@ -34,7 +34,7 @@
android:layout_height="wrap_content"
android:gravity="center_vertical"
android:clipChildren="false"
- android:paddingTop="11dp"
+ android:paddingTop="@dimen/notification_guts_header_top_padding"
android:clipToPadding="true">
<ImageView
android:id="@+id/conversation_icon"
diff --git a/packages/SystemUI/res/layout/notification_info.xml b/packages/SystemUI/res/layout/notification_info.xml
index 92b3ff335cba..0a33d5e4429b 100644
--- a/packages/SystemUI/res/layout/notification_info.xml
+++ b/packages/SystemUI/res/layout/notification_info.xml
@@ -30,10 +30,11 @@
<LinearLayout
android:id="@+id/header"
android:layout_width="match_parent"
- android:layout_height="@dimen/notification_guts_conversation_header_height"
+ android:layout_height="wrap_content"
android:gravity="center_vertical"
android:clipChildren="false"
- android:clipToPadding="false">
+ android:paddingTop="@dimen/notification_guts_header_top_padding"
+ android:clipToPadding="true">
<ImageView
android:id="@+id/pkg_icon"
android:layout_width="@dimen/notification_guts_conversation_icon_size"
diff --git a/packages/SystemUI/res/layout/partial_conversation_info.xml b/packages/SystemUI/res/layout/partial_conversation_info.xml
index c353d089895c..af66f8ba4cd6 100644
--- a/packages/SystemUI/res/layout/partial_conversation_info.xml
+++ b/packages/SystemUI/res/layout/partial_conversation_info.xml
@@ -30,10 +30,11 @@
<LinearLayout
android:id="@+id/header"
android:layout_width="match_parent"
- android:layout_height="@dimen/notification_guts_conversation_header_height"
+ android:layout_height="wrap_content"
android:gravity="center_vertical"
android:clipChildren="false"
- android:clipToPadding="false">
+ android:paddingTop="@dimen/notification_guts_header_top_padding"
+ android:clipToPadding="true">
<ImageView
android:id="@+id/icon"
android:layout_width="@dimen/notification_guts_conversation_icon_size"
diff --git a/packages/SystemUI/res/layout/tv_audio_recording_indicator.xml b/packages/SystemUI/res/layout/tv_audio_recording_indicator.xml
index f9336a540376..b62018d7cb9e 100644
--- a/packages/SystemUI/res/layout/tv_audio_recording_indicator.xml
+++ b/packages/SystemUI/res/layout/tv_audio_recording_indicator.xml
@@ -22,80 +22,17 @@
android:padding="12dp">
<FrameLayout
- android:layout_width="wrap_content"
- android:layout_height="wrap_content">
+ android:layout_width="34dp"
+ android:layout_height="24dp"
+ android:layout_gravity="center"
+ android:background="@drawable/tv_rect_shadow_rounded">
- <LinearLayout
- android:id="@+id/icon_texts_container"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:orientation="horizontal">
-
- <FrameLayout
- android:layout_width="wrap_content"
- android:layout_height="match_parent">
-
- <View
- android:id="@+id/icon_container_bg"
- android:layout_width="50dp"
- android:layout_height="match_parent"
- android:background="@drawable/tv_rect_dark_left_rounded"/>
-
- <FrameLayout
- android:id="@+id/icon_mic"
- android:layout_width="34dp"
- android:layout_height="24dp"
- android:layout_gravity="center"
- android:background="@drawable/tv_rect_shadow_rounded">
-
- <ImageView
- android:layout_width="13dp"
- android:layout_height="13dp"
- android:layout_gravity="center"
- android:background="@drawable/tv_ic_mic_white"/>
- </FrameLayout>
-
- </FrameLayout>
-
- <LinearLayout
- android:id="@+id/texts_container"
- android:layout_width="wrap_content"
- android:layout_height="47dp"
- android:background="@color/tv_audio_recording_indicator_background"
- android:orientation="vertical"
- android:visibility="visible">
-
- <TextView
- android:layout_width="wrap_content"
- android:layout_height="14dp"
- android:layout_marginTop="10dp"
- android:layout_marginBottom="1dp"
- android:text="@string/mic_active"
- android:textColor="@android:color/white"
- android:fontFamily="sans-serif"
- android:textSize="10dp"/>
-
- <TextView
- android:id="@+id/text"
- android:layout_width="wrap_content"
- android:layout_height="14dp"
- android:singleLine="true"
- android:text="SomeApplication accessed your microphone"
- android:textColor="@android:color/white"
- android:fontFamily="sans-serif"
- android:textSize="8dp"/>
-
- </LinearLayout>
-
- </LinearLayout>
+ <ImageView
+ android:layout_width="13dp"
+ android:layout_height="13dp"
+ android:layout_gravity="center"
+ android:src="@drawable/tv_ic_mic_white"/>
</FrameLayout>
- <View
- android:id="@+id/bg_end"
- android:layout_width="12dp"
- android:layout_height="47dp"
- android:background="@drawable/tv_rect_dark_right_rounded"
- android:visibility="visible"/>
-
</LinearLayout>
diff --git a/packages/SystemUI/res/layout/window_magnifier_view.xml b/packages/SystemUI/res/layout/window_magnifier_view.xml
index 25d63e3175d0..efd24c7d9d4f 100644
--- a/packages/SystemUI/res/layout/window_magnifier_view.xml
+++ b/packages/SystemUI/res/layout/window_magnifier_view.xml
@@ -17,55 +17,57 @@
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="wrap_content"
- android:layout_height="wrap_content">
-
+ android:layout_height="wrap_content"
+ android:screenReaderFocusable="true">
<View
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_margin="@dimen/magnification_outer_border_margin"
- android:background="@android:color/black" />
+ android:importantForAccessibility="no"
+ android:background="@android:color/black"/>
<View
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_margin="@dimen/magnification_inner_border_margin"
- android:background="@color/magnification_border_color" />
+ android:importantForAccessibility="no"
+ android:background="@color/magnification_border_color"/>
<RelativeLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
- android:orientation="vertical">
+ android:importantForAccessibility="noHideDescendants">
<View
android:id="@+id/left_handle"
android:layout_width="@dimen/magnification_border_drag_size"
android:layout_height="match_parent"
- android:layout_above="@+id/bottom_handle" />
+ android:layout_above="@+id/bottom_handle"/>
<View
android:id="@+id/top_handle"
android:layout_width="match_parent"
android:layout_height="@dimen/magnification_border_drag_size"
- android:layout_alignParentTop="true" />
+ android:layout_alignParentTop="true"/>
<View
android:id="@+id/right_handle"
android:layout_width="@dimen/magnification_border_drag_size"
android:layout_height="match_parent"
android:layout_above="@+id/bottom_handle"
- android:layout_alignParentEnd="true" />
+ android:layout_alignParentEnd="true"/>
<View
android:id="@+id/bottom_handle"
android:layout_width="match_parent"
android:layout_height="@dimen/magnification_border_drag_size"
- android:layout_alignParentBottom="true" />
+ android:layout_alignParentBottom="true"/>
<SurfaceView
android:id="@+id/surface_view"
android:layout_width="match_parent"
android:layout_height="match_parent"
- android:layout_margin="@dimen/magnification_mirror_surface_margin" />
+ android:layout_margin="@dimen/magnification_mirror_surface_margin"/>
</RelativeLayout>
@@ -73,8 +75,10 @@
android:id="@+id/drag_handle"
android:layout_width="@dimen/magnification_drag_view_size"
android:layout_height="@dimen/magnification_drag_view_size"
+ android:layout_margin="@dimen/magnification_outer_border_margin"
android:layout_gravity="right|bottom"
android:scaleType="center"
- android:src="@drawable/ic_move" />
+ android:importantForAccessibility="no"
+ android:src="@drawable/ic_move_magnification"/>
</FrameLayout> \ No newline at end of file
diff --git a/packages/SystemUI/res/values-af/strings.xml b/packages/SystemUI/res/values-af/strings.xml
index 4a55ce3f077b..72dfe74b2fde 100644
--- a/packages/SystemUI/res/values-af/strings.xml
+++ b/packages/SystemUI/res/values-af/strings.xml
@@ -879,12 +879,13 @@
</string-array>
<string name="tuner_low_priority" msgid="8412666814123009820">"Wys laeprioriteit-kennisgewingikone"</string>
<string name="other" msgid="429768510980739978">"Ander"</string>
- <string name="accessibility_qs_edit_tile_label" msgid="9079791448815232967">"Posisie <xliff:g id="POSITION">%1$d</xliff:g>, <xliff:g id="TILE_NAME">%2$s</xliff:g>. Dubbeltik om te wysig."</string>
- <string name="accessibility_qs_edit_add_tile_label" msgid="8292218072049068613">"<xliff:g id="TILE_NAME">%1$s</xliff:g>. Dubbeltik om by te voeg."</string>
- <string name="accessibility_qs_edit_move_tile" msgid="6027997446473163426">"Skuif <xliff:g id="TILE_NAME">%1$s</xliff:g>"</string>
- <string name="accessibility_qs_edit_remove_tile" msgid="3406781901949899624">"Verwyder <xliff:g id="TILE_NAME">%1$s</xliff:g>"</string>
- <string name="accessibility_qs_edit_tile_add" msgid="6289879620154587233">"Voeg <xliff:g id="TILE_NAME">%1$s</xliff:g> by posisie <xliff:g id="POSITION">%2$d</xliff:g>"</string>
- <string name="accessibility_qs_edit_tile_move" msgid="4841770637244326837">"Skuif <xliff:g id="TILE_NAME">%1$s</xliff:g> na posisie <xliff:g id="POSITION">%2$d</xliff:g>"</string>
+ <string name="accessibility_qs_edit_remove_tile_action" msgid="775511891457193480">"verwyder teël"</string>
+ <string name="accessibility_qs_edit_tile_add_action" msgid="5051211910345301833">"voeg teël aan einde by"</string>
+ <string name="accessibility_qs_edit_tile_start_move" msgid="2009373939914517817">"Skuif teël"</string>
+ <string name="accessibility_qs_edit_tile_start_add" msgid="7560798153975555772">"Voeg teël by"</string>
+ <string name="accessibility_qs_edit_tile_move_to_position" msgid="5198161544045930556">"Skuif na <xliff:g id="POSITION">%1$d</xliff:g>"</string>
+ <string name="accessibility_qs_edit_tile_add_to_position" msgid="9029163095148274690">"Voeg by posisie <xliff:g id="POSITION">%1$d</xliff:g>"</string>
+ <string name="accessibility_qs_edit_position" msgid="4509277359815711830">"Posisie <xliff:g id="POSITION">%1$d</xliff:g>"</string>
<string name="accessibility_desc_quick_settings_edit" msgid="741658939453595297">"Kitsinstellingswysiger."</string>
<string name="accessibility_desc_notification_icon" msgid="7331265967584178674">"<xliff:g id="ID_1">%1$s</xliff:g>-kennisgewing: <xliff:g id="ID_2">%2$s</xliff:g>"</string>
<string name="accessibility_quick_settings_settings" msgid="7098489591715844713">"Maak instellings oop."</string>
@@ -1067,4 +1068,11 @@
<string name="controls_added_tooltip" msgid="4842812921719153085">"Hou aan/af-skakelaar in om nuwe kontroles te sien"</string>
<string name="controls_menu_add" msgid="4447246119229920050">"Voeg kontroles by"</string>
<string name="controls_menu_edit" msgid="890623986951347062">"Wysig kontroles"</string>
+ <string name="media_output_dialog_add_output" msgid="5642703238877329518">"Voeg uitvoere by"</string>
+ <string name="media_output_dialog_group" msgid="5571251347877452212">"Groep"</string>
+ <string name="media_output_dialog_single_device" msgid="3102758980643351058">"1 toestel gekies"</string>
+ <string name="media_output_dialog_multiple_devices" msgid="1093771040315422350">"<xliff:g id="COUNT">%1$d</xliff:g> toestelle gekies"</string>
+ <string name="media_output_dialog_disconnected" msgid="1834473104836986046">"<xliff:g id="DEVICE_NAME">%1$s</xliff:g> (ontkoppel)"</string>
+ <string name="media_output_dialog_connect_failed" msgid="3225190634236259010">"Kon nie koppel nie. Probeer weer."</string>
+ <string name="media_output_dialog_pairing_new" msgid="9099497976087485862">"Bind nuwe toestel saam"</string>
</resources>
diff --git a/packages/SystemUI/res/values-am/strings.xml b/packages/SystemUI/res/values-am/strings.xml
index 7de4f443240c..0ec236de8867 100644
--- a/packages/SystemUI/res/values-am/strings.xml
+++ b/packages/SystemUI/res/values-am/strings.xml
@@ -879,12 +879,13 @@
</string-array>
<string name="tuner_low_priority" msgid="8412666814123009820">"አነስተኛ ቅድሚያ ያላቸው የማሳወቂያ አዶዎችን አሳይ"</string>
<string name="other" msgid="429768510980739978">"ሌላ"</string>
- <string name="accessibility_qs_edit_tile_label" msgid="9079791448815232967">"ቦታ <xliff:g id="POSITION">%1$d</xliff:g>፣ <xliff:g id="TILE_NAME">%2$s</xliff:g>። ለማርትዕ ሁለቴ መታ ያድርጉ።"</string>
- <string name="accessibility_qs_edit_add_tile_label" msgid="8292218072049068613">"<xliff:g id="TILE_NAME">%1$s</xliff:g>። ለማከል ሁለቴ መታ ያድርጉ።"</string>
- <string name="accessibility_qs_edit_move_tile" msgid="6027997446473163426">"<xliff:g id="TILE_NAME">%1$s</xliff:g>ን ይውሰዱ"</string>
- <string name="accessibility_qs_edit_remove_tile" msgid="3406781901949899624">"<xliff:g id="TILE_NAME">%1$s</xliff:g>ን ያስወግዱ"</string>
- <string name="accessibility_qs_edit_tile_add" msgid="6289879620154587233">"<xliff:g id="TILE_NAME">%1$s</xliff:g>ን ወደ አቀማመጥ <xliff:g id="POSITION">%2$d</xliff:g> አክል"</string>
- <string name="accessibility_qs_edit_tile_move" msgid="4841770637244326837">"<xliff:g id="TILE_NAME">%1$s</xliff:g>ን ወደ አቀማመጥ <xliff:g id="POSITION">%2$d</xliff:g> አንቀሳቅስ"</string>
+ <string name="accessibility_qs_edit_remove_tile_action" msgid="775511891457193480">"ሰቅ አስወግድ"</string>
+ <string name="accessibility_qs_edit_tile_add_action" msgid="5051211910345301833">"ሰቅ መጨረሻው ላይ አክል"</string>
+ <string name="accessibility_qs_edit_tile_start_move" msgid="2009373939914517817">"ሰቁን ውሰድ"</string>
+ <string name="accessibility_qs_edit_tile_start_add" msgid="7560798153975555772">"ሰቅ ያክሉ"</string>
+ <string name="accessibility_qs_edit_tile_move_to_position" msgid="5198161544045930556">"ወደ <xliff:g id="POSITION">%1$d</xliff:g> ውሰድ"</string>
+ <string name="accessibility_qs_edit_tile_add_to_position" msgid="9029163095148274690">"ወደ <xliff:g id="POSITION">%1$d</xliff:g> ቦታ አክል"</string>
+ <string name="accessibility_qs_edit_position" msgid="4509277359815711830">"የ<xliff:g id="POSITION">%1$d</xliff:g> አቀማመጥ"</string>
<string name="accessibility_desc_quick_settings_edit" msgid="741658939453595297">"የፈጣን ቅንብሮች አርታዒ።"</string>
<string name="accessibility_desc_notification_icon" msgid="7331265967584178674">"የ<xliff:g id="ID_1">%1$s</xliff:g> ማሳወቂያ፦ <xliff:g id="ID_2">%2$s</xliff:g>"</string>
<string name="accessibility_quick_settings_settings" msgid="7098489591715844713">"ቅንብሮችን ክፈት።"</string>
@@ -1067,4 +1068,11 @@
<string name="controls_added_tooltip" msgid="4842812921719153085">"አዲስ መቆጣጠሪያዎችን ለማየት የኃይል አዝራር ይያዙ"</string>
<string name="controls_menu_add" msgid="4447246119229920050">"መቆጣጠሪያዎችን አክል"</string>
<string name="controls_menu_edit" msgid="890623986951347062">"መቆጣጠሪያዎችን ያርትዑ"</string>
+ <string name="media_output_dialog_add_output" msgid="5642703238877329518">"ውጽዓቶችን ያክሉ"</string>
+ <string name="media_output_dialog_group" msgid="5571251347877452212">"ቡድን"</string>
+ <string name="media_output_dialog_single_device" msgid="3102758980643351058">"1 መሣሪያ ተመርጧል"</string>
+ <string name="media_output_dialog_multiple_devices" msgid="1093771040315422350">"<xliff:g id="COUNT">%1$d</xliff:g> መሣሪያዎች ተመርጠዋል"</string>
+ <string name="media_output_dialog_disconnected" msgid="1834473104836986046">"<xliff:g id="DEVICE_NAME">%1$s</xliff:g> (ግንኙነት ተቋርጧል)"</string>
+ <string name="media_output_dialog_connect_failed" msgid="3225190634236259010">"ማገናኘት አልተቻለም። እንደገና ይሞክሩ።"</string>
+ <string name="media_output_dialog_pairing_new" msgid="9099497976087485862">"አዲስ መሣሪያ ያጣምሩ"</string>
</resources>
diff --git a/packages/SystemUI/res/values-ar/strings.xml b/packages/SystemUI/res/values-ar/strings.xml
index 9874d70a87dc..e1d195028dcb 100644
--- a/packages/SystemUI/res/values-ar/strings.xml
+++ b/packages/SystemUI/res/values-ar/strings.xml
@@ -318,7 +318,7 @@
<string name="data_usage_disabled_dialog" msgid="7933201635215099780">"تم الوصول إلى حد البيانات الذي عيَّنته. لم يُعد بإمكانك استخدام بيانات الجوال.\n\nفي حالة الاستئناف، قد يتم تطبيق الرسوم لاستخدام البيانات."</string>
<string name="data_usage_disabled_dialog_enable" msgid="2796648546086408937">"استئناف"</string>
<string name="gps_notification_searching_text" msgid="231304732649348313">"‏جارٍ البحث عن GPS"</string>
- <string name="gps_notification_found_text" msgid="3145873880174658526">"‏تم تعيين الموقع بواسطة GPS"</string>
+ <string name="gps_notification_found_text" msgid="3145873880174658526">"‏تم ضبط الموقع بواسطة GPS"</string>
<string name="accessibility_location_active" msgid="2845747916764660369">"طلبات الموقع نشطة"</string>
<string name="accessibility_sensors_off_active" msgid="2619725434618911551">"أجهزة الاستشعار غير مفعّلة"</string>
<string name="accessibility_clear_all" msgid="970525598287244592">"محو جميع الإشعارات."</string>
@@ -697,7 +697,7 @@
<string name="tuner_full_importance_settings" msgid="1388025816553459059">"عناصر التحكم في إشعارات التشغيل"</string>
<string name="tuner_full_importance_settings_on" msgid="917981436602311547">"تشغيل"</string>
<string name="tuner_full_importance_settings_off" msgid="5580102038749680829">"إيقاف"</string>
- <string name="power_notification_controls_description" msgid="1334963837572708952">"باستخدام عناصر التحكم في إشعار التشغيل، يمكنك تعيين مستوى الأهمية من 0 إلى 5 لإشعارات التطبيق. \n\n"<b>"المستوى 5"</b>" \n- العرض أعلى قائمة الإشعارات \n- يسمح بمقاطعة ملء الشاشة \n- الظهور الخاطف دائمًا \n\n"<b>"المستوى 4"</b>" \n- منع مقاطعة ملء الشاشة \n- الظهور الخاطف دائمًا \n\n"<b>"المستوى 3"</b>" \n- منع مقاطعة ملء الشاشة \n- عدم الظهور الخاطف أبدًا \n\n"<b>"المستوى 2"</b>" \n- منع مقاطعة ملء الشاشة \n- عدم الظهور الخاطف أبدًا \n- عدم إصدار أصوات واهتزاز \n\n"<b>"المستوى 1"</b>" \n- منع مقاطعة ملء الشاشة \n- عدم الظهور الخاطف أبدًا \n- عدم إصدار أصوات أو اهتزاز أبدًا \n- الإخفاء من شاشة القفل وشريط الحالة \n- العرض أسفل قائمة الإشعارات \n\n"<b>"المستوى 0"</b>" \n- حظر جميع الإشعارات من التطبيق"</string>
+ <string name="power_notification_controls_description" msgid="1334963837572708952">"باستخدام عناصر التحكم في إشعار التشغيل، يمكنك ضبط مستوى الأهمية من 0 إلى 5 لإشعارات التطبيق. \n\n"<b>"المستوى 5"</b>" \n- العرض أعلى قائمة الإشعارات \n- يسمح بمقاطعة ملء الشاشة \n- الظهور الخاطف دائمًا \n\n"<b>"المستوى 4"</b>" \n- منع مقاطعة ملء الشاشة \n- الظهور الخاطف دائمًا \n\n"<b>"المستوى 3"</b>" \n- منع مقاطعة ملء الشاشة \n- عدم الظهور الخاطف أبدًا \n\n"<b>"المستوى 2"</b>" \n- منع مقاطعة ملء الشاشة \n- عدم الظهور الخاطف أبدًا \n- عدم إصدار أصوات واهتزاز \n\n"<b>"المستوى 1"</b>" \n- منع مقاطعة ملء الشاشة \n- عدم الظهور الخاطف أبدًا \n- عدم إصدار أصوات أو اهتزاز أبدًا \n- الإخفاء من شاشة القفل وشريط الحالة \n- العرض أسفل قائمة الإشعارات \n\n"<b>"المستوى 0"</b>" \n- حظر جميع الإشعارات من التطبيق"</string>
<string name="notification_header_default_channel" msgid="225454696914642444">"الإشعارات"</string>
<string name="notification_channel_disabled" msgid="928065923928416337">"لن تتلقى هذه الإشعارات بعد الآن."</string>
<string name="notification_channel_minimized" msgid="6892672757877552959">"سيتم تصغير هذه الإشعارات."</string>
@@ -899,12 +899,13 @@
</string-array>
<string name="tuner_low_priority" msgid="8412666814123009820">"إظهار رموز الإشعارات ذات الأولوية المنخفضة"</string>
<string name="other" msgid="429768510980739978">"غير ذلك"</string>
- <string name="accessibility_qs_edit_tile_label" msgid="9079791448815232967">"الموضع <xliff:g id="POSITION">%1$d</xliff:g>، <xliff:g id="TILE_NAME">%2$s</xliff:g>. انقر مرّتين للتعديل."</string>
- <string name="accessibility_qs_edit_add_tile_label" msgid="8292218072049068613">"<xliff:g id="TILE_NAME">%1$s</xliff:g>. انقر مرّتين للإضافة."</string>
- <string name="accessibility_qs_edit_move_tile" msgid="6027997446473163426">"نقل <xliff:g id="TILE_NAME">%1$s</xliff:g>"</string>
- <string name="accessibility_qs_edit_remove_tile" msgid="3406781901949899624">"إزالة <xliff:g id="TILE_NAME">%1$s</xliff:g>"</string>
- <string name="accessibility_qs_edit_tile_add" msgid="6289879620154587233">"إضافة <xliff:g id="TILE_NAME">%1$s</xliff:g> إلى الموضع <xliff:g id="POSITION">%2$d</xliff:g>"</string>
- <string name="accessibility_qs_edit_tile_move" msgid="4841770637244326837">"نقل <xliff:g id="TILE_NAME">%1$s</xliff:g> إلى الموضع <xliff:g id="POSITION">%2$d</xliff:g>"</string>
+ <string name="accessibility_qs_edit_remove_tile_action" msgid="775511891457193480">"إزالة البطاقة"</string>
+ <string name="accessibility_qs_edit_tile_add_action" msgid="5051211910345301833">"إضافة بطاقة إلى النهاية"</string>
+ <string name="accessibility_qs_edit_tile_start_move" msgid="2009373939914517817">"نقل البطاقة"</string>
+ <string name="accessibility_qs_edit_tile_start_add" msgid="7560798153975555772">"إضافة بطاقة"</string>
+ <string name="accessibility_qs_edit_tile_move_to_position" msgid="5198161544045930556">"الانتقال إلى <xliff:g id="POSITION">%1$d</xliff:g>"</string>
+ <string name="accessibility_qs_edit_tile_add_to_position" msgid="9029163095148274690">"الإضافة إلى الموضع <xliff:g id="POSITION">%1$d</xliff:g>"</string>
+ <string name="accessibility_qs_edit_position" msgid="4509277359815711830">"الموضع: <xliff:g id="POSITION">%1$d</xliff:g>"</string>
<string name="accessibility_desc_quick_settings_edit" msgid="741658939453595297">"برنامج تعديل الإعدادات السريعة."</string>
<string name="accessibility_desc_notification_icon" msgid="7331265967584178674">"إشعار <xliff:g id="ID_1">%1$s</xliff:g>: <xliff:g id="ID_2">%2$s</xliff:g>"</string>
<string name="accessibility_quick_settings_settings" msgid="7098489591715844713">"فتح الإعدادات."</string>
@@ -1091,4 +1092,11 @@
<string name="controls_added_tooltip" msgid="4842812921719153085">"اضغط مع الاستمرار على زر التشغيل لعرض عناصر التحكّم الجديدة."</string>
<string name="controls_menu_add" msgid="4447246119229920050">"إضافة عناصر تحكّم"</string>
<string name="controls_menu_edit" msgid="890623986951347062">"تعديل عناصر التحكّم"</string>
+ <string name="media_output_dialog_add_output" msgid="5642703238877329518">"إضافة مخرجات"</string>
+ <string name="media_output_dialog_group" msgid="5571251347877452212">"مجموعة"</string>
+ <string name="media_output_dialog_single_device" msgid="3102758980643351058">"تم اختيار جهاز واحد."</string>
+ <string name="media_output_dialog_multiple_devices" msgid="1093771040315422350">"تم اختيار <xliff:g id="COUNT">%1$d</xliff:g> جهاز."</string>
+ <string name="media_output_dialog_disconnected" msgid="1834473104836986046">"<xliff:g id="DEVICE_NAME">%1$s</xliff:g> (غير متّصل)"</string>
+ <string name="media_output_dialog_connect_failed" msgid="3225190634236259010">"تعذّر الاتصال. يُرجى إعادة المحاولة."</string>
+ <string name="media_output_dialog_pairing_new" msgid="9099497976087485862">"إقران جهاز جديد"</string>
</resources>
diff --git a/packages/SystemUI/res/values-as/strings.xml b/packages/SystemUI/res/values-as/strings.xml
index 55799a86089a..61ba6dc1265c 100644
--- a/packages/SystemUI/res/values-as/strings.xml
+++ b/packages/SystemUI/res/values-as/strings.xml
@@ -879,12 +879,13 @@
</string-array>
<string name="tuner_low_priority" msgid="8412666814123009820">"কম গুৰুত্বপূৰ্ণ জাননীৰ আইকনসমূহ দেখুৱাওক"</string>
<string name="other" msgid="429768510980739978">"অন্যান্য"</string>
- <string name="accessibility_qs_edit_tile_label" msgid="9079791448815232967">"অৱস্থান <xliff:g id="POSITION">%1$d</xliff:g>, <xliff:g id="TILE_NAME">%2$s</xliff:g>। সম্পাদনা কৰিবৰ বাবে দুবাৰ টিপক।"</string>
- <string name="accessibility_qs_edit_add_tile_label" msgid="8292218072049068613">"<xliff:g id="TILE_NAME">%1$s</xliff:g>। যোগ কৰিবলৈ দুবাৰ টিপক।"</string>
- <string name="accessibility_qs_edit_move_tile" msgid="6027997446473163426">"<xliff:g id="TILE_NAME">%1$s</xliff:g> স্থানান্তৰ কৰক"</string>
- <string name="accessibility_qs_edit_remove_tile" msgid="3406781901949899624">"<xliff:g id="TILE_NAME">%1$s</xliff:g>ক আঁতৰাওক"</string>
- <string name="accessibility_qs_edit_tile_add" msgid="6289879620154587233">"এই স্থান <xliff:g id="POSITION">%2$d</xliff:g>ত <xliff:g id="TILE_NAME">%1$s</xliff:g> যোগ কৰক"</string>
- <string name="accessibility_qs_edit_tile_move" msgid="4841770637244326837">"<xliff:g id="TILE_NAME">%1$s</xliff:g>ক এই স্থান <xliff:g id="POSITION">%2$d</xliff:g>লৈ যাওক"</string>
+ <string name="accessibility_qs_edit_remove_tile_action" msgid="775511891457193480">"টাইল আঁতৰাবলৈ"</string>
+ <string name="accessibility_qs_edit_tile_add_action" msgid="5051211910345301833">"টাইল শেষত যোগ দিবলৈ"</string>
+ <string name="accessibility_qs_edit_tile_start_move" msgid="2009373939914517817">"টাইল স্থানান্তৰ কৰক"</string>
+ <string name="accessibility_qs_edit_tile_start_add" msgid="7560798153975555772">"টাইল যোগ দিয়ক"</string>
+ <string name="accessibility_qs_edit_tile_move_to_position" msgid="5198161544045930556">"<xliff:g id="POSITION">%1$d</xliff:g> নম্বৰলৈ স্থানান্তৰ কৰক"</string>
+ <string name="accessibility_qs_edit_tile_add_to_position" msgid="9029163095148274690">"<xliff:g id="POSITION">%1$d</xliff:g> নম্বৰ স্থানত যোগ দিয়ক"</string>
+ <string name="accessibility_qs_edit_position" msgid="4509277359815711830">"<xliff:g id="POSITION">%1$d</xliff:g> নম্বৰ স্থান"</string>
<string name="accessibility_desc_quick_settings_edit" msgid="741658939453595297">"ক্ষিপ্ৰ ছেটিংসমূহৰ সম্পাদক।"</string>
<string name="accessibility_desc_notification_icon" msgid="7331265967584178674">"<xliff:g id="ID_1">%1$s</xliff:g> জাননী: <xliff:g id="ID_2">%2$s</xliff:g>"</string>
<string name="accessibility_quick_settings_settings" msgid="7098489591715844713">"ছেটিংসমূহ খোলক।"</string>
@@ -1067,4 +1068,11 @@
<string name="controls_added_tooltip" msgid="4842812921719153085">"নতুন নিয়ন্ত্ৰণসমূহ চাবলৈ পাৱাৰৰ বুটামটো ধৰি ৰাখক"</string>
<string name="controls_menu_add" msgid="4447246119229920050">"নিয়ন্ত্ৰণসমূহ যোগ দিয়ক"</string>
<string name="controls_menu_edit" msgid="890623986951347062">"নিয়ন্ত্ৰণসমূহ সম্পাদনা কৰক"</string>
+ <string name="media_output_dialog_add_output" msgid="5642703238877329518">"আউটপুটসমূহ যোগ দিয়ক"</string>
+ <string name="media_output_dialog_group" msgid="5571251347877452212">"গোট"</string>
+ <string name="media_output_dialog_single_device" msgid="3102758980643351058">"১ টা ডিভাইচ বাছনি কৰা হৈছে"</string>
+ <string name="media_output_dialog_multiple_devices" msgid="1093771040315422350">"<xliff:g id="COUNT">%1$d</xliff:g> টা ডিভাইচ বাছনি কৰা হৈছে"</string>
+ <string name="media_output_dialog_disconnected" msgid="1834473104836986046">"<xliff:g id="DEVICE_NAME">%1$s</xliff:g> (সংযোগ বিচ্ছিন্ন হৈছে)"</string>
+ <string name="media_output_dialog_connect_failed" msgid="3225190634236259010">"সংযোগ কৰিব পৰা নগ’ল। পুনৰ চেষ্টা কৰক।"</string>
+ <string name="media_output_dialog_pairing_new" msgid="9099497976087485862">"নতুন ডিভাইচ পেয়াৰ কৰক"</string>
</resources>
diff --git a/packages/SystemUI/res/values-az/strings.xml b/packages/SystemUI/res/values-az/strings.xml
index 83af4623b54c..bee0e5fdca54 100644
--- a/packages/SystemUI/res/values-az/strings.xml
+++ b/packages/SystemUI/res/values-az/strings.xml
@@ -879,12 +879,13 @@
</string-array>
<string name="tuner_low_priority" msgid="8412666814123009820">"Aşağı prioritet bildiriş işarələrini göstərin"</string>
<string name="other" msgid="429768510980739978">"Digər"</string>
- <string name="accessibility_qs_edit_tile_label" msgid="9079791448815232967">"<xliff:g id="POSITION">%1$d</xliff:g> mövqeyi, <xliff:g id="TILE_NAME">%2$s</xliff:g>. Redaktə etmək üçün iki dəfə tıklayın."</string>
- <string name="accessibility_qs_edit_add_tile_label" msgid="8292218072049068613">"<xliff:g id="TILE_NAME">%1$s</xliff:g>. Əlavə etmək üçün iki dəfə tıklayın."</string>
- <string name="accessibility_qs_edit_move_tile" msgid="6027997446473163426">"<xliff:g id="TILE_NAME">%1$s</xliff:g> köçürün"</string>
- <string name="accessibility_qs_edit_remove_tile" msgid="3406781901949899624">"<xliff:g id="TILE_NAME">%1$s</xliff:g> silin"</string>
- <string name="accessibility_qs_edit_tile_add" msgid="6289879620154587233">"<xliff:g id="POSITION">%2$d</xliff:g> pozisiyasına <xliff:g id="TILE_NAME">%1$s</xliff:g> əlavə edin"</string>
- <string name="accessibility_qs_edit_tile_move" msgid="4841770637244326837">"<xliff:g id="POSITION">%2$d</xliff:g> pozisiyasına <xliff:g id="TILE_NAME">%1$s</xliff:g> köçürün"</string>
+ <string name="accessibility_qs_edit_remove_tile_action" msgid="775511891457193480">"lövhəni silin"</string>
+ <string name="accessibility_qs_edit_tile_add_action" msgid="5051211910345301833">"sona lövhə əlavə edin"</string>
+ <string name="accessibility_qs_edit_tile_start_move" msgid="2009373939914517817">"Lövhəni köçürün"</string>
+ <string name="accessibility_qs_edit_tile_start_add" msgid="7560798153975555772">"Lövhə əlavə edin"</string>
+ <string name="accessibility_qs_edit_tile_move_to_position" msgid="5198161544045930556">"<xliff:g id="POSITION">%1$d</xliff:g> mövqeyinə köçürün"</string>
+ <string name="accessibility_qs_edit_tile_add_to_position" msgid="9029163095148274690">"<xliff:g id="POSITION">%1$d</xliff:g> mövqeyinə əlavə edin"</string>
+ <string name="accessibility_qs_edit_position" msgid="4509277359815711830">"<xliff:g id="POSITION">%1$d</xliff:g> mövqeyi"</string>
<string name="accessibility_desc_quick_settings_edit" msgid="741658939453595297">"Sürətli ayarlar redaktoru."</string>
<string name="accessibility_desc_notification_icon" msgid="7331265967584178674">"<xliff:g id="ID_1">%1$s</xliff:g> bildiriş: <xliff:g id="ID_2">%2$s</xliff:g>"</string>
<string name="accessibility_quick_settings_settings" msgid="7098489591715844713">"Ayarları açın."</string>
@@ -1067,4 +1068,11 @@
<string name="controls_added_tooltip" msgid="4842812921719153085">"Yeni nizamlayıcıları görmək üçün yandırıb-söndürmə düyməsinə basıb saxlayın"</string>
<string name="controls_menu_add" msgid="4447246119229920050">"Nizamlayıcılar əlavə edin"</string>
<string name="controls_menu_edit" msgid="890623986951347062">"Nizamlayıcıları redaktə edin"</string>
+ <string name="media_output_dialog_add_output" msgid="5642703238877329518">"Nəticələri əlavə edin"</string>
+ <string name="media_output_dialog_group" msgid="5571251347877452212">"Qrup"</string>
+ <string name="media_output_dialog_single_device" msgid="3102758980643351058">"1 cihaz seçilib"</string>
+ <string name="media_output_dialog_multiple_devices" msgid="1093771040315422350">"<xliff:g id="COUNT">%1$d</xliff:g> cihaz seçilib"</string>
+ <string name="media_output_dialog_disconnected" msgid="1834473104836986046">"<xliff:g id="DEVICE_NAME">%1$s</xliff:g> (bağlantı kəsilib)"</string>
+ <string name="media_output_dialog_connect_failed" msgid="3225190634236259010">"Qoşulmaq alınmadı. Yenə cəhd edin."</string>
+ <string name="media_output_dialog_pairing_new" msgid="9099497976087485862">"Yeni cihazı qoşalaşdırın"</string>
</resources>
diff --git a/packages/SystemUI/res/values-b+sr+Latn/strings.xml b/packages/SystemUI/res/values-b+sr+Latn/strings.xml
index ce7b6077129f..5966356d1e66 100644
--- a/packages/SystemUI/res/values-b+sr+Latn/strings.xml
+++ b/packages/SystemUI/res/values-b+sr+Latn/strings.xml
@@ -884,12 +884,13 @@
</string-array>
<string name="tuner_low_priority" msgid="8412666814123009820">"Prikaži ikone obaveštenja niskog prioriteta"</string>
<string name="other" msgid="429768510980739978">"Drugo"</string>
- <string name="accessibility_qs_edit_tile_label" msgid="9079791448815232967">"<xliff:g id="POSITION">%1$d</xliff:g>. pozicija, <xliff:g id="TILE_NAME">%2$s</xliff:g>. Dvaput dodirnite da biste izmenili."</string>
- <string name="accessibility_qs_edit_add_tile_label" msgid="8292218072049068613">"<xliff:g id="TILE_NAME">%1$s</xliff:g>. Dvaput dodirnite da biste dodali."</string>
- <string name="accessibility_qs_edit_move_tile" msgid="6027997446473163426">"Premesti pločicu <xliff:g id="TILE_NAME">%1$s</xliff:g>"</string>
- <string name="accessibility_qs_edit_remove_tile" msgid="3406781901949899624">"Ukloni pločicu <xliff:g id="TILE_NAME">%1$s</xliff:g>"</string>
- <string name="accessibility_qs_edit_tile_add" msgid="6289879620154587233">"Dodajte „<xliff:g id="TILE_NAME">%1$s</xliff:g>“ na poziciju <xliff:g id="POSITION">%2$d</xliff:g>"</string>
- <string name="accessibility_qs_edit_tile_move" msgid="4841770637244326837">"Premestite „<xliff:g id="TILE_NAME">%1$s</xliff:g>“ na poziciju <xliff:g id="POSITION">%2$d</xliff:g>"</string>
+ <string name="accessibility_qs_edit_remove_tile_action" msgid="775511891457193480">"uklonili pločicu"</string>
+ <string name="accessibility_qs_edit_tile_add_action" msgid="5051211910345301833">"dodali pločicu na kraj"</string>
+ <string name="accessibility_qs_edit_tile_start_move" msgid="2009373939914517817">"Premestite pločicu"</string>
+ <string name="accessibility_qs_edit_tile_start_add" msgid="7560798153975555772">"Dodajte pločicu"</string>
+ <string name="accessibility_qs_edit_tile_move_to_position" msgid="5198161544045930556">"Premestite na <xliff:g id="POSITION">%1$d</xliff:g>. poziciju"</string>
+ <string name="accessibility_qs_edit_tile_add_to_position" msgid="9029163095148274690">"Dodajte na <xliff:g id="POSITION">%1$d</xliff:g>. poziciju"</string>
+ <string name="accessibility_qs_edit_position" msgid="4509277359815711830">"<xliff:g id="POSITION">%1$d</xliff:g>. pozicija"</string>
<string name="accessibility_desc_quick_settings_edit" msgid="741658939453595297">"Uređivač za Brza podešavanja."</string>
<string name="accessibility_desc_notification_icon" msgid="7331265967584178674">"Obaveštenja za <xliff:g id="ID_1">%1$s</xliff:g>: <xliff:g id="ID_2">%2$s</xliff:g>"</string>
<string name="accessibility_quick_settings_settings" msgid="7098489591715844713">"Otvori Podešavanja."</string>
@@ -1073,4 +1074,11 @@
<string name="controls_added_tooltip" msgid="4842812921719153085">"Zadržite dugme za uključivanje da biste videli nove kontrole"</string>
<string name="controls_menu_add" msgid="4447246119229920050">"Dodaj kontrole"</string>
<string name="controls_menu_edit" msgid="890623986951347062">"Izmeni kontrole"</string>
+ <string name="media_output_dialog_add_output" msgid="5642703238877329518">"Dodajte izlaze"</string>
+ <string name="media_output_dialog_group" msgid="5571251347877452212">"Grupa"</string>
+ <string name="media_output_dialog_single_device" msgid="3102758980643351058">"Izabran je 1 uređaj"</string>
+ <string name="media_output_dialog_multiple_devices" msgid="1093771040315422350">"Izabranih uređaja: <xliff:g id="COUNT">%1$d</xliff:g>"</string>
+ <string name="media_output_dialog_disconnected" msgid="1834473104836986046">"<xliff:g id="DEVICE_NAME">%1$s</xliff:g> (veza je prekinuta)"</string>
+ <string name="media_output_dialog_connect_failed" msgid="3225190634236259010">"Povezivanje nije uspelo. Probajte ponovo."</string>
+ <string name="media_output_dialog_pairing_new" msgid="9099497976087485862">"Upari novi uređaj"</string>
</resources>
diff --git a/packages/SystemUI/res/values-be/strings.xml b/packages/SystemUI/res/values-be/strings.xml
index f1fc8f565a3b..cedc3753da42 100644
--- a/packages/SystemUI/res/values-be/strings.xml
+++ b/packages/SystemUI/res/values-be/strings.xml
@@ -722,7 +722,7 @@
<string name="notification_channel_summary_default_with_bubbles" msgid="1782419896613644568">"У залежнасці ад налад тэлефона магчымы званок або вібрацыя. Размовы ў праграме \"<xliff:g id="APP_NAME">%1$s</xliff:g>\" стандартна паяўляюцца ў выглядзе ўсплывальных апавяшчэнняў."</string>
<string name="notification_channel_summary_bubble" msgid="7235935211580860537">"Прыцягвае ўвагу да гэтага змесціва ўсплывальнай кнопкай."</string>
<string name="notification_channel_summary_automatic" msgid="5813109268050235275">"Сістэма сама будзе вызначаць, ці трэба для гэтага апавяшчэння ўключаць гук або вібрацыю"</string>
- <string name="notification_channel_summary_priority" msgid="7952654515769021553">"Паказваецца ўверсе раздзела размоў у выглядзе ўсплывальнага апавяшчэння, а на экране блакіроўкі – у выглядзе відарыса профілю"</string>
+ <string name="notification_channel_summary_priority" msgid="7952654515769021553">"Паказваецца ўверсе раздзела размоў, як усплывальнае апавяшчэнне, паказвае фота профілю на экране блакіроўкі"</string>
<string name="notification_conversation_channel_settings" msgid="2409977688430606835">"Налады"</string>
<string name="notification_priority_title" msgid="2079708866333537093">"Прыярытэт"</string>
<string name="no_shortcut" msgid="8257177117568230126">"<xliff:g id="APP_NAME">%1$s</xliff:g> не падтрымлівае функцыі размовы"</string>
@@ -889,12 +889,13 @@
</string-array>
<string name="tuner_low_priority" msgid="8412666814123009820">"Паказваць значкі апавяшчэнняў з нізкім прыярытэтам"</string>
<string name="other" msgid="429768510980739978">"Іншае"</string>
- <string name="accessibility_qs_edit_tile_label" msgid="9079791448815232967">"Месца: <xliff:g id="POSITION">%1$d</xliff:g>, <xliff:g id="TILE_NAME">%2$s</xliff:g>. Краніце двойчы, каб рэдагаваць."</string>
- <string name="accessibility_qs_edit_add_tile_label" msgid="8292218072049068613">"<xliff:g id="TILE_NAME">%1$s</xliff:g>. Краніце двойчы, каб дадаць."</string>
- <string name="accessibility_qs_edit_move_tile" msgid="6027997446473163426">"Перамясціць <xliff:g id="TILE_NAME">%1$s</xliff:g>"</string>
- <string name="accessibility_qs_edit_remove_tile" msgid="3406781901949899624">"Выдаліць <xliff:g id="TILE_NAME">%1$s</xliff:g>"</string>
- <string name="accessibility_qs_edit_tile_add" msgid="6289879620154587233">"Дадаць \"<xliff:g id="TILE_NAME">%1$s</xliff:g>\" у наступнае месца: <xliff:g id="POSITION">%2$d</xliff:g>"</string>
- <string name="accessibility_qs_edit_tile_move" msgid="4841770637244326837">"Перамясціць \"<xliff:g id="TILE_NAME">%1$s</xliff:g>\" у наступнае месца: <xliff:g id="POSITION">%2$d</xliff:g>"</string>
+ <string name="accessibility_qs_edit_remove_tile_action" msgid="775511891457193480">"выдаліць плітку"</string>
+ <string name="accessibility_qs_edit_tile_add_action" msgid="5051211910345301833">"дадаць плітку ў канец"</string>
+ <string name="accessibility_qs_edit_tile_start_move" msgid="2009373939914517817">"Перамясціць плітку"</string>
+ <string name="accessibility_qs_edit_tile_start_add" msgid="7560798153975555772">"Дадаць плітку"</string>
+ <string name="accessibility_qs_edit_tile_move_to_position" msgid="5198161544045930556">"Перамясціць на пазіцыю <xliff:g id="POSITION">%1$d</xliff:g>"</string>
+ <string name="accessibility_qs_edit_tile_add_to_position" msgid="9029163095148274690">"Дадаць на пазіцыю <xliff:g id="POSITION">%1$d</xliff:g>"</string>
+ <string name="accessibility_qs_edit_position" msgid="4509277359815711830">"Пазіцыя <xliff:g id="POSITION">%1$d</xliff:g>"</string>
<string name="accessibility_desc_quick_settings_edit" msgid="741658939453595297">"Рэдактар хуткіх налад."</string>
<string name="accessibility_desc_notification_icon" msgid="7331265967584178674">"Апавяшчэнне <xliff:g id="ID_1">%1$s</xliff:g>: <xliff:g id="ID_2">%2$s</xliff:g>"</string>
<string name="accessibility_quick_settings_settings" msgid="7098489591715844713">"Адкрыць налады."</string>
@@ -1079,4 +1080,11 @@
<string name="controls_added_tooltip" msgid="4842812921719153085">"Каб убачыць новыя элементы кіравання, утрымлівайце кнопку сілкавання націснутай"</string>
<string name="controls_menu_add" msgid="4447246119229920050">"Дадаць элементы кіравання"</string>
<string name="controls_menu_edit" msgid="890623986951347062">"Змяніць элементы кіравання"</string>
+ <string name="media_output_dialog_add_output" msgid="5642703238877329518">"Дадайце прылады вываду"</string>
+ <string name="media_output_dialog_group" msgid="5571251347877452212">"Група"</string>
+ <string name="media_output_dialog_single_device" msgid="3102758980643351058">"Выбрана 1 прылада"</string>
+ <string name="media_output_dialog_multiple_devices" msgid="1093771040315422350">"Выбрана прылад: <xliff:g id="COUNT">%1$d</xliff:g>"</string>
+ <string name="media_output_dialog_disconnected" msgid="1834473104836986046">"<xliff:g id="DEVICE_NAME">%1$s</xliff:g> (адключана)"</string>
+ <string name="media_output_dialog_connect_failed" msgid="3225190634236259010">"Не ўдалося падключыцца. Паўтарыце спробу."</string>
+ <string name="media_output_dialog_pairing_new" msgid="9099497976087485862">"Спалучыць з новай прыладай"</string>
</resources>
diff --git a/packages/SystemUI/res/values-bg/strings.xml b/packages/SystemUI/res/values-bg/strings.xml
index 1678abadfeff..14088404e81b 100644
--- a/packages/SystemUI/res/values-bg/strings.xml
+++ b/packages/SystemUI/res/values-bg/strings.xml
@@ -716,7 +716,7 @@
<string name="notification_channel_summary_default_with_bubbles" msgid="1782419896613644568">"Може да звъни или да вибрира въз основа на настройките за телефона. Разговорите от <xliff:g id="APP_NAME">%1$s</xliff:g> се показват като балончета по подразбиране."</string>
<string name="notification_channel_summary_bubble" msgid="7235935211580860537">"Задържа вниманието ви посредством плаващ пряк път към това съдържание."</string>
<string name="notification_channel_summary_automatic" msgid="5813109268050235275">"Нека системата да определя дали дадено известие да се придружава от звук, или вибриране"</string>
- <string name="notification_channel_summary_priority" msgid="7952654515769021553">"Показва се като плаващо балонче в горната част на секцията с разговори и показва снимката на потребителския профил на заключения екран"</string>
+ <string name="notification_channel_summary_priority" msgid="7952654515769021553">"Показва се като плаващо балонче в горната част на секцията с разговори, показва снимката на потр. профил на заключения екран"</string>
<string name="notification_conversation_channel_settings" msgid="2409977688430606835">"Настройки"</string>
<string name="notification_priority_title" msgid="2079708866333537093">"Приоритет"</string>
<string name="no_shortcut" msgid="8257177117568230126">"<xliff:g id="APP_NAME">%1$s</xliff:g> не поддържа функциите за разговор"</string>
@@ -879,12 +879,13 @@
</string-array>
<string name="tuner_low_priority" msgid="8412666814123009820">"Показване на иконите за известията с нисък приоритет"</string>
<string name="other" msgid="429768510980739978">"Друго"</string>
- <string name="accessibility_qs_edit_tile_label" msgid="9079791448815232967">"Позиция <xliff:g id="POSITION">%1$d</xliff:g>, „<xliff:g id="TILE_NAME">%2$s</xliff:g>“. Докоснете двукратно, за да редактирате."</string>
- <string name="accessibility_qs_edit_add_tile_label" msgid="8292218072049068613">"„<xliff:g id="TILE_NAME">%1$s</xliff:g>“. Докоснете двукратно, за да добавите."</string>
- <string name="accessibility_qs_edit_move_tile" msgid="6027997446473163426">"Преместване на „<xliff:g id="TILE_NAME">%1$s</xliff:g>“"</string>
- <string name="accessibility_qs_edit_remove_tile" msgid="3406781901949899624">"Премахване на „<xliff:g id="TILE_NAME">%1$s</xliff:g>“"</string>
- <string name="accessibility_qs_edit_tile_add" msgid="6289879620154587233">"Добавете „<xliff:g id="TILE_NAME">%1$s</xliff:g>“ на позиция <xliff:g id="POSITION">%2$d</xliff:g>"</string>
- <string name="accessibility_qs_edit_tile_move" msgid="4841770637244326837">"Преместете „<xliff:g id="TILE_NAME">%1$s</xliff:g>“ на позиция <xliff:g id="POSITION">%2$d</xliff:g>"</string>
+ <string name="accessibility_qs_edit_remove_tile_action" msgid="775511891457193480">"премахване на панел"</string>
+ <string name="accessibility_qs_edit_tile_add_action" msgid="5051211910345301833">"добавяне на панел в края"</string>
+ <string name="accessibility_qs_edit_tile_start_move" msgid="2009373939914517817">"Преместване на панел"</string>
+ <string name="accessibility_qs_edit_tile_start_add" msgid="7560798153975555772">"Добавяне на панел"</string>
+ <string name="accessibility_qs_edit_tile_move_to_position" msgid="5198161544045930556">"Преместване към позиция <xliff:g id="POSITION">%1$d</xliff:g>"</string>
+ <string name="accessibility_qs_edit_tile_add_to_position" msgid="9029163095148274690">"Добавяне към позиция <xliff:g id="POSITION">%1$d</xliff:g>"</string>
+ <string name="accessibility_qs_edit_position" msgid="4509277359815711830">"Позиция <xliff:g id="POSITION">%1$d</xliff:g>"</string>
<string name="accessibility_desc_quick_settings_edit" msgid="741658939453595297">"Редактор за бързи настройки."</string>
<string name="accessibility_desc_notification_icon" msgid="7331265967584178674">"Известие от <xliff:g id="ID_1">%1$s</xliff:g>: <xliff:g id="ID_2">%2$s</xliff:g>"</string>
<string name="accessibility_quick_settings_settings" msgid="7098489591715844713">"Отваряне на настройките."</string>
@@ -1067,4 +1068,11 @@
<string name="controls_added_tooltip" msgid="4842812921719153085">"Задръжте бутона за захранване, за да видите новите контроли"</string>
<string name="controls_menu_add" msgid="4447246119229920050">"Добавяне на контроли"</string>
<string name="controls_menu_edit" msgid="890623986951347062">"Редактиране на контролите"</string>
+ <string name="media_output_dialog_add_output" msgid="5642703238877329518">"Добавяне на изходящи устройства"</string>
+ <string name="media_output_dialog_group" msgid="5571251347877452212">"Група"</string>
+ <string name="media_output_dialog_single_device" msgid="3102758980643351058">"1 избрано устройство"</string>
+ <string name="media_output_dialog_multiple_devices" msgid="1093771040315422350">"<xliff:g id="COUNT">%1$d</xliff:g> избрани устройства"</string>
+ <string name="media_output_dialog_disconnected" msgid="1834473104836986046">"<xliff:g id="DEVICE_NAME">%1$s</xliff:g> (връзката е прекратена)"</string>
+ <string name="media_output_dialog_connect_failed" msgid="3225190634236259010">"Неуспешно свързване. Опитайте отново."</string>
+ <string name="media_output_dialog_pairing_new" msgid="9099497976087485862">"Сдвояване на ново устройство"</string>
</resources>
diff --git a/packages/SystemUI/res/values-bn/strings.xml b/packages/SystemUI/res/values-bn/strings.xml
index 9d5415fe5af1..fcdd286b6f55 100644
--- a/packages/SystemUI/res/values-bn/strings.xml
+++ b/packages/SystemUI/res/values-bn/strings.xml
@@ -879,12 +879,20 @@
</string-array>
<string name="tuner_low_priority" msgid="8412666814123009820">"কম-গুরুত্বপূর্ণ বিজ্ঞপ্তির আইকন দেখুন"</string>
<string name="other" msgid="429768510980739978">"অন্যান্য"</string>
- <string name="accessibility_qs_edit_tile_label" msgid="9079791448815232967">"<xliff:g id="POSITION">%1$d</xliff:g> লোকেশন, <xliff:g id="TILE_NAME">%2$s</xliff:g>৷ সম্পাদনা করতে দুবার আলতো চাপুন৷"</string>
- <string name="accessibility_qs_edit_add_tile_label" msgid="8292218072049068613">"<xliff:g id="TILE_NAME">%1$s</xliff:g>৷ যোগ করতে দুবার আলতো চাপুন৷"</string>
- <string name="accessibility_qs_edit_move_tile" msgid="6027997446473163426">"<xliff:g id="TILE_NAME">%1$s</xliff:g> সরান"</string>
- <string name="accessibility_qs_edit_remove_tile" msgid="3406781901949899624">"<xliff:g id="TILE_NAME">%1$s</xliff:g> সরান"</string>
- <string name="accessibility_qs_edit_tile_add" msgid="6289879620154587233">"<xliff:g id="TILE_NAME">%1$s</xliff:g> <xliff:g id="POSITION">%2$d</xliff:g>-এ যোগ করুন"</string>
- <string name="accessibility_qs_edit_tile_move" msgid="4841770637244326837">"<xliff:g id="TILE_NAME">%1$s</xliff:g> <xliff:g id="POSITION">%2$d</xliff:g>-এ সরান"</string>
+ <!-- no translation found for accessibility_qs_edit_remove_tile_action (775511891457193480) -->
+ <skip />
+ <!-- no translation found for accessibility_qs_edit_tile_add_action (5051211910345301833) -->
+ <skip />
+ <!-- no translation found for accessibility_qs_edit_tile_start_move (2009373939914517817) -->
+ <skip />
+ <!-- no translation found for accessibility_qs_edit_tile_start_add (7560798153975555772) -->
+ <skip />
+ <!-- no translation found for accessibility_qs_edit_tile_move_to_position (5198161544045930556) -->
+ <skip />
+ <!-- no translation found for accessibility_qs_edit_tile_add_to_position (9029163095148274690) -->
+ <skip />
+ <!-- no translation found for accessibility_qs_edit_position (4509277359815711830) -->
+ <skip />
<string name="accessibility_desc_quick_settings_edit" msgid="741658939453595297">"দ্রুত সেটিংস সম্পাদক৷"</string>
<string name="accessibility_desc_notification_icon" msgid="7331265967584178674">"<xliff:g id="ID_1">%1$s</xliff:g> বিজ্ঞপ্তি: <xliff:g id="ID_2">%2$s</xliff:g>"</string>
<string name="accessibility_quick_settings_settings" msgid="7098489591715844713">"সেটিংস খুলুন।"</string>
@@ -1067,4 +1075,11 @@
<string name="controls_added_tooltip" msgid="4842812921719153085">"নতুন কন্ট্রোল দেখতে পাওয়ার বোতাম টিপে ধরে থাকুন"</string>
<string name="controls_menu_add" msgid="4447246119229920050">"কন্ট্রোল যোগ করুন"</string>
<string name="controls_menu_edit" msgid="890623986951347062">"কন্ট্রোল এডিট করুন"</string>
+ <string name="media_output_dialog_add_output" msgid="5642703238877329518">"আউটপুট যোগ করুন"</string>
+ <string name="media_output_dialog_group" msgid="5571251347877452212">"গ্রুপ"</string>
+ <string name="media_output_dialog_single_device" msgid="3102758980643351058">"১টি ডিভাইস বেছে নেওয়া হয়েছে"</string>
+ <string name="media_output_dialog_multiple_devices" msgid="1093771040315422350">"<xliff:g id="COUNT">%1$d</xliff:g>টি ডিভাইস বেছে নেওয়া হয়েছে"</string>
+ <string name="media_output_dialog_disconnected" msgid="1834473104836986046">"<xliff:g id="DEVICE_NAME">%1$s</xliff:g> (কানেক্ট করা নেই)"</string>
+ <string name="media_output_dialog_connect_failed" msgid="3225190634236259010">"কানেক্ট করা যায়নি। আবার চেষ্টা করুন।"</string>
+ <string name="media_output_dialog_pairing_new" msgid="9099497976087485862">"নতুন ডিভাইস পেয়ার করুন"</string>
</resources>
diff --git a/packages/SystemUI/res/values-bs/strings.xml b/packages/SystemUI/res/values-bs/strings.xml
index 819bfcf58674..41a2671466df 100644
--- a/packages/SystemUI/res/values-bs/strings.xml
+++ b/packages/SystemUI/res/values-bs/strings.xml
@@ -884,12 +884,13 @@
</string-array>
<string name="tuner_low_priority" msgid="8412666814123009820">"Prikaži ikone obavještenja niskog prioriteta"</string>
<string name="other" msgid="429768510980739978">"Ostalo"</string>
- <string name="accessibility_qs_edit_tile_label" msgid="9079791448815232967">"Pozicija <xliff:g id="POSITION">%1$d</xliff:g>, <xliff:g id="TILE_NAME">%2$s</xliff:g>. Dodirnite dvaput za uređivanje."</string>
- <string name="accessibility_qs_edit_add_tile_label" msgid="8292218072049068613">"<xliff:g id="TILE_NAME">%1$s</xliff:g> Dodirnite dvaput za dodavanje."</string>
- <string name="accessibility_qs_edit_move_tile" msgid="6027997446473163426">"Pomjeri <xliff:g id="TILE_NAME">%1$s</xliff:g>"</string>
- <string name="accessibility_qs_edit_remove_tile" msgid="3406781901949899624">"Ukloni <xliff:g id="TILE_NAME">%1$s</xliff:g>"</string>
- <string name="accessibility_qs_edit_tile_add" msgid="6289879620154587233">"Dodaj <xliff:g id="TILE_NAME">%1$s</xliff:g> na poziciju <xliff:g id="POSITION">%2$d</xliff:g>"</string>
- <string name="accessibility_qs_edit_tile_move" msgid="4841770637244326837">"Premjesti <xliff:g id="TILE_NAME">%1$s</xliff:g> na poziciju <xliff:g id="POSITION">%2$d</xliff:g>"</string>
+ <string name="accessibility_qs_edit_remove_tile_action" msgid="775511891457193480">"uklanjanje kartice"</string>
+ <string name="accessibility_qs_edit_tile_add_action" msgid="5051211910345301833">"dodavanje kartice na kraj"</string>
+ <string name="accessibility_qs_edit_tile_start_move" msgid="2009373939914517817">"Pomjeranje kartice"</string>
+ <string name="accessibility_qs_edit_tile_start_add" msgid="7560798153975555772">"Dodavanje kartice"</string>
+ <string name="accessibility_qs_edit_tile_move_to_position" msgid="5198161544045930556">"Pomjeranje u položaj <xliff:g id="POSITION">%1$d</xliff:g>"</string>
+ <string name="accessibility_qs_edit_tile_add_to_position" msgid="9029163095148274690">"Dodavanje u položaj <xliff:g id="POSITION">%1$d</xliff:g>"</string>
+ <string name="accessibility_qs_edit_position" msgid="4509277359815711830">"Položaj <xliff:g id="POSITION">%1$d</xliff:g>"</string>
<string name="accessibility_desc_quick_settings_edit" msgid="741658939453595297">"Uređivanje brzih postavki"</string>
<string name="accessibility_desc_notification_icon" msgid="7331265967584178674">"<xliff:g id="ID_1">%1$s</xliff:g> obavještenje: <xliff:g id="ID_2">%2$s</xliff:g>"</string>
<string name="accessibility_quick_settings_settings" msgid="7098489591715844713">"Otvori postavke."</string>
@@ -1073,4 +1074,11 @@
<string name="controls_added_tooltip" msgid="4842812921719153085">"Zadržite dugme za uključivanje da vidite nove kontrole"</string>
<string name="controls_menu_add" msgid="4447246119229920050">"Dodaj kontrole"</string>
<string name="controls_menu_edit" msgid="890623986951347062">"Uredi kontrole"</string>
+ <string name="media_output_dialog_add_output" msgid="5642703238877329518">"Dodajte izlaze"</string>
+ <string name="media_output_dialog_group" msgid="5571251347877452212">"Grupa"</string>
+ <string name="media_output_dialog_single_device" msgid="3102758980643351058">"Odabran je 1 uređaj"</string>
+ <string name="media_output_dialog_multiple_devices" msgid="1093771040315422350">"Broj odabranih uređaja: <xliff:g id="COUNT">%1$d</xliff:g>"</string>
+ <string name="media_output_dialog_disconnected" msgid="1834473104836986046">"<xliff:g id="DEVICE_NAME">%1$s</xliff:g> (veza je prekinuta)"</string>
+ <string name="media_output_dialog_connect_failed" msgid="3225190634236259010">"Povezivanje nije uspjelo. Pokušajte ponovo."</string>
+ <string name="media_output_dialog_pairing_new" msgid="9099497976087485862">"Uparite novi uređaj"</string>
</resources>
diff --git a/packages/SystemUI/res/values-ca/strings.xml b/packages/SystemUI/res/values-ca/strings.xml
index a7aff9850a6b..783f7877e230 100644
--- a/packages/SystemUI/res/values-ca/strings.xml
+++ b/packages/SystemUI/res/values-ca/strings.xml
@@ -92,10 +92,10 @@
<string name="screenrecord_background_processing_label" msgid="7244617554884238898">"Processant gravació de pantalla"</string>
<string name="screenrecord_channel_description" msgid="4147077128486138351">"Notificació en curs d\'una sessió de gravació de la pantalla"</string>
<string name="screenrecord_start_label" msgid="1750350278888217473">"Vols iniciar la gravació?"</string>
- <string name="screenrecord_description" msgid="1123231719680353736">"Quan graves contingut, el sistema Android pot capturar qualsevol informació sensible que es mostri a la pantalla o que es reprodueixi al dispositiu. Això inclou les contrasenyes, la informació de pagament, les fotos, els missatges i l\'àudio."</string>
+ <string name="screenrecord_description" msgid="1123231719680353736">"Durant la gravació, el sistema Android pot capturar qualsevol informació sensible que es mostri a la pantalla o que es reprodueixi al dispositiu. Això inclou contrasenyes, informació de pagament, fotos, missatges i àudio."</string>
<string name="screenrecord_audio_label" msgid="6183558856175159629">"Grava l\'àudio"</string>
<string name="screenrecord_device_audio_label" msgid="9016927171280567791">"Àudio del dispositiu"</string>
- <string name="screenrecord_device_audio_description" msgid="4922694220572186193">"Sons del dispositiu, com ara la música, les trucades i els sons de trucada"</string>
+ <string name="screenrecord_device_audio_description" msgid="4922694220572186193">"So del dispositiu, com ara música, trucades i sons de trucada"</string>
<string name="screenrecord_mic_label" msgid="2111264835791332350">"Micròfon"</string>
<string name="screenrecord_device_audio_and_mic_label" msgid="1831323771978646841">"Àudio del dispositiu i micròfon"</string>
<string name="screenrecord_start" msgid="330991441575775004">"Inicia"</string>
@@ -387,7 +387,7 @@
<string name="quick_settings_cast_no_wifi" msgid="6980194769795014875">"La Wi‑Fi no està connectada"</string>
<string name="quick_settings_brightness_dialog_title" msgid="4980669966716685588">"Brillantor"</string>
<string name="quick_settings_brightness_dialog_auto_brightness_label" msgid="2325362583903258677">"AUTOMÀTICA"</string>
- <string name="quick_settings_inversion_label" msgid="5078769633069667698">"Inverteix els colors"</string>
+ <string name="quick_settings_inversion_label" msgid="5078769633069667698">"Inverteix colors"</string>
<string name="quick_settings_color_space_label" msgid="537528291083575559">"Mode de correcció de color"</string>
<string name="quick_settings_more_settings" msgid="2878235926753776694">"Més opcions"</string>
<string name="quick_settings_done" msgid="2163641301648855793">"Fet"</string>
@@ -879,12 +879,13 @@
</string-array>
<string name="tuner_low_priority" msgid="8412666814123009820">"Mostra les icones de notificació amb prioritat baixa"</string>
<string name="other" msgid="429768510980739978">"Altres"</string>
- <string name="accessibility_qs_edit_tile_label" msgid="9079791448815232967">"Posició <xliff:g id="POSITION">%1$d</xliff:g>, <xliff:g id="TILE_NAME">%2$s</xliff:g>. Fes doble toc per editar-la."</string>
- <string name="accessibility_qs_edit_add_tile_label" msgid="8292218072049068613">"<xliff:g id="TILE_NAME">%1$s</xliff:g>. Fes doble toc per afegir-ho."</string>
- <string name="accessibility_qs_edit_move_tile" msgid="6027997446473163426">"Mou <xliff:g id="TILE_NAME">%1$s</xliff:g>"</string>
- <string name="accessibility_qs_edit_remove_tile" msgid="3406781901949899624">"Suprimeix <xliff:g id="TILE_NAME">%1$s</xliff:g>"</string>
- <string name="accessibility_qs_edit_tile_add" msgid="6289879620154587233">"Afegeix <xliff:g id="TILE_NAME">%1$s</xliff:g> a la posició <xliff:g id="POSITION">%2$d</xliff:g>"</string>
- <string name="accessibility_qs_edit_tile_move" msgid="4841770637244326837">"Mou <xliff:g id="TILE_NAME">%1$s</xliff:g> a la posició <xliff:g id="POSITION">%2$d</xliff:g>"</string>
+ <string name="accessibility_qs_edit_remove_tile_action" msgid="775511891457193480">"suprimir el mosaic"</string>
+ <string name="accessibility_qs_edit_tile_add_action" msgid="5051211910345301833">"afegir una targeta al final"</string>
+ <string name="accessibility_qs_edit_tile_start_move" msgid="2009373939914517817">"Mou la targeta"</string>
+ <string name="accessibility_qs_edit_tile_start_add" msgid="7560798153975555772">"Afegeix una targeta"</string>
+ <string name="accessibility_qs_edit_tile_move_to_position" msgid="5198161544045930556">"Mou a la posició <xliff:g id="POSITION">%1$d</xliff:g>"</string>
+ <string name="accessibility_qs_edit_tile_add_to_position" msgid="9029163095148274690">"Afegeix a la posició <xliff:g id="POSITION">%1$d</xliff:g>"</string>
+ <string name="accessibility_qs_edit_position" msgid="4509277359815711830">"Posició <xliff:g id="POSITION">%1$d</xliff:g>"</string>
<string name="accessibility_desc_quick_settings_edit" msgid="741658939453595297">"Editor de configuració ràpida."</string>
<string name="accessibility_desc_notification_icon" msgid="7331265967584178674">"Notificació de <xliff:g id="ID_1">%1$s</xliff:g>: <xliff:g id="ID_2">%2$s</xliff:g>"</string>
<string name="accessibility_quick_settings_settings" msgid="7098489591715844713">"Obre la configuració."</string>
@@ -1067,4 +1068,11 @@
<string name="controls_added_tooltip" msgid="4842812921719153085">"Mantén premut el botó d\'engegada per veure controls nous"</string>
<string name="controls_menu_add" msgid="4447246119229920050">"Afegeix controls"</string>
<string name="controls_menu_edit" msgid="890623986951347062">"Edita els controls"</string>
+ <string name="media_output_dialog_add_output" msgid="5642703238877329518">"Afegeix sortides"</string>
+ <string name="media_output_dialog_group" msgid="5571251347877452212">"Grup"</string>
+ <string name="media_output_dialog_single_device" msgid="3102758980643351058">"1 dispositiu seleccionat"</string>
+ <string name="media_output_dialog_multiple_devices" msgid="1093771040315422350">"S\'han seleccionat <xliff:g id="COUNT">%1$d</xliff:g> dispositius"</string>
+ <string name="media_output_dialog_disconnected" msgid="1834473104836986046">"<xliff:g id="DEVICE_NAME">%1$s</xliff:g> (desconnectat)"</string>
+ <string name="media_output_dialog_connect_failed" msgid="3225190634236259010">"No s\'ha pogut connectar. Torna-ho a provar."</string>
+ <string name="media_output_dialog_pairing_new" msgid="9099497976087485862">"Vincula un dispositiu nou"</string>
</resources>
diff --git a/packages/SystemUI/res/values-cs/strings.xml b/packages/SystemUI/res/values-cs/strings.xml
index 44864f0e8141..3f3d6be63f9c 100644
--- a/packages/SystemUI/res/values-cs/strings.xml
+++ b/packages/SystemUI/res/values-cs/strings.xml
@@ -722,7 +722,7 @@
<string name="notification_channel_summary_default_with_bubbles" msgid="1782419896613644568">"Může vyzvánět nebo vibrovat v závislosti na nastavení telefonu. Konverzace z aplikace <xliff:g id="APP_NAME">%1$s</xliff:g> ve výchozím nastavení bublají."</string>
<string name="notification_channel_summary_bubble" msgid="7235935211580860537">"Přitahuje pozornost pomocí plovoucí zkratky k tomuto obsahu."</string>
<string name="notification_channel_summary_automatic" msgid="5813109268050235275">"Nechat systém rozhodnout, zda má toto oznámení vydat zvuk či zavibrovat"</string>
- <string name="notification_channel_summary_priority" msgid="7952654515769021553">"Zobrazuje se v horní části sekce konverzace a má podobu plovoucí bubliny, zobrazuje profilovou fotku na obrazovce uzamčení"</string>
+ <string name="notification_channel_summary_priority" msgid="7952654515769021553">"Zobrazuje se v horní části sekce konverzací a má podobu plovoucí bubliny, zobrazuje profilovou fotku na obrazovce uzamčení"</string>
<string name="notification_conversation_channel_settings" msgid="2409977688430606835">"Nastavení"</string>
<string name="notification_priority_title" msgid="2079708866333537093">"Priorita"</string>
<string name="no_shortcut" msgid="8257177117568230126">"Aplikace <xliff:g id="APP_NAME">%1$s</xliff:g> funkce konverzace nepodporuje"</string>
@@ -889,12 +889,20 @@
</string-array>
<string name="tuner_low_priority" msgid="8412666814123009820">"Zobrazit ikony oznámení s nízkou prioritou"</string>
<string name="other" msgid="429768510980739978">"Jiné"</string>
- <string name="accessibility_qs_edit_tile_label" msgid="9079791448815232967">"Pozice <xliff:g id="POSITION">%1$d</xliff:g>, <xliff:g id="TILE_NAME">%2$s</xliff:g>. Dvojitým klepnutím ji upravíte."</string>
- <string name="accessibility_qs_edit_add_tile_label" msgid="8292218072049068613">"<xliff:g id="TILE_NAME">%1$s</xliff:g>. Dlaždici přidáte dvojitým klepnutím."</string>
- <string name="accessibility_qs_edit_move_tile" msgid="6027997446473163426">"Přesunout dlaždici <xliff:g id="TILE_NAME">%1$s</xliff:g>"</string>
- <string name="accessibility_qs_edit_remove_tile" msgid="3406781901949899624">"Odstranit dlaždici <xliff:g id="TILE_NAME">%1$s</xliff:g>"</string>
- <string name="accessibility_qs_edit_tile_add" msgid="6289879620154587233">"Přidat dlaždici <xliff:g id="TILE_NAME">%1$s</xliff:g> na pozici <xliff:g id="POSITION">%2$d</xliff:g>"</string>
- <string name="accessibility_qs_edit_tile_move" msgid="4841770637244326837">"Přesunout dlaždici <xliff:g id="TILE_NAME">%1$s</xliff:g> na pozici <xliff:g id="POSITION">%2$d</xliff:g>"</string>
+ <!-- no translation found for accessibility_qs_edit_remove_tile_action (775511891457193480) -->
+ <skip />
+ <!-- no translation found for accessibility_qs_edit_tile_add_action (5051211910345301833) -->
+ <skip />
+ <!-- no translation found for accessibility_qs_edit_tile_start_move (2009373939914517817) -->
+ <skip />
+ <!-- no translation found for accessibility_qs_edit_tile_start_add (7560798153975555772) -->
+ <skip />
+ <!-- no translation found for accessibility_qs_edit_tile_move_to_position (5198161544045930556) -->
+ <skip />
+ <!-- no translation found for accessibility_qs_edit_tile_add_to_position (9029163095148274690) -->
+ <skip />
+ <!-- no translation found for accessibility_qs_edit_position (4509277359815711830) -->
+ <skip />
<string name="accessibility_desc_quick_settings_edit" msgid="741658939453595297">"Editor rychlého nastavení"</string>
<string name="accessibility_desc_notification_icon" msgid="7331265967584178674">"Oznámení aplikace <xliff:g id="ID_1">%1$s</xliff:g>: <xliff:g id="ID_2">%2$s</xliff:g>"</string>
<string name="accessibility_quick_settings_settings" msgid="7098489591715844713">"Otevřít nastavení."</string>
@@ -1013,7 +1021,7 @@
<string name="inattentive_sleep_warning_title" msgid="3891371591713990373">"Pohotovostní režim"</string>
<string name="priority_onboarding_title" msgid="2893070698479227616">"Konverzace byla nastavena jako prioritní"</string>
<string name="priority_onboarding_behavior" msgid="5342816047020432929">"Chování prioritních konverzací:"</string>
- <string name="priority_onboarding_show_at_top_text" msgid="1678400241025513541">"Zobrazovat v horní části sekce konverzace"</string>
+ <string name="priority_onboarding_show_at_top_text" msgid="1678400241025513541">"Zobrazovat v horní části sekce konverzací"</string>
<string name="priority_onboarding_show_avatar_text" msgid="5756291381124091508">"Zobrazovat profilovou fotku na zámku obrazovky"</string>
<string name="priority_onboarding_appear_as_bubble_text" msgid="4227039772250263122">"Zobrazuje se jako plovoucí bublina nad aplikacemi"</string>
<string name="priority_onboarding_ignores_dnd_text" msgid="2918952762719600529">"Přerušit režim Nerušit"</string>
@@ -1079,4 +1087,11 @@
<string name="controls_added_tooltip" msgid="4842812921719153085">"Nové ovládací prvky zobrazíte podržením vypínače"</string>
<string name="controls_menu_add" msgid="4447246119229920050">"Přidat ovládací prvky"</string>
<string name="controls_menu_edit" msgid="890623986951347062">"Upravit ovládací prvky"</string>
+ <string name="media_output_dialog_add_output" msgid="5642703238877329518">"Přidání výstupů"</string>
+ <string name="media_output_dialog_group" msgid="5571251347877452212">"Skupina"</string>
+ <string name="media_output_dialog_single_device" msgid="3102758980643351058">"Je vybráno 1 zařízení"</string>
+ <string name="media_output_dialog_multiple_devices" msgid="1093771040315422350">"Vybraná zařízení: <xliff:g id="COUNT">%1$d</xliff:g>"</string>
+ <string name="media_output_dialog_disconnected" msgid="1834473104836986046">"<xliff:g id="DEVICE_NAME">%1$s</xliff:g> (odpojeno)"</string>
+ <string name="media_output_dialog_connect_failed" msgid="3225190634236259010">"Spojení se nezdařilo. Zkuste to znovu."</string>
+ <string name="media_output_dialog_pairing_new" msgid="9099497976087485862">"Spárovat nové zařízení"</string>
</resources>
diff --git a/packages/SystemUI/res/values-da/strings.xml b/packages/SystemUI/res/values-da/strings.xml
index b6702126323d..f2e8a3f908f3 100644
--- a/packages/SystemUI/res/values-da/strings.xml
+++ b/packages/SystemUI/res/values-da/strings.xml
@@ -716,7 +716,7 @@
<string name="notification_channel_summary_default_with_bubbles" msgid="1782419896613644568">"Kan ringe eller vibrere baseret på telefonens indstillinger. Samtaler fra <xliff:g id="APP_NAME">%1$s</xliff:g> vises som standard i bobler."</string>
<string name="notification_channel_summary_bubble" msgid="7235935211580860537">"Fastholder din opmærksomhed med en svævende genvej til indholdet."</string>
<string name="notification_channel_summary_automatic" msgid="5813109268050235275">"Få systemet til at afgøre, om denne notifikation skal vibrere eller afspille en lyd"</string>
- <string name="notification_channel_summary_priority" msgid="7952654515769021553">"Vises øverst i samtalesektionen, som en svævende boble og med profilbillede på låseskærmen"</string>
+ <string name="notification_channel_summary_priority" msgid="7952654515769021553">"Vises øverst i samtalesektionen som en svævende boble og med profilbillede på låseskærmen"</string>
<string name="notification_conversation_channel_settings" msgid="2409977688430606835">"Indstillinger"</string>
<string name="notification_priority_title" msgid="2079708866333537093">"Prioritet"</string>
<string name="no_shortcut" msgid="8257177117568230126">"<xliff:g id="APP_NAME">%1$s</xliff:g> understøtter ikke samtalefunktioner"</string>
@@ -879,12 +879,13 @@
</string-array>
<string name="tuner_low_priority" msgid="8412666814123009820">"Vis ikoner for notifikationer med lav prioritet"</string>
<string name="other" msgid="429768510980739978">"Andet"</string>
- <string name="accessibility_qs_edit_tile_label" msgid="9079791448815232967">"Position <xliff:g id="POSITION">%1$d</xliff:g>, <xliff:g id="TILE_NAME">%2$s</xliff:g>. Tryk to gange for at redigere."</string>
- <string name="accessibility_qs_edit_add_tile_label" msgid="8292218072049068613">"<xliff:g id="TILE_NAME">%1$s</xliff:g>. Tryk to gange for at tilføje."</string>
- <string name="accessibility_qs_edit_move_tile" msgid="6027997446473163426">"Flyt <xliff:g id="TILE_NAME">%1$s</xliff:g>"</string>
- <string name="accessibility_qs_edit_remove_tile" msgid="3406781901949899624">"Fjern <xliff:g id="TILE_NAME">%1$s</xliff:g>"</string>
- <string name="accessibility_qs_edit_tile_add" msgid="6289879620154587233">"Føj <xliff:g id="TILE_NAME">%1$s</xliff:g> til position <xliff:g id="POSITION">%2$d</xliff:g>"</string>
- <string name="accessibility_qs_edit_tile_move" msgid="4841770637244326837">"Flyt <xliff:g id="TILE_NAME">%1$s</xliff:g> til position <xliff:g id="POSITION">%2$d</xliff:g>"</string>
+ <string name="accessibility_qs_edit_remove_tile_action" msgid="775511891457193480">"fjern kortet"</string>
+ <string name="accessibility_qs_edit_tile_add_action" msgid="5051211910345301833">"føj kortet til slutningen"</string>
+ <string name="accessibility_qs_edit_tile_start_move" msgid="2009373939914517817">"Flyt kortet"</string>
+ <string name="accessibility_qs_edit_tile_start_add" msgid="7560798153975555772">"Tilføj et kort"</string>
+ <string name="accessibility_qs_edit_tile_move_to_position" msgid="5198161544045930556">"Flyt til <xliff:g id="POSITION">%1$d</xliff:g>"</string>
+ <string name="accessibility_qs_edit_tile_add_to_position" msgid="9029163095148274690">"Føj til placering <xliff:g id="POSITION">%1$d</xliff:g>"</string>
+ <string name="accessibility_qs_edit_position" msgid="4509277359815711830">"Placering <xliff:g id="POSITION">%1$d</xliff:g>"</string>
<string name="accessibility_desc_quick_settings_edit" msgid="741658939453595297">"Redigeringsværktøj til Kvikmenu."</string>
<string name="accessibility_desc_notification_icon" msgid="7331265967584178674">"<xliff:g id="ID_1">%1$s</xliff:g>-notifikation: <xliff:g id="ID_2">%2$s</xliff:g>"</string>
<string name="accessibility_quick_settings_settings" msgid="7098489591715844713">"Åbn Indstillinger."</string>
@@ -1067,4 +1068,11 @@
<string name="controls_added_tooltip" msgid="4842812921719153085">"Hold afbryderknappen nede for at se nye betjeningselementer"</string>
<string name="controls_menu_add" msgid="4447246119229920050">"Tilføj styring"</string>
<string name="controls_menu_edit" msgid="890623986951347062">"Rediger styring"</string>
+ <string name="media_output_dialog_add_output" msgid="5642703238877329518">"Tilføj medieudgange"</string>
+ <string name="media_output_dialog_group" msgid="5571251347877452212">"Gruppe"</string>
+ <string name="media_output_dialog_single_device" msgid="3102758980643351058">"Der er valgt 1 enhed"</string>
+ <string name="media_output_dialog_multiple_devices" msgid="1093771040315422350">"Der er valgt <xliff:g id="COUNT">%1$d</xliff:g> enhed"</string>
+ <string name="media_output_dialog_disconnected" msgid="1834473104836986046">"<xliff:g id="DEVICE_NAME">%1$s</xliff:g> (ingen forbindelse)"</string>
+ <string name="media_output_dialog_connect_failed" msgid="3225190634236259010">"Der kunne ikke oprettes forbindelse. Prøv igen."</string>
+ <string name="media_output_dialog_pairing_new" msgid="9099497976087485862">"Par ny enhed"</string>
</resources>
diff --git a/packages/SystemUI/res/values-de/strings.xml b/packages/SystemUI/res/values-de/strings.xml
index af8d22ea4cd0..887aad5d9aa6 100644
--- a/packages/SystemUI/res/values-de/strings.xml
+++ b/packages/SystemUI/res/values-de/strings.xml
@@ -879,12 +879,13 @@
</string-array>
<string name="tuner_low_priority" msgid="8412666814123009820">"Symbole für Benachrichtigungen mit einer niedrigen Priorität anzeigen"</string>
<string name="other" msgid="429768510980739978">"Sonstiges"</string>
- <string name="accessibility_qs_edit_tile_label" msgid="9079791448815232967">"Position <xliff:g id="POSITION">%1$d</xliff:g>, <xliff:g id="TILE_NAME">%2$s</xliff:g>. Zum Bearbeiten doppeltippen."</string>
- <string name="accessibility_qs_edit_add_tile_label" msgid="8292218072049068613">"<xliff:g id="TILE_NAME">%1$s</xliff:g>. Zum Hinzufügen doppeltippen."</string>
- <string name="accessibility_qs_edit_move_tile" msgid="6027997446473163426">"<xliff:g id="TILE_NAME">%1$s</xliff:g> verschieben"</string>
- <string name="accessibility_qs_edit_remove_tile" msgid="3406781901949899624">"<xliff:g id="TILE_NAME">%1$s</xliff:g> entfernen"</string>
- <string name="accessibility_qs_edit_tile_add" msgid="6289879620154587233">"<xliff:g id="TILE_NAME">%1$s</xliff:g> auf Position <xliff:g id="POSITION">%2$d</xliff:g> hinzufügen"</string>
- <string name="accessibility_qs_edit_tile_move" msgid="4841770637244326837">"<xliff:g id="TILE_NAME">%1$s</xliff:g> auf Position <xliff:g id="POSITION">%2$d</xliff:g> verschieben"</string>
+ <string name="accessibility_qs_edit_remove_tile_action" msgid="775511891457193480">"die Kachel zu entfernen"</string>
+ <string name="accessibility_qs_edit_tile_add_action" msgid="5051211910345301833">"die Kachel am Ende hinzuzufügen"</string>
+ <string name="accessibility_qs_edit_tile_start_move" msgid="2009373939914517817">"Kachel verschieben"</string>
+ <string name="accessibility_qs_edit_tile_start_add" msgid="7560798153975555772">"Kachel hinzufügen"</string>
+ <string name="accessibility_qs_edit_tile_move_to_position" msgid="5198161544045930556">"Auf Position <xliff:g id="POSITION">%1$d</xliff:g> verschieben"</string>
+ <string name="accessibility_qs_edit_tile_add_to_position" msgid="9029163095148274690">"Zur Position <xliff:g id="POSITION">%1$d</xliff:g> hinzufügen"</string>
+ <string name="accessibility_qs_edit_position" msgid="4509277359815711830">"Position <xliff:g id="POSITION">%1$d</xliff:g>"</string>
<string name="accessibility_desc_quick_settings_edit" msgid="741658939453595297">"Editor für Schnelleinstellungen."</string>
<string name="accessibility_desc_notification_icon" msgid="7331265967584178674">"Benachrichtigung von <xliff:g id="ID_1">%1$s</xliff:g>: <xliff:g id="ID_2">%2$s</xliff:g>"</string>
<string name="accessibility_quick_settings_settings" msgid="7098489591715844713">"Einstellungen öffnen."</string>
@@ -1067,4 +1068,11 @@
<string name="controls_added_tooltip" msgid="4842812921719153085">"Zum Anzeigen der Karten für neue Geräte Ein-/Aus-Taste gedrückt halten"</string>
<string name="controls_menu_add" msgid="4447246119229920050">"Steuerelemente hinzufügen"</string>
<string name="controls_menu_edit" msgid="890623986951347062">"Steuerelemente bearbeiten"</string>
+ <string name="media_output_dialog_add_output" msgid="5642703238877329518">"Ausgabegeräte hinzufügen"</string>
+ <string name="media_output_dialog_group" msgid="5571251347877452212">"Gruppe"</string>
+ <string name="media_output_dialog_single_device" msgid="3102758980643351058">"Ein Gerät ausgewählt"</string>
+ <string name="media_output_dialog_multiple_devices" msgid="1093771040315422350">"<xliff:g id="COUNT">%1$d</xliff:g> Geräte ausgewählt"</string>
+ <string name="media_output_dialog_disconnected" msgid="1834473104836986046">"<xliff:g id="DEVICE_NAME">%1$s</xliff:g> (nicht verbunden)"</string>
+ <string name="media_output_dialog_connect_failed" msgid="3225190634236259010">"Verbindung nicht möglich. Versuch es noch einmal."</string>
+ <string name="media_output_dialog_pairing_new" msgid="9099497976087485862">"Neues Gerät koppeln"</string>
</resources>
diff --git a/packages/SystemUI/res/values-el/strings.xml b/packages/SystemUI/res/values-el/strings.xml
index 951f01d54bdf..1b912c7a6ed9 100644
--- a/packages/SystemUI/res/values-el/strings.xml
+++ b/packages/SystemUI/res/values-el/strings.xml
@@ -716,7 +716,7 @@
<string name="notification_channel_summary_default_with_bubbles" msgid="1782419896613644568">"Ενδέχεται να κουδουνίζει ή να δονείται βάσει των ρυθμίσεων του τηλεφώνου. Οι συζητήσεις από την εφαρμογή <xliff:g id="APP_NAME">%1$s</xliff:g> εμφανίζονται σε συννεφάκι από προεπιλογή."</string>
<string name="notification_channel_summary_bubble" msgid="7235935211580860537">"Κρατάει την προσοχή σας με μια κινούμενη συντόμευση προς αυτό το περιεχόμενο."</string>
<string name="notification_channel_summary_automatic" msgid="5813109268050235275">"Επιτρέψτε στο σύστημα να αποφασίσει αν αυτή η ειδοποίηση θα αναπαράγει έναν ήχο ή θα ενεργοποιήσει τη δόνηση της συσκευής"</string>
- <string name="notification_channel_summary_priority" msgid="7952654515769021553">"Εμφανίζεται στο επάνω μέρος της ενότητας συζητήσεων, προβάλλεται ως κινούμενο συννεφάκι, εμφανίζει τη φωτογραφία προφίλ στην οθόνη κλειδώματος"</string>
+ <string name="notification_channel_summary_priority" msgid="7952654515769021553">"Εμφανίζεται επάνω στις συζητήσεις, προβάλλεται ως κιν. συννεφάκι, εμφανίζει τη φωτ. προφίλ στην οθ. κλειδ."</string>
<string name="notification_conversation_channel_settings" msgid="2409977688430606835">"Ρυθμίσεις"</string>
<string name="notification_priority_title" msgid="2079708866333537093">"Προτεραιότητα"</string>
<string name="no_shortcut" msgid="8257177117568230126">"Η εφαρμογή <xliff:g id="APP_NAME">%1$s</xliff:g> δεν υποστηρίζει τις λειτουργίες συζήτησης"</string>
@@ -879,12 +879,13 @@
</string-array>
<string name="tuner_low_priority" msgid="8412666814123009820">"Εμφάνιση εικονιδίων ειδοποιήσεων χαμηλής προτεραιότητας"</string>
<string name="other" msgid="429768510980739978">"Άλλο"</string>
- <string name="accessibility_qs_edit_tile_label" msgid="9079791448815232967">"Θέση <xliff:g id="POSITION">%1$d</xliff:g>, <xliff:g id="TILE_NAME">%2$s</xliff:g>. Πατήστε δύο φορές για επεξεργασία."</string>
- <string name="accessibility_qs_edit_add_tile_label" msgid="8292218072049068613">"<xliff:g id="TILE_NAME">%1$s</xliff:g>. Πατήστε δύο φορές για προσθήκη."</string>
- <string name="accessibility_qs_edit_move_tile" msgid="6027997446473163426">"Μετακίνηση <xliff:g id="TILE_NAME">%1$s</xliff:g>"</string>
- <string name="accessibility_qs_edit_remove_tile" msgid="3406781901949899624">"Κατάργηση <xliff:g id="TILE_NAME">%1$s</xliff:g>"</string>
- <string name="accessibility_qs_edit_tile_add" msgid="6289879620154587233">"Προσθήκη <xliff:g id="TILE_NAME">%1$s</xliff:g> στη θέση <xliff:g id="POSITION">%2$d</xliff:g>"</string>
- <string name="accessibility_qs_edit_tile_move" msgid="4841770637244326837">"Μετακίνηση <xliff:g id="TILE_NAME">%1$s</xliff:g> στη θέση <xliff:g id="POSITION">%2$d</xliff:g>"</string>
+ <string name="accessibility_qs_edit_remove_tile_action" msgid="775511891457193480">"κατάργηση πλακιδίου"</string>
+ <string name="accessibility_qs_edit_tile_add_action" msgid="5051211910345301833">"προσθήκη πλακιδίου στο τέλος"</string>
+ <string name="accessibility_qs_edit_tile_start_move" msgid="2009373939914517817">"Μετακίνηση πλακιδίου"</string>
+ <string name="accessibility_qs_edit_tile_start_add" msgid="7560798153975555772">"Προσθήκη πλακιδίου"</string>
+ <string name="accessibility_qs_edit_tile_move_to_position" msgid="5198161544045930556">"Μετακίνηση στη θέση <xliff:g id="POSITION">%1$d</xliff:g>"</string>
+ <string name="accessibility_qs_edit_tile_add_to_position" msgid="9029163095148274690">"Προσθήκη στη θέση <xliff:g id="POSITION">%1$d</xliff:g>"</string>
+ <string name="accessibility_qs_edit_position" msgid="4509277359815711830">"Θέση <xliff:g id="POSITION">%1$d</xliff:g>"</string>
<string name="accessibility_desc_quick_settings_edit" msgid="741658939453595297">"Επεξεργασία γρήγορων ρυθμίσεων."</string>
<string name="accessibility_desc_notification_icon" msgid="7331265967584178674">"Ειδοποίηση <xliff:g id="ID_1">%1$s</xliff:g>: <xliff:g id="ID_2">%2$s</xliff:g>"</string>
<string name="accessibility_quick_settings_settings" msgid="7098489591715844713">"Άνοιγμα ρυθμίσεων."</string>
@@ -1067,4 +1068,11 @@
<string name="controls_added_tooltip" msgid="4842812921719153085">"Πατήστε το κουμπί λειτουργίας για να δείτε νέα στοιχεία ελέγχου."</string>
<string name="controls_menu_add" msgid="4447246119229920050">"Προσθήκη στοιχείων ελέγχου"</string>
<string name="controls_menu_edit" msgid="890623986951347062">"Επεξεργασία στοιχείων ελέγχου"</string>
+ <string name="media_output_dialog_add_output" msgid="5642703238877329518">"Προσθήκη εξόδων"</string>
+ <string name="media_output_dialog_group" msgid="5571251347877452212">"Ομάδα"</string>
+ <string name="media_output_dialog_single_device" msgid="3102758980643351058">"Επιλέχτηκε 1 συσκευή"</string>
+ <string name="media_output_dialog_multiple_devices" msgid="1093771040315422350">"Επιλέχτηκαν <xliff:g id="COUNT">%1$d</xliff:g> συσκευές"</string>
+ <string name="media_output_dialog_disconnected" msgid="1834473104836986046">"<xliff:g id="DEVICE_NAME">%1$s</xliff:g> (αποσυνδέθηκε)"</string>
+ <string name="media_output_dialog_connect_failed" msgid="3225190634236259010">"Δεν ήταν δυνατή η σύνδεση. Δοκιμάστε ξανά."</string>
+ <string name="media_output_dialog_pairing_new" msgid="9099497976087485862">"Σύζευξη νέας συσκευής"</string>
</resources>
diff --git a/packages/SystemUI/res/values-en-rAU/strings.xml b/packages/SystemUI/res/values-en-rAU/strings.xml
index debbd834f98d..065c17bec41a 100644
--- a/packages/SystemUI/res/values-en-rAU/strings.xml
+++ b/packages/SystemUI/res/values-en-rAU/strings.xml
@@ -92,7 +92,7 @@
<string name="screenrecord_background_processing_label" msgid="7244617554884238898">"Processing screen recording"</string>
<string name="screenrecord_channel_description" msgid="4147077128486138351">"Ongoing notification for a screen record session"</string>
<string name="screenrecord_start_label" msgid="1750350278888217473">"Start recording?"</string>
- <string name="screenrecord_description" msgid="1123231719680353736">"While recording, Android System can capture any sensitive information that’s visible on your screen or played on your device. This includes passwords, payment info, photos, messages and audio."</string>
+ <string name="screenrecord_description" msgid="1123231719680353736">"While recording, the Android System can capture any sensitive information that’s visible on your screen or played on your device. This includes passwords, payment info, photos, messages and audio."</string>
<string name="screenrecord_audio_label" msgid="6183558856175159629">"Record audio"</string>
<string name="screenrecord_device_audio_label" msgid="9016927171280567791">"Device audio"</string>
<string name="screenrecord_device_audio_description" msgid="4922694220572186193">"Sound from your device, like music, calls and ringtones"</string>
@@ -879,12 +879,13 @@
</string-array>
<string name="tuner_low_priority" msgid="8412666814123009820">"Show low-priority notification icons"</string>
<string name="other" msgid="429768510980739978">"Other"</string>
- <string name="accessibility_qs_edit_tile_label" msgid="9079791448815232967">"Position <xliff:g id="POSITION">%1$d</xliff:g>, <xliff:g id="TILE_NAME">%2$s</xliff:g>. Double tap to edit."</string>
- <string name="accessibility_qs_edit_add_tile_label" msgid="8292218072049068613">"<xliff:g id="TILE_NAME">%1$s</xliff:g>. Double tap to add."</string>
- <string name="accessibility_qs_edit_move_tile" msgid="6027997446473163426">"Move <xliff:g id="TILE_NAME">%1$s</xliff:g>"</string>
- <string name="accessibility_qs_edit_remove_tile" msgid="3406781901949899624">"Remove <xliff:g id="TILE_NAME">%1$s</xliff:g>"</string>
- <string name="accessibility_qs_edit_tile_add" msgid="6289879620154587233">"Add <xliff:g id="TILE_NAME">%1$s</xliff:g> to position <xliff:g id="POSITION">%2$d</xliff:g>"</string>
- <string name="accessibility_qs_edit_tile_move" msgid="4841770637244326837">"Move <xliff:g id="TILE_NAME">%1$s</xliff:g> to position <xliff:g id="POSITION">%2$d</xliff:g>"</string>
+ <string name="accessibility_qs_edit_remove_tile_action" msgid="775511891457193480">"remove tile"</string>
+ <string name="accessibility_qs_edit_tile_add_action" msgid="5051211910345301833">"add tile to end"</string>
+ <string name="accessibility_qs_edit_tile_start_move" msgid="2009373939914517817">"Move tile"</string>
+ <string name="accessibility_qs_edit_tile_start_add" msgid="7560798153975555772">"Add tile"</string>
+ <string name="accessibility_qs_edit_tile_move_to_position" msgid="5198161544045930556">"Move to <xliff:g id="POSITION">%1$d</xliff:g>"</string>
+ <string name="accessibility_qs_edit_tile_add_to_position" msgid="9029163095148274690">"Add to position <xliff:g id="POSITION">%1$d</xliff:g>"</string>
+ <string name="accessibility_qs_edit_position" msgid="4509277359815711830">"Position <xliff:g id="POSITION">%1$d</xliff:g>"</string>
<string name="accessibility_desc_quick_settings_edit" msgid="741658939453595297">"Quick settings editor."</string>
<string name="accessibility_desc_notification_icon" msgid="7331265967584178674">"<xliff:g id="ID_1">%1$s</xliff:g> notification: <xliff:g id="ID_2">%2$s</xliff:g>"</string>
<string name="accessibility_quick_settings_settings" msgid="7098489591715844713">"Open settings."</string>
@@ -1067,4 +1068,11 @@
<string name="controls_added_tooltip" msgid="4842812921719153085">"Hold Power button to see new controls"</string>
<string name="controls_menu_add" msgid="4447246119229920050">"Add controls"</string>
<string name="controls_menu_edit" msgid="890623986951347062">"Edit controls"</string>
+ <string name="media_output_dialog_add_output" msgid="5642703238877329518">"Add outputs"</string>
+ <string name="media_output_dialog_group" msgid="5571251347877452212">"Group"</string>
+ <string name="media_output_dialog_single_device" msgid="3102758980643351058">"One device selected"</string>
+ <string name="media_output_dialog_multiple_devices" msgid="1093771040315422350">"<xliff:g id="COUNT">%1$d</xliff:g> devices selected"</string>
+ <string name="media_output_dialog_disconnected" msgid="1834473104836986046">"<xliff:g id="DEVICE_NAME">%1$s</xliff:g> (disconnected)"</string>
+ <string name="media_output_dialog_connect_failed" msgid="3225190634236259010">"Couldn\'t connect. Try again."</string>
+ <string name="media_output_dialog_pairing_new" msgid="9099497976087485862">"Pair new device"</string>
</resources>
diff --git a/packages/SystemUI/res/values-en-rCA/strings.xml b/packages/SystemUI/res/values-en-rCA/strings.xml
index b7ad5977e78f..1cc662521645 100644
--- a/packages/SystemUI/res/values-en-rCA/strings.xml
+++ b/packages/SystemUI/res/values-en-rCA/strings.xml
@@ -92,7 +92,7 @@
<string name="screenrecord_background_processing_label" msgid="7244617554884238898">"Processing screen recording"</string>
<string name="screenrecord_channel_description" msgid="4147077128486138351">"Ongoing notification for a screen record session"</string>
<string name="screenrecord_start_label" msgid="1750350278888217473">"Start recording?"</string>
- <string name="screenrecord_description" msgid="1123231719680353736">"While recording, Android System can capture any sensitive information that’s visible on your screen or played on your device. This includes passwords, payment info, photos, messages and audio."</string>
+ <string name="screenrecord_description" msgid="1123231719680353736">"While recording, the Android System can capture any sensitive information that’s visible on your screen or played on your device. This includes passwords, payment info, photos, messages and audio."</string>
<string name="screenrecord_audio_label" msgid="6183558856175159629">"Record audio"</string>
<string name="screenrecord_device_audio_label" msgid="9016927171280567791">"Device audio"</string>
<string name="screenrecord_device_audio_description" msgid="4922694220572186193">"Sound from your device, like music, calls and ringtones"</string>
@@ -879,12 +879,13 @@
</string-array>
<string name="tuner_low_priority" msgid="8412666814123009820">"Show low-priority notification icons"</string>
<string name="other" msgid="429768510980739978">"Other"</string>
- <string name="accessibility_qs_edit_tile_label" msgid="9079791448815232967">"Position <xliff:g id="POSITION">%1$d</xliff:g>, <xliff:g id="TILE_NAME">%2$s</xliff:g>. Double tap to edit."</string>
- <string name="accessibility_qs_edit_add_tile_label" msgid="8292218072049068613">"<xliff:g id="TILE_NAME">%1$s</xliff:g>. Double tap to add."</string>
- <string name="accessibility_qs_edit_move_tile" msgid="6027997446473163426">"Move <xliff:g id="TILE_NAME">%1$s</xliff:g>"</string>
- <string name="accessibility_qs_edit_remove_tile" msgid="3406781901949899624">"Remove <xliff:g id="TILE_NAME">%1$s</xliff:g>"</string>
- <string name="accessibility_qs_edit_tile_add" msgid="6289879620154587233">"Add <xliff:g id="TILE_NAME">%1$s</xliff:g> to position <xliff:g id="POSITION">%2$d</xliff:g>"</string>
- <string name="accessibility_qs_edit_tile_move" msgid="4841770637244326837">"Move <xliff:g id="TILE_NAME">%1$s</xliff:g> to position <xliff:g id="POSITION">%2$d</xliff:g>"</string>
+ <string name="accessibility_qs_edit_remove_tile_action" msgid="775511891457193480">"remove tile"</string>
+ <string name="accessibility_qs_edit_tile_add_action" msgid="5051211910345301833">"add tile to end"</string>
+ <string name="accessibility_qs_edit_tile_start_move" msgid="2009373939914517817">"Move tile"</string>
+ <string name="accessibility_qs_edit_tile_start_add" msgid="7560798153975555772">"Add tile"</string>
+ <string name="accessibility_qs_edit_tile_move_to_position" msgid="5198161544045930556">"Move to <xliff:g id="POSITION">%1$d</xliff:g>"</string>
+ <string name="accessibility_qs_edit_tile_add_to_position" msgid="9029163095148274690">"Add to position <xliff:g id="POSITION">%1$d</xliff:g>"</string>
+ <string name="accessibility_qs_edit_position" msgid="4509277359815711830">"Position <xliff:g id="POSITION">%1$d</xliff:g>"</string>
<string name="accessibility_desc_quick_settings_edit" msgid="741658939453595297">"Quick settings editor."</string>
<string name="accessibility_desc_notification_icon" msgid="7331265967584178674">"<xliff:g id="ID_1">%1$s</xliff:g> notification: <xliff:g id="ID_2">%2$s</xliff:g>"</string>
<string name="accessibility_quick_settings_settings" msgid="7098489591715844713">"Open settings."</string>
@@ -1067,4 +1068,11 @@
<string name="controls_added_tooltip" msgid="4842812921719153085">"Hold Power button to see new controls"</string>
<string name="controls_menu_add" msgid="4447246119229920050">"Add controls"</string>
<string name="controls_menu_edit" msgid="890623986951347062">"Edit controls"</string>
+ <string name="media_output_dialog_add_output" msgid="5642703238877329518">"Add outputs"</string>
+ <string name="media_output_dialog_group" msgid="5571251347877452212">"Group"</string>
+ <string name="media_output_dialog_single_device" msgid="3102758980643351058">"One device selected"</string>
+ <string name="media_output_dialog_multiple_devices" msgid="1093771040315422350">"<xliff:g id="COUNT">%1$d</xliff:g> devices selected"</string>
+ <string name="media_output_dialog_disconnected" msgid="1834473104836986046">"<xliff:g id="DEVICE_NAME">%1$s</xliff:g> (disconnected)"</string>
+ <string name="media_output_dialog_connect_failed" msgid="3225190634236259010">"Couldn\'t connect. Try again."</string>
+ <string name="media_output_dialog_pairing_new" msgid="9099497976087485862">"Pair new device"</string>
</resources>
diff --git a/packages/SystemUI/res/values-en-rGB/strings.xml b/packages/SystemUI/res/values-en-rGB/strings.xml
index debbd834f98d..065c17bec41a 100644
--- a/packages/SystemUI/res/values-en-rGB/strings.xml
+++ b/packages/SystemUI/res/values-en-rGB/strings.xml
@@ -92,7 +92,7 @@
<string name="screenrecord_background_processing_label" msgid="7244617554884238898">"Processing screen recording"</string>
<string name="screenrecord_channel_description" msgid="4147077128486138351">"Ongoing notification for a screen record session"</string>
<string name="screenrecord_start_label" msgid="1750350278888217473">"Start recording?"</string>
- <string name="screenrecord_description" msgid="1123231719680353736">"While recording, Android System can capture any sensitive information that’s visible on your screen or played on your device. This includes passwords, payment info, photos, messages and audio."</string>
+ <string name="screenrecord_description" msgid="1123231719680353736">"While recording, the Android System can capture any sensitive information that’s visible on your screen or played on your device. This includes passwords, payment info, photos, messages and audio."</string>
<string name="screenrecord_audio_label" msgid="6183558856175159629">"Record audio"</string>
<string name="screenrecord_device_audio_label" msgid="9016927171280567791">"Device audio"</string>
<string name="screenrecord_device_audio_description" msgid="4922694220572186193">"Sound from your device, like music, calls and ringtones"</string>
@@ -879,12 +879,13 @@
</string-array>
<string name="tuner_low_priority" msgid="8412666814123009820">"Show low-priority notification icons"</string>
<string name="other" msgid="429768510980739978">"Other"</string>
- <string name="accessibility_qs_edit_tile_label" msgid="9079791448815232967">"Position <xliff:g id="POSITION">%1$d</xliff:g>, <xliff:g id="TILE_NAME">%2$s</xliff:g>. Double tap to edit."</string>
- <string name="accessibility_qs_edit_add_tile_label" msgid="8292218072049068613">"<xliff:g id="TILE_NAME">%1$s</xliff:g>. Double tap to add."</string>
- <string name="accessibility_qs_edit_move_tile" msgid="6027997446473163426">"Move <xliff:g id="TILE_NAME">%1$s</xliff:g>"</string>
- <string name="accessibility_qs_edit_remove_tile" msgid="3406781901949899624">"Remove <xliff:g id="TILE_NAME">%1$s</xliff:g>"</string>
- <string name="accessibility_qs_edit_tile_add" msgid="6289879620154587233">"Add <xliff:g id="TILE_NAME">%1$s</xliff:g> to position <xliff:g id="POSITION">%2$d</xliff:g>"</string>
- <string name="accessibility_qs_edit_tile_move" msgid="4841770637244326837">"Move <xliff:g id="TILE_NAME">%1$s</xliff:g> to position <xliff:g id="POSITION">%2$d</xliff:g>"</string>
+ <string name="accessibility_qs_edit_remove_tile_action" msgid="775511891457193480">"remove tile"</string>
+ <string name="accessibility_qs_edit_tile_add_action" msgid="5051211910345301833">"add tile to end"</string>
+ <string name="accessibility_qs_edit_tile_start_move" msgid="2009373939914517817">"Move tile"</string>
+ <string name="accessibility_qs_edit_tile_start_add" msgid="7560798153975555772">"Add tile"</string>
+ <string name="accessibility_qs_edit_tile_move_to_position" msgid="5198161544045930556">"Move to <xliff:g id="POSITION">%1$d</xliff:g>"</string>
+ <string name="accessibility_qs_edit_tile_add_to_position" msgid="9029163095148274690">"Add to position <xliff:g id="POSITION">%1$d</xliff:g>"</string>
+ <string name="accessibility_qs_edit_position" msgid="4509277359815711830">"Position <xliff:g id="POSITION">%1$d</xliff:g>"</string>
<string name="accessibility_desc_quick_settings_edit" msgid="741658939453595297">"Quick settings editor."</string>
<string name="accessibility_desc_notification_icon" msgid="7331265967584178674">"<xliff:g id="ID_1">%1$s</xliff:g> notification: <xliff:g id="ID_2">%2$s</xliff:g>"</string>
<string name="accessibility_quick_settings_settings" msgid="7098489591715844713">"Open settings."</string>
@@ -1067,4 +1068,11 @@
<string name="controls_added_tooltip" msgid="4842812921719153085">"Hold Power button to see new controls"</string>
<string name="controls_menu_add" msgid="4447246119229920050">"Add controls"</string>
<string name="controls_menu_edit" msgid="890623986951347062">"Edit controls"</string>
+ <string name="media_output_dialog_add_output" msgid="5642703238877329518">"Add outputs"</string>
+ <string name="media_output_dialog_group" msgid="5571251347877452212">"Group"</string>
+ <string name="media_output_dialog_single_device" msgid="3102758980643351058">"One device selected"</string>
+ <string name="media_output_dialog_multiple_devices" msgid="1093771040315422350">"<xliff:g id="COUNT">%1$d</xliff:g> devices selected"</string>
+ <string name="media_output_dialog_disconnected" msgid="1834473104836986046">"<xliff:g id="DEVICE_NAME">%1$s</xliff:g> (disconnected)"</string>
+ <string name="media_output_dialog_connect_failed" msgid="3225190634236259010">"Couldn\'t connect. Try again."</string>
+ <string name="media_output_dialog_pairing_new" msgid="9099497976087485862">"Pair new device"</string>
</resources>
diff --git a/packages/SystemUI/res/values-en-rIN/strings.xml b/packages/SystemUI/res/values-en-rIN/strings.xml
index debbd834f98d..065c17bec41a 100644
--- a/packages/SystemUI/res/values-en-rIN/strings.xml
+++ b/packages/SystemUI/res/values-en-rIN/strings.xml
@@ -92,7 +92,7 @@
<string name="screenrecord_background_processing_label" msgid="7244617554884238898">"Processing screen recording"</string>
<string name="screenrecord_channel_description" msgid="4147077128486138351">"Ongoing notification for a screen record session"</string>
<string name="screenrecord_start_label" msgid="1750350278888217473">"Start recording?"</string>
- <string name="screenrecord_description" msgid="1123231719680353736">"While recording, Android System can capture any sensitive information that’s visible on your screen or played on your device. This includes passwords, payment info, photos, messages and audio."</string>
+ <string name="screenrecord_description" msgid="1123231719680353736">"While recording, the Android System can capture any sensitive information that’s visible on your screen or played on your device. This includes passwords, payment info, photos, messages and audio."</string>
<string name="screenrecord_audio_label" msgid="6183558856175159629">"Record audio"</string>
<string name="screenrecord_device_audio_label" msgid="9016927171280567791">"Device audio"</string>
<string name="screenrecord_device_audio_description" msgid="4922694220572186193">"Sound from your device, like music, calls and ringtones"</string>
@@ -879,12 +879,13 @@
</string-array>
<string name="tuner_low_priority" msgid="8412666814123009820">"Show low-priority notification icons"</string>
<string name="other" msgid="429768510980739978">"Other"</string>
- <string name="accessibility_qs_edit_tile_label" msgid="9079791448815232967">"Position <xliff:g id="POSITION">%1$d</xliff:g>, <xliff:g id="TILE_NAME">%2$s</xliff:g>. Double tap to edit."</string>
- <string name="accessibility_qs_edit_add_tile_label" msgid="8292218072049068613">"<xliff:g id="TILE_NAME">%1$s</xliff:g>. Double tap to add."</string>
- <string name="accessibility_qs_edit_move_tile" msgid="6027997446473163426">"Move <xliff:g id="TILE_NAME">%1$s</xliff:g>"</string>
- <string name="accessibility_qs_edit_remove_tile" msgid="3406781901949899624">"Remove <xliff:g id="TILE_NAME">%1$s</xliff:g>"</string>
- <string name="accessibility_qs_edit_tile_add" msgid="6289879620154587233">"Add <xliff:g id="TILE_NAME">%1$s</xliff:g> to position <xliff:g id="POSITION">%2$d</xliff:g>"</string>
- <string name="accessibility_qs_edit_tile_move" msgid="4841770637244326837">"Move <xliff:g id="TILE_NAME">%1$s</xliff:g> to position <xliff:g id="POSITION">%2$d</xliff:g>"</string>
+ <string name="accessibility_qs_edit_remove_tile_action" msgid="775511891457193480">"remove tile"</string>
+ <string name="accessibility_qs_edit_tile_add_action" msgid="5051211910345301833">"add tile to end"</string>
+ <string name="accessibility_qs_edit_tile_start_move" msgid="2009373939914517817">"Move tile"</string>
+ <string name="accessibility_qs_edit_tile_start_add" msgid="7560798153975555772">"Add tile"</string>
+ <string name="accessibility_qs_edit_tile_move_to_position" msgid="5198161544045930556">"Move to <xliff:g id="POSITION">%1$d</xliff:g>"</string>
+ <string name="accessibility_qs_edit_tile_add_to_position" msgid="9029163095148274690">"Add to position <xliff:g id="POSITION">%1$d</xliff:g>"</string>
+ <string name="accessibility_qs_edit_position" msgid="4509277359815711830">"Position <xliff:g id="POSITION">%1$d</xliff:g>"</string>
<string name="accessibility_desc_quick_settings_edit" msgid="741658939453595297">"Quick settings editor."</string>
<string name="accessibility_desc_notification_icon" msgid="7331265967584178674">"<xliff:g id="ID_1">%1$s</xliff:g> notification: <xliff:g id="ID_2">%2$s</xliff:g>"</string>
<string name="accessibility_quick_settings_settings" msgid="7098489591715844713">"Open settings."</string>
@@ -1067,4 +1068,11 @@
<string name="controls_added_tooltip" msgid="4842812921719153085">"Hold Power button to see new controls"</string>
<string name="controls_menu_add" msgid="4447246119229920050">"Add controls"</string>
<string name="controls_menu_edit" msgid="890623986951347062">"Edit controls"</string>
+ <string name="media_output_dialog_add_output" msgid="5642703238877329518">"Add outputs"</string>
+ <string name="media_output_dialog_group" msgid="5571251347877452212">"Group"</string>
+ <string name="media_output_dialog_single_device" msgid="3102758980643351058">"One device selected"</string>
+ <string name="media_output_dialog_multiple_devices" msgid="1093771040315422350">"<xliff:g id="COUNT">%1$d</xliff:g> devices selected"</string>
+ <string name="media_output_dialog_disconnected" msgid="1834473104836986046">"<xliff:g id="DEVICE_NAME">%1$s</xliff:g> (disconnected)"</string>
+ <string name="media_output_dialog_connect_failed" msgid="3225190634236259010">"Couldn\'t connect. Try again."</string>
+ <string name="media_output_dialog_pairing_new" msgid="9099497976087485862">"Pair new device"</string>
</resources>
diff --git a/packages/SystemUI/res/values-en-rXC/strings.xml b/packages/SystemUI/res/values-en-rXC/strings.xml
index c76dc72714de..7702337f080f 100644
--- a/packages/SystemUI/res/values-en-rXC/strings.xml
+++ b/packages/SystemUI/res/values-en-rXC/strings.xml
@@ -879,12 +879,13 @@
</string-array>
<string name="tuner_low_priority" msgid="8412666814123009820">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‏‏‎‏‎‎‏‎‏‏‏‏‏‏‏‏‎‎‏‎‏‏‏‏‏‎‎‏‎‏‎‎‏‎‏‏‎‏‏‎‎‎‎‏‎‎‏‏‏‎‎‏‏‏‎‎‎‏‏‏‎‎‎Show low-priority notification icons‎‏‎‎‏‎"</string>
<string name="other" msgid="429768510980739978">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‎‏‏‏‎‏‏‏‏‏‎‏‏‎‏‏‎‏‏‎‎‎‎‎‏‎‏‏‏‏‎‎‎‎‏‎‎‎‏‎‏‎‎‏‏‏‏‏‎‏‎‎‏‏‏‎‎‎‏‎‏‎‎Other‎‏‎‎‏‎"</string>
- <string name="accessibility_qs_edit_tile_label" msgid="9079791448815232967">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‏‏‏‏‏‎‎‎‎‎‎‎‎‏‏‏‏‎‎‏‏‎‎‎‏‏‎‏‏‏‎‏‎‎‎‏‎‏‎‏‎‎‎‎‏‎‏‏‏‎‎‏‏‏‏‏‎‎‎‏‏‏‎Position ‎‏‎‎‏‏‎<xliff:g id="POSITION">%1$d</xliff:g>‎‏‎‎‏‏‏‎, ‎‏‎‎‏‏‎<xliff:g id="TILE_NAME">%2$s</xliff:g>‎‏‎‎‏‏‏‎. Double tap to edit.‎‏‎‎‏‎"</string>
- <string name="accessibility_qs_edit_add_tile_label" msgid="8292218072049068613">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‏‏‎‎‏‏‎‎‎‏‎‎‏‏‏‏‏‎‎‎‎‎‎‏‏‎‎‏‏‏‎‎‎‎‏‎‎‎‏‎‎‎‏‏‏‏‎‎‏‎‏‎‏‎‎‏‎‎‎‏‎‏‎‎‏‎‎‏‏‎<xliff:g id="TILE_NAME">%1$s</xliff:g>‎‏‎‎‏‏‏‎. Double tap to add.‎‏‎‎‏‎"</string>
- <string name="accessibility_qs_edit_move_tile" msgid="6027997446473163426">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‎‏‎‎‏‏‏‎‏‎‎‏‏‏‏‎‏‏‏‏‏‏‏‎‏‏‏‏‎‏‎‏‏‎‎‏‏‏‏‏‏‏‏‏‏‎‏‎‏‎‎‎‏‎‏‎‏‎‎‎‏‎‎Move ‎‏‎‎‏‏‎<xliff:g id="TILE_NAME">%1$s</xliff:g>‎‏‎‎‏‏‏‎‎‏‎‎‏‎"</string>
- <string name="accessibility_qs_edit_remove_tile" msgid="3406781901949899624">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‎‏‎‏‏‏‏‎‏‎‎‎‏‏‏‎‏‎‏‎‎‏‎‎‎‎‏‎‏‏‏‎‎‎‎‎‏‎‎‏‏‏‏‎‏‎‎‎‎‎‏‎‎‏‏‎‏‏‎‏‎‎‎‎Remove ‎‏‎‎‏‏‎<xliff:g id="TILE_NAME">%1$s</xliff:g>‎‏‎‎‏‏‏‎‎‏‎‎‏‎"</string>
- <string name="accessibility_qs_edit_tile_add" msgid="6289879620154587233">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‎‏‎‏‏‏‎‏‎‎‏‎‏‎‎‎‏‎‎‏‎‎‎‎‏‏‎‎‎‎‎‏‏‏‎‏‏‏‎‏‎‎‏‎‎‎‎‏‏‏‏‎‎‎‎‏‏‎‎‎‎‏‎Add ‎‏‎‎‏‏‎<xliff:g id="TILE_NAME">%1$s</xliff:g>‎‏‎‎‏‏‏‎ to position ‎‏‎‎‏‏‎<xliff:g id="POSITION">%2$d</xliff:g>‎‏‎‎‏‏‏‎‎‏‎‎‏‎"</string>
- <string name="accessibility_qs_edit_tile_move" msgid="4841770637244326837">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‎‎‎‎‏‏‎‎‏‏‎‎‎‏‎‏‏‎‏‏‎‎‏‎‏‏‏‏‎‏‏‏‏‎‎‏‎‎‏‏‎‏‏‏‎‏‎‏‏‎‎‎‏‏‏‎‏‏‎‏‎‏‎Move ‎‏‎‎‏‏‎<xliff:g id="TILE_NAME">%1$s</xliff:g>‎‏‎‎‏‏‏‎ to position ‎‏‎‎‏‏‎<xliff:g id="POSITION">%2$d</xliff:g>‎‏‎‎‏‏‏‎‎‏‎‎‏‎"</string>
+ <string name="accessibility_qs_edit_remove_tile_action" msgid="775511891457193480">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‎‎‏‎‏‎‏‏‎‎‎‎‏‏‎‎‏‎‏‎‏‏‏‏‏‏‎‏‎‎‏‏‎‏‏‎‎‏‎‏‏‎‎‏‎‏‏‎‏‏‎‏‏‎‎‎‎‎‏‎‎‎‎remove tile‎‏‎‎‏‎"</string>
+ <string name="accessibility_qs_edit_tile_add_action" msgid="5051211910345301833">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‎‎‎‏‏‎‎‎‎‏‏‎‎‏‏‎‎‎‎‎‏‎‎‏‏‏‏‎‎‎‎‏‎‏‎‎‎‏‎‏‎‏‏‎‏‎‏‎‎‏‏‏‏‏‎‏‎‎‏‎‎‏‎add tile to end‎‏‎‎‏‎"</string>
+ <string name="accessibility_qs_edit_tile_start_move" msgid="2009373939914517817">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‎‏‏‏‎‏‏‏‏‏‎‎‎‏‎‏‎‏‏‏‎‏‎‏‏‏‏‎‎‏‏‏‏‎‎‎‏‎‏‏‎‎‎‏‎‎‎‎‎‏‎‏‏‎‏‎‎‏‏‏‎‎‏‎Move tile‎‏‎‎‏‎"</string>
+ <string name="accessibility_qs_edit_tile_start_add" msgid="7560798153975555772">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‏‎‏‎‎‎‏‏‏‎‏‏‎‏‎‏‎‏‏‎‎‏‏‏‎‏‏‏‎‎‏‎‏‏‏‎‏‎‎‎‏‎‎‏‎‎‎‏‏‎‎‏‏‎‏‎‏‏‏‏‎‎‎Add tile‎‏‎‎‏‎"</string>
+ <string name="accessibility_qs_edit_tile_move_to_position" msgid="5198161544045930556">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‎‎‏‎‎‎‎‎‏‎‎‎‏‏‏‎‎‏‎‏‎‎‎‏‏‎‎‎‏‎‎‎‎‏‏‏‎‏‏‏‏‏‎‏‎‎‎‎‏‎‏‎‎‎‎‎‏‏‏‏‎‎‎Move to ‎‏‎‎‏‏‎<xliff:g id="POSITION">%1$d</xliff:g>‎‏‎‎‏‏‏‎‎‏‎‎‏‎"</string>
+ <string name="accessibility_qs_edit_tile_add_to_position" msgid="9029163095148274690">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‏‏‏‏‎‏‎‏‎‎‏‏‏‎‎‎‎‎‎‏‏‏‏‏‏‏‏‏‏‏‎‎‎‏‏‎‎‏‎‏‏‎‎‎‎‏‎‎‏‎‎‏‎‎‎‎‎‎‎‎‏‎‎Add to position ‎‏‎‎‏‏‎<xliff:g id="POSITION">%1$d</xliff:g>‎‏‎‎‏‏‏‎‎‏‎‎‏‎"</string>
+ <string name="accessibility_qs_edit_position" msgid="4509277359815711830">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‎‏‏‏‏‏‎‏‎‎‏‎‏‎‎‎‎‏‎‏‎‏‏‏‏‎‏‏‏‏‎‎‎‎‏‏‎‎‏‏‎‎‎‎‏‏‎‏‏‎‏‏‏‎‎‎‏‎‏‎‏‏‎‎Position ‎‏‎‎‏‏‎<xliff:g id="POSITION">%1$d</xliff:g>‎‏‎‎‏‏‏‎‎‏‎‎‏‎"</string>
<string name="accessibility_desc_quick_settings_edit" msgid="741658939453595297">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‎‎‏‎‏‎‎‏‎‎‏‎‏‎‏‏‏‎‎‏‏‎‏‏‏‎‎‎‎‎‎‎‏‏‏‏‏‎‏‎‎‏‏‏‏‏‏‏‎‏‏‏‏‎‏‎‏‎‎‎‎‏‎Quick settings editor.‎‏‎‎‏‎"</string>
<string name="accessibility_desc_notification_icon" msgid="7331265967584178674">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‏‎‎‏‎‏‏‎‏‏‏‏‎‏‏‏‏‎‎‎‏‏‏‎‎‎‏‏‏‎‎‎‎‎‎‏‏‏‏‏‎‎‎‎‏‏‏‏‏‏‎‎‎‏‏‏‏‏‎‎‏‎‎‎‏‎‎‏‏‎<xliff:g id="ID_1">%1$s</xliff:g>‎‏‎‎‏‏‏‎ notification: ‎‏‎‎‏‏‎<xliff:g id="ID_2">%2$s</xliff:g>‎‏‎‎‏‏‏‎‎‏‎‎‏‎"</string>
<string name="accessibility_quick_settings_settings" msgid="7098489591715844713">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‏‎‎‎‏‎‏‎‎‎‎‎‏‎‏‏‏‎‎‏‏‎‏‎‏‎‏‏‎‎‏‎‎‏‏‏‎‎‎‎‎‎‎‏‏‏‏‏‎‏‎‎‏‎‎‏‏‎‏‎‎‏‎Open settings.‎‏‎‎‏‎"</string>
@@ -1067,4 +1068,11 @@
<string name="controls_added_tooltip" msgid="4842812921719153085">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‎‎‎‎‏‏‎‎‏‏‎‏‎‏‎‎‏‎‎‎‎‎‏‎‏‏‎‎‎‏‏‎‏‎‏‎‎‎‏‎‏‏‏‏‎‏‏‏‏‎‎‎‎‏‏‎‏‏‏‏‎‏‎Hold Power button to see new controls‎‏‎‎‏‎"</string>
<string name="controls_menu_add" msgid="4447246119229920050">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‎‏‏‏‏‎‏‏‎‏‏‎‏‏‏‏‏‎‎‏‎‏‎‏‏‎‎‏‎‎‎‎‏‏‎‏‏‎‏‎‏‏‎‏‏‏‏‎‎‎‏‎‎‏‏‎‎‏‏‎‎‏‎‎Add controls‎‏‎‎‏‎"</string>
<string name="controls_menu_edit" msgid="890623986951347062">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‎‎‏‏‎‎‎‏‎‏‏‏‎‎‎‎‏‎‎‎‎‏‏‏‎‎‏‏‎‎‎‎‏‎‎‎‏‎‏‎‏‎‏‏‎‏‎‏‎‏‏‎‏‏‎‏‏‏‎‏‏‎‎Edit controls‎‏‎‎‏‎"</string>
+ <string name="media_output_dialog_add_output" msgid="5642703238877329518">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‎‎‏‏‏‎‎‏‎‎‏‏‏‎‏‏‏‎‏‎‎‎‏‎‏‏‎‎‏‏‎‎‎‎‏‏‏‎‎‎‏‏‏‎‏‎‏‎‏‏‏‎‎‎‎‏‏‎‏‏‏‎‎Add outputs‎‏‎‎‏‎"</string>
+ <string name="media_output_dialog_group" msgid="5571251347877452212">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‎‎‏‏‎‏‎‏‎‏‎‎‎‏‎‎‎‎‏‏‏‏‏‎‎‏‎‏‎‏‎‏‎‎‏‎‎‎‏‎‏‎‎‏‏‎‏‏‎‏‏‎‎‏‏‎‏‏‎‏‎‎‎Group‎‏‎‎‏‎"</string>
+ <string name="media_output_dialog_single_device" msgid="3102758980643351058">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‎‏‎‏‎‏‏‎‎‎‎‏‏‏‏‎‎‏‏‎‏‏‎‏‏‎‏‏‎‏‎‏‎‎‏‎‎‎‎‏‏‏‎‎‏‏‎‎‏‏‎‏‎‏‎‎‎‎‏‎‎‏‎‎1 device selected‎‏‎‎‏‎"</string>
+ <string name="media_output_dialog_multiple_devices" msgid="1093771040315422350">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‎‎‏‏‏‏‎‎‏‎‏‏‎‏‏‏‎‏‏‎‏‎‏‏‏‏‎‏‏‏‎‏‎‏‎‎‏‏‏‎‎‏‏‏‎‎‎‎‏‏‎‏‏‎‏‎‎‎‏‏‏‎‎‎‏‎‎‏‏‎<xliff:g id="COUNT">%1$d</xliff:g>‎‏‎‎‏‏‏‎ devices selected‎‏‎‎‏‎"</string>
+ <string name="media_output_dialog_disconnected" msgid="1834473104836986046">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‎‏‏‏‎‎‏‎‏‏‏‎‏‎‏‎‏‎‏‏‎‏‏‏‎‎‏‎‎‎‏‏‏‎‎‎‎‏‏‏‏‎‏‎‎‎‏‎‎‎‎‎‏‎‎‏‎‏‏‏‏‏‎‎‎‏‎‎‏‏‎<xliff:g id="DEVICE_NAME">%1$s</xliff:g>‎‏‎‎‏‏‏‎ (disconnected)‎‏‎‎‏‎"</string>
+ <string name="media_output_dialog_connect_failed" msgid="3225190634236259010">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‎‏‎‏‏‎‎‏‏‎‎‎‎‏‎‎‎‏‎‏‏‎‏‏‏‎‎‏‎‏‏‎‏‎‎‎‏‏‎‏‎‎‏‎‎‎‏‏‎‏‎‏‎‏‎‏‏‎‎‎‎‏‎‎Couldn\'t connect. Try again.‎‏‎‎‏‎"</string>
+ <string name="media_output_dialog_pairing_new" msgid="9099497976087485862">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‏‏‏‏‏‎‎‏‎‎‎‏‏‏‏‏‏‎‏‎‎‏‎‎‏‏‎‎‏‎‏‎‏‏‎‎‏‏‎‎‎‏‎‏‎‎‏‎‎‏‏‏‎‏‏‎‏‎‎‏‏‎‎Pair new device‎‏‎‎‏‎"</string>
</resources>
diff --git a/packages/SystemUI/res/values-es-rUS/strings.xml b/packages/SystemUI/res/values-es-rUS/strings.xml
index 1ef965d22748..dfd42c3ea54f 100644
--- a/packages/SystemUI/res/values-es-rUS/strings.xml
+++ b/packages/SystemUI/res/values-es-rUS/strings.xml
@@ -706,19 +706,19 @@
<string name="inline_silent_button_keep_alerting" msgid="6577845442184724992">"Seguir recibiendo alertas"</string>
<string name="inline_turn_off_notifications" msgid="8543989584403106071">"Desactivar notificaciones"</string>
<string name="inline_keep_showing_app" msgid="4393429060390649757">"¿Quieres seguir viendo las notificaciones de esta app?"</string>
- <string name="notification_silence_title" msgid="8608090968400832335">"Silencio"</string>
+ <string name="notification_silence_title" msgid="8608090968400832335">"Silenciada"</string>
<string name="notification_alert_title" msgid="3656229781017543655">"Predeterminada"</string>
<string name="notification_bubble_title" msgid="8330481035191903164">"Cuadro"</string>
<string name="notification_automatic_title" msgid="3745465364578762652">"Automática"</string>
<string name="notification_channel_summary_low" msgid="4860617986908931158">"Sin sonido ni vibración"</string>
- <string name="notification_conversation_summary_low" msgid="1734433426085468009">"No suena ni vibra, y aparece en una parte inferior de la sección de conversaciones"</string>
- <string name="notification_channel_summary_default" msgid="3282930979307248890">"Puede sonar o vibrar en función de la configuración del teléfono"</string>
+ <string name="notification_conversation_summary_low" msgid="1734433426085468009">"No suena ni vibra, y aparece en la parte inferior de la sección de conversaciones."</string>
+ <string name="notification_channel_summary_default" msgid="3282930979307248890">"Puede sonar o vibrar en función de la configuración del teléfono."</string>
<string name="notification_channel_summary_default_with_bubbles" msgid="1782419896613644568">"Puede sonar o vibrar en función de la configuración del teléfono. Conversaciones de la burbuja de <xliff:g id="APP_NAME">%1$s</xliff:g> de forma predeterminada."</string>
<string name="notification_channel_summary_bubble" msgid="7235935211580860537">"Retiene tu atención con un acceso directo flotante a este contenido."</string>
<string name="notification_channel_summary_automatic" msgid="5813109268050235275">"Dejar que el sistema determine si esta notificación debe emitir un sonido o una vibración"</string>
- <string name="notification_channel_summary_priority" msgid="7952654515769021553">"Aparece en la parte superior de la sección de conversaciones, en forma de burbuja flotante, y muestra la foto de perfil en la pantalla de bloqueo"</string>
+ <string name="notification_channel_summary_priority" msgid="7952654515769021553">"Aparece en la parte superior de la sección de conversaciones, en forma de burbuja flotante, y muestra la foto de perfil en la pantalla de bloqueo."</string>
<string name="notification_conversation_channel_settings" msgid="2409977688430606835">"Configuración"</string>
- <string name="notification_priority_title" msgid="2079708866333537093">"Prioridad"</string>
+ <string name="notification_priority_title" msgid="2079708866333537093">"Prioritaria"</string>
<string name="no_shortcut" msgid="8257177117568230126">"<xliff:g id="APP_NAME">%1$s</xliff:g> no admite funciones de conversación"</string>
<string name="bubble_overflow_empty_title" msgid="3120029421991510842">"No hay burbujas recientes"</string>
<string name="bubble_overflow_empty_subtitle" msgid="2030874469510497397">"Las burbujas recientes y las que se descartaron aparecerán aquí"</string>
@@ -879,12 +879,13 @@
</string-array>
<string name="tuner_low_priority" msgid="8412666814123009820">"Mostrar íconos de notificaciones con prioridad baja"</string>
<string name="other" msgid="429768510980739978">"Otros"</string>
- <string name="accessibility_qs_edit_tile_label" msgid="9079791448815232967">"Posición <xliff:g id="POSITION">%1$d</xliff:g>, <xliff:g id="TILE_NAME">%2$s</xliff:g>. Presiona dos veces para editarla."</string>
- <string name="accessibility_qs_edit_add_tile_label" msgid="8292218072049068613">"<xliff:g id="TILE_NAME">%1$s</xliff:g>. Presiona dos veces para agregarlo."</string>
- <string name="accessibility_qs_edit_move_tile" msgid="6027997446473163426">"Mover <xliff:g id="TILE_NAME">%1$s</xliff:g>"</string>
- <string name="accessibility_qs_edit_remove_tile" msgid="3406781901949899624">"Quitar <xliff:g id="TILE_NAME">%1$s</xliff:g>"</string>
- <string name="accessibility_qs_edit_tile_add" msgid="6289879620154587233">"Agregar <xliff:g id="TILE_NAME">%1$s</xliff:g> a la posición <xliff:g id="POSITION">%2$d</xliff:g>"</string>
- <string name="accessibility_qs_edit_tile_move" msgid="4841770637244326837">"Mover <xliff:g id="TILE_NAME">%1$s</xliff:g> a la posición <xliff:g id="POSITION">%2$d</xliff:g>"</string>
+ <string name="accessibility_qs_edit_remove_tile_action" msgid="775511891457193480">"Quitar tarjeta"</string>
+ <string name="accessibility_qs_edit_tile_add_action" msgid="5051211910345301833">"Agregar tarjeta al final"</string>
+ <string name="accessibility_qs_edit_tile_start_move" msgid="2009373939914517817">"Mover la tarjeta"</string>
+ <string name="accessibility_qs_edit_tile_start_add" msgid="7560798153975555772">"Agregar tarjeta"</string>
+ <string name="accessibility_qs_edit_tile_move_to_position" msgid="5198161544045930556">"Mover a <xliff:g id="POSITION">%1$d</xliff:g>"</string>
+ <string name="accessibility_qs_edit_tile_add_to_position" msgid="9029163095148274690">"Agregar a la posición <xliff:g id="POSITION">%1$d</xliff:g>"</string>
+ <string name="accessibility_qs_edit_position" msgid="4509277359815711830">"Posición <xliff:g id="POSITION">%1$d</xliff:g>"</string>
<string name="accessibility_desc_quick_settings_edit" msgid="741658939453595297">"Editor de Configuración rápida"</string>
<string name="accessibility_desc_notification_icon" msgid="7331265967584178674">"Notificación de <xliff:g id="ID_1">%1$s</xliff:g>: <xliff:g id="ID_2">%2$s</xliff:g>"</string>
<string name="accessibility_quick_settings_settings" msgid="7098489591715844713">"Abrir Configuración"</string>
@@ -1067,4 +1068,11 @@
<string name="controls_added_tooltip" msgid="4842812921719153085">"Mantén presionado el botón de encendido para ver los nuevos controles"</string>
<string name="controls_menu_add" msgid="4447246119229920050">"Agregar controles"</string>
<string name="controls_menu_edit" msgid="890623986951347062">"Editar controles"</string>
+ <string name="media_output_dialog_add_output" msgid="5642703238877329518">"Agregar salidas"</string>
+ <string name="media_output_dialog_group" msgid="5571251347877452212">"Grupo"</string>
+ <string name="media_output_dialog_single_device" msgid="3102758980643351058">"Se seleccionó 1 dispositivo"</string>
+ <string name="media_output_dialog_multiple_devices" msgid="1093771040315422350">"Se seleccionaron <xliff:g id="COUNT">%1$d</xliff:g> dispositivos"</string>
+ <string name="media_output_dialog_disconnected" msgid="1834473104836986046">"<xliff:g id="DEVICE_NAME">%1$s</xliff:g> (desconectado)"</string>
+ <string name="media_output_dialog_connect_failed" msgid="3225190634236259010">"No se pudo establecer la conexión. Vuelve a intentarlo."</string>
+ <string name="media_output_dialog_pairing_new" msgid="9099497976087485862">"Vincular dispositivo nuevo"</string>
</resources>
diff --git a/packages/SystemUI/res/values-es/strings.xml b/packages/SystemUI/res/values-es/strings.xml
index e483c0e1ac6d..20b50abd5b08 100644
--- a/packages/SystemUI/res/values-es/strings.xml
+++ b/packages/SystemUI/res/values-es/strings.xml
@@ -711,12 +711,12 @@
<string name="notification_bubble_title" msgid="8330481035191903164">"Burbuja"</string>
<string name="notification_automatic_title" msgid="3745465364578762652">"Automática"</string>
<string name="notification_channel_summary_low" msgid="4860617986908931158">"Sin sonido ni vibración"</string>
- <string name="notification_conversation_summary_low" msgid="1734433426085468009">"Sin sonido ni vibración y se muestra más abajo en la sección de conversaciones"</string>
+ <string name="notification_conversation_summary_low" msgid="1734433426085468009">"Sin sonido ni vibración, y se muestra más abajo en la sección de conversaciones"</string>
<string name="notification_channel_summary_default" msgid="3282930979307248890">"Es posible que suene o vibre según los ajustes del teléfono"</string>
<string name="notification_channel_summary_default_with_bubbles" msgid="1782419896613644568">"Es posible que suene o vibre según los ajustes del teléfono. Las conversaciones de <xliff:g id="APP_NAME">%1$s</xliff:g> aparecen como burbujas de forma predeterminada."</string>
<string name="notification_channel_summary_bubble" msgid="7235935211580860537">"Llama tu atención con un acceso directo flotante a este contenido."</string>
<string name="notification_channel_summary_automatic" msgid="5813109268050235275">"Haz que el sistema determine si con esta notificación el dispositivo debe sonar o vibrar"</string>
- <string name="notification_channel_summary_priority" msgid="7952654515769021553">"Se muestra arriba en la sección de conversaciones en forma de burbuja flotante, y la imagen de perfil aparece en la pantalla de bloqueo"</string>
+ <string name="notification_channel_summary_priority" msgid="7952654515769021553">"Se muestra arriba en la sección de conversaciones, como burbuja flotante, y la imagen de perfil aparece en la pantalla de bloqueo"</string>
<string name="notification_conversation_channel_settings" msgid="2409977688430606835">"Ajustes"</string>
<string name="notification_priority_title" msgid="2079708866333537093">"Prioridad"</string>
<string name="no_shortcut" msgid="8257177117568230126">"No se pueden usar funciones de conversación con <xliff:g id="APP_NAME">%1$s</xliff:g>"</string>
@@ -879,12 +879,13 @@
</string-array>
<string name="tuner_low_priority" msgid="8412666814123009820">"Mostrar iconos de notificaciones con prioridad baja"</string>
<string name="other" msgid="429768510980739978">"Otros"</string>
- <string name="accessibility_qs_edit_tile_label" msgid="9079791448815232967">"Posición <xliff:g id="POSITION">%1$d</xliff:g>, <xliff:g id="TILE_NAME">%2$s</xliff:g>. Toca dos veces para cambiarla."</string>
- <string name="accessibility_qs_edit_add_tile_label" msgid="8292218072049068613">"<xliff:g id="TILE_NAME">%1$s</xliff:g>. Toca dos veces para añadirlo."</string>
- <string name="accessibility_qs_edit_move_tile" msgid="6027997446473163426">"Mover <xliff:g id="TILE_NAME">%1$s</xliff:g>"</string>
- <string name="accessibility_qs_edit_remove_tile" msgid="3406781901949899624">"Quitar <xliff:g id="TILE_NAME">%1$s</xliff:g>"</string>
- <string name="accessibility_qs_edit_tile_add" msgid="6289879620154587233">"Añadir <xliff:g id="TILE_NAME">%1$s</xliff:g> a la posición <xliff:g id="POSITION">%2$d</xliff:g>"</string>
- <string name="accessibility_qs_edit_tile_move" msgid="4841770637244326837">"Mover <xliff:g id="TILE_NAME">%1$s</xliff:g> a la posición <xliff:g id="POSITION">%2$d</xliff:g>"</string>
+ <string name="accessibility_qs_edit_remove_tile_action" msgid="775511891457193480">"quitar icono"</string>
+ <string name="accessibility_qs_edit_tile_add_action" msgid="5051211910345301833">"añadir icono al final"</string>
+ <string name="accessibility_qs_edit_tile_start_move" msgid="2009373939914517817">"Mover icono"</string>
+ <string name="accessibility_qs_edit_tile_start_add" msgid="7560798153975555772">"Añadir icono"</string>
+ <string name="accessibility_qs_edit_tile_move_to_position" msgid="5198161544045930556">"Mover a <xliff:g id="POSITION">%1$d</xliff:g>"</string>
+ <string name="accessibility_qs_edit_tile_add_to_position" msgid="9029163095148274690">"Añadir a la posición <xliff:g id="POSITION">%1$d</xliff:g>"</string>
+ <string name="accessibility_qs_edit_position" msgid="4509277359815711830">"Posición <xliff:g id="POSITION">%1$d</xliff:g>"</string>
<string name="accessibility_desc_quick_settings_edit" msgid="741658939453595297">"Editor de ajustes rápidos."</string>
<string name="accessibility_desc_notification_icon" msgid="7331265967584178674">"Notificación de <xliff:g id="ID_1">%1$s</xliff:g>: <xliff:g id="ID_2">%2$s</xliff:g>"</string>
<string name="accessibility_quick_settings_settings" msgid="7098489591715844713">"Abrir ajustes."</string>
@@ -1067,4 +1068,11 @@
<string name="controls_added_tooltip" msgid="4842812921719153085">"Mantén pulsado el botón de encendido para ver los controles nuevos"</string>
<string name="controls_menu_add" msgid="4447246119229920050">"Añadir controles"</string>
<string name="controls_menu_edit" msgid="890623986951347062">"Editar controles"</string>
+ <string name="media_output_dialog_add_output" msgid="5642703238877329518">"Añadir dispositivos de salida"</string>
+ <string name="media_output_dialog_group" msgid="5571251347877452212">"Grupo"</string>
+ <string name="media_output_dialog_single_device" msgid="3102758980643351058">"1 dispositivo seleccionado"</string>
+ <string name="media_output_dialog_multiple_devices" msgid="1093771040315422350">"<xliff:g id="COUNT">%1$d</xliff:g> dispositivos seleccionados"</string>
+ <string name="media_output_dialog_disconnected" msgid="1834473104836986046">"<xliff:g id="DEVICE_NAME">%1$s</xliff:g> (desconectado)"</string>
+ <string name="media_output_dialog_connect_failed" msgid="3225190634236259010">"No se ha podido conectar. Inténtalo de nuevo."</string>
+ <string name="media_output_dialog_pairing_new" msgid="9099497976087485862">"Vincular nuevo dispositivo"</string>
</resources>
diff --git a/packages/SystemUI/res/values-et/strings.xml b/packages/SystemUI/res/values-et/strings.xml
index fea0df62e625..ced06044f331 100644
--- a/packages/SystemUI/res/values-et/strings.xml
+++ b/packages/SystemUI/res/values-et/strings.xml
@@ -879,12 +879,13 @@
</string-array>
<string name="tuner_low_priority" msgid="8412666814123009820">"Kuva madala prioriteediga märguande ikoonid"</string>
<string name="other" msgid="429768510980739978">"Muu"</string>
- <string name="accessibility_qs_edit_tile_label" msgid="9079791448815232967">"Asend <xliff:g id="POSITION">%1$d</xliff:g>, paan <xliff:g id="TILE_NAME">%2$s</xliff:g>. Topeltpuudutage muutmiseks."</string>
- <string name="accessibility_qs_edit_add_tile_label" msgid="8292218072049068613">"<xliff:g id="TILE_NAME">%1$s</xliff:g>. Topeltpuudutage lisamiseks."</string>
- <string name="accessibility_qs_edit_move_tile" msgid="6027997446473163426">"Paani <xliff:g id="TILE_NAME">%1$s</xliff:g> teisaldamine"</string>
- <string name="accessibility_qs_edit_remove_tile" msgid="3406781901949899624">"Paani <xliff:g id="TILE_NAME">%1$s</xliff:g> eemaldamine"</string>
- <string name="accessibility_qs_edit_tile_add" msgid="6289879620154587233">"Lisage <xliff:g id="TILE_NAME">%1$s</xliff:g> asendisse <xliff:g id="POSITION">%2$d</xliff:g>"</string>
- <string name="accessibility_qs_edit_tile_move" msgid="4841770637244326837">"Teisaldage <xliff:g id="TILE_NAME">%1$s</xliff:g> asendisse <xliff:g id="POSITION">%2$d</xliff:g>"</string>
+ <string name="accessibility_qs_edit_remove_tile_action" msgid="775511891457193480">"paani eemaldamiseks"</string>
+ <string name="accessibility_qs_edit_tile_add_action" msgid="5051211910345301833">"paani lõppu lisamiseks"</string>
+ <string name="accessibility_qs_edit_tile_start_move" msgid="2009373939914517817">"Teisalda paan"</string>
+ <string name="accessibility_qs_edit_tile_start_add" msgid="7560798153975555772">"Lisa paan"</string>
+ <string name="accessibility_qs_edit_tile_move_to_position" msgid="5198161544045930556">"Teisaldamine asendisse <xliff:g id="POSITION">%1$d</xliff:g>"</string>
+ <string name="accessibility_qs_edit_tile_add_to_position" msgid="9029163095148274690">"Lisamine asendisse <xliff:g id="POSITION">%1$d</xliff:g>"</string>
+ <string name="accessibility_qs_edit_position" msgid="4509277359815711830">"Asend <xliff:g id="POSITION">%1$d</xliff:g>"</string>
<string name="accessibility_desc_quick_settings_edit" msgid="741658939453595297">"Kiirseadete redigeerija."</string>
<string name="accessibility_desc_notification_icon" msgid="7331265967584178674">"Teenuse <xliff:g id="ID_1">%1$s</xliff:g> märguanne: <xliff:g id="ID_2">%2$s</xliff:g>"</string>
<string name="accessibility_quick_settings_settings" msgid="7098489591715844713">"Ava seaded."</string>
@@ -1067,4 +1068,11 @@
<string name="controls_added_tooltip" msgid="4842812921719153085">"Uute juhtelementide vaatamiseks hoidke all toitenuppu"</string>
<string name="controls_menu_add" msgid="4447246119229920050">"Lisa juhtelemente"</string>
<string name="controls_menu_edit" msgid="890623986951347062">"Muuda juhtelemente"</string>
+ <string name="media_output_dialog_add_output" msgid="5642703238877329518">"Väljundite lisamine"</string>
+ <string name="media_output_dialog_group" msgid="5571251347877452212">"Grupp"</string>
+ <string name="media_output_dialog_single_device" msgid="3102758980643351058">"1 seade on valitud"</string>
+ <string name="media_output_dialog_multiple_devices" msgid="1093771040315422350">"<xliff:g id="COUNT">%1$d</xliff:g> seadet on valitud"</string>
+ <string name="media_output_dialog_disconnected" msgid="1834473104836986046">"<xliff:g id="DEVICE_NAME">%1$s</xliff:g> (pole ühendatud)"</string>
+ <string name="media_output_dialog_connect_failed" msgid="3225190634236259010">"Ühenduse loomine ebaõnnestus. Proovige uuesti."</string>
+ <string name="media_output_dialog_pairing_new" msgid="9099497976087485862">"Uue seadme sidumine"</string>
</resources>
diff --git a/packages/SystemUI/res/values-eu/strings.xml b/packages/SystemUI/res/values-eu/strings.xml
index 297472a66325..c1ab20c9ebb6 100644
--- a/packages/SystemUI/res/values-eu/strings.xml
+++ b/packages/SystemUI/res/values-eu/strings.xml
@@ -879,12 +879,13 @@
</string-array>
<string name="tuner_low_priority" msgid="8412666814123009820">"Erakutsi lehentasun txikiko jakinarazpenen ikonoak"</string>
<string name="other" msgid="429768510980739978">"Beste bat"</string>
- <string name="accessibility_qs_edit_tile_label" msgid="9079791448815232967">"<xliff:g id="POSITION">%1$d</xliff:g>. posizioa, <xliff:g id="TILE_NAME">%2$s</xliff:g>. Editatzeko, sakatu birritan."</string>
- <string name="accessibility_qs_edit_add_tile_label" msgid="8292218072049068613">"<xliff:g id="TILE_NAME">%1$s</xliff:g>. Gehitzeko, sakatu birritan."</string>
- <string name="accessibility_qs_edit_move_tile" msgid="6027997446473163426">"Mugitu <xliff:g id="TILE_NAME">%1$s</xliff:g>"</string>
- <string name="accessibility_qs_edit_remove_tile" msgid="3406781901949899624">"Kendu <xliff:g id="TILE_NAME">%1$s</xliff:g>"</string>
- <string name="accessibility_qs_edit_tile_add" msgid="6289879620154587233">"Gehitu <xliff:g id="TILE_NAME">%1$s</xliff:g> <xliff:g id="POSITION">%2$d</xliff:g>garren postuan"</string>
- <string name="accessibility_qs_edit_tile_move" msgid="4841770637244326837">"Eraman <xliff:g id="TILE_NAME">%1$s</xliff:g> <xliff:g id="POSITION">%2$d</xliff:g>garren postura"</string>
+ <string name="accessibility_qs_edit_remove_tile_action" msgid="775511891457193480">"kendu lauza"</string>
+ <string name="accessibility_qs_edit_tile_add_action" msgid="5051211910345301833">"gehitu lauza amaieran"</string>
+ <string name="accessibility_qs_edit_tile_start_move" msgid="2009373939914517817">"Aldatu tokiz lauza"</string>
+ <string name="accessibility_qs_edit_tile_start_add" msgid="7560798153975555772">"Gehitu lauza"</string>
+ <string name="accessibility_qs_edit_tile_move_to_position" msgid="5198161544045930556">"Eraman <xliff:g id="POSITION">%1$d</xliff:g>garren kokapenera"</string>
+ <string name="accessibility_qs_edit_tile_add_to_position" msgid="9029163095148274690">"Gehitu <xliff:g id="POSITION">%1$d</xliff:g>garren kokapenean"</string>
+ <string name="accessibility_qs_edit_position" msgid="4509277359815711830">"Kokapena: <xliff:g id="POSITION">%1$d</xliff:g>"</string>
<string name="accessibility_desc_quick_settings_edit" msgid="741658939453595297">"Ezarpen bizkorren editorea."</string>
<string name="accessibility_desc_notification_icon" msgid="7331265967584178674">"<xliff:g id="ID_1">%1$s</xliff:g> zerbitzuaren jakinarazpena: <xliff:g id="ID_2">%2$s</xliff:g>"</string>
<string name="accessibility_quick_settings_settings" msgid="7098489591715844713">"Ireki ezarpenak."</string>
@@ -1067,4 +1068,11 @@
<string name="controls_added_tooltip" msgid="4842812921719153085">"Eduki sakatuta etengailua kontrolatzeko aukera berriak ikusteko"</string>
<string name="controls_menu_add" msgid="4447246119229920050">"Gehitu aukerak"</string>
<string name="controls_menu_edit" msgid="890623986951347062">"Editatu aukerak"</string>
+ <string name="media_output_dialog_add_output" msgid="5642703238877329518">"Gehitu irteerak"</string>
+ <string name="media_output_dialog_group" msgid="5571251347877452212">"Taldea"</string>
+ <string name="media_output_dialog_single_device" msgid="3102758980643351058">"1 gailu hautatu da"</string>
+ <string name="media_output_dialog_multiple_devices" msgid="1093771040315422350">"<xliff:g id="COUNT">%1$d</xliff:g> gailu hautatu dira"</string>
+ <string name="media_output_dialog_disconnected" msgid="1834473104836986046">"<xliff:g id="DEVICE_NAME">%1$s</xliff:g> (deskonektatuta)"</string>
+ <string name="media_output_dialog_connect_failed" msgid="3225190634236259010">"Ezin izan da konektatu. Saiatu berriro."</string>
+ <string name="media_output_dialog_pairing_new" msgid="9099497976087485862">"Parekatu beste gailu batekin"</string>
</resources>
diff --git a/packages/SystemUI/res/values-fa/strings.xml b/packages/SystemUI/res/values-fa/strings.xml
index 2c32225d5ec2..f6cf179d7d00 100644
--- a/packages/SystemUI/res/values-fa/strings.xml
+++ b/packages/SystemUI/res/values-fa/strings.xml
@@ -879,12 +879,13 @@
</string-array>
<string name="tuner_low_priority" msgid="8412666814123009820">"نمایش نمادهای اعلان کم‌اهمیت"</string>
<string name="other" msgid="429768510980739978">"موارد دیگر"</string>
- <string name="accessibility_qs_edit_tile_label" msgid="9079791448815232967">"موقعیت <xliff:g id="POSITION">%1$d</xliff:g>، <xliff:g id="TILE_NAME">%2$s</xliff:g>. برای ویرایش دو ضربه سریع بزنید."</string>
- <string name="accessibility_qs_edit_add_tile_label" msgid="8292218072049068613">"<xliff:g id="TILE_NAME">%1$s</xliff:g>. برای افزودن دو ضربه سریع بزنید."</string>
- <string name="accessibility_qs_edit_move_tile" msgid="6027997446473163426">"انتقال <xliff:g id="TILE_NAME">%1$s</xliff:g>"</string>
- <string name="accessibility_qs_edit_remove_tile" msgid="3406781901949899624">"حذف <xliff:g id="TILE_NAME">%1$s</xliff:g>"</string>
- <string name="accessibility_qs_edit_tile_add" msgid="6289879620154587233">"افزودن <xliff:g id="TILE_NAME">%1$s</xliff:g> به موقعیت <xliff:g id="POSITION">%2$d</xliff:g>"</string>
- <string name="accessibility_qs_edit_tile_move" msgid="4841770637244326837">"انتقال <xliff:g id="TILE_NAME">%1$s</xliff:g> به موقعیت <xliff:g id="POSITION">%2$d</xliff:g>"</string>
+ <string name="accessibility_qs_edit_remove_tile_action" msgid="775511891457193480">"برداشتن کاشی"</string>
+ <string name="accessibility_qs_edit_tile_add_action" msgid="5051211910345301833">"افزودن کاشی به انتها"</string>
+ <string name="accessibility_qs_edit_tile_start_move" msgid="2009373939914517817">"انتقال کاشی"</string>
+ <string name="accessibility_qs_edit_tile_start_add" msgid="7560798153975555772">"افزودن کاشی"</string>
+ <string name="accessibility_qs_edit_tile_move_to_position" msgid="5198161544045930556">"انتقال به <xliff:g id="POSITION">%1$d</xliff:g>"</string>
+ <string name="accessibility_qs_edit_tile_add_to_position" msgid="9029163095148274690">"افزودن به موقعیت <xliff:g id="POSITION">%1$d</xliff:g>"</string>
+ <string name="accessibility_qs_edit_position" msgid="4509277359815711830">"موقعیت <xliff:g id="POSITION">%1$d</xliff:g>"</string>
<string name="accessibility_desc_quick_settings_edit" msgid="741658939453595297">"ویرایشگر تنظیمات سریع."</string>
<string name="accessibility_desc_notification_icon" msgid="7331265967584178674">"اعلان <xliff:g id="ID_1">%1$s</xliff:g>: <xliff:g id="ID_2">%2$s</xliff:g>"</string>
<string name="accessibility_quick_settings_settings" msgid="7098489591715844713">"باز کردن تنظیمات."</string>
@@ -959,7 +960,7 @@
<string name="slice_permission_text_2" msgid="6758906940360746983">"- می‌تواند در <xliff:g id="APP">%1$s</xliff:g> اقدام انجام دهد"</string>
<string name="slice_permission_checkbox" msgid="4242888137592298523">"به <xliff:g id="APP">%1$s</xliff:g> اجازه داده شود تکه‌هایی از برنامه‌ها نشان دهد"</string>
<string name="slice_permission_allow" msgid="6340449521277951123">"مجاز"</string>
- <string name="slice_permission_deny" msgid="6870256451658176895">"رد کردن"</string>
+ <string name="slice_permission_deny" msgid="6870256451658176895">"مجاز نبودن"</string>
<string name="auto_saver_title" msgid="6873691178754086596">"برای زمان‌بندی «بهینه‌سازی باتری» ضربه بزنید"</string>
<string name="auto_saver_text" msgid="3214960308353838764">"وقتی باتری روبه‌اتمام است، بهینه‌سازی باتری را روشن کنید"</string>
<string name="no_auto_saver_action" msgid="7467924389609773835">"نه متشکرم"</string>
@@ -1067,4 +1068,11 @@
<string name="controls_added_tooltip" msgid="4842812921719153085">"برای دیدن کنترل‌های جدید، دکمه روشن/خاموش را پایین نگه دارید"</string>
<string name="controls_menu_add" msgid="4447246119229920050">"افزودن کنترل‌ها"</string>
<string name="controls_menu_edit" msgid="890623986951347062">"ویرایش کنترل‌ها"</string>
+ <string name="media_output_dialog_add_output" msgid="5642703238877329518">"افزودن خروجی"</string>
+ <string name="media_output_dialog_group" msgid="5571251347877452212">"گروه"</string>
+ <string name="media_output_dialog_single_device" msgid="3102758980643351058">"۱ دستگاه انتخاب شد"</string>
+ <string name="media_output_dialog_multiple_devices" msgid="1093771040315422350">"<xliff:g id="COUNT">%1$d</xliff:g> دستگاه انتخاب شد"</string>
+ <string name="media_output_dialog_disconnected" msgid="1834473104836986046">"<xliff:g id="DEVICE_NAME">%1$s</xliff:g> (اتصال قطع شد)"</string>
+ <string name="media_output_dialog_connect_failed" msgid="3225190634236259010">"متصل نشد. دوباره امتحان کنید."</string>
+ <string name="media_output_dialog_pairing_new" msgid="9099497976087485862">"مرتبط کردن دستگاه جدید"</string>
</resources>
diff --git a/packages/SystemUI/res/values-fi/strings.xml b/packages/SystemUI/res/values-fi/strings.xml
index b6c4ba0c2578..3d9a6dbee1f9 100644
--- a/packages/SystemUI/res/values-fi/strings.xml
+++ b/packages/SystemUI/res/values-fi/strings.xml
@@ -379,7 +379,7 @@
<string name="quick_settings_wifi_on_label" msgid="2489928193654318511">"Wi-Fi on käytössä"</string>
<string name="quick_settings_wifi_detail_empty_text" msgid="483130889414601732">"Ei Wi-Fi-verkkoja käytettävissä"</string>
<string name="quick_settings_wifi_secondary_label_transient" msgid="7501659015509357887">"Otetaan käyttöön…"</string>
- <string name="quick_settings_cast_title" msgid="2279220930629235211">"Näytön suoratoisto"</string>
+ <string name="quick_settings_cast_title" msgid="2279220930629235211">"Näytön striimaus"</string>
<string name="quick_settings_casting" msgid="1435880708719268055">"Lähetetään"</string>
<string name="quick_settings_cast_device_default_name" msgid="6988469571141331700">"Nimetön laite"</string>
<string name="quick_settings_cast_device_default_description" msgid="2580520859212250265">"Valmis lähetystä varten"</string>
@@ -879,12 +879,13 @@
</string-array>
<string name="tuner_low_priority" msgid="8412666814123009820">"Näytä vähemmän tärkeät ilmoituskuvakkeet"</string>
<string name="other" msgid="429768510980739978">"Muu"</string>
- <string name="accessibility_qs_edit_tile_label" msgid="9079791448815232967">"Paikka <xliff:g id="POSITION">%1$d</xliff:g>, <xliff:g id="TILE_NAME">%2$s</xliff:g>. Muokkaa kaksoisnapauttamalla."</string>
- <string name="accessibility_qs_edit_add_tile_label" msgid="8292218072049068613">"<xliff:g id="TILE_NAME">%1$s</xliff:g>. Lisää kaksoisnapauttamalla."</string>
- <string name="accessibility_qs_edit_move_tile" msgid="6027997446473163426">"Siirrä <xliff:g id="TILE_NAME">%1$s</xliff:g>."</string>
- <string name="accessibility_qs_edit_remove_tile" msgid="3406781901949899624">"Poista <xliff:g id="TILE_NAME">%1$s</xliff:g>."</string>
- <string name="accessibility_qs_edit_tile_add" msgid="6289879620154587233">"Siirrä <xliff:g id="TILE_NAME">%1$s</xliff:g> kohtaan <xliff:g id="POSITION">%2$d</xliff:g>"</string>
- <string name="accessibility_qs_edit_tile_move" msgid="4841770637244326837">"Siirrä <xliff:g id="TILE_NAME">%1$s</xliff:g> kohtaan <xliff:g id="POSITION">%2$d</xliff:g>"</string>
+ <string name="accessibility_qs_edit_remove_tile_action" msgid="775511891457193480">"poista kiekko"</string>
+ <string name="accessibility_qs_edit_tile_add_action" msgid="5051211910345301833">"lisää kiekko loppuun"</string>
+ <string name="accessibility_qs_edit_tile_start_move" msgid="2009373939914517817">"Siirrä kiekkoa"</string>
+ <string name="accessibility_qs_edit_tile_start_add" msgid="7560798153975555772">"Lisää kiekko"</string>
+ <string name="accessibility_qs_edit_tile_move_to_position" msgid="5198161544045930556">"Siirrä paikkaan <xliff:g id="POSITION">%1$d</xliff:g>"</string>
+ <string name="accessibility_qs_edit_tile_add_to_position" msgid="9029163095148274690">"Lisää paikkaan <xliff:g id="POSITION">%1$d</xliff:g>"</string>
+ <string name="accessibility_qs_edit_position" msgid="4509277359815711830">"Paikka <xliff:g id="POSITION">%1$d</xliff:g>"</string>
<string name="accessibility_desc_quick_settings_edit" msgid="741658939453595297">"Pika-asetusten muokkausnäkymä"</string>
<string name="accessibility_desc_notification_icon" msgid="7331265967584178674">"Ilmoitus kohteesta <xliff:g id="ID_1">%1$s</xliff:g>: <xliff:g id="ID_2">%2$s</xliff:g>"</string>
<string name="accessibility_quick_settings_settings" msgid="7098489591715844713">"Avaa asetukset."</string>
@@ -1067,4 +1068,11 @@
<string name="controls_added_tooltip" msgid="4842812921719153085">"Paina virtapainiketta pitkään nähdäksesi uudet säätimet"</string>
<string name="controls_menu_add" msgid="4447246119229920050">"Lisää säätimiä"</string>
<string name="controls_menu_edit" msgid="890623986951347062">"Muokkaa säätimiä"</string>
+ <string name="media_output_dialog_add_output" msgid="5642703238877329518">"Lisää toistotapoja"</string>
+ <string name="media_output_dialog_group" msgid="5571251347877452212">"Ryhmä"</string>
+ <string name="media_output_dialog_single_device" msgid="3102758980643351058">"1 laite valittu"</string>
+ <string name="media_output_dialog_multiple_devices" msgid="1093771040315422350">"<xliff:g id="COUNT">%1$d</xliff:g> laitetta valittu"</string>
+ <string name="media_output_dialog_disconnected" msgid="1834473104836986046">"<xliff:g id="DEVICE_NAME">%1$s</xliff:g> (yhteys katkaistu)"</string>
+ <string name="media_output_dialog_connect_failed" msgid="3225190634236259010">"Ei yhteyttä. Yritä uudelleen."</string>
+ <string name="media_output_dialog_pairing_new" msgid="9099497976087485862">"Muodosta uusi laitepari"</string>
</resources>
diff --git a/packages/SystemUI/res/values-fr-rCA/strings.xml b/packages/SystemUI/res/values-fr-rCA/strings.xml
index 34391efe460d..ea6de66db50f 100644
--- a/packages/SystemUI/res/values-fr-rCA/strings.xml
+++ b/packages/SystemUI/res/values-fr-rCA/strings.xml
@@ -879,12 +879,20 @@
</string-array>
<string name="tuner_low_priority" msgid="8412666814123009820">"Afficher les icônes de notification de faible priorité"</string>
<string name="other" msgid="429768510980739978">"Autre"</string>
- <string name="accessibility_qs_edit_tile_label" msgid="9079791448815232967">"Position <xliff:g id="POSITION">%1$d</xliff:g>, <xliff:g id="TILE_NAME">%2$s</xliff:g>. Touchez deux fois pour modifier."</string>
- <string name="accessibility_qs_edit_add_tile_label" msgid="8292218072049068613">"<xliff:g id="TILE_NAME">%1$s</xliff:g>. Touchez deux fois pour ajouter."</string>
- <string name="accessibility_qs_edit_move_tile" msgid="6027997446473163426">"Déplacer <xliff:g id="TILE_NAME">%1$s</xliff:g>"</string>
- <string name="accessibility_qs_edit_remove_tile" msgid="3406781901949899624">"Supprimer <xliff:g id="TILE_NAME">%1$s</xliff:g>"</string>
- <string name="accessibility_qs_edit_tile_add" msgid="6289879620154587233">"Ajouter <xliff:g id="TILE_NAME">%1$s</xliff:g> à la position <xliff:g id="POSITION">%2$d</xliff:g>"</string>
- <string name="accessibility_qs_edit_tile_move" msgid="4841770637244326837">"Déplacer <xliff:g id="TILE_NAME">%1$s</xliff:g> à la position <xliff:g id="POSITION">%2$d</xliff:g>"</string>
+ <!-- no translation found for accessibility_qs_edit_remove_tile_action (775511891457193480) -->
+ <skip />
+ <!-- no translation found for accessibility_qs_edit_tile_add_action (5051211910345301833) -->
+ <skip />
+ <!-- no translation found for accessibility_qs_edit_tile_start_move (2009373939914517817) -->
+ <skip />
+ <!-- no translation found for accessibility_qs_edit_tile_start_add (7560798153975555772) -->
+ <skip />
+ <!-- no translation found for accessibility_qs_edit_tile_move_to_position (5198161544045930556) -->
+ <skip />
+ <!-- no translation found for accessibility_qs_edit_tile_add_to_position (9029163095148274690) -->
+ <skip />
+ <!-- no translation found for accessibility_qs_edit_position (4509277359815711830) -->
+ <skip />
<string name="accessibility_desc_quick_settings_edit" msgid="741658939453595297">"Éditeur de paramètres rapides."</string>
<string name="accessibility_desc_notification_icon" msgid="7331265967584178674">"Notification <xliff:g id="ID_1">%1$s</xliff:g> : <xliff:g id="ID_2">%2$s</xliff:g>"</string>
<string name="accessibility_quick_settings_settings" msgid="7098489591715844713">"Ouvrir les paramètres."</string>
@@ -1067,4 +1075,11 @@
<string name="controls_added_tooltip" msgid="4842812921719153085">"Maintenez enfoncé l\'interrupteur pour afficher les nouvelles commandes"</string>
<string name="controls_menu_add" msgid="4447246119229920050">"Ajouter des commandes"</string>
<string name="controls_menu_edit" msgid="890623986951347062">"Modifier des commandes"</string>
+ <string name="media_output_dialog_add_output" msgid="5642703238877329518">"Ajouter des sorties"</string>
+ <string name="media_output_dialog_group" msgid="5571251347877452212">"Groupe"</string>
+ <string name="media_output_dialog_single_device" msgid="3102758980643351058">"Un appareil sélectionné"</string>
+ <string name="media_output_dialog_multiple_devices" msgid="1093771040315422350">"<xliff:g id="COUNT">%1$d</xliff:g> appareil sélectionné"</string>
+ <string name="media_output_dialog_disconnected" msgid="1834473104836986046">"<xliff:g id="DEVICE_NAME">%1$s</xliff:g> (déconnecté)"</string>
+ <string name="media_output_dialog_connect_failed" msgid="3225190634236259010">"Impossible de se connecter. Réessayez."</string>
+ <string name="media_output_dialog_pairing_new" msgid="9099497976087485862">"Associer un autre appareil"</string>
</resources>
diff --git a/packages/SystemUI/res/values-fr/strings.xml b/packages/SystemUI/res/values-fr/strings.xml
index ebd9b9b9f40c..fc2691e3570d 100644
--- a/packages/SystemUI/res/values-fr/strings.xml
+++ b/packages/SystemUI/res/values-fr/strings.xml
@@ -879,12 +879,20 @@
</string-array>
<string name="tuner_low_priority" msgid="8412666814123009820">"Afficher les icônes de notification à faible priorité"</string>
<string name="other" msgid="429768510980739978">"Autre"</string>
- <string name="accessibility_qs_edit_tile_label" msgid="9079791448815232967">"Position <xliff:g id="POSITION">%1$d</xliff:g>, \"<xliff:g id="TILE_NAME">%2$s</xliff:g>\". Appuyer deux fois pour modifier."</string>
- <string name="accessibility_qs_edit_add_tile_label" msgid="8292218072049068613">"<xliff:g id="TILE_NAME">%1$s</xliff:g>. Appuyer deux fois pour ajouter."</string>
- <string name="accessibility_qs_edit_move_tile" msgid="6027997446473163426">"Déplacer \"<xliff:g id="TILE_NAME">%1$s</xliff:g>\""</string>
- <string name="accessibility_qs_edit_remove_tile" msgid="3406781901949899624">"Supprimer \"<xliff:g id="TILE_NAME">%1$s</xliff:g>\""</string>
- <string name="accessibility_qs_edit_tile_add" msgid="6289879620154587233">"Ajouter l\'élément \"<xliff:g id="TILE_NAME">%1$s</xliff:g>\" à la position <xliff:g id="POSITION">%2$d</xliff:g>"</string>
- <string name="accessibility_qs_edit_tile_move" msgid="4841770637244326837">"Déplacer l\'élément \"<xliff:g id="TILE_NAME">%1$s</xliff:g>\" à la position <xliff:g id="POSITION">%2$d</xliff:g>"</string>
+ <!-- no translation found for accessibility_qs_edit_remove_tile_action (775511891457193480) -->
+ <skip />
+ <!-- no translation found for accessibility_qs_edit_tile_add_action (5051211910345301833) -->
+ <skip />
+ <!-- no translation found for accessibility_qs_edit_tile_start_move (2009373939914517817) -->
+ <skip />
+ <!-- no translation found for accessibility_qs_edit_tile_start_add (7560798153975555772) -->
+ <skip />
+ <!-- no translation found for accessibility_qs_edit_tile_move_to_position (5198161544045930556) -->
+ <skip />
+ <!-- no translation found for accessibility_qs_edit_tile_add_to_position (9029163095148274690) -->
+ <skip />
+ <!-- no translation found for accessibility_qs_edit_position (4509277359815711830) -->
+ <skip />
<string name="accessibility_desc_quick_settings_edit" msgid="741658939453595297">"Éditeur de configuration rapide."</string>
<string name="accessibility_desc_notification_icon" msgid="7331265967584178674">"Notification <xliff:g id="ID_1">%1$s</xliff:g> : <xliff:g id="ID_2">%2$s</xliff:g>"</string>
<string name="accessibility_quick_settings_settings" msgid="7098489591715844713">"Ouvrir les paramètres."</string>
@@ -1067,4 +1075,11 @@
<string name="controls_added_tooltip" msgid="4842812921719153085">"Appuyez de manière prolongée sur le bouton Marche/Arrêt pour afficher les nouvelles commandes"</string>
<string name="controls_menu_add" msgid="4447246119229920050">"Ajouter des commandes"</string>
<string name="controls_menu_edit" msgid="890623986951347062">"Modifier des commandes"</string>
+ <string name="media_output_dialog_add_output" msgid="5642703238877329518">"Ajouter des sorties"</string>
+ <string name="media_output_dialog_group" msgid="5571251347877452212">"Groupe"</string>
+ <string name="media_output_dialog_single_device" msgid="3102758980643351058">"1 appareil sélectionné"</string>
+ <string name="media_output_dialog_multiple_devices" msgid="1093771040315422350">"<xliff:g id="COUNT">%1$d</xliff:g> appareils sélectionnés"</string>
+ <string name="media_output_dialog_disconnected" msgid="1834473104836986046">"<xliff:g id="DEVICE_NAME">%1$s</xliff:g> (déconnecté)"</string>
+ <string name="media_output_dialog_connect_failed" msgid="3225190634236259010">"Impossible de se connecter. Réessayez."</string>
+ <string name="media_output_dialog_pairing_new" msgid="9099497976087485862">"Associer un nouvel appareil"</string>
</resources>
diff --git a/packages/SystemUI/res/values-gl/strings.xml b/packages/SystemUI/res/values-gl/strings.xml
index 79795927c1af..9075a69b2ed8 100644
--- a/packages/SystemUI/res/values-gl/strings.xml
+++ b/packages/SystemUI/res/values-gl/strings.xml
@@ -711,12 +711,12 @@
<string name="notification_bubble_title" msgid="8330481035191903164">"Burbulla"</string>
<string name="notification_automatic_title" msgid="3745465364578762652">"Automática"</string>
<string name="notification_channel_summary_low" msgid="4860617986908931158">"Sen son nin vibración"</string>
- <string name="notification_conversation_summary_low" msgid="1734433426085468009">"Non soa nin vibra, e aparece máis abaixo na sección de conversas"</string>
- <string name="notification_channel_summary_default" msgid="3282930979307248890">"Podería soar ou vibrar en función da configuración do teléfono"</string>
+ <string name="notification_conversation_summary_low" msgid="1734433426085468009">"Sen son nin vibración, e aparecen máis abaixo na sección de conversas"</string>
+ <string name="notification_channel_summary_default" msgid="3282930979307248890">"Poderían soar ou vibrar en función da configuración do teléfono"</string>
<string name="notification_channel_summary_default_with_bubbles" msgid="1782419896613644568">"Podería soar ou vibrar en función da configuración do teléfono. Conversas desde a burbulla da aplicación <xliff:g id="APP_NAME">%1$s</xliff:g> de forma predeterminada."</string>
<string name="notification_channel_summary_bubble" msgid="7235935211580860537">"Mantén a túa atención cun atallo flotante a este contido."</string>
<string name="notification_channel_summary_automatic" msgid="5813109268050235275">"Fai que o sistema determine se a notificación debe emitir un son ou unha vibración"</string>
- <string name="notification_channel_summary_priority" msgid="7952654515769021553">"Móstrase na parte superior da sección de conversas en forma de burbulla flotante e aparece a imaxe do perfil na pantalla de bloqueo"</string>
+ <string name="notification_channel_summary_priority" msgid="7952654515769021553">"Móstranse na parte superior da sección de conversas en forma de burbulla flotante e aparece a imaxe do perfil na pantalla de bloqueo"</string>
<string name="notification_conversation_channel_settings" msgid="2409977688430606835">"Configuración"</string>
<string name="notification_priority_title" msgid="2079708866333537093">"Prioridade"</string>
<string name="no_shortcut" msgid="8257177117568230126">"<xliff:g id="APP_NAME">%1$s</xliff:g> non admite funcións de conversa"</string>
@@ -879,12 +879,13 @@
</string-array>
<string name="tuner_low_priority" msgid="8412666814123009820">"Mostrar iconas das notificacións que teñan baixa prioridade"</string>
<string name="other" msgid="429768510980739978">"Outros"</string>
- <string name="accessibility_qs_edit_tile_label" msgid="9079791448815232967">"Posición <xliff:g id="POSITION">%1$d</xliff:g>, <xliff:g id="TILE_NAME">%2$s</xliff:g>. Toca dúas veces o elemento para editalo."</string>
- <string name="accessibility_qs_edit_add_tile_label" msgid="8292218072049068613">"<xliff:g id="TILE_NAME">%1$s</xliff:g>. Toca dúas veces o elemento para engadilo"</string>
- <string name="accessibility_qs_edit_move_tile" msgid="6027997446473163426">"Move <xliff:g id="TILE_NAME">%1$s</xliff:g>"</string>
- <string name="accessibility_qs_edit_remove_tile" msgid="3406781901949899624">"Elimina <xliff:g id="TILE_NAME">%1$s</xliff:g>"</string>
- <string name="accessibility_qs_edit_tile_add" msgid="6289879620154587233">"Engadir \"<xliff:g id="TILE_NAME">%1$s</xliff:g>\" á posición <xliff:g id="POSITION">%2$d</xliff:g>"</string>
- <string name="accessibility_qs_edit_tile_move" msgid="4841770637244326837">"Mover \"<xliff:g id="TILE_NAME">%1$s</xliff:g>\" á posición <xliff:g id="POSITION">%2$d</xliff:g>"</string>
+ <string name="accessibility_qs_edit_remove_tile_action" msgid="775511891457193480">"quitar tarxeta"</string>
+ <string name="accessibility_qs_edit_tile_add_action" msgid="5051211910345301833">"engadir tarxeta ao final"</string>
+ <string name="accessibility_qs_edit_tile_start_move" msgid="2009373939914517817">"Mover tarxeta"</string>
+ <string name="accessibility_qs_edit_tile_start_add" msgid="7560798153975555772">"Engadir tarxeta"</string>
+ <string name="accessibility_qs_edit_tile_move_to_position" msgid="5198161544045930556">"Mover a <xliff:g id="POSITION">%1$d</xliff:g>"</string>
+ <string name="accessibility_qs_edit_tile_add_to_position" msgid="9029163095148274690">"Engadir á posición <xliff:g id="POSITION">%1$d</xliff:g>"</string>
+ <string name="accessibility_qs_edit_position" msgid="4509277359815711830">"Posición <xliff:g id="POSITION">%1$d</xliff:g>"</string>
<string name="accessibility_desc_quick_settings_edit" msgid="741658939453595297">"Editor de configuración rápida."</string>
<string name="accessibility_desc_notification_icon" msgid="7331265967584178674">"Notificación de <xliff:g id="ID_1">%1$s</xliff:g>: <xliff:g id="ID_2">%2$s</xliff:g>"</string>
<string name="accessibility_quick_settings_settings" msgid="7098489591715844713">"Abrir configuración."</string>
@@ -1067,4 +1068,11 @@
<string name="controls_added_tooltip" msgid="4842812921719153085">"Mantén premido o botón de acendido para ver os novos controis"</string>
<string name="controls_menu_add" msgid="4447246119229920050">"Engadir controis"</string>
<string name="controls_menu_edit" msgid="890623986951347062">"Editar controis"</string>
+ <string name="media_output_dialog_add_output" msgid="5642703238877329518">"Engadir saídas"</string>
+ <string name="media_output_dialog_group" msgid="5571251347877452212">"Grupo"</string>
+ <string name="media_output_dialog_single_device" msgid="3102758980643351058">"Seleccionouse 1 dispositivo"</string>
+ <string name="media_output_dialog_multiple_devices" msgid="1093771040315422350">"Seleccionáronse <xliff:g id="COUNT">%1$d</xliff:g> dispositivos"</string>
+ <string name="media_output_dialog_disconnected" msgid="1834473104836986046">"<xliff:g id="DEVICE_NAME">%1$s</xliff:g> (dispositivo desconectado)"</string>
+ <string name="media_output_dialog_connect_failed" msgid="3225190634236259010">"Non se puido establecer a conexión. Téntao de novo."</string>
+ <string name="media_output_dialog_pairing_new" msgid="9099497976087485862">"Vincular dispositivo novo"</string>
</resources>
diff --git a/packages/SystemUI/res/values-gu/strings.xml b/packages/SystemUI/res/values-gu/strings.xml
index 9599ca868365..e8b446063922 100644
--- a/packages/SystemUI/res/values-gu/strings.xml
+++ b/packages/SystemUI/res/values-gu/strings.xml
@@ -865,7 +865,7 @@
<string name="drag_to_rearrange_tiles" msgid="2143204300089638620">"ટાઇલને ફરીથી ગોઠવવા માટે આંગળી દબાવીને ખેંચો"</string>
<string name="drag_to_remove_tiles" msgid="4682194717573850385">"દૂર કરવા માટે અહીં ખેંચો"</string>
<string name="drag_to_remove_disabled" msgid="933046987838658850">"તમને ઓછામાં ઓછી <xliff:g id="MIN_NUM_TILES">%1$d</xliff:g> ટાઇલની જરૂર છે"</string>
- <string name="qs_edit" msgid="5583565172803472437">"સંપાદિત કરો"</string>
+ <string name="qs_edit" msgid="5583565172803472437">"ફેરફાર કરો"</string>
<string name="tuner_time" msgid="2450785840990529997">"સમય"</string>
<string-array name="clock_options">
<item msgid="3986445361435142273">"કલાક, મિનિટ અને સેકન્ડ બતાવો"</item>
@@ -879,12 +879,13 @@
</string-array>
<string name="tuner_low_priority" msgid="8412666814123009820">"ઓછી પ્રાધાન્યતાનું નોટિફિકેશન આઇકન બતાવો"</string>
<string name="other" msgid="429768510980739978">"અન્ય"</string>
- <string name="accessibility_qs_edit_tile_label" msgid="9079791448815232967">"સ્થિતિ <xliff:g id="POSITION">%1$d</xliff:g>, <xliff:g id="TILE_NAME">%2$s</xliff:g>. સંપાદિત કરવા માટે બે વાર ટૅપ કરો."</string>
- <string name="accessibility_qs_edit_add_tile_label" msgid="8292218072049068613">"<xliff:g id="TILE_NAME">%1$s</xliff:g>. ઉમેરવા માટે બે વાર ટૅપ કરો."</string>
- <string name="accessibility_qs_edit_move_tile" msgid="6027997446473163426">"<xliff:g id="TILE_NAME">%1$s</xliff:g> ખસેડો"</string>
- <string name="accessibility_qs_edit_remove_tile" msgid="3406781901949899624">"<xliff:g id="TILE_NAME">%1$s</xliff:g> દૂર કરો"</string>
- <string name="accessibility_qs_edit_tile_add" msgid="6289879620154587233">"<xliff:g id="POSITION">%2$d</xliff:g> જગ્યા પર <xliff:g id="TILE_NAME">%1$s</xliff:g>ને ઉમેરો"</string>
- <string name="accessibility_qs_edit_tile_move" msgid="4841770637244326837">"<xliff:g id="POSITION">%2$d</xliff:g> જગ્યા પર <xliff:g id="TILE_NAME">%1$s</xliff:g>ને ખસેડો"</string>
+ <string name="accessibility_qs_edit_remove_tile_action" msgid="775511891457193480">"ટાઇલ કાઢી નાખો"</string>
+ <string name="accessibility_qs_edit_tile_add_action" msgid="5051211910345301833">"ટાઇલને અંતે ઉમેરો"</string>
+ <string name="accessibility_qs_edit_tile_start_move" msgid="2009373939914517817">"ટાઇલ ખસેડો"</string>
+ <string name="accessibility_qs_edit_tile_start_add" msgid="7560798153975555772">"ટાઇલ ઉમેરો"</string>
+ <string name="accessibility_qs_edit_tile_move_to_position" msgid="5198161544045930556">"<xliff:g id="POSITION">%1$d</xliff:g> પર ખસેડો"</string>
+ <string name="accessibility_qs_edit_tile_add_to_position" msgid="9029163095148274690">"જગ્યા પર <xliff:g id="POSITION">%1$d</xliff:g> ઉમેરો"</string>
+ <string name="accessibility_qs_edit_position" msgid="4509277359815711830">"જગ્યા <xliff:g id="POSITION">%1$d</xliff:g>"</string>
<string name="accessibility_desc_quick_settings_edit" msgid="741658939453595297">"ઝડપી સેટિંગ્સ સંપાદક."</string>
<string name="accessibility_desc_notification_icon" msgid="7331265967584178674">"<xliff:g id="ID_1">%1$s</xliff:g> નોટિફિકેશન: <xliff:g id="ID_2">%2$s</xliff:g>"</string>
<string name="accessibility_quick_settings_settings" msgid="7098489591715844713">"સેટિંગ્સ ખોલો."</string>
@@ -1067,4 +1068,11 @@
<string name="controls_added_tooltip" msgid="4842812921719153085">"નવા નિયંત્રણ જોવા માટે પાવર બટનને દબાવી રાખો"</string>
<string name="controls_menu_add" msgid="4447246119229920050">"નિયંત્રણો ઉમેરો"</string>
<string name="controls_menu_edit" msgid="890623986951347062">"નિયંત્રણોમાં ફેરફાર કરો"</string>
+ <string name="media_output_dialog_add_output" msgid="5642703238877329518">"આઉટપુટ ઉમેરો"</string>
+ <string name="media_output_dialog_group" msgid="5571251347877452212">"ગ્રૂપ"</string>
+ <string name="media_output_dialog_single_device" msgid="3102758980643351058">"1 ડિવાઇસ પસંદ કર્યું"</string>
+ <string name="media_output_dialog_multiple_devices" msgid="1093771040315422350">"<xliff:g id="COUNT">%1$d</xliff:g> ડિવાઇસ પસંદ કર્યા"</string>
+ <string name="media_output_dialog_disconnected" msgid="1834473104836986046">"<xliff:g id="DEVICE_NAME">%1$s</xliff:g> (ડિસ્કનેક્ટ થયેલું)"</string>
+ <string name="media_output_dialog_connect_failed" msgid="3225190634236259010">"કનેક્ટ કરી શકાયું નહીં. ફરી પ્રયાસ કરો."</string>
+ <string name="media_output_dialog_pairing_new" msgid="9099497976087485862">"નવા ડિવાઇસ સાથે જોડાણ કરો"</string>
</resources>
diff --git a/packages/SystemUI/res/values-hi/strings.xml b/packages/SystemUI/res/values-hi/strings.xml
index f3a7ec1c0cca..1a69e81a8191 100644
--- a/packages/SystemUI/res/values-hi/strings.xml
+++ b/packages/SystemUI/res/values-hi/strings.xml
@@ -512,7 +512,7 @@
<string name="manage_notifications_history_text" msgid="57055985396576230">"इतिहास"</string>
<string name="notification_section_header_incoming" msgid="850925217908095197">"नई सूचनाएं"</string>
<string name="notification_section_header_gentle" msgid="6804099527336337197">"बिना आवाज़ किए मिलने वाली सूचनाएं"</string>
- <string name="notification_section_header_alerting" msgid="5581175033680477651">"वाइब्रेशन या आवाज़ के साथ मिलने वाली सूचनाएं"</string>
+ <string name="notification_section_header_alerting" msgid="5581175033680477651">"सूचनाएं"</string>
<string name="notification_section_header_conversations" msgid="821834744538345661">"बातचीत"</string>
<string name="accessibility_notification_section_header_gentle_clear_all" msgid="6490207897764933919">"बिना आवाज़ की सभी सूचनाएं हटाएं"</string>
<string name="dnd_suppressing_shade_text" msgid="5588252250634464042">"\'परेशान न करें\' सुविधा के ज़रिए कुछ समय के लिए सूचनाएं दिखाना रोक दिया गया है"</string>
@@ -713,12 +713,12 @@
<string name="notification_bubble_title" msgid="8330481035191903164">"बबल"</string>
<string name="notification_automatic_title" msgid="3745465364578762652">"अपने-आप"</string>
<string name="notification_channel_summary_low" msgid="4860617986908931158">"किसी तरह की आवाज़ या वाइब्रेशन न हो"</string>
- <string name="notification_conversation_summary_low" msgid="1734433426085468009">"इससे किसी तरह की आवाज़ या वाइब्रेशन नहीं होता और \'बातचीत\', सेक्शन में सबसे नीचे दिखती है"</string>
+ <string name="notification_conversation_summary_low" msgid="1734433426085468009">"इससे किसी तरह की आवाज़ या वाइब्रेशन नहीं होता और बातचीत, सेक्शन में सबसे नीचे दिखती है"</string>
<string name="notification_channel_summary_default" msgid="3282930979307248890">"फ़ोन की सेटिंग के आधार पर, सूचना आने पर घंटी बज सकती है या वाइब्रेशन हो सकता है"</string>
<string name="notification_channel_summary_default_with_bubbles" msgid="1782419896613644568">"फ़ोन की सेटिंग के आधार पर, सूचना आने पर घंटी बज सकती है या वाइब्रेशन हो सकता है. <xliff:g id="APP_NAME">%1$s</xliff:g> में होने वाली बातचीत, डिफ़ॉल्ट रूप से बबल के तौर पर दिखती है."</string>
<string name="notification_channel_summary_bubble" msgid="7235935211580860537">"फ़्लोट करने वाले शॉर्टकट की मदद से इस सामग्री पर आपका ध्यान बना रहता है."</string>
<string name="notification_channel_summary_automatic" msgid="5813109268050235275">"सिस्टम को यह तय करने की अनुमति दें कि इस सूचना के मिलने पर आवाज़ हो या वाइब्रेशन हो"</string>
- <string name="notification_channel_summary_priority" msgid="7952654515769021553">"इससे बातचीत की सुविधा, सेक्शन में सबसे ऊपर और फ़्लोटिंग बबल के तौर पर दिखती है. साथ ही, लॉक स्क्रीन पर प्रोफ़ाइल फ़ोटो दिखती है"</string>
+ <string name="notification_channel_summary_priority" msgid="7952654515769021553">"इससे बातचीत, सेक्शन में सबसे ऊपर और फ़्लोटिंग बबल के तौर पर दिखती है. साथ ही, लॉक स्क्रीन पर प्रोफ़ाइल फ़ोटो दिखती है"</string>
<string name="notification_conversation_channel_settings" msgid="2409977688430606835">"सेटिंग"</string>
<string name="notification_priority_title" msgid="2079708866333537093">"प्राथमिकता"</string>
<string name="no_shortcut" msgid="8257177117568230126">"<xliff:g id="APP_NAME">%1$s</xliff:g> पर बातचीत की सुविधाएं काम नहीं करतीं"</string>
@@ -881,12 +881,20 @@
</string-array>
<string name="tuner_low_priority" msgid="8412666814123009820">"कम प्राथमिकता वाली सूचना के आइकॉन दिखाएं"</string>
<string name="other" msgid="429768510980739978">"अन्य"</string>
- <string name="accessibility_qs_edit_tile_label" msgid="9079791448815232967">"स्थिति <xliff:g id="POSITION">%1$d</xliff:g>, <xliff:g id="TILE_NAME">%2$s</xliff:g>. में बदलाव करने के लिए दो बार छूएं."</string>
- <string name="accessibility_qs_edit_add_tile_label" msgid="8292218072049068613">"<xliff:g id="TILE_NAME">%1$s</xliff:g>. जोड़ने के लिए दो बार छूएं."</string>
- <string name="accessibility_qs_edit_move_tile" msgid="6027997446473163426">"<xliff:g id="TILE_NAME">%1$s</xliff:g> को ले जाएं"</string>
- <string name="accessibility_qs_edit_remove_tile" msgid="3406781901949899624">"<xliff:g id="TILE_NAME">%1$s</xliff:g> निकालें"</string>
- <string name="accessibility_qs_edit_tile_add" msgid="6289879620154587233">"<xliff:g id="TILE_NAME">%1$s</xliff:g> काे क्रम संख्या <xliff:g id="POSITION">%2$d</xliff:g> पर जाेड़ें"</string>
- <string name="accessibility_qs_edit_tile_move" msgid="4841770637244326837">"<xliff:g id="TILE_NAME">%1$s</xliff:g> काे क्रम संख्या <xliff:g id="POSITION">%2$d</xliff:g> पर ले जाएं"</string>
+ <!-- no translation found for accessibility_qs_edit_remove_tile_action (775511891457193480) -->
+ <skip />
+ <!-- no translation found for accessibility_qs_edit_tile_add_action (5051211910345301833) -->
+ <skip />
+ <!-- no translation found for accessibility_qs_edit_tile_start_move (2009373939914517817) -->
+ <skip />
+ <!-- no translation found for accessibility_qs_edit_tile_start_add (7560798153975555772) -->
+ <skip />
+ <!-- no translation found for accessibility_qs_edit_tile_move_to_position (5198161544045930556) -->
+ <skip />
+ <!-- no translation found for accessibility_qs_edit_tile_add_to_position (9029163095148274690) -->
+ <skip />
+ <!-- no translation found for accessibility_qs_edit_position (4509277359815711830) -->
+ <skip />
<string name="accessibility_desc_quick_settings_edit" msgid="741658939453595297">"त्वरित सेटिंग संपादक."</string>
<string name="accessibility_desc_notification_icon" msgid="7331265967584178674">"<xliff:g id="ID_1">%1$s</xliff:g> सूचना: <xliff:g id="ID_2">%2$s</xliff:g>"</string>
<string name="accessibility_quick_settings_settings" msgid="7098489591715844713">"सेटिंग खोलें."</string>
@@ -1069,4 +1077,11 @@
<string name="controls_added_tooltip" msgid="4842812921719153085">"नए कंट्रोल देखने के लिए पावर बटन दबाकर रखें"</string>
<string name="controls_menu_add" msgid="4447246119229920050">"कंट्राेल जोड़ें"</string>
<string name="controls_menu_edit" msgid="890623986951347062">"कंट्रोल मेन्यू में बदलाव करें"</string>
+ <string name="media_output_dialog_add_output" msgid="5642703238877329518">"आउटपुट जोड़ें"</string>
+ <string name="media_output_dialog_group" msgid="5571251347877452212">"ग्रुप"</string>
+ <string name="media_output_dialog_single_device" msgid="3102758980643351058">"एक डिवाइस चुना गया"</string>
+ <string name="media_output_dialog_multiple_devices" msgid="1093771040315422350">"<xliff:g id="COUNT">%1$d</xliff:g> डिवाइस चुने गए"</string>
+ <string name="media_output_dialog_disconnected" msgid="1834473104836986046">"<xliff:g id="DEVICE_NAME">%1$s</xliff:g> (डिसकनेक्ट किया गया)"</string>
+ <string name="media_output_dialog_connect_failed" msgid="3225190634236259010">"कनेक्ट नहीं किया जा सका. फिर से कोशिश करें."</string>
+ <string name="media_output_dialog_pairing_new" msgid="9099497976087485862">"नया डिवाइस जोड़ें"</string>
</resources>
diff --git a/packages/SystemUI/res/values-hr/strings.xml b/packages/SystemUI/res/values-hr/strings.xml
index 870ca49add60..649299b1cd7d 100644
--- a/packages/SystemUI/res/values-hr/strings.xml
+++ b/packages/SystemUI/res/values-hr/strings.xml
@@ -884,12 +884,13 @@
</string-array>
<string name="tuner_low_priority" msgid="8412666814123009820">"Prikaži ikone obavijesti niskog prioriteta"</string>
<string name="other" msgid="429768510980739978">"Ostalo"</string>
- <string name="accessibility_qs_edit_tile_label" msgid="9079791448815232967">"Položaj <xliff:g id="POSITION">%1$d</xliff:g>, <xliff:g id="TILE_NAME">%2$s</xliff:g>. Dodirnite dvaput da biste uredili."</string>
- <string name="accessibility_qs_edit_add_tile_label" msgid="8292218072049068613">"<xliff:g id="TILE_NAME">%1$s</xliff:g>. Dodirnite dvaput da biste dodali."</string>
- <string name="accessibility_qs_edit_move_tile" msgid="6027997446473163426">"Premjesti <xliff:g id="TILE_NAME">%1$s</xliff:g>"</string>
- <string name="accessibility_qs_edit_remove_tile" msgid="3406781901949899624">"Ukloni <xliff:g id="TILE_NAME">%1$s</xliff:g>"</string>
- <string name="accessibility_qs_edit_tile_add" msgid="6289879620154587233">"Dodajte pločicu <xliff:g id="TILE_NAME">%1$s</xliff:g> na položaj <xliff:g id="POSITION">%2$d</xliff:g>"</string>
- <string name="accessibility_qs_edit_tile_move" msgid="4841770637244326837">"Premjestite pločicu <xliff:g id="TILE_NAME">%1$s</xliff:g> na položaj <xliff:g id="POSITION">%2$d</xliff:g>"</string>
+ <string name="accessibility_qs_edit_remove_tile_action" msgid="775511891457193480">"uklanjanje kartice"</string>
+ <string name="accessibility_qs_edit_tile_add_action" msgid="5051211910345301833">"dodavanje kartice na kraj"</string>
+ <string name="accessibility_qs_edit_tile_start_move" msgid="2009373939914517817">"Premještanje kartice"</string>
+ <string name="accessibility_qs_edit_tile_start_add" msgid="7560798153975555772">"Dodavanje kartice"</string>
+ <string name="accessibility_qs_edit_tile_move_to_position" msgid="5198161544045930556">"Premještanje u prostoriju <xliff:g id="POSITION">%1$d</xliff:g>"</string>
+ <string name="accessibility_qs_edit_tile_add_to_position" msgid="9029163095148274690">"Dodavanje na položaj <xliff:g id="POSITION">%1$d</xliff:g>"</string>
+ <string name="accessibility_qs_edit_position" msgid="4509277359815711830">"Položaj <xliff:g id="POSITION">%1$d</xliff:g>"</string>
<string name="accessibility_desc_quick_settings_edit" msgid="741658939453595297">"Uređivač brzih postavki."</string>
<string name="accessibility_desc_notification_icon" msgid="7331265967584178674">"<xliff:g id="ID_1">%1$s</xliff:g> obavijest: <xliff:g id="ID_2">%2$s</xliff:g>"</string>
<string name="accessibility_quick_settings_settings" msgid="7098489591715844713">"Otvaranje postavki."</string>
@@ -1073,4 +1074,11 @@
<string name="controls_added_tooltip" msgid="4842812921719153085">"Zadržite tipku za uključivanje/isključivanje za prikaz novih kontrola"</string>
<string name="controls_menu_add" msgid="4447246119229920050">"Dodaj kontrole"</string>
<string name="controls_menu_edit" msgid="890623986951347062">"Uredi kontrole"</string>
+ <string name="media_output_dialog_add_output" msgid="5642703238877329518">"Dodavanje izlaza"</string>
+ <string name="media_output_dialog_group" msgid="5571251347877452212">"Grupa"</string>
+ <string name="media_output_dialog_single_device" msgid="3102758980643351058">"Odabran je jedan uređaj"</string>
+ <string name="media_output_dialog_multiple_devices" msgid="1093771040315422350">"Odabrano uređaja: <xliff:g id="COUNT">%1$d</xliff:g>"</string>
+ <string name="media_output_dialog_disconnected" msgid="1834473104836986046">"<xliff:g id="DEVICE_NAME">%1$s</xliff:g> (nije povezano)"</string>
+ <string name="media_output_dialog_connect_failed" msgid="3225190634236259010">"Povezivanje nije bilo moguće. Pokušajte ponovo."</string>
+ <string name="media_output_dialog_pairing_new" msgid="9099497976087485862">"Upari novi uređaj"</string>
</resources>
diff --git a/packages/SystemUI/res/values-hu/strings.xml b/packages/SystemUI/res/values-hu/strings.xml
index f88076f54b22..f658bb76c649 100644
--- a/packages/SystemUI/res/values-hu/strings.xml
+++ b/packages/SystemUI/res/values-hu/strings.xml
@@ -879,12 +879,13 @@
</string-array>
<string name="tuner_low_priority" msgid="8412666814123009820">"Alacsony prioritású értesítési ikonok mutatása"</string>
<string name="other" msgid="429768510980739978">"Egyéb"</string>
- <string name="accessibility_qs_edit_tile_label" msgid="9079791448815232967">"<xliff:g id="POSITION">%1$d</xliff:g>. pozíció: <xliff:g id="TILE_NAME">%2$s</xliff:g>. Koppintson duplán a szerkesztéshez."</string>
- <string name="accessibility_qs_edit_add_tile_label" msgid="8292218072049068613">"<xliff:g id="TILE_NAME">%1$s</xliff:g>. Koppintson duplán a hozzáadáshoz."</string>
- <string name="accessibility_qs_edit_move_tile" msgid="6027997446473163426">"A(z) <xliff:g id="TILE_NAME">%1$s</xliff:g> áthelyezése"</string>
- <string name="accessibility_qs_edit_remove_tile" msgid="3406781901949899624">"A(z) <xliff:g id="TILE_NAME">%1$s</xliff:g> eltávolítása"</string>
- <string name="accessibility_qs_edit_tile_add" msgid="6289879620154587233">"<xliff:g id="TILE_NAME">%1$s</xliff:g> hozzáadása a következő pozícióhoz: <xliff:g id="POSITION">%2$d</xliff:g>"</string>
- <string name="accessibility_qs_edit_tile_move" msgid="4841770637244326837">"<xliff:g id="TILE_NAME">%1$s</xliff:g> áthelyezése a következő pozícióba: <xliff:g id="POSITION">%2$d</xliff:g>"</string>
+ <string name="accessibility_qs_edit_remove_tile_action" msgid="775511891457193480">"mozaik eltávolításához"</string>
+ <string name="accessibility_qs_edit_tile_add_action" msgid="5051211910345301833">"mozaiknak a végéhez való hozzáadásához"</string>
+ <string name="accessibility_qs_edit_tile_start_move" msgid="2009373939914517817">"Mozaik áthelyezése"</string>
+ <string name="accessibility_qs_edit_tile_start_add" msgid="7560798153975555772">"Mozaik hozzáadása"</string>
+ <string name="accessibility_qs_edit_tile_move_to_position" msgid="5198161544045930556">"Áthelyezés ide: <xliff:g id="POSITION">%1$d</xliff:g>"</string>
+ <string name="accessibility_qs_edit_tile_add_to_position" msgid="9029163095148274690">"Hozzáadás a következő pozícióhoz: <xliff:g id="POSITION">%1$d</xliff:g>"</string>
+ <string name="accessibility_qs_edit_position" msgid="4509277359815711830">"<xliff:g id="POSITION">%1$d</xliff:g>. hely"</string>
<string name="accessibility_desc_quick_settings_edit" msgid="741658939453595297">"Gyorsbeállítások szerkesztője"</string>
<string name="accessibility_desc_notification_icon" msgid="7331265967584178674">"<xliff:g id="ID_1">%1$s</xliff:g>-értesítések: <xliff:g id="ID_2">%2$s</xliff:g>"</string>
<string name="accessibility_quick_settings_settings" msgid="7098489591715844713">"Beállítások megnyitása."</string>
@@ -1067,4 +1068,11 @@
<string name="controls_added_tooltip" msgid="4842812921719153085">"Az új vezérlők megtekintéséhez tartsa nyomva a bekapcsológombot"</string>
<string name="controls_menu_add" msgid="4447246119229920050">"Vezérlők hozzáadása"</string>
<string name="controls_menu_edit" msgid="890623986951347062">"Vezérlők szerkesztése"</string>
+ <string name="media_output_dialog_add_output" msgid="5642703238877329518">"Kimenetek hozzáadása"</string>
+ <string name="media_output_dialog_group" msgid="5571251347877452212">"Csoport"</string>
+ <string name="media_output_dialog_single_device" msgid="3102758980643351058">"1 eszköz kiválasztva"</string>
+ <string name="media_output_dialog_multiple_devices" msgid="1093771040315422350">"<xliff:g id="COUNT">%1$d</xliff:g> eszköz kiválasztva"</string>
+ <string name="media_output_dialog_disconnected" msgid="1834473104836986046">"<xliff:g id="DEVICE_NAME">%1$s</xliff:g> (leválasztva)"</string>
+ <string name="media_output_dialog_connect_failed" msgid="3225190634236259010">"Sikertelen csatlakozás. Próbálja újra."</string>
+ <string name="media_output_dialog_pairing_new" msgid="9099497976087485862">"Új eszköz párosítása"</string>
</resources>
diff --git a/packages/SystemUI/res/values-hy/strings.xml b/packages/SystemUI/res/values-hy/strings.xml
index 04c2bb1a5237..28c79fbc8852 100644
--- a/packages/SystemUI/res/values-hy/strings.xml
+++ b/packages/SystemUI/res/values-hy/strings.xml
@@ -511,7 +511,7 @@
<string name="notification_section_header_incoming" msgid="850925217908095197">"Նոր"</string>
<string name="notification_section_header_gentle" msgid="6804099527336337197">"Անձայն"</string>
<string name="notification_section_header_alerting" msgid="5581175033680477651">"Ծանուցումներ"</string>
- <string name="notification_section_header_conversations" msgid="821834744538345661">"Խոսակցություններ"</string>
+ <string name="notification_section_header_conversations" msgid="821834744538345661">"Զրույցներ"</string>
<string name="accessibility_notification_section_header_gentle_clear_all" msgid="6490207897764933919">"Ջնջել բոլոր անձայն ծանուցումները"</string>
<string name="dnd_suppressing_shade_text" msgid="5588252250634464042">"Ծանուցումները չեն ցուցադրվի «Չանհանգստացնել» ռեժիմում"</string>
<string name="media_projection_action_text" msgid="3634906766918186440">"Սկսել հիմա"</string>
@@ -879,12 +879,13 @@
</string-array>
<string name="tuner_low_priority" msgid="8412666814123009820">"Ցուցադրել ցածր առաջնահերթության ծանուցումների պատկերակները"</string>
<string name="other" msgid="429768510980739978">"Այլ"</string>
- <string name="accessibility_qs_edit_tile_label" msgid="9079791448815232967">"Դիրք <xliff:g id="POSITION">%1$d</xliff:g>, <xliff:g id="TILE_NAME">%2$s</xliff:g>: Կրկնակի հպեք՝ փոխելու համար:"</string>
- <string name="accessibility_qs_edit_add_tile_label" msgid="8292218072049068613">"<xliff:g id="TILE_NAME">%1$s</xliff:g>: Կրկնակի հպեք՝ ավելացնելու համար:"</string>
- <string name="accessibility_qs_edit_move_tile" msgid="6027997446473163426">"Տեղափոխել <xliff:g id="TILE_NAME">%1$s</xliff:g> սալիկը"</string>
- <string name="accessibility_qs_edit_remove_tile" msgid="3406781901949899624">"Հեռացնել <xliff:g id="TILE_NAME">%1$s</xliff:g> սալիկը"</string>
- <string name="accessibility_qs_edit_tile_add" msgid="6289879620154587233">"Ավելացնել <xliff:g id="TILE_NAME">%1$s</xliff:g> սալիկը դիրք <xliff:g id="POSITION">%2$d</xliff:g>-ում"</string>
- <string name="accessibility_qs_edit_tile_move" msgid="4841770637244326837">"<xliff:g id="TILE_NAME">%1$s</xliff:g> սալիկը տեղափոխել դիրք <xliff:g id="POSITION">%2$d</xliff:g>"</string>
+ <string name="accessibility_qs_edit_remove_tile_action" msgid="775511891457193480">"հեռացնել սալիկը"</string>
+ <string name="accessibility_qs_edit_tile_add_action" msgid="5051211910345301833">"ավելացնել սալիկ վերջում"</string>
+ <string name="accessibility_qs_edit_tile_start_move" msgid="2009373939914517817">"Տեղափոխել սալիկը"</string>
+ <string name="accessibility_qs_edit_tile_start_add" msgid="7560798153975555772">"Ավելացնել սալիկ"</string>
+ <string name="accessibility_qs_edit_tile_move_to_position" msgid="5198161544045930556">"Տեղափոխել դիրք <xliff:g id="POSITION">%1$d</xliff:g>"</string>
+ <string name="accessibility_qs_edit_tile_add_to_position" msgid="9029163095148274690">"Ավելացնել դիրք <xliff:g id="POSITION">%1$d</xliff:g>-ում"</string>
+ <string name="accessibility_qs_edit_position" msgid="4509277359815711830">"Դիրք <xliff:g id="POSITION">%1$d</xliff:g>"</string>
<string name="accessibility_desc_quick_settings_edit" msgid="741658939453595297">"Արագ կարգավորումների խմբագրիչ:"</string>
<string name="accessibility_desc_notification_icon" msgid="7331265967584178674">"<xliff:g id="ID_1">%1$s</xliff:g> ծանուցում՝ <xliff:g id="ID_2">%2$s</xliff:g>"</string>
<string name="accessibility_quick_settings_settings" msgid="7098489591715844713">"Բացել կարգավորումները:"</string>
@@ -1067,4 +1068,11 @@
<string name="controls_added_tooltip" msgid="4842812921719153085">"Սեղմած պահեք սնուցման կոճակը՝ կառավարման նոր տարրերը տեսնելու համար։"</string>
<string name="controls_menu_add" msgid="4447246119229920050">"Ավելացնել կառավարման տարրեր"</string>
<string name="controls_menu_edit" msgid="890623986951347062">"Փոփոխել կառավարման տարրերը"</string>
+ <string name="media_output_dialog_add_output" msgid="5642703238877329518">"Ավելացրեք մուտքագրման սարքեր"</string>
+ <string name="media_output_dialog_group" msgid="5571251347877452212">"Խումբ"</string>
+ <string name="media_output_dialog_single_device" msgid="3102758980643351058">"Ընտրված է 1 սարք"</string>
+ <string name="media_output_dialog_multiple_devices" msgid="1093771040315422350">"Ընտրված է <xliff:g id="COUNT">%1$d</xliff:g> սարք"</string>
+ <string name="media_output_dialog_disconnected" msgid="1834473104836986046">"<xliff:g id="DEVICE_NAME">%1$s</xliff:g> (անջատված է)"</string>
+ <string name="media_output_dialog_connect_failed" msgid="3225190634236259010">"Չհաջողվեց միանալ։ Նորից փորձեք։"</string>
+ <string name="media_output_dialog_pairing_new" msgid="9099497976087485862">"Նոր սարքի զուգակցում"</string>
</resources>
diff --git a/packages/SystemUI/res/values-in/strings.xml b/packages/SystemUI/res/values-in/strings.xml
index 403af01642dd..12f42e84c3db 100644
--- a/packages/SystemUI/res/values-in/strings.xml
+++ b/packages/SystemUI/res/values-in/strings.xml
@@ -862,7 +862,7 @@
<string name="left_icon" msgid="5036278531966897006">"Ikon kiri"</string>
<string name="right_icon" msgid="1103955040645237425">"Ikon kanan"</string>
<string name="drag_to_add_tiles" msgid="8933270127508303672">"Tahan dan tarik untuk menambahkan kartu"</string>
- <string name="drag_to_rearrange_tiles" msgid="2143204300089638620">"Tahan dan tarik untuk mengatur ulang kartu"</string>
+ <string name="drag_to_rearrange_tiles" msgid="2143204300089638620">"Tahan dan tarik untuk menata ulang kartu"</string>
<string name="drag_to_remove_tiles" msgid="4682194717573850385">"Tarik ke sini untuk menghapus"</string>
<string name="drag_to_remove_disabled" msgid="933046987838658850">"Anda membutuhkan setidaknya <xliff:g id="MIN_NUM_TILES">%1$d</xliff:g> kartu"</string>
<string name="qs_edit" msgid="5583565172803472437">"Edit"</string>
@@ -879,12 +879,13 @@
</string-array>
<string name="tuner_low_priority" msgid="8412666814123009820">"Tampilkan ikon notifikasi prioritas rendah"</string>
<string name="other" msgid="429768510980739978">"Lainnya"</string>
- <string name="accessibility_qs_edit_tile_label" msgid="9079791448815232967">"Posisi <xliff:g id="POSITION">%1$d</xliff:g>, <xliff:g id="TILE_NAME">%2$s</xliff:g>. Ketuk dua kali untuk mengedit."</string>
- <string name="accessibility_qs_edit_add_tile_label" msgid="8292218072049068613">"<xliff:g id="TILE_NAME">%1$s</xliff:g>. Ketuk dua kali untuk menambahkan."</string>
- <string name="accessibility_qs_edit_move_tile" msgid="6027997446473163426">"Pindahkan <xliff:g id="TILE_NAME">%1$s</xliff:g>"</string>
- <string name="accessibility_qs_edit_remove_tile" msgid="3406781901949899624">"Hapus <xliff:g id="TILE_NAME">%1$s</xliff:g>"</string>
- <string name="accessibility_qs_edit_tile_add" msgid="6289879620154587233">"Tambahkan <xliff:g id="TILE_NAME">%1$s</xliff:g> ke posisi <xliff:g id="POSITION">%2$d</xliff:g>"</string>
- <string name="accessibility_qs_edit_tile_move" msgid="4841770637244326837">"Pindahkan <xliff:g id="TILE_NAME">%1$s</xliff:g> ke posisi <xliff:g id="POSITION">%2$d</xliff:g>"</string>
+ <string name="accessibility_qs_edit_remove_tile_action" msgid="775511891457193480">"menghapus kartu"</string>
+ <string name="accessibility_qs_edit_tile_add_action" msgid="5051211910345301833">"menambahkan kartu ke akhir"</string>
+ <string name="accessibility_qs_edit_tile_start_move" msgid="2009373939914517817">"Pindahkan kartu"</string>
+ <string name="accessibility_qs_edit_tile_start_add" msgid="7560798153975555772">"Tambahkan kartu"</string>
+ <string name="accessibility_qs_edit_tile_move_to_position" msgid="5198161544045930556">"Pindahkan ke <xliff:g id="POSITION">%1$d</xliff:g>"</string>
+ <string name="accessibility_qs_edit_tile_add_to_position" msgid="9029163095148274690">"Tambahkan ke posisi <xliff:g id="POSITION">%1$d</xliff:g>"</string>
+ <string name="accessibility_qs_edit_position" msgid="4509277359815711830">"Posisi <xliff:g id="POSITION">%1$d</xliff:g>"</string>
<string name="accessibility_desc_quick_settings_edit" msgid="741658939453595297">"Editor setelan cepat."</string>
<string name="accessibility_desc_notification_icon" msgid="7331265967584178674">"Notifikasi <xliff:g id="ID_1">%1$s</xliff:g>: <xliff:g id="ID_2">%2$s</xliff:g>"</string>
<string name="accessibility_quick_settings_settings" msgid="7098489591715844713">"Buka setelan."</string>
@@ -1067,4 +1068,11 @@
<string name="controls_added_tooltip" msgid="4842812921719153085">"Tahan Tombol daya untuk melihat kontrol baru"</string>
<string name="controls_menu_add" msgid="4447246119229920050">"Tambahkan kontrol"</string>
<string name="controls_menu_edit" msgid="890623986951347062">"Edit kontrol"</string>
+ <string name="media_output_dialog_add_output" msgid="5642703238877329518">"Tambahkan output"</string>
+ <string name="media_output_dialog_group" msgid="5571251347877452212">"Grup"</string>
+ <string name="media_output_dialog_single_device" msgid="3102758980643351058">"1 perangkat dipilih"</string>
+ <string name="media_output_dialog_multiple_devices" msgid="1093771040315422350">"<xliff:g id="COUNT">%1$d</xliff:g> perangkat dipilih"</string>
+ <string name="media_output_dialog_disconnected" msgid="1834473104836986046">"<xliff:g id="DEVICE_NAME">%1$s</xliff:g> (terputus)"</string>
+ <string name="media_output_dialog_connect_failed" msgid="3225190634236259010">"Tidak dapat terhubung. Coba lagi."</string>
+ <string name="media_output_dialog_pairing_new" msgid="9099497976087485862">"Sambungkan perangkat baru"</string>
</resources>
diff --git a/packages/SystemUI/res/values-is/strings.xml b/packages/SystemUI/res/values-is/strings.xml
index a1c4a7f651c7..1f8f2aa2fc27 100644
--- a/packages/SystemUI/res/values-is/strings.xml
+++ b/packages/SystemUI/res/values-is/strings.xml
@@ -861,10 +861,10 @@
<string name="right_keycode" msgid="2480715509844798438">"Lykiltákn til hægri"</string>
<string name="left_icon" msgid="5036278531966897006">"Tákn til vinstri"</string>
<string name="right_icon" msgid="1103955040645237425">"Tákn til hægri"</string>
- <string name="drag_to_add_tiles" msgid="8933270127508303672">"Haltu inni og dragðu til að bæta við reitum"</string>
+ <string name="drag_to_add_tiles" msgid="8933270127508303672">"Haltu inni og dragðu til að bæta við flísum"</string>
<string name="drag_to_rearrange_tiles" msgid="2143204300089638620">"Haltu og dragðu til að endurraða flísum"</string>
<string name="drag_to_remove_tiles" msgid="4682194717573850385">"Dragðu hingað til að fjarlægja"</string>
- <string name="drag_to_remove_disabled" msgid="933046987838658850">"Reitirnir mega ekki vera færri en <xliff:g id="MIN_NUM_TILES">%1$d</xliff:g>"</string>
+ <string name="drag_to_remove_disabled" msgid="933046987838658850">"Flísarnar mega ekki vera færri en <xliff:g id="MIN_NUM_TILES">%1$d</xliff:g>"</string>
<string name="qs_edit" msgid="5583565172803472437">"Breyta"</string>
<string name="tuner_time" msgid="2450785840990529997">"Tími"</string>
<string-array name="clock_options">
@@ -879,12 +879,13 @@
</string-array>
<string name="tuner_low_priority" msgid="8412666814123009820">"Sýna tákn fyrir tilkynningar með litlum forgangi"</string>
<string name="other" msgid="429768510980739978">"Annað"</string>
- <string name="accessibility_qs_edit_tile_label" msgid="9079791448815232967">"Staða <xliff:g id="POSITION">%1$d</xliff:g>, <xliff:g id="TILE_NAME">%2$s</xliff:g>. Ýttu tvisvar til að breyta."</string>
- <string name="accessibility_qs_edit_add_tile_label" msgid="8292218072049068613">"<xliff:g id="TILE_NAME">%1$s</xliff:g>. Ýttu tvisvar til að bæta við."</string>
- <string name="accessibility_qs_edit_move_tile" msgid="6027997446473163426">"Færa <xliff:g id="TILE_NAME">%1$s</xliff:g>"</string>
- <string name="accessibility_qs_edit_remove_tile" msgid="3406781901949899624">"Fjarlægja <xliff:g id="TILE_NAME">%1$s</xliff:g>"</string>
- <string name="accessibility_qs_edit_tile_add" msgid="6289879620154587233">"Bæta <xliff:g id="TILE_NAME">%1$s</xliff:g> við í stöðu <xliff:g id="POSITION">%2$d</xliff:g>"</string>
- <string name="accessibility_qs_edit_tile_move" msgid="4841770637244326837">"Færa <xliff:g id="TILE_NAME">%1$s</xliff:g> í stöðu <xliff:g id="POSITION">%2$d</xliff:g>"</string>
+ <string name="accessibility_qs_edit_remove_tile_action" msgid="775511891457193480">"fjarlægja reit"</string>
+ <string name="accessibility_qs_edit_tile_add_action" msgid="5051211910345301833">"bæta reit við aftast"</string>
+ <string name="accessibility_qs_edit_tile_start_move" msgid="2009373939914517817">"Færa reit"</string>
+ <string name="accessibility_qs_edit_tile_start_add" msgid="7560798153975555772">"Bæta reit við"</string>
+ <string name="accessibility_qs_edit_tile_move_to_position" msgid="5198161544045930556">"Færa í <xliff:g id="POSITION">%1$d</xliff:g>"</string>
+ <string name="accessibility_qs_edit_tile_add_to_position" msgid="9029163095148274690">"Bæta við í stöðu <xliff:g id="POSITION">%1$d</xliff:g>"</string>
+ <string name="accessibility_qs_edit_position" msgid="4509277359815711830">"Staða <xliff:g id="POSITION">%1$d</xliff:g>"</string>
<string name="accessibility_desc_quick_settings_edit" msgid="741658939453595297">"Flýtistillingaritill."</string>
<string name="accessibility_desc_notification_icon" msgid="7331265967584178674">"<xliff:g id="ID_1">%1$s</xliff:g> tilkynning: <xliff:g id="ID_2">%2$s</xliff:g>"</string>
<string name="accessibility_quick_settings_settings" msgid="7098489591715844713">"Opna stillingar."</string>
@@ -1067,4 +1068,11 @@
<string name="controls_added_tooltip" msgid="4842812921719153085">"Haltu aflrofanum inni til að sjá nýjar stýringar"</string>
<string name="controls_menu_add" msgid="4447246119229920050">"Bæta við stýringum"</string>
<string name="controls_menu_edit" msgid="890623986951347062">"Breyta stýringum"</string>
+ <string name="media_output_dialog_add_output" msgid="5642703238877329518">"Bæta við úttaki"</string>
+ <string name="media_output_dialog_group" msgid="5571251347877452212">"Hópur"</string>
+ <string name="media_output_dialog_single_device" msgid="3102758980643351058">"1 tæki valið"</string>
+ <string name="media_output_dialog_multiple_devices" msgid="1093771040315422350">"<xliff:g id="COUNT">%1$d</xliff:g> tæki valin"</string>
+ <string name="media_output_dialog_disconnected" msgid="1834473104836986046">"<xliff:g id="DEVICE_NAME">%1$s</xliff:g> (aftengt)"</string>
+ <string name="media_output_dialog_connect_failed" msgid="3225190634236259010">"Tenging mistókst. Reyndu aftur."</string>
+ <string name="media_output_dialog_pairing_new" msgid="9099497976087485862">"Para nýtt tæki"</string>
</resources>
diff --git a/packages/SystemUI/res/values-it/strings.xml b/packages/SystemUI/res/values-it/strings.xml
index be017e11330a..eb76a4b58fcd 100644
--- a/packages/SystemUI/res/values-it/strings.xml
+++ b/packages/SystemUI/res/values-it/strings.xml
@@ -716,7 +716,7 @@
<string name="notification_channel_summary_default_with_bubbles" msgid="1782419896613644568">"Può suonare o vibrare in base alle impostazioni del telefono. Conversazioni dalla bolla <xliff:g id="APP_NAME">%1$s</xliff:g> per impostazione predefinita."</string>
<string name="notification_channel_summary_bubble" msgid="7235935211580860537">"Mantiene la tua attenzione con una scorciatoia mobile a questi contenuti."</string>
<string name="notification_channel_summary_automatic" msgid="5813109268050235275">"Fai stabilire al sistema se questa notifica deve emettere suoni o vibrazioni"</string>
- <string name="notification_channel_summary_priority" msgid="7952654515769021553">"Viene mostrata in cima alla sezione delle conversazioni, appare sotto forma di bolla mobile, mostra l\'immagine del profilo nella schermata di blocco"</string>
+ <string name="notification_channel_summary_priority" msgid="7952654515769021553">"Appare in cima alla sezione delle conversazioni e sotto forma di bolla mobile, mostra l\'immagine del profilo nella schermata di blocco"</string>
<string name="notification_conversation_channel_settings" msgid="2409977688430606835">"Impostazioni"</string>
<string name="notification_priority_title" msgid="2079708866333537093">"Priorità"</string>
<string name="no_shortcut" msgid="8257177117568230126">"<xliff:g id="APP_NAME">%1$s</xliff:g> non supporta le funzionalità delle conversazioni"</string>
@@ -879,12 +879,13 @@
</string-array>
<string name="tuner_low_priority" msgid="8412666814123009820">"Mostra icone di notifiche con priorità bassa"</string>
<string name="other" msgid="429768510980739978">"Altro"</string>
- <string name="accessibility_qs_edit_tile_label" msgid="9079791448815232967">"Posizione <xliff:g id="POSITION">%1$d</xliff:g>, <xliff:g id="TILE_NAME">%2$s</xliff:g>. Tocca due volte per modificare."</string>
- <string name="accessibility_qs_edit_add_tile_label" msgid="8292218072049068613">"<xliff:g id="TILE_NAME">%1$s</xliff:g>. Tocca due volte per aggiungere."</string>
- <string name="accessibility_qs_edit_move_tile" msgid="6027997446473163426">"Sposta <xliff:g id="TILE_NAME">%1$s</xliff:g>"</string>
- <string name="accessibility_qs_edit_remove_tile" msgid="3406781901949899624">"Rimuovi <xliff:g id="TILE_NAME">%1$s</xliff:g>"</string>
- <string name="accessibility_qs_edit_tile_add" msgid="6289879620154587233">"Aggiungi il riquadro <xliff:g id="TILE_NAME">%1$s</xliff:g> alla posizione <xliff:g id="POSITION">%2$d</xliff:g>"</string>
- <string name="accessibility_qs_edit_tile_move" msgid="4841770637244326837">"Sposta il riquadro <xliff:g id="TILE_NAME">%1$s</xliff:g> nella posizione <xliff:g id="POSITION">%2$d</xliff:g>"</string>
+ <string name="accessibility_qs_edit_remove_tile_action" msgid="775511891457193480">"rimuovere il riquadro"</string>
+ <string name="accessibility_qs_edit_tile_add_action" msgid="5051211910345301833">"aggiungere il riquadro alla fine"</string>
+ <string name="accessibility_qs_edit_tile_start_move" msgid="2009373939914517817">"Sposta riquadro"</string>
+ <string name="accessibility_qs_edit_tile_start_add" msgid="7560798153975555772">"Aggiungi riquadro"</string>
+ <string name="accessibility_qs_edit_tile_move_to_position" msgid="5198161544045930556">"Sposta nella posizione <xliff:g id="POSITION">%1$d</xliff:g>"</string>
+ <string name="accessibility_qs_edit_tile_add_to_position" msgid="9029163095148274690">"Aggiungi alla posizione <xliff:g id="POSITION">%1$d</xliff:g>"</string>
+ <string name="accessibility_qs_edit_position" msgid="4509277359815711830">"Posizione <xliff:g id="POSITION">%1$d</xliff:g>"</string>
<string name="accessibility_desc_quick_settings_edit" msgid="741658939453595297">"Editor di impostazioni rapide."</string>
<string name="accessibility_desc_notification_icon" msgid="7331265967584178674">"Notifica di <xliff:g id="ID_1">%1$s</xliff:g>: <xliff:g id="ID_2">%2$s</xliff:g>"</string>
<string name="accessibility_quick_settings_settings" msgid="7098489591715844713">"Apri le impostazioni."</string>
@@ -967,7 +968,7 @@
<string name="auto_saver_enabled_text" msgid="7889491183116752719">"Il Risparmio energetico verrà attivato automaticamente quando la carica della batteria sarà inferiore a <xliff:g id="PERCENTAGE">%d</xliff:g>%%."</string>
<string name="open_saver_setting_action" msgid="2111461909782935190">"Impostazioni"</string>
<string name="auto_saver_okay_action" msgid="7815925750741935386">"OK"</string>
- <string name="heap_dump_tile_name" msgid="2464189856478823046">"Esegui dump heap SysUI"</string>
+ <string name="heap_dump_tile_name" msgid="2464189856478823046">"Dump heap SysUI"</string>
<string name="ongoing_privacy_chip_content_single_app" msgid="2969750601815230385">"L\'app <xliff:g id="APP">%1$s</xliff:g> sta usando <xliff:g id="TYPES_LIST">%2$s</xliff:g>."</string>
<string name="ongoing_privacy_chip_content_multiple_apps" msgid="8341216022442383954">"Le app stanno usando <xliff:g id="TYPES_LIST">%s</xliff:g>."</string>
<string name="ongoing_privacy_dialog_separator" msgid="1866222499727706187">", "</string>
@@ -1067,4 +1068,11 @@
<string name="controls_added_tooltip" msgid="4842812921719153085">"Tieni premuto il tasto di accensione per visualizzare i nuovi controlli"</string>
<string name="controls_menu_add" msgid="4447246119229920050">"Aggiungi controlli"</string>
<string name="controls_menu_edit" msgid="890623986951347062">"Modifica controlli"</string>
+ <string name="media_output_dialog_add_output" msgid="5642703238877329518">"Aggiungi uscite"</string>
+ <string name="media_output_dialog_group" msgid="5571251347877452212">"Gruppo"</string>
+ <string name="media_output_dialog_single_device" msgid="3102758980643351058">"1 dispositivo selezionato"</string>
+ <string name="media_output_dialog_multiple_devices" msgid="1093771040315422350">"<xliff:g id="COUNT">%1$d</xliff:g> dispositivi selezionati"</string>
+ <string name="media_output_dialog_disconnected" msgid="1834473104836986046">"<xliff:g id="DEVICE_NAME">%1$s</xliff:g> (disconnesso)"</string>
+ <string name="media_output_dialog_connect_failed" msgid="3225190634236259010">"Impossibile connettersi. Riprova."</string>
+ <string name="media_output_dialog_pairing_new" msgid="9099497976087485862">"Accoppia nuovo dispositivo"</string>
</resources>
diff --git a/packages/SystemUI/res/values-iw/strings.xml b/packages/SystemUI/res/values-iw/strings.xml
index 1baa627c5a7d..31e6befca5ff 100644
--- a/packages/SystemUI/res/values-iw/strings.xml
+++ b/packages/SystemUI/res/values-iw/strings.xml
@@ -889,12 +889,13 @@
</string-array>
<string name="tuner_low_priority" msgid="8412666814123009820">"הצגת סמלי התראות בעדיפות נמוכה"</string>
<string name="other" msgid="429768510980739978">"אחר"</string>
- <string name="accessibility_qs_edit_tile_label" msgid="9079791448815232967">"מיקום <xliff:g id="POSITION">%1$d</xliff:g>, <xliff:g id="TILE_NAME">%2$s</xliff:g>. הקש פעמיים כדי לערוך."</string>
- <string name="accessibility_qs_edit_add_tile_label" msgid="8292218072049068613">"<xliff:g id="TILE_NAME">%1$s</xliff:g>. הקש פעמיים כדי להוסיף."</string>
- <string name="accessibility_qs_edit_move_tile" msgid="6027997446473163426">"הזזת <xliff:g id="TILE_NAME">%1$s</xliff:g>"</string>
- <string name="accessibility_qs_edit_remove_tile" msgid="3406781901949899624">"הסרת <xliff:g id="TILE_NAME">%1$s</xliff:g>"</string>
- <string name="accessibility_qs_edit_tile_add" msgid="6289879620154587233">"הוספת <xliff:g id="TILE_NAME">%1$s</xliff:g> למיקום <xliff:g id="POSITION">%2$d</xliff:g>"</string>
- <string name="accessibility_qs_edit_tile_move" msgid="4841770637244326837">"העברת <xliff:g id="TILE_NAME">%1$s</xliff:g> למיקום <xliff:g id="POSITION">%2$d</xliff:g>"</string>
+ <string name="accessibility_qs_edit_remove_tile_action" msgid="775511891457193480">"הסרת האריח"</string>
+ <string name="accessibility_qs_edit_tile_add_action" msgid="5051211910345301833">"הוספת האריח לקצה"</string>
+ <string name="accessibility_qs_edit_tile_start_move" msgid="2009373939914517817">"הזזת האריח"</string>
+ <string name="accessibility_qs_edit_tile_start_add" msgid="7560798153975555772">"הוספת אריח"</string>
+ <string name="accessibility_qs_edit_tile_move_to_position" msgid="5198161544045930556">"העברה אל <xliff:g id="POSITION">%1$d</xliff:g>"</string>
+ <string name="accessibility_qs_edit_tile_add_to_position" msgid="9029163095148274690">"הוספה למיקום <xliff:g id="POSITION">%1$d</xliff:g>"</string>
+ <string name="accessibility_qs_edit_position" msgid="4509277359815711830">"מיקום <xliff:g id="POSITION">%1$d</xliff:g>"</string>
<string name="accessibility_desc_quick_settings_edit" msgid="741658939453595297">"עורך הגדרות מהירות."</string>
<string name="accessibility_desc_notification_icon" msgid="7331265967584178674">"התראות <xliff:g id="ID_1">%1$s</xliff:g>: <xliff:g id="ID_2">%2$s</xliff:g>"</string>
<string name="accessibility_quick_settings_settings" msgid="7098489591715844713">"פתיחת הגדרות."</string>
@@ -1079,4 +1080,11 @@
<string name="controls_added_tooltip" msgid="4842812921719153085">"ניתן ללחוץ על לחצן ההפעלה כדי להציג פקדים חדשים"</string>
<string name="controls_menu_add" msgid="4447246119229920050">"הוספת פקדים"</string>
<string name="controls_menu_edit" msgid="890623986951347062">"עריכת פקדים"</string>
+ <string name="media_output_dialog_add_output" msgid="5642703238877329518">"הוספת מכשירי פלט"</string>
+ <string name="media_output_dialog_group" msgid="5571251347877452212">"קבוצה"</string>
+ <string name="media_output_dialog_single_device" msgid="3102758980643351058">"נבחר מכשיר אחד"</string>
+ <string name="media_output_dialog_multiple_devices" msgid="1093771040315422350">"נבחרו <xliff:g id="COUNT">%1$d</xliff:g> מכשירים"</string>
+ <string name="media_output_dialog_disconnected" msgid="1834473104836986046">"<xliff:g id="DEVICE_NAME">%1$s</xliff:g> (מנותק)"</string>
+ <string name="media_output_dialog_connect_failed" msgid="3225190634236259010">"לא ניתן היה להתחבר. יש לנסות שוב."</string>
+ <string name="media_output_dialog_pairing_new" msgid="9099497976087485862">"התאמה של מכשיר חדש"</string>
</resources>
diff --git a/packages/SystemUI/res/values-ja/strings.xml b/packages/SystemUI/res/values-ja/strings.xml
index fd1aa57f39d9..b358f3d866dc 100644
--- a/packages/SystemUI/res/values-ja/strings.xml
+++ b/packages/SystemUI/res/values-ja/strings.xml
@@ -879,12 +879,13 @@
</string-array>
<string name="tuner_low_priority" msgid="8412666814123009820">"優先度の低い通知アイコンを表示"</string>
<string name="other" msgid="429768510980739978">"その他"</string>
- <string name="accessibility_qs_edit_tile_label" msgid="9079791448815232967">"ポジション <xliff:g id="POSITION">%1$d</xliff:g> の <xliff:g id="TILE_NAME">%2$s</xliff:g> を編集するにはダブルタップします。"</string>
- <string name="accessibility_qs_edit_add_tile_label" msgid="8292218072049068613">"<xliff:g id="TILE_NAME">%1$s</xliff:g> を追加するにはダブルタップします。"</string>
- <string name="accessibility_qs_edit_move_tile" msgid="6027997446473163426">"<xliff:g id="TILE_NAME">%1$s</xliff:g> を移動します"</string>
- <string name="accessibility_qs_edit_remove_tile" msgid="3406781901949899624">"<xliff:g id="TILE_NAME">%1$s</xliff:g> を削除します"</string>
- <string name="accessibility_qs_edit_tile_add" msgid="6289879620154587233">"<xliff:g id="TILE_NAME">%1$s</xliff:g> をポジション <xliff:g id="POSITION">%2$d</xliff:g> に追加"</string>
- <string name="accessibility_qs_edit_tile_move" msgid="4841770637244326837">"<xliff:g id="TILE_NAME">%1$s</xliff:g> をポジション <xliff:g id="POSITION">%2$d</xliff:g> に移動"</string>
+ <string name="accessibility_qs_edit_remove_tile_action" msgid="775511891457193480">"タイルを削除"</string>
+ <string name="accessibility_qs_edit_tile_add_action" msgid="5051211910345301833">"タイルを最後に追加"</string>
+ <string name="accessibility_qs_edit_tile_start_move" msgid="2009373939914517817">"タイルを移動"</string>
+ <string name="accessibility_qs_edit_tile_start_add" msgid="7560798153975555772">"タイルを追加"</string>
+ <string name="accessibility_qs_edit_tile_move_to_position" msgid="5198161544045930556">"<xliff:g id="POSITION">%1$d</xliff:g> に移動"</string>
+ <string name="accessibility_qs_edit_tile_add_to_position" msgid="9029163095148274690">"ポジション <xliff:g id="POSITION">%1$d</xliff:g> に追加"</string>
+ <string name="accessibility_qs_edit_position" msgid="4509277359815711830">"位置: <xliff:g id="POSITION">%1$d</xliff:g>"</string>
<string name="accessibility_desc_quick_settings_edit" msgid="741658939453595297">"クイック設定エディタ"</string>
<string name="accessibility_desc_notification_icon" msgid="7331265967584178674">"<xliff:g id="ID_1">%1$s</xliff:g> の通知: <xliff:g id="ID_2">%2$s</xliff:g>"</string>
<string name="accessibility_quick_settings_settings" msgid="7098489591715844713">"設定を開きます。"</string>
@@ -1067,4 +1068,11 @@
<string name="controls_added_tooltip" msgid="4842812921719153085">"電源ボタンを長押しすると、新しいコントロールが表示されます"</string>
<string name="controls_menu_add" msgid="4447246119229920050">"コントロールを追加"</string>
<string name="controls_menu_edit" msgid="890623986951347062">"コントロールを編集"</string>
+ <string name="media_output_dialog_add_output" msgid="5642703238877329518">"出力の追加"</string>
+ <string name="media_output_dialog_group" msgid="5571251347877452212">"グループ"</string>
+ <string name="media_output_dialog_single_device" msgid="3102758980643351058">"選択したデバイス: 1 台"</string>
+ <string name="media_output_dialog_multiple_devices" msgid="1093771040315422350">"選択したデバイス: <xliff:g id="COUNT">%1$d</xliff:g> 台"</string>
+ <string name="media_output_dialog_disconnected" msgid="1834473104836986046">"<xliff:g id="DEVICE_NAME">%1$s</xliff:g>(未接続)"</string>
+ <string name="media_output_dialog_connect_failed" msgid="3225190634236259010">"接続できませんでした。もう一度お試しください。"</string>
+ <string name="media_output_dialog_pairing_new" msgid="9099497976087485862">"新しいデバイスとのペア設定"</string>
</resources>
diff --git a/packages/SystemUI/res/values-ka/strings.xml b/packages/SystemUI/res/values-ka/strings.xml
index 86451b8da7a5..4e85d608a72b 100644
--- a/packages/SystemUI/res/values-ka/strings.xml
+++ b/packages/SystemUI/res/values-ka/strings.xml
@@ -716,7 +716,7 @@
<string name="notification_channel_summary_default_with_bubbles" msgid="1782419896613644568">"დარეკვა ან ვიბრაცია ტელეფონის პარამეტრების მიხედვით. მიმოწერები <xliff:g id="APP_NAME">%1$s</xliff:g>-ის ბუშტიდან, ნაგულისხმევად."</string>
<string name="notification_channel_summary_bubble" msgid="7235935211580860537">"იპყრობს თქვენს ყურადღებას ამ კონტენტის მოლივლივე მალსახმობით."</string>
<string name="notification_channel_summary_automatic" msgid="5813109268050235275">"სისტემისთვის ისეთი უფლების მინიჭება, რომ მან განსაზღვროს, ამ შეტყობინებამ ხმოვანი სიგნალი უნდა აამოქმედოს თუ ვიბრაცია"</string>
- <string name="notification_channel_summary_priority" msgid="7952654515769021553">"გამოჩნდება მიმოწერების სექციის ზედა ნაწილში მოლივლივე ბუშტის სახით, აჩვენებს პროფილის სურათს ჩაკეტილ ეკრანზე"</string>
+ <string name="notification_channel_summary_priority" msgid="7952654515769021553">"გამოჩნდება მიმოწერების ზედა ნაწილში ბუშტის სახით, აჩვენებს პროფილის სურათს ჩაკეტილ ეკრანზე"</string>
<string name="notification_conversation_channel_settings" msgid="2409977688430606835">"პარამეტრები"</string>
<string name="notification_priority_title" msgid="2079708866333537093">"პრიორიტეტი"</string>
<string name="no_shortcut" msgid="8257177117568230126">"<xliff:g id="APP_NAME">%1$s</xliff:g>-ს არ აქვს მიმოწერის ფუნქციების მხარდაჭერა"</string>
@@ -879,12 +879,13 @@
</string-array>
<string name="tuner_low_priority" msgid="8412666814123009820">"დაბალი პრიორიტეტის მქონე შეტყობინებების ხატულების ჩვენება"</string>
<string name="other" msgid="429768510980739978">"სხვა"</string>
- <string name="accessibility_qs_edit_tile_label" msgid="9079791448815232967">"პოზიცია <xliff:g id="POSITION">%1$d</xliff:g>, <xliff:g id="TILE_NAME">%2$s</xliff:g>. რედაქტირებისთვის, შეეხეთ ორმაგად."</string>
- <string name="accessibility_qs_edit_add_tile_label" msgid="8292218072049068613">"<xliff:g id="TILE_NAME">%1$s</xliff:g>. დასამატებლად, შეეხეთ ორმაგად."</string>
- <string name="accessibility_qs_edit_move_tile" msgid="6027997446473163426">"<xliff:g id="TILE_NAME">%1$s</xliff:g>-ის გადატანა"</string>
- <string name="accessibility_qs_edit_remove_tile" msgid="3406781901949899624">"<xliff:g id="TILE_NAME">%1$s</xliff:g>-ის წაშლა"</string>
- <string name="accessibility_qs_edit_tile_add" msgid="6289879620154587233">"<xliff:g id="TILE_NAME">%1$s</xliff:g>-ის დამატება პოზიციაზე <xliff:g id="POSITION">%2$d</xliff:g>"</string>
- <string name="accessibility_qs_edit_tile_move" msgid="4841770637244326837">"<xliff:g id="TILE_NAME">%1$s</xliff:g>-ის გადატანა პოზიციაზე <xliff:g id="POSITION">%2$d</xliff:g>"</string>
+ <string name="accessibility_qs_edit_remove_tile_action" msgid="775511891457193480">"მოზაიკის ფილის წაშლა"</string>
+ <string name="accessibility_qs_edit_tile_add_action" msgid="5051211910345301833">"ფილის ბოლოში დამატება"</string>
+ <string name="accessibility_qs_edit_tile_start_move" msgid="2009373939914517817">"მოზაიკის გადატანა"</string>
+ <string name="accessibility_qs_edit_tile_start_add" msgid="7560798153975555772">"მოზაიკის დამატება"</string>
+ <string name="accessibility_qs_edit_tile_move_to_position" msgid="5198161544045930556">"გადატანა <xliff:g id="POSITION">%1$d</xliff:g>-ზე"</string>
+ <string name="accessibility_qs_edit_tile_add_to_position" msgid="9029163095148274690">"დამატება პოზიციაზე <xliff:g id="POSITION">%1$d</xliff:g>"</string>
+ <string name="accessibility_qs_edit_position" msgid="4509277359815711830">"პოზიცია <xliff:g id="POSITION">%1$d</xliff:g>"</string>
<string name="accessibility_desc_quick_settings_edit" msgid="741658939453595297">"სწრაფი პარამეტრების რედაქტორი."</string>
<string name="accessibility_desc_notification_icon" msgid="7331265967584178674">"<xliff:g id="ID_1">%1$s</xliff:g> შეტყობინება: <xliff:g id="ID_2">%2$s</xliff:g>"</string>
<string name="accessibility_quick_settings_settings" msgid="7098489591715844713">"პარამეტრების გახსნა."</string>
@@ -1067,4 +1068,11 @@
<string name="controls_added_tooltip" msgid="4842812921719153085">"ხანგრძლივად დააჭირეთ ჩართვის ღილაკს მართვის ახალი საშუალებების სანახავად"</string>
<string name="controls_menu_add" msgid="4447246119229920050">"მართვის საშუალებების დამატება"</string>
<string name="controls_menu_edit" msgid="890623986951347062">"მართვის საშუალებათა რედაქტირება"</string>
+ <string name="media_output_dialog_add_output" msgid="5642703238877329518">"მედია-გამოსავლების დამატება"</string>
+ <string name="media_output_dialog_group" msgid="5571251347877452212">"ჯგუფი"</string>
+ <string name="media_output_dialog_single_device" msgid="3102758980643351058">"არჩეულია 1 მოწყობილობა"</string>
+ <string name="media_output_dialog_multiple_devices" msgid="1093771040315422350">"არჩეულია <xliff:g id="COUNT">%1$d</xliff:g> მოწყობილობა"</string>
+ <string name="media_output_dialog_disconnected" msgid="1834473104836986046">"<xliff:g id="DEVICE_NAME">%1$s</xliff:g> (კავშირი გაწყვეტილია)"</string>
+ <string name="media_output_dialog_connect_failed" msgid="3225190634236259010">"დაკავშირება ვერ მოხერხდა. ცადეთ ხელახლა."</string>
+ <string name="media_output_dialog_pairing_new" msgid="9099497976087485862">"ახალი მოწყობილობის დაწყვილება"</string>
</resources>
diff --git a/packages/SystemUI/res/values-kk/strings.xml b/packages/SystemUI/res/values-kk/strings.xml
index ed6c05d60bb6..f17210602b83 100644
--- a/packages/SystemUI/res/values-kk/strings.xml
+++ b/packages/SystemUI/res/values-kk/strings.xml
@@ -28,7 +28,7 @@
<string name="battery_low_percent_format" msgid="4276661262843170964">"<xliff:g id="PERCENTAGE">%s</xliff:g> қалды"</string>
<string name="battery_low_percent_format_hybrid" msgid="3985614339605686167">"Пайдалану барысына байланысты <xliff:g id="PERCENTAGE">%1$s</xliff:g> заряд, шамамен <xliff:g id="TIME">%2$s</xliff:g> қалды"</string>
<string name="battery_low_percent_format_hybrid_short" msgid="5917433188456218857">"<xliff:g id="PERCENTAGE">%1$s</xliff:g> заряд, шамамен <xliff:g id="TIME">%2$s</xliff:g> қалды"</string>
- <string name="battery_low_percent_format_saver_started" msgid="4968468824040940688">"<xliff:g id="PERCENTAGE">%s</xliff:g> қалды. Battery Saver қосулы."</string>
+ <string name="battery_low_percent_format_saver_started" msgid="4968468824040940688">"<xliff:g id="PERCENTAGE">%s</xliff:g> қалды. Батареяны үнемдеу режимі қосулы."</string>
<string name="invalid_charger" msgid="4370074072117767416">"USB арқылы зарядтау мүмкін емес. Құрылғымен бірге берілген зарядтау құралын пайдаланыңыз."</string>
<string name="invalid_charger_title" msgid="938685362320735167">"USB арқылы зарядтау мүмкін емес"</string>
<string name="invalid_charger_text" msgid="2339310107232691577">"Құрылғымен бірге берілген зарядтау құралын пайдаланыңыз"</string>
@@ -419,7 +419,7 @@
<string name="quick_settings_night_secondary_label_on_at" msgid="3584738542293528235">"Қосылу уақыты: <xliff:g id="TIME">%s</xliff:g>"</string>
<string name="quick_settings_secondary_label_until" msgid="1883981263191927372">"<xliff:g id="TIME">%s</xliff:g> дейін"</string>
<string name="quick_settings_ui_mode_night_label" msgid="1398928270610780470">"Қараңғы тақырып"</string>
- <string name="quick_settings_dark_mode_secondary_label_battery_saver" msgid="4990712734503013251">"Battery Saver"</string>
+ <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>
@@ -711,14 +711,14 @@
<string name="notification_bubble_title" msgid="8330481035191903164">"Көпіршік"</string>
<string name="notification_automatic_title" msgid="3745465364578762652">"Автоматты"</string>
<string name="notification_channel_summary_low" msgid="4860617986908931158">"Дыбыс не діріл қолданылмайды"</string>
- <string name="notification_conversation_summary_low" msgid="1734433426085468009">"Дыбыс не діріл қолданылмайды, төменде әңгімелер бөлімінде шығады"</string>
+ <string name="notification_conversation_summary_low" msgid="1734433426085468009">"Дыбыс не діріл қолданылмайды, әңгімелер бөлімінің төмен жағында шығады"</string>
<string name="notification_channel_summary_default" msgid="3282930979307248890">"Телефон параметрлеріне байланысты шылдырлауы не дірілдеуі мүмкін"</string>
<string name="notification_channel_summary_default_with_bubbles" msgid="1782419896613644568">"Телефон параметрлеріне байланысты шылдырлауы не дірілдеуі мүмкін. <xliff:g id="APP_NAME">%1$s</xliff:g> чаттары әдепкісінше қалқымалы етіп көрсетіледі."</string>
<string name="notification_channel_summary_bubble" msgid="7235935211580860537">"Осы мазмұнға бекітілген қалқымалы таңбашамен назарыңызды өзіне тартады."</string>
<string name="notification_channel_summary_automatic" msgid="5813109268050235275">"Хабарландыру дыбысының немесе дірілдің қосылуын жүйе анықтайтын болады"</string>
<string name="notification_channel_summary_priority" msgid="7952654515769021553">"Әңгімелер бөлімінің жоғарғы жағында тұрады, қалқыма хабар түрінде шығады, құлыптаулы экранда профиль суретін көрсетеді"</string>
<string name="notification_conversation_channel_settings" msgid="2409977688430606835">"Параметрлер"</string>
- <string name="notification_priority_title" msgid="2079708866333537093">"Маңыздылығы"</string>
+ <string name="notification_priority_title" msgid="2079708866333537093">"Маңызды"</string>
<string name="no_shortcut" msgid="8257177117568230126">"<xliff:g id="APP_NAME">%1$s</xliff:g> әңгімелесу функцияларын қолдамайды."</string>
<string name="bubble_overflow_empty_title" msgid="3120029421991510842">"Жақындағы қалқыма хабарлар жоқ"</string>
<string name="bubble_overflow_empty_subtitle" msgid="2030874469510497397">"Соңғы және жабылған қалқыма хабарлар осы жерде көрсетіледі."</string>
@@ -774,7 +774,7 @@
</plurals>
<string name="battery_panel_title" msgid="5931157246673665963">"Батареяны пайдалану"</string>
<string name="battery_detail_charging_summary" msgid="8821202155297559706">"Зарядтау кезінде Батарея үнемдегіш қол жетімді емес"</string>
- <string name="battery_detail_switch_title" msgid="6940976502957380405">"Battery Saver"</string>
+ <string name="battery_detail_switch_title" msgid="6940976502957380405">"Батареяны үнемдеу режимі"</string>
<string name="battery_detail_switch_summary" msgid="3668748557848025990">"Өнімділікті және фондық деректерді азайтады"</string>
<string name="keyboard_key_button_template" msgid="8005673627272051429">"<xliff:g id="NAME">%1$s</xliff:g> түймесі"</string>
<string name="keyboard_key_home" msgid="3734400625170020657">"Home"</string>
@@ -865,7 +865,7 @@
<string name="drag_to_rearrange_tiles" msgid="2143204300089638620">"Элементтердің ретін өзгерту үшін оларды басып тұрып сүйреңіз"</string>
<string name="drag_to_remove_tiles" msgid="4682194717573850385">"Керексіздерін осы жерге сүйреңіз"</string>
<string name="drag_to_remove_disabled" msgid="933046987838658850">"Кемінде <xliff:g id="MIN_NUM_TILES">%1$d</xliff:g> бөлшек қажет."</string>
- <string name="qs_edit" msgid="5583565172803472437">"Өңдеу"</string>
+ <string name="qs_edit" msgid="5583565172803472437">"Өзгерту"</string>
<string name="tuner_time" msgid="2450785840990529997">"Уақыт"</string>
<string-array name="clock_options">
<item msgid="3986445361435142273">"Сағаттарды, минуттарды және секундтарды көрсету"</item>
@@ -879,12 +879,13 @@
</string-array>
<string name="tuner_low_priority" msgid="8412666814123009820">"Маңызды емес хабарландыру белгішелерін көрсету"</string>
<string name="other" msgid="429768510980739978">"Басқа"</string>
- <string name="accessibility_qs_edit_tile_label" msgid="9079791448815232967">"<xliff:g id="POSITION">%1$d</xliff:g> орны, <xliff:g id="TILE_NAME">%2$s</xliff:g>. Өңдеу үшін екі рет түртіңіз."</string>
- <string name="accessibility_qs_edit_add_tile_label" msgid="8292218072049068613">"<xliff:g id="TILE_NAME">%1$s</xliff:g>. Қосу үшін екі рет түртіңіз."</string>
- <string name="accessibility_qs_edit_move_tile" msgid="6027997446473163426">"<xliff:g id="TILE_NAME">%1$s</xliff:g> жылжыту"</string>
- <string name="accessibility_qs_edit_remove_tile" msgid="3406781901949899624">"<xliff:g id="TILE_NAME">%1$s</xliff:g> жою"</string>
- <string name="accessibility_qs_edit_tile_add" msgid="6289879620154587233">"<xliff:g id="TILE_NAME">%1$s</xliff:g> бөлшегін <xliff:g id="POSITION">%2$d</xliff:g>-позицияға енгізу"</string>
- <string name="accessibility_qs_edit_tile_move" msgid="4841770637244326837">"<xliff:g id="TILE_NAME">%1$s</xliff:g> бөлшегін <xliff:g id="POSITION">%2$d</xliff:g>-позицияға жылжыту"</string>
+ <string name="accessibility_qs_edit_remove_tile_action" msgid="775511891457193480">"бөлшекті өшіру"</string>
+ <string name="accessibility_qs_edit_tile_add_action" msgid="5051211910345301833">"бөлшекті соңына қосу"</string>
+ <string name="accessibility_qs_edit_tile_start_move" msgid="2009373939914517817">"Бөлшекті жылжыту"</string>
+ <string name="accessibility_qs_edit_tile_start_add" msgid="7560798153975555772">"Бөлшек қосу"</string>
+ <string name="accessibility_qs_edit_tile_move_to_position" msgid="5198161544045930556">"<xliff:g id="POSITION">%1$d</xliff:g> орнына жылжыту"</string>
+ <string name="accessibility_qs_edit_tile_add_to_position" msgid="9029163095148274690">"<xliff:g id="POSITION">%1$d</xliff:g> орнына қосу"</string>
+ <string name="accessibility_qs_edit_position" msgid="4509277359815711830">"<xliff:g id="POSITION">%1$d</xliff:g> орны"</string>
<string name="accessibility_desc_quick_settings_edit" msgid="741658939453595297">"Жылдам параметрлер өңдегіші."</string>
<string name="accessibility_desc_notification_icon" msgid="7331265967584178674">"<xliff:g id="ID_1">%1$s</xliff:g> хабарландыруы: <xliff:g id="ID_2">%2$s</xliff:g>"</string>
<string name="accessibility_quick_settings_settings" msgid="7098489591715844713">"Параметрлерді ашу."</string>
@@ -1067,4 +1068,11 @@
<string name="controls_added_tooltip" msgid="4842812921719153085">"Жаңа басқару элементтерін көру үшін \"Қуат\" түймесін басып тұрыңыз."</string>
<string name="controls_menu_add" msgid="4447246119229920050">"Басқару элементтерін енгізу"</string>
<string name="controls_menu_edit" msgid="890623986951347062">"Басқару элементтерін өзгерту"</string>
+ <string name="media_output_dialog_add_output" msgid="5642703238877329518">"Шығыс сигналдарды қосу"</string>
+ <string name="media_output_dialog_group" msgid="5571251347877452212">"Топ"</string>
+ <string name="media_output_dialog_single_device" msgid="3102758980643351058">"1 құрылғы таңдалды."</string>
+ <string name="media_output_dialog_multiple_devices" msgid="1093771040315422350">"<xliff:g id="COUNT">%1$d</xliff:g> құрылғы таңдалды."</string>
+ <string name="media_output_dialog_disconnected" msgid="1834473104836986046">"<xliff:g id="DEVICE_NAME">%1$s</xliff:g> (ажыратылған)"</string>
+ <string name="media_output_dialog_connect_failed" msgid="3225190634236259010">"Қосылмады. Қайта қосылып көріңіз."</string>
+ <string name="media_output_dialog_pairing_new" msgid="9099497976087485862">"Жаңа құрылғыны жұптау"</string>
</resources>
diff --git a/packages/SystemUI/res/values-km/strings.xml b/packages/SystemUI/res/values-km/strings.xml
index 7636fdd6d8c2..b126a11d1310 100644
--- a/packages/SystemUI/res/values-km/strings.xml
+++ b/packages/SystemUI/res/values-km/strings.xml
@@ -419,7 +419,7 @@
<string name="quick_settings_night_secondary_label_on_at" msgid="3584738542293528235">"បើក​នៅម៉ោង <xliff:g id="TIME">%s</xliff:g>"</string>
<string name="quick_settings_secondary_label_until" msgid="1883981263191927372">"រហូតដល់​ម៉ោង <xliff:g id="TIME">%s</xliff:g>"</string>
<string name="quick_settings_ui_mode_night_label" msgid="1398928270610780470">"រចនាប័ទ្ម​ងងឹត"</string>
- <string name="quick_settings_dark_mode_secondary_label_battery_saver" msgid="4990712734503013251">"កម្មវិធីសន្សំថ្ម"</string>
+ <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>
@@ -774,7 +774,7 @@
</plurals>
<string name="battery_panel_title" msgid="5931157246673665963">"ការប្រើប្រាស់ថ្ម"</string>
<string name="battery_detail_charging_summary" msgid="8821202155297559706">"កម្មវិធីសន្សំថ្មមិនអាចប្រើបានអំឡុងពេលសាកថ្មទេ"</string>
- <string name="battery_detail_switch_title" msgid="6940976502957380405">"កម្មវិធីសន្សំថ្ម"</string>
+ <string name="battery_detail_switch_title" msgid="6940976502957380405">"មុខងារ​សន្សំ​ថ្ម"</string>
<string name="battery_detail_switch_summary" msgid="3668748557848025990">"កាត់បន្ថយប្រតិបត្តិការ និងទិន្នន័យផ្ទៃខាងក្រោយ"</string>
<string name="keyboard_key_button_template" msgid="8005673627272051429">"ប៊ូតុង <xliff:g id="NAME">%1$s</xliff:g>"</string>
<string name="keyboard_key_home" msgid="3734400625170020657">"Home"</string>
@@ -879,12 +879,13 @@
</string-array>
<string name="tuner_low_priority" msgid="8412666814123009820">"បង្ហាញ​រូប​ការជូនដំណឹង​ដែលមានអាទិភាពទាប"</string>
<string name="other" msgid="429768510980739978">"ផ្សេងៗ"</string>
- <string name="accessibility_qs_edit_tile_label" msgid="9079791448815232967">"ទីតាំង <xliff:g id="POSITION">%1$d</xliff:g>, <xliff:g id="TILE_NAME">%2$s</xliff:g>, ប៉ះពីរដងដើម្បីកែ"</string>
- <string name="accessibility_qs_edit_add_tile_label" msgid="8292218072049068613">"<xliff:g id="TILE_NAME">%1$s</xliff:g>, ប៉ះពីរដងដើម្បីបន្ថែម"</string>
- <string name="accessibility_qs_edit_move_tile" msgid="6027997446473163426">"ផ្លាស់ទី <xliff:g id="TILE_NAME">%1$s</xliff:g>"</string>
- <string name="accessibility_qs_edit_remove_tile" msgid="3406781901949899624">"យក <xliff:g id="TILE_NAME">%1$s</xliff:g> ចេញ"</string>
- <string name="accessibility_qs_edit_tile_add" msgid="6289879620154587233">"បញ្ចូល <xliff:g id="TILE_NAME">%1$s</xliff:g> ទៅទីតាំង <xliff:g id="POSITION">%2$d</xliff:g>"</string>
- <string name="accessibility_qs_edit_tile_move" msgid="4841770637244326837">"ផ្លាស់ទី <xliff:g id="TILE_NAME">%1$s</xliff:g> ទៅទីតាំង <xliff:g id="POSITION">%2$d</xliff:g>"</string>
+ <string name="accessibility_qs_edit_remove_tile_action" msgid="775511891457193480">"ដកប្រអប់ចេញ"</string>
+ <string name="accessibility_qs_edit_tile_add_action" msgid="5051211910345301833">"បញ្ចូល​ប្រអប់ទៅ​ខាងចុង"</string>
+ <string name="accessibility_qs_edit_tile_start_move" msgid="2009373939914517817">"ផ្លាស់ទី​ប្រអប់"</string>
+ <string name="accessibility_qs_edit_tile_start_add" msgid="7560798153975555772">"បញ្ចូល​ប្រអប់"</string>
+ <string name="accessibility_qs_edit_tile_move_to_position" msgid="5198161544045930556">"ផ្លាស់​ទីទៅ <xliff:g id="POSITION">%1$d</xliff:g>"</string>
+ <string name="accessibility_qs_edit_tile_add_to_position" msgid="9029163095148274690">"បញ្ចូលទៅ​ទីតាំងទី <xliff:g id="POSITION">%1$d</xliff:g>"</string>
+ <string name="accessibility_qs_edit_position" msgid="4509277359815711830">"ទីតាំងទី <xliff:g id="POSITION">%1$d</xliff:g>"</string>
<string name="accessibility_desc_quick_settings_edit" msgid="741658939453595297">"កម្មវិធីកែការកំណត់រហ័ស"</string>
<string name="accessibility_desc_notification_icon" msgid="7331265967584178674">"<xliff:g id="ID_1">%1$s</xliff:g> ការជូនដំណឹង៖ <xliff:g id="ID_2">%2$s</xliff:g>"</string>
<string name="accessibility_quick_settings_settings" msgid="7098489591715844713">"បើកការកំណត់"</string>
@@ -1067,4 +1068,11 @@
<string name="controls_added_tooltip" msgid="4842812921719153085">"សង្កត់​ប៊ូតុង​ថាមពល ដើម្បី​មើលឃើញ​ការគ្រប់គ្រង​ថ្មីៗ"</string>
<string name="controls_menu_add" msgid="4447246119229920050">"បញ្ចូល​ផ្ទាំងគ្រប់គ្រង"</string>
<string name="controls_menu_edit" msgid="890623986951347062">"កែ​ផ្ទាំងគ្រប់គ្រង"</string>
+ <string name="media_output_dialog_add_output" msgid="5642703238877329518">"បញ្ចូល​ឧបករណ៍​មេឌៀ"</string>
+ <string name="media_output_dialog_group" msgid="5571251347877452212">"ក្រុម"</string>
+ <string name="media_output_dialog_single_device" msgid="3102758980643351058">"បានជ្រើសរើស​ឧបករណ៍ 1"</string>
+ <string name="media_output_dialog_multiple_devices" msgid="1093771040315422350">"បានជ្រើសរើស​ឧបករណ៍ <xliff:g id="COUNT">%1$d</xliff:g>"</string>
+ <string name="media_output_dialog_disconnected" msgid="1834473104836986046">"<xliff:g id="DEVICE_NAME">%1$s</xliff:g> (បាន​ផ្ដាច់)"</string>
+ <string name="media_output_dialog_connect_failed" msgid="3225190634236259010">"មិន​អាច​ភ្ជាប់​បាន​ទេ។ សូមព្យាយាមម្ដងទៀត។"</string>
+ <string name="media_output_dialog_pairing_new" msgid="9099497976087485862">"ផ្គូផ្គង​ឧបករណ៍ថ្មី"</string>
</resources>
diff --git a/packages/SystemUI/res/values-kn/strings.xml b/packages/SystemUI/res/values-kn/strings.xml
index 7980790c3feb..475677be3845 100644
--- a/packages/SystemUI/res/values-kn/strings.xml
+++ b/packages/SystemUI/res/values-kn/strings.xml
@@ -387,7 +387,7 @@
<string name="quick_settings_cast_no_wifi" msgid="6980194769795014875">"ವೈ-ಫೈ ಸಂಪರ್ಕಗೊಂಡಿಲ್ಲ"</string>
<string name="quick_settings_brightness_dialog_title" msgid="4980669966716685588">"ಪ್ರಕಾಶಮಾನ"</string>
<string name="quick_settings_brightness_dialog_auto_brightness_label" msgid="2325362583903258677">"ಸ್ವಯಂ"</string>
- <string name="quick_settings_inversion_label" msgid="5078769633069667698">"ಬಣ್ಣಗಳನ್ನು ಬದಲಾಯಿಸಿ"</string>
+ <string name="quick_settings_inversion_label" msgid="5078769633069667698">"ಬಣ್ಣಗಳನ್ನು ಇನ್ವರ್ಟ್ ಮಾಡಿ"</string>
<string name="quick_settings_color_space_label" msgid="537528291083575559">"ಬಣ್ಣ ತಿದ್ದುಪಡಿ ಮೋಡ್"</string>
<string name="quick_settings_more_settings" msgid="2878235926753776694">"ಹೆಚ್ಚಿನ ಸೆಟ್ಟಿಂಗ್‌ಗಳು"</string>
<string name="quick_settings_done" msgid="2163641301648855793">"ಮುಗಿದಿದೆ"</string>
@@ -879,12 +879,13 @@
</string-array>
<string name="tuner_low_priority" msgid="8412666814123009820">"ಕಡಿಮೆ-ಆದ್ಯತೆ ಸೂಚನೆಯ ಐಕಾನ್‌ಗಳನ್ನು ತೋರಿಸಿ"</string>
<string name="other" msgid="429768510980739978">"ಇತರ"</string>
- <string name="accessibility_qs_edit_tile_label" msgid="9079791448815232967">"ಸ್ಥಳ <xliff:g id="POSITION">%1$d</xliff:g>, <xliff:g id="TILE_NAME">%2$s</xliff:g>. ಎಡಿಟ್ ಮಾಡಲು ಡಬಲ್ ಟ್ಯಾಪ್ ಮಾಡಿ."</string>
- <string name="accessibility_qs_edit_add_tile_label" msgid="8292218072049068613">"<xliff:g id="TILE_NAME">%1$s</xliff:g>. ಸೇರಿಸಲು ಡಬಲ್ ಟ್ಯಾಪ್ ಮಾಡಿ."</string>
- <string name="accessibility_qs_edit_move_tile" msgid="6027997446473163426">"<xliff:g id="TILE_NAME">%1$s</xliff:g> ಸರಿಸಿ"</string>
- <string name="accessibility_qs_edit_remove_tile" msgid="3406781901949899624">"<xliff:g id="TILE_NAME">%1$s</xliff:g> ತೆಗೆದುಹಾಕಿ"</string>
- <string name="accessibility_qs_edit_tile_add" msgid="6289879620154587233">"<xliff:g id="TILE_NAME">%1$s</xliff:g> ಅನ್ನು <xliff:g id="POSITION">%2$d</xliff:g> ಗೆ ಸೇರಿಸಿ"</string>
- <string name="accessibility_qs_edit_tile_move" msgid="4841770637244326837">"<xliff:g id="TILE_NAME">%1$s</xliff:g> ಅನ್ನು <xliff:g id="POSITION">%2$d</xliff:g> ಗೆ ಸರಿಸಿ"</string>
+ <string name="accessibility_qs_edit_remove_tile_action" msgid="775511891457193480">"ಟೈಲ್ ಅನ್ನು ತೆಗೆದುಹಾಕಿ"</string>
+ <string name="accessibility_qs_edit_tile_add_action" msgid="5051211910345301833">"ಕೊನೆಯಲ್ಲಿ ಟೈಲ್ ಸೇರಿಸಿ"</string>
+ <string name="accessibility_qs_edit_tile_start_move" msgid="2009373939914517817">"ಟೈಲ್ ಸರಿಸಿ"</string>
+ <string name="accessibility_qs_edit_tile_start_add" msgid="7560798153975555772">"ಟೈಲ್ ಸೇರಿಸಿ"</string>
+ <string name="accessibility_qs_edit_tile_move_to_position" msgid="5198161544045930556">"ಇಲ್ಲಿಗೆ ಸರಿಸಿ <xliff:g id="POSITION">%1$d</xliff:g>"</string>
+ <string name="accessibility_qs_edit_tile_add_to_position" msgid="9029163095148274690">"<xliff:g id="POSITION">%1$d</xliff:g> ಸ್ಥಾನಕ್ಕೆ ಸೇರಿಸಿ"</string>
+ <string name="accessibility_qs_edit_position" msgid="4509277359815711830">"ಸ್ಥಾನ <xliff:g id="POSITION">%1$d</xliff:g>"</string>
<string name="accessibility_desc_quick_settings_edit" msgid="741658939453595297">"ತ್ವರಿತ ಸೆಟ್ಟಿಂಗ್‍ಗಳ ಎಡಿಟರ್."</string>
<string name="accessibility_desc_notification_icon" msgid="7331265967584178674">"<xliff:g id="ID_1">%1$s</xliff:g> ಅಧಿಸೂಚನೆ: <xliff:g id="ID_2">%2$s</xliff:g>"</string>
<string name="accessibility_quick_settings_settings" msgid="7098489591715844713">"ಸೆಟ್ಟಿಂಗ್‌ಗಳನ್ನು ತೆರೆಯಿರಿ."</string>
@@ -1067,4 +1068,11 @@
<string name="controls_added_tooltip" msgid="4842812921719153085">"ಹೊಸ ನಿಯಂತ್ರಣಗಳನ್ನು ನೋಡಲು ಪವರ್ ಬಟನ್ ಹಿಡಿದುಕೊಳ್ಳಿ"</string>
<string name="controls_menu_add" msgid="4447246119229920050">"ನಿಯಂತ್ರಣಗಳನ್ನು ಸೇರಿಸಿ"</string>
<string name="controls_menu_edit" msgid="890623986951347062">"ನಿಯಂತ್ರಣಗಳನ್ನು ಎಡಿಟ್ ಮಾಡಿ"</string>
+ <string name="media_output_dialog_add_output" msgid="5642703238877329518">"ಔಟ್‌ಪುಟ್‌ಗಳನ್ನು ಸೇರಿಸಿ"</string>
+ <string name="media_output_dialog_group" msgid="5571251347877452212">"ಗುಂಪು"</string>
+ <string name="media_output_dialog_single_device" msgid="3102758980643351058">"1 ಸಾಧನವನ್ನು ಆಯ್ಕೆ ಮಾಡಲಾಗಿದೆ"</string>
+ <string name="media_output_dialog_multiple_devices" msgid="1093771040315422350">"<xliff:g id="COUNT">%1$d</xliff:g> ಸಾಧನಗಳನ್ನು ಆಯ್ಕೆ ಮಾಡಲಾಗಿದೆ"</string>
+ <string name="media_output_dialog_disconnected" msgid="1834473104836986046">"<xliff:g id="DEVICE_NAME">%1$s</xliff:g> (ಸಂಪರ್ಕ ಕಡಿತಗೊಳಿಸಲಾಗಿದೆ)"</string>
+ <string name="media_output_dialog_connect_failed" msgid="3225190634236259010">"ಸಂಪರ್ಕಿಸಲು ಸಾಧ್ಯವಾಗುತ್ತಿಲ್ಲ. ಪುನಃ ಪ್ರಯತ್ನಿಸಿ."</string>
+ <string name="media_output_dialog_pairing_new" msgid="9099497976087485862">"ಹೊಸ ಸಾಧನವನ್ನು ಜೋಡಿಸಿ"</string>
</resources>
diff --git a/packages/SystemUI/res/values-ko/strings.xml b/packages/SystemUI/res/values-ko/strings.xml
index ce59c6f796c5..ac7f04e77586 100644
--- a/packages/SystemUI/res/values-ko/strings.xml
+++ b/packages/SystemUI/res/values-ko/strings.xml
@@ -371,7 +371,7 @@
<string name="quick_settings_time_label" msgid="3352680970557509303">"시간"</string>
<string name="quick_settings_user_label" msgid="1253515509432672496">"나"</string>
<string name="quick_settings_user_title" msgid="8673045967216204537">"사용자"</string>
- <string name="quick_settings_user_new_user" msgid="3347905871336069666">"새 사용자"</string>
+ <string name="quick_settings_user_new_user" msgid="3347905871336069666">"신규 사용자"</string>
<string name="quick_settings_wifi_label" msgid="2879507532983487244">"Wi-Fi"</string>
<string name="quick_settings_wifi_not_connected" msgid="4071097522427039160">"연결되어 있지 않음"</string>
<string name="quick_settings_wifi_no_network" msgid="6003178398713839313">"네트워크가 연결되지 않음"</string>
@@ -473,7 +473,7 @@
<string name="accessibility_multi_user_switch_inactive" msgid="383168614528618402">"현재 사용자: <xliff:g id="CURRENT_USER_NAME">%s</xliff:g>"</string>
<string name="accessibility_multi_user_switch_quick_contact" msgid="4504508915324898576">"프로필 표시"</string>
<string name="user_add_user" msgid="4336657383006913022">"사용자 추가"</string>
- <string name="user_new_user_name" msgid="2019166282704195789">"새 사용자"</string>
+ <string name="user_new_user_name" msgid="2019166282704195789">"신규 사용자"</string>
<string name="guest_exit_guest_dialog_title" msgid="5015697561580641422">"게스트를 삭제하시겠습니까?"</string>
<string name="guest_exit_guest_dialog_message" msgid="8183450985628495709">"이 세션에 있는 모든 앱과 데이터가 삭제됩니다."</string>
<string name="guest_exit_guest_dialog_remove" msgid="7505817591242703757">"삭제"</string>
@@ -716,7 +716,7 @@
<string name="notification_channel_summary_default_with_bubbles" msgid="1782419896613644568">"휴대전화 설정에 따라 벨소리나 진동이 울릴 수 있습니다. 기본적으로 <xliff:g id="APP_NAME">%1$s</xliff:g>의 대화는 대화창으로 표시됩니다."</string>
<string name="notification_channel_summary_bubble" msgid="7235935211580860537">"이 콘텐츠로 연결되는 플로팅 바로가기로 사용자의 주의를 끕니다."</string>
<string name="notification_channel_summary_automatic" msgid="5813109268050235275">"시스템에서 알림 시 소리 또는 진동을 사용할지 결정하도록 허용합니다."</string>
- <string name="notification_channel_summary_priority" msgid="7952654515769021553">"대화 섹션 상단에 표시, 플로팅 대화창으로 표시, 그리고 잠금 화면에 프로필 사진이 표시됩니다."</string>
+ <string name="notification_channel_summary_priority" msgid="7952654515769021553">"대화 섹션 상단에 표시, 플로팅 대화창으로 표시, 그리고 잠금 화면에 프로필 사진이 표시됨"</string>
<string name="notification_conversation_channel_settings" msgid="2409977688430606835">"설정"</string>
<string name="notification_priority_title" msgid="2079708866333537093">"우선순위"</string>
<string name="no_shortcut" msgid="8257177117568230126">"<xliff:g id="APP_NAME">%1$s</xliff:g> 앱은 대화 기능을 지원하지 않습니다."</string>
@@ -879,12 +879,13 @@
</string-array>
<string name="tuner_low_priority" msgid="8412666814123009820">"우선순위가 낮은 알림 아이콘 표시"</string>
<string name="other" msgid="429768510980739978">"기타"</string>
- <string name="accessibility_qs_edit_tile_label" msgid="9079791448815232967">"위치 <xliff:g id="POSITION">%1$d</xliff:g>, <xliff:g id="TILE_NAME">%2$s</xliff:g>. 수정하려면 두 번 탭하세요."</string>
- <string name="accessibility_qs_edit_add_tile_label" msgid="8292218072049068613">"<xliff:g id="TILE_NAME">%1$s</xliff:g>. 추가하려면 두 번 탭하세요."</string>
- <string name="accessibility_qs_edit_move_tile" msgid="6027997446473163426">"<xliff:g id="TILE_NAME">%1$s</xliff:g> 이동"</string>
- <string name="accessibility_qs_edit_remove_tile" msgid="3406781901949899624">"<xliff:g id="TILE_NAME">%1$s</xliff:g> 삭제"</string>
- <string name="accessibility_qs_edit_tile_add" msgid="6289879620154587233">"<xliff:g id="TILE_NAME">%1$s</xliff:g> 타일을 위치 <xliff:g id="POSITION">%2$d</xliff:g>에 추가"</string>
- <string name="accessibility_qs_edit_tile_move" msgid="4841770637244326837">"<xliff:g id="TILE_NAME">%1$s</xliff:g> 타일을 위치 <xliff:g id="POSITION">%2$d</xliff:g>(으)로 이동"</string>
+ <string name="accessibility_qs_edit_remove_tile_action" msgid="775511891457193480">"타일 삭제"</string>
+ <string name="accessibility_qs_edit_tile_add_action" msgid="5051211910345301833">"끝에 타일 추가"</string>
+ <string name="accessibility_qs_edit_tile_start_move" msgid="2009373939914517817">"타일 이동"</string>
+ <string name="accessibility_qs_edit_tile_start_add" msgid="7560798153975555772">"타일 추가"</string>
+ <string name="accessibility_qs_edit_tile_move_to_position" msgid="5198161544045930556">"<xliff:g id="POSITION">%1$d</xliff:g> 위치로 이동"</string>
+ <string name="accessibility_qs_edit_tile_add_to_position" msgid="9029163095148274690">"<xliff:g id="POSITION">%1$d</xliff:g> 위치에 추가"</string>
+ <string name="accessibility_qs_edit_position" msgid="4509277359815711830">"<xliff:g id="POSITION">%1$d</xliff:g> 위치"</string>
<string name="accessibility_desc_quick_settings_edit" msgid="741658939453595297">"빠른 설정 편집기"</string>
<string name="accessibility_desc_notification_icon" msgid="7331265967584178674">"<xliff:g id="ID_1">%1$s</xliff:g> 알림: <xliff:g id="ID_2">%2$s</xliff:g>"</string>
<string name="accessibility_quick_settings_settings" msgid="7098489591715844713">"설정 열기"</string>
@@ -1067,4 +1068,11 @@
<string name="controls_added_tooltip" msgid="4842812921719153085">"새 컨트롤을 보려면 전원 버튼을 길게 누르세요."</string>
<string name="controls_menu_add" msgid="4447246119229920050">"컨트롤 추가"</string>
<string name="controls_menu_edit" msgid="890623986951347062">"컨트롤 수정"</string>
+ <string name="media_output_dialog_add_output" msgid="5642703238877329518">"출력 추가"</string>
+ <string name="media_output_dialog_group" msgid="5571251347877452212">"그룹"</string>
+ <string name="media_output_dialog_single_device" msgid="3102758980643351058">"기기 1대 선택됨"</string>
+ <string name="media_output_dialog_multiple_devices" msgid="1093771040315422350">"기기 <xliff:g id="COUNT">%1$d</xliff:g>대 선택됨"</string>
+ <string name="media_output_dialog_disconnected" msgid="1834473104836986046">"<xliff:g id="DEVICE_NAME">%1$s</xliff:g>(연결 끊김)"</string>
+ <string name="media_output_dialog_connect_failed" msgid="3225190634236259010">"연결할 수 없습니다. 다시 시도하세요."</string>
+ <string name="media_output_dialog_pairing_new" msgid="9099497976087485862">"새 기기와 페어링"</string>
</resources>
diff --git a/packages/SystemUI/res/values-ky/strings.xml b/packages/SystemUI/res/values-ky/strings.xml
index 57132e43dae9..d775823b298c 100644
--- a/packages/SystemUI/res/values-ky/strings.xml
+++ b/packages/SystemUI/res/values-ky/strings.xml
@@ -716,7 +716,7 @@
<string name="notification_channel_summary_default_with_bubbles" msgid="1782419896613644568">"Телефондун жөндөөлөрүнө жараша шыңгырап же дирилдеши мүмкүн. <xliff:g id="APP_NAME">%1$s</xliff:g> колдонмосундагы жазышуулар демейки жөндөө боюнча калкып чыкма билдирмелер түрүндө көрүнөт."</string>
<string name="notification_channel_summary_bubble" msgid="7235935211580860537">"Калкыма ыкчам баскыч менен көңүлүңүздү бул мазмунга буруп турат."</string>
<string name="notification_channel_summary_automatic" msgid="5813109268050235275">"Билдирменин үнүн чыгартууну же басууну тутумга тапшырыңыз"</string>
- <string name="notification_channel_summary_priority" msgid="7952654515769021553">"Жазышуу бөлүмүнүн жогорку жагында калкып чыкма билдирме түрүндө көрүнүп, профиль сүрөтү кулпуланган экрандан чагылдырылат"</string>
+ <string name="notification_channel_summary_priority" msgid="7952654515769021553">"Жазышуулар тизмесинин өйдө жагында калкып чыкма билдирме түрүндө көрүнүп, профиль сүрөтү кулпуланган экрандан чагылдырылат"</string>
<string name="notification_conversation_channel_settings" msgid="2409977688430606835">"Жөндөөлөр"</string>
<string name="notification_priority_title" msgid="2079708866333537093">"Маанилүүлүгү"</string>
<string name="no_shortcut" msgid="8257177117568230126">"<xliff:g id="APP_NAME">%1$s</xliff:g> жазышуу функцияларын колдоого албайт"</string>
@@ -862,7 +862,7 @@
<string name="left_icon" msgid="5036278531966897006">"¨Солго¨ сүрөтчөсү"</string>
<string name="right_icon" msgid="1103955040645237425">"¨Оңго¨ сүрөтчөсү"</string>
<string name="drag_to_add_tiles" msgid="8933270127508303672">"Керектүү элементтерди сүйрөп келиңиз"</string>
- <string name="drag_to_rearrange_tiles" msgid="2143204300089638620">"Элементтердин иретин өзгөртүү үчүн, кармап туруп, сүйрөңүз"</string>
+ <string name="drag_to_rearrange_tiles" msgid="2143204300089638620">"Элементтердин иретин өзгөртүү үчүн кармап туруп, сүйрөңүз"</string>
<string name="drag_to_remove_tiles" msgid="4682194717573850385">"Алып салуу үчүн бул жерге сүйрөңүз"</string>
<string name="drag_to_remove_disabled" msgid="933046987838658850">"Сизге жок дегенде <xliff:g id="MIN_NUM_TILES">%1$d</xliff:g> мозаика керек"</string>
<string name="qs_edit" msgid="5583565172803472437">"Түзөтүү"</string>
@@ -879,12 +879,13 @@
</string-array>
<string name="tuner_low_priority" msgid="8412666814123009820">"Анча маанилүү эмес билдирменин сүрөтчөлөрүн көрсөтүү"</string>
<string name="other" msgid="429768510980739978">"Башка"</string>
- <string name="accessibility_qs_edit_tile_label" msgid="9079791448815232967">"Орду - <xliff:g id="POSITION">%1$d</xliff:g>, <xliff:g id="TILE_NAME">%2$s</xliff:g>. Түзөтүү үчүн эки жолу таптаңыз."</string>
- <string name="accessibility_qs_edit_add_tile_label" msgid="8292218072049068613">"<xliff:g id="TILE_NAME">%1$s</xliff:g>. Кошуу үчүн эки жолу таптаңыз."</string>
- <string name="accessibility_qs_edit_move_tile" msgid="6027997446473163426">"<xliff:g id="TILE_NAME">%1$s</xliff:g> дегенди жылдыруу"</string>
- <string name="accessibility_qs_edit_remove_tile" msgid="3406781901949899624">"<xliff:g id="TILE_NAME">%1$s</xliff:g> дегенди алып салуу"</string>
- <string name="accessibility_qs_edit_tile_add" msgid="6289879620154587233">"<xliff:g id="TILE_NAME">%1$s</xliff:g> плиткасы <xliff:g id="POSITION">%2$d</xliff:g>-позицияга кошулсун"</string>
- <string name="accessibility_qs_edit_tile_move" msgid="4841770637244326837">"<xliff:g id="TILE_NAME">%1$s</xliff:g> плиткасы <xliff:g id="POSITION">%2$d</xliff:g>-позицияга кошулсун"</string>
+ <string name="accessibility_qs_edit_remove_tile_action" msgid="775511891457193480">"плитканы өчүрүү"</string>
+ <string name="accessibility_qs_edit_tile_add_action" msgid="5051211910345301833">"плитканы аягына кошуу"</string>
+ <string name="accessibility_qs_edit_tile_start_move" msgid="2009373939914517817">"Плитканы жылдыруу"</string>
+ <string name="accessibility_qs_edit_tile_start_add" msgid="7560798153975555772">"Плитка кошуу"</string>
+ <string name="accessibility_qs_edit_tile_move_to_position" msgid="5198161544045930556">"Төмөнкүгө жылдыруу: <xliff:g id="POSITION">%1$d</xliff:g>"</string>
+ <string name="accessibility_qs_edit_tile_add_to_position" msgid="9029163095148274690">"<xliff:g id="POSITION">%1$d</xliff:g>-позицияга кошуу"</string>
+ <string name="accessibility_qs_edit_position" msgid="4509277359815711830">"<xliff:g id="POSITION">%1$d</xliff:g>-позиция"</string>
<string name="accessibility_desc_quick_settings_edit" msgid="741658939453595297">"Ыкчам жөндөөлөр түзөткүчү."</string>
<string name="accessibility_desc_notification_icon" msgid="7331265967584178674">"<xliff:g id="ID_1">%1$s</xliff:g> эскертмеси: <xliff:g id="ID_2">%2$s</xliff:g>"</string>
<string name="accessibility_quick_settings_settings" msgid="7098489591715844713">"Жөндөөлөрдү ачуу."</string>
@@ -1067,4 +1068,11 @@
<string name="controls_added_tooltip" msgid="4842812921719153085">"Башкаруу элементтерин көрүү үчүн күйгүзүү/өчүрүү баскычын коё бербей басып туруңуз"</string>
<string name="controls_menu_add" msgid="4447246119229920050">"Башкаруу элементтерин кошуу"</string>
<string name="controls_menu_edit" msgid="890623986951347062">"Башкаруу элементтерин түзөтүү"</string>
+ <string name="media_output_dialog_add_output" msgid="5642703238877329518">"Медиа түзмөктөрдү кошуу"</string>
+ <string name="media_output_dialog_group" msgid="5571251347877452212">"Топ"</string>
+ <string name="media_output_dialog_single_device" msgid="3102758980643351058">"1 түзмөк тандалды"</string>
+ <string name="media_output_dialog_multiple_devices" msgid="1093771040315422350">"<xliff:g id="COUNT">%1$d</xliff:g> түзмөк тандалды"</string>
+ <string name="media_output_dialog_disconnected" msgid="1834473104836986046">"<xliff:g id="DEVICE_NAME">%1$s</xliff:g> (ажыратылды)"</string>
+ <string name="media_output_dialog_connect_failed" msgid="3225190634236259010">"Байланышпай койду. Кайталоо."</string>
+ <string name="media_output_dialog_pairing_new" msgid="9099497976087485862">"Жаңы түзмөктү жупташтыруу"</string>
</resources>
diff --git a/packages/SystemUI/res/values-lo/strings.xml b/packages/SystemUI/res/values-lo/strings.xml
index 0ff4509891b0..67642d3471f5 100644
--- a/packages/SystemUI/res/values-lo/strings.xml
+++ b/packages/SystemUI/res/values-lo/strings.xml
@@ -879,12 +879,13 @@
</string-array>
<string name="tuner_low_priority" msgid="8412666814123009820">"ສະແດງໄອຄອນການແຈ້ງເຕືອນຄວາມສຳຄັນຕ່ຳ"</string>
<string name="other" msgid="429768510980739978">"ອື່ນໆ"</string>
- <string name="accessibility_qs_edit_tile_label" msgid="9079791448815232967">"ຕຳແໜ່ງ <xliff:g id="POSITION">%1$d</xliff:g>, <xliff:g id="TILE_NAME">%2$s</xliff:g>. ແຕະສອງເທື່ອເພື່ອແກ້ໄຂ."</string>
- <string name="accessibility_qs_edit_add_tile_label" msgid="8292218072049068613">"<xliff:g id="TILE_NAME">%1$s</xliff:g>. ແຕະສອງເທື່ອເພື່ອເພີ່ມ."</string>
- <string name="accessibility_qs_edit_move_tile" msgid="6027997446473163426">"ຍ້າຍ <xliff:g id="TILE_NAME">%1$s</xliff:g>"</string>
- <string name="accessibility_qs_edit_remove_tile" msgid="3406781901949899624">"ລຶບ <xliff:g id="TILE_NAME">%1$s</xliff:g>"</string>
- <string name="accessibility_qs_edit_tile_add" msgid="6289879620154587233">"ເພີ່ມ <xliff:g id="TILE_NAME">%1$s</xliff:g> ໄປຕຳແໜ່ງ <xliff:g id="POSITION">%2$d</xliff:g>"</string>
- <string name="accessibility_qs_edit_tile_move" msgid="4841770637244326837">"ຍ້າຍ <xliff:g id="TILE_NAME">%1$s</xliff:g> ໄປຕຳແໜ່ງ <xliff:g id="POSITION">%2$d</xliff:g>"</string>
+ <string name="accessibility_qs_edit_remove_tile_action" msgid="775511891457193480">"ລຶບແຜ່ນອອກ"</string>
+ <string name="accessibility_qs_edit_tile_add_action" msgid="5051211910345301833">"ເພີ່ມແຜ່ນໃສ່ທ້າຍ"</string>
+ <string name="accessibility_qs_edit_tile_start_move" msgid="2009373939914517817">"ຍ້າຍແຜ່ນ"</string>
+ <string name="accessibility_qs_edit_tile_start_add" msgid="7560798153975555772">"ເພີ່ມແຜ່ນ"</string>
+ <string name="accessibility_qs_edit_tile_move_to_position" msgid="5198161544045930556">"ຍ້າຍໄປ <xliff:g id="POSITION">%1$d</xliff:g>"</string>
+ <string name="accessibility_qs_edit_tile_add_to_position" msgid="9029163095148274690">"ເພີ່ມໃສ່ຕຳແໜ່ງ <xliff:g id="POSITION">%1$d</xliff:g>"</string>
+ <string name="accessibility_qs_edit_position" msgid="4509277359815711830">"ຕຳແໜ່ງ <xliff:g id="POSITION">%1$d</xliff:g>"</string>
<string name="accessibility_desc_quick_settings_edit" msgid="741658939453595297">"ຕົວແກ້ໄຂການຕັ້ງຄ່າດ່ວນ"</string>
<string name="accessibility_desc_notification_icon" msgid="7331265967584178674">"ການແຈ້ງເຕືອນ <xliff:g id="ID_1">%1$s</xliff:g>: <xliff:g id="ID_2">%2$s</xliff:g>"</string>
<string name="accessibility_quick_settings_settings" msgid="7098489591715844713">"ເປີດການຕັ້ງຄ່າ."</string>
@@ -1067,4 +1068,11 @@
<string name="controls_added_tooltip" msgid="4842812921719153085">"ກົດປຸ່ມເປີດປິດຄ້າງໄວ້ເພື່ອເບິ່ງການຄວບຄຸມໃໝ່"</string>
<string name="controls_menu_add" msgid="4447246119229920050">"ເພີ່ມການຄວບຄຸມ"</string>
<string name="controls_menu_edit" msgid="890623986951347062">"ແກ້ໄຂການຄວບຄຸມ"</string>
+ <string name="media_output_dialog_add_output" msgid="5642703238877329518">"ເພີ່ມເອົ້າພຸດ"</string>
+ <string name="media_output_dialog_group" msgid="5571251347877452212">"ກຸ່ມ"</string>
+ <string name="media_output_dialog_single_device" msgid="3102758980643351058">"ເລືອກ 1 ອຸປະກອນແລ້ວ"</string>
+ <string name="media_output_dialog_multiple_devices" msgid="1093771040315422350">"ເລືອກ <xliff:g id="COUNT">%1$d</xliff:g> ອຸປະກອນແລ້ວ"</string>
+ <string name="media_output_dialog_disconnected" msgid="1834473104836986046">"<xliff:g id="DEVICE_NAME">%1$s</xliff:g> (ຕັດການເຊື່ອມຕໍ່ແລ້ວ)"</string>
+ <string name="media_output_dialog_connect_failed" msgid="3225190634236259010">"ບໍ່ສາມາດເຊື່ອມຕໍ່ໄດ້. ລອງໃໝ່."</string>
+ <string name="media_output_dialog_pairing_new" msgid="9099497976087485862">"ຈັບຄູ່ອຸປະກອນໃໝ່"</string>
</resources>
diff --git a/packages/SystemUI/res/values-lt/strings.xml b/packages/SystemUI/res/values-lt/strings.xml
index 239151ac5be4..e17f6e490373 100644
--- a/packages/SystemUI/res/values-lt/strings.xml
+++ b/packages/SystemUI/res/values-lt/strings.xml
@@ -784,7 +784,7 @@
</plurals>
<string name="battery_panel_title" msgid="5931157246673665963">"Akum. energ. vartoj."</string>
<string name="battery_detail_charging_summary" msgid="8821202155297559706">"Akumuliatoriaus tausojimo priemonė nepasiekiama įkraunant"</string>
- <string name="battery_detail_switch_title" msgid="6940976502957380405">"Akumuliat. taus. pr."</string>
+ <string name="battery_detail_switch_title" msgid="6940976502957380405">"Akum. taus. pr."</string>
<string name="battery_detail_switch_summary" msgid="3668748557848025990">"Sumažinamas našumas ir foninių duomenų naudojimas"</string>
<string name="keyboard_key_button_template" msgid="8005673627272051429">"Mygtukas <xliff:g id="NAME">%1$s</xliff:g>"</string>
<string name="keyboard_key_home" msgid="3734400625170020657">"Pagrindinis"</string>
@@ -889,12 +889,13 @@
</string-array>
<string name="tuner_low_priority" msgid="8412666814123009820">"Rodyti mažo prioriteto pranešimų piktogramas"</string>
<string name="other" msgid="429768510980739978">"Kita"</string>
- <string name="accessibility_qs_edit_tile_label" msgid="9079791448815232967">"<xliff:g id="POSITION">%1$d</xliff:g> padėtis, išklotinės elementas „<xliff:g id="TILE_NAME">%2$s</xliff:g>“. Dukart palieskite, kad redaguotumėte."</string>
- <string name="accessibility_qs_edit_add_tile_label" msgid="8292218072049068613">"Išklotinės elementas „<xliff:g id="TILE_NAME">%1$s</xliff:g>“. Dukart palieskite, kad pridėtumėte."</string>
- <string name="accessibility_qs_edit_move_tile" msgid="6027997446473163426">"Perkelti išklotinės elementą „<xliff:g id="TILE_NAME">%1$s</xliff:g>“"</string>
- <string name="accessibility_qs_edit_remove_tile" msgid="3406781901949899624">"Pašalinti išklotinės elementą „<xliff:g id="TILE_NAME">%1$s</xliff:g>“"</string>
- <string name="accessibility_qs_edit_tile_add" msgid="6289879620154587233">"Pridėti išklotinės elementą „<xliff:g id="TILE_NAME">%1$s</xliff:g>“ į <xliff:g id="POSITION">%2$d</xliff:g> padėtį"</string>
- <string name="accessibility_qs_edit_tile_move" msgid="4841770637244326837">"Perkelti išklotinės elementą „<xliff:g id="TILE_NAME">%1$s</xliff:g>“ į <xliff:g id="POSITION">%2$d</xliff:g> padėtį"</string>
+ <string name="accessibility_qs_edit_remove_tile_action" msgid="775511891457193480">"pašalintumėte išklotinės elementą"</string>
+ <string name="accessibility_qs_edit_tile_add_action" msgid="5051211910345301833">"pridėtumėte išklotinės elementą gale"</string>
+ <string name="accessibility_qs_edit_tile_start_move" msgid="2009373939914517817">"Perkelti išklotinės elementą"</string>
+ <string name="accessibility_qs_edit_tile_start_add" msgid="7560798153975555772">"Pridėti išklotinės elementą"</string>
+ <string name="accessibility_qs_edit_tile_move_to_position" msgid="5198161544045930556">"Perkelkite į <xliff:g id="POSITION">%1$d</xliff:g> poziciją"</string>
+ <string name="accessibility_qs_edit_tile_add_to_position" msgid="9029163095148274690">"Pridėkite <xliff:g id="POSITION">%1$d</xliff:g> pozicijoje"</string>
+ <string name="accessibility_qs_edit_position" msgid="4509277359815711830">"<xliff:g id="POSITION">%1$d</xliff:g> pozicija"</string>
<string name="accessibility_desc_quick_settings_edit" msgid="741658939453595297">"Sparčiųjų nustatymų redagavimo priemonė."</string>
<string name="accessibility_desc_notification_icon" msgid="7331265967584178674">"„<xliff:g id="ID_1">%1$s</xliff:g>“ pranešimas: <xliff:g id="ID_2">%2$s</xliff:g>"</string>
<string name="accessibility_quick_settings_settings" msgid="7098489591715844713">"Atidaryti nustatymus."</string>
@@ -1079,4 +1080,11 @@
<string name="controls_added_tooltip" msgid="4842812921719153085">"Jei norite peržiūrėti naujus valdiklius, laikykite paspaudę maitinimo mygtuką"</string>
<string name="controls_menu_add" msgid="4447246119229920050">"Pridėti valdiklių"</string>
<string name="controls_menu_edit" msgid="890623986951347062">"Redaguoti valdiklius"</string>
+ <string name="media_output_dialog_add_output" msgid="5642703238877329518">"Išvesčių pridėjimas"</string>
+ <string name="media_output_dialog_group" msgid="5571251347877452212">"Grupė"</string>
+ <string name="media_output_dialog_single_device" msgid="3102758980643351058">"Pasirinktas 1 įrenginys"</string>
+ <string name="media_output_dialog_multiple_devices" msgid="1093771040315422350">"Pasirinkta įrenginių: <xliff:g id="COUNT">%1$d</xliff:g>"</string>
+ <string name="media_output_dialog_disconnected" msgid="1834473104836986046">"„<xliff:g id="DEVICE_NAME">%1$s</xliff:g>“ (atjungta)"</string>
+ <string name="media_output_dialog_connect_failed" msgid="3225190634236259010">"Nepavyko prijungti. Bandykite dar kartą."</string>
+ <string name="media_output_dialog_pairing_new" msgid="9099497976087485862">"Naujo įrenginio susiejimas"</string>
</resources>
diff --git a/packages/SystemUI/res/values-lv/strings.xml b/packages/SystemUI/res/values-lv/strings.xml
index 1552a9e68363..96d563c61a75 100644
--- a/packages/SystemUI/res/values-lv/strings.xml
+++ b/packages/SystemUI/res/values-lv/strings.xml
@@ -714,7 +714,7 @@
<string name="notification_bubble_title" msgid="8330481035191903164">"Burbulis"</string>
<string name="notification_automatic_title" msgid="3745465364578762652">"Automātiski"</string>
<string name="notification_channel_summary_low" msgid="4860617986908931158">"Nav skaņas signāla vai vibrācijas"</string>
- <string name="notification_conversation_summary_low" msgid="1734433426085468009">"Nav skaņas signāla vai vibrācijas, kā arī atrodas zemāk sarunu sadaļā"</string>
+ <string name="notification_conversation_summary_low" msgid="1734433426085468009">"Nav skaņas signāla vai vibrācijas, kā arī atrodas tālāk sarunu sadaļā"</string>
<string name="notification_channel_summary_default" msgid="3282930979307248890">"Atkarībā no tālruņa iestatījumiem var zvanīt vai vibrēt"</string>
<string name="notification_channel_summary_default_with_bubbles" msgid="1782419896613644568">"Atkarībā no tālruņa iestatījumiem var zvanīt vai vibrēt. Sarunas no lietotnes <xliff:g id="APP_NAME">%1$s</xliff:g> pēc noklusējuma tiek parādītas burbulī."</string>
<string name="notification_channel_summary_bubble" msgid="7235935211580860537">"Piesaista jūsu uzmanību, rādot peldošu saīsni uz šo saturu."</string>
@@ -884,12 +884,13 @@
</string-array>
<string name="tuner_low_priority" msgid="8412666814123009820">"Rādīt zemas prioritātes paziņojumu ikonas"</string>
<string name="other" msgid="429768510980739978">"Citi"</string>
- <string name="accessibility_qs_edit_tile_label" msgid="9079791448815232967">"<xliff:g id="POSITION">%1$d</xliff:g>. pozīcija, <xliff:g id="TILE_NAME">%2$s</xliff:g>. Lai rediģētu, veiciet dubultskārienu."</string>
- <string name="accessibility_qs_edit_add_tile_label" msgid="8292218072049068613">"<xliff:g id="TILE_NAME">%1$s</xliff:g>. Lai pievienotu, veiciet dubultskārienu."</string>
- <string name="accessibility_qs_edit_move_tile" msgid="6027997446473163426">"Pārvietot elementu <xliff:g id="TILE_NAME">%1$s</xliff:g>"</string>
- <string name="accessibility_qs_edit_remove_tile" msgid="3406781901949899624">"Noņemt elementu <xliff:g id="TILE_NAME">%1$s</xliff:g>"</string>
- <string name="accessibility_qs_edit_tile_add" msgid="6289879620154587233">"Pievienot elementu “<xliff:g id="TILE_NAME">%1$s</xliff:g>” <xliff:g id="POSITION">%2$d</xliff:g>. pozīcijā"</string>
- <string name="accessibility_qs_edit_tile_move" msgid="4841770637244326837">"Pārvietot elementu “<xliff:g id="TILE_NAME">%1$s</xliff:g>” uz <xliff:g id="POSITION">%2$d</xliff:g>. pozīciju"</string>
+ <string name="accessibility_qs_edit_remove_tile_action" msgid="775511891457193480">"noņemt elementu"</string>
+ <string name="accessibility_qs_edit_tile_add_action" msgid="5051211910345301833">"pievienot elementu beigās"</string>
+ <string name="accessibility_qs_edit_tile_start_move" msgid="2009373939914517817">"Pārvietot elementu"</string>
+ <string name="accessibility_qs_edit_tile_start_add" msgid="7560798153975555772">"Pievienot elementu"</string>
+ <string name="accessibility_qs_edit_tile_move_to_position" msgid="5198161544045930556">"Pārvietot uz pozīciju numur <xliff:g id="POSITION">%1$d</xliff:g>"</string>
+ <string name="accessibility_qs_edit_tile_add_to_position" msgid="9029163095148274690">"Pievienot elementu pozīcijā numur <xliff:g id="POSITION">%1$d</xliff:g>"</string>
+ <string name="accessibility_qs_edit_position" msgid="4509277359815711830">"Pozīcija numur <xliff:g id="POSITION">%1$d</xliff:g>"</string>
<string name="accessibility_desc_quick_settings_edit" msgid="741658939453595297">"Ātro iestatījumu redaktors."</string>
<string name="accessibility_desc_notification_icon" msgid="7331265967584178674">"<xliff:g id="ID_1">%1$s</xliff:g> paziņojums: <xliff:g id="ID_2">%2$s</xliff:g>"</string>
<string name="accessibility_quick_settings_settings" msgid="7098489591715844713">"Atvērt iestatījumus."</string>
@@ -1073,4 +1074,11 @@
<string name="controls_added_tooltip" msgid="4842812921719153085">"Nospiediet barošanas pogu un turiet to, lai skatītu jaunas vadīklas"</string>
<string name="controls_menu_add" msgid="4447246119229920050">"Pievienot vadīklas"</string>
<string name="controls_menu_edit" msgid="890623986951347062">"Rediģēt vadīklas"</string>
+ <string name="media_output_dialog_add_output" msgid="5642703238877329518">"Izejas ierīču pievienošana"</string>
+ <string name="media_output_dialog_group" msgid="5571251347877452212">"Grupa"</string>
+ <string name="media_output_dialog_single_device" msgid="3102758980643351058">"Atlasīta viena ierīce"</string>
+ <string name="media_output_dialog_multiple_devices" msgid="1093771040315422350">"Atlasītas vairākas ierīces (kopā <xliff:g id="COUNT">%1$d</xliff:g>)"</string>
+ <string name="media_output_dialog_disconnected" msgid="1834473104836986046">"<xliff:g id="DEVICE_NAME">%1$s</xliff:g> (savienojums pārtraukts)"</string>
+ <string name="media_output_dialog_connect_failed" msgid="3225190634236259010">"Nevarēja izveidot savienojumu. Mēģiniet vēlreiz."</string>
+ <string name="media_output_dialog_pairing_new" msgid="9099497976087485862">"Savienošana pārī ar jaunu ierīci"</string>
</resources>
diff --git a/packages/SystemUI/res/values-mk/strings.xml b/packages/SystemUI/res/values-mk/strings.xml
index 42b758fd518b..f53147e03c84 100644
--- a/packages/SystemUI/res/values-mk/strings.xml
+++ b/packages/SystemUI/res/values-mk/strings.xml
@@ -711,14 +711,14 @@
<string name="notification_bubble_title" msgid="8330481035191903164">"Балонче"</string>
<string name="notification_automatic_title" msgid="3745465364578762652">"Автоматски"</string>
<string name="notification_channel_summary_low" msgid="4860617986908931158">"Без звук или вибрации"</string>
- <string name="notification_conversation_summary_low" msgid="1734433426085468009">"Без звук или вибрации и се појавува под делот за разговор"</string>
+ <string name="notification_conversation_summary_low" msgid="1734433426085468009">"Без звук или вибрации и се појавува подолу во делот со разговори"</string>
<string name="notification_channel_summary_default" msgid="3282930979307248890">"Може да ѕвони или вибрира во зависност од поставките на телефонот"</string>
<string name="notification_channel_summary_default_with_bubbles" msgid="1782419896613644568">"Може да ѕвони или вибрира во зависност од поставките на телефонот Стандардно, разговорите од <xliff:g id="APP_NAME">%1$s</xliff:g> се во балончиња."</string>
<string name="notification_channel_summary_bubble" msgid="7235935211580860537">"Ви го задржува вниманието со лебдечка кратенка на содржинава."</string>
<string name="notification_channel_summary_automatic" msgid="5813109268050235275">"Дозволете системот да определи дали известувањево треба да испушти звук или да вибрира"</string>
<string name="notification_channel_summary_priority" msgid="7952654515769021553">"Се појавува на горниот дел од секцијата на разговорот во вид на лебдечко меурче, покажувајќи ја профилната слика на заклучениот екран"</string>
<string name="notification_conversation_channel_settings" msgid="2409977688430606835">"Поставки"</string>
- <string name="notification_priority_title" msgid="2079708866333537093">"Приоритет"</string>
+ <string name="notification_priority_title" msgid="2079708866333537093">"Приоритетно"</string>
<string name="no_shortcut" msgid="8257177117568230126">"<xliff:g id="APP_NAME">%1$s</xliff:g> не поддржува функции за разговор"</string>
<string name="bubble_overflow_empty_title" msgid="3120029421991510842">"Нема неодамнешни балончиња"</string>
<string name="bubble_overflow_empty_subtitle" msgid="2030874469510497397">"Неодамнешните и отфрлените балончиња ќе се појавуваат тука"</string>
@@ -879,12 +879,13 @@
</string-array>
<string name="tuner_low_priority" msgid="8412666814123009820">"Прикажувај икони за известувања со низок приоритет"</string>
<string name="other" msgid="429768510980739978">"Друго"</string>
- <string name="accessibility_qs_edit_tile_label" msgid="9079791448815232967">"Место <xliff:g id="POSITION">%1$d</xliff:g>, <xliff:g id="TILE_NAME">%2$s</xliff:g>. Допрете двапати за уредување."</string>
- <string name="accessibility_qs_edit_add_tile_label" msgid="8292218072049068613">"<xliff:g id="TILE_NAME">%1$s</xliff:g>. Допрете двапати за додавање."</string>
- <string name="accessibility_qs_edit_move_tile" msgid="6027997446473163426">"Преместете <xliff:g id="TILE_NAME">%1$s</xliff:g>"</string>
- <string name="accessibility_qs_edit_remove_tile" msgid="3406781901949899624">"Отстранете <xliff:g id="TILE_NAME">%1$s</xliff:g>"</string>
- <string name="accessibility_qs_edit_tile_add" msgid="6289879620154587233">"Додајте <xliff:g id="TILE_NAME">%1$s</xliff:g> на позицијата <xliff:g id="POSITION">%2$d</xliff:g>"</string>
- <string name="accessibility_qs_edit_tile_move" msgid="4841770637244326837">"Преместете <xliff:g id="TILE_NAME">%1$s</xliff:g> на позицијата <xliff:g id="POSITION">%2$d</xliff:g>"</string>
+ <string name="accessibility_qs_edit_remove_tile_action" msgid="775511891457193480">"отстранување на плочката"</string>
+ <string name="accessibility_qs_edit_tile_add_action" msgid="5051211910345301833">"додавање на плочката на крај"</string>
+ <string name="accessibility_qs_edit_tile_start_move" msgid="2009373939914517817">"Преместување на плочката"</string>
+ <string name="accessibility_qs_edit_tile_start_add" msgid="7560798153975555772">"Додавање плочка"</string>
+ <string name="accessibility_qs_edit_tile_move_to_position" msgid="5198161544045930556">"Преместување на <xliff:g id="POSITION">%1$d</xliff:g>"</string>
+ <string name="accessibility_qs_edit_tile_add_to_position" msgid="9029163095148274690">"Додавање на позиција <xliff:g id="POSITION">%1$d</xliff:g>"</string>
+ <string name="accessibility_qs_edit_position" msgid="4509277359815711830">"Позиција <xliff:g id="POSITION">%1$d</xliff:g>"</string>
<string name="accessibility_desc_quick_settings_edit" msgid="741658939453595297">"Уредник за брзи поставки."</string>
<string name="accessibility_desc_notification_icon" msgid="7331265967584178674">"Известување од <xliff:g id="ID_1">%1$s</xliff:g>: <xliff:g id="ID_2">%2$s</xliff:g>"</string>
<string name="accessibility_quick_settings_settings" msgid="7098489591715844713">"Отворете ги поставките."</string>
@@ -1067,4 +1068,11 @@
<string name="controls_added_tooltip" msgid="4842812921719153085">"Задржете го копчето за вклучување за да ги видите новите контроли"</string>
<string name="controls_menu_add" msgid="4447246119229920050">"Додајте контроли"</string>
<string name="controls_menu_edit" msgid="890623986951347062">"Изменете ги контролите"</string>
+ <string name="media_output_dialog_add_output" msgid="5642703238877329518">"Додајте излези"</string>
+ <string name="media_output_dialog_group" msgid="5571251347877452212">"Група"</string>
+ <string name="media_output_dialog_single_device" msgid="3102758980643351058">"Избран е 1 уред"</string>
+ <string name="media_output_dialog_multiple_devices" msgid="1093771040315422350">"Избрани се <xliff:g id="COUNT">%1$d</xliff:g> уреди"</string>
+ <string name="media_output_dialog_disconnected" msgid="1834473104836986046">"<xliff:g id="DEVICE_NAME">%1$s</xliff:g> (исклучен)"</string>
+ <string name="media_output_dialog_connect_failed" msgid="3225190634236259010">"Не можеше да се поврзе. Обидете се повторно."</string>
+ <string name="media_output_dialog_pairing_new" msgid="9099497976087485862">"Спарете нов уред"</string>
</resources>
diff --git a/packages/SystemUI/res/values-ml/strings.xml b/packages/SystemUI/res/values-ml/strings.xml
index 2e68d3b8717d..fc6868152dc0 100644
--- a/packages/SystemUI/res/values-ml/strings.xml
+++ b/packages/SystemUI/res/values-ml/strings.xml
@@ -387,7 +387,7 @@
<string name="quick_settings_cast_no_wifi" msgid="6980194769795014875">"വൈഫൈ കണക്റ്റ് ചെയ്‌തിട്ടില്ല"</string>
<string name="quick_settings_brightness_dialog_title" msgid="4980669966716685588">"തെളിച്ചം"</string>
<string name="quick_settings_brightness_dialog_auto_brightness_label" msgid="2325362583903258677">"യാന്ത്രികം"</string>
- <string name="quick_settings_inversion_label" msgid="5078769633069667698">"നിറം മാറ്റുക"</string>
+ <string name="quick_settings_inversion_label" msgid="5078769633069667698">"നെഗറ്റീവ് ലുക്ക്"</string>
<string name="quick_settings_color_space_label" msgid="537528291083575559">"വർണ്ണം ശരിയാക്കൽ മോഡ്"</string>
<string name="quick_settings_more_settings" msgid="2878235926753776694">"കൂടുതൽ ക്രമീകരണങ്ങൾ"</string>
<string name="quick_settings_done" msgid="2163641301648855793">"പൂർത്തിയാക്കി"</string>
@@ -879,12 +879,13 @@
</string-array>
<string name="tuner_low_priority" msgid="8412666814123009820">"പ്രാധാന്യം കുറഞ്ഞ അറിയിപ്പ് ചിഹ്‌നങ്ങൾ"</string>
<string name="other" msgid="429768510980739978">"മറ്റുള്ളവ"</string>
- <string name="accessibility_qs_edit_tile_label" msgid="9079791448815232967">"സ്ഥാനം <xliff:g id="POSITION">%1$d</xliff:g>, <xliff:g id="TILE_NAME">%2$s</xliff:g>. എഡിറ്റുചെയ്യുന്നതിന് രണ്ടുതവണ ടാപ്പുചെയ്യുക."</string>
- <string name="accessibility_qs_edit_add_tile_label" msgid="8292218072049068613">"<xliff:g id="TILE_NAME">%1$s</xliff:g>. ചേർക്കാൻ രണ്ടുതവണ ടാപ്പുചെയ്യുക."</string>
- <string name="accessibility_qs_edit_move_tile" msgid="6027997446473163426">"<xliff:g id="TILE_NAME">%1$s</xliff:g> നീക്കുക"</string>
- <string name="accessibility_qs_edit_remove_tile" msgid="3406781901949899624">"<xliff:g id="TILE_NAME">%1$s</xliff:g> നീക്കംചെയ്യുക"</string>
- <string name="accessibility_qs_edit_tile_add" msgid="6289879620154587233">"<xliff:g id="POSITION">%2$d</xliff:g> സ്ഥാനത്തേയ്ക്ക് <xliff:g id="TILE_NAME">%1$s</xliff:g> ചേർക്കുക"</string>
- <string name="accessibility_qs_edit_tile_move" msgid="4841770637244326837">"<xliff:g id="POSITION">%2$d</xliff:g> സ്ഥാനത്തേയ്ക്ക് <xliff:g id="TILE_NAME">%1$s</xliff:g> നീക്കുക"</string>
+ <string name="accessibility_qs_edit_remove_tile_action" msgid="775511891457193480">"ടൈൽ നീക്കം ചെയ്യുക"</string>
+ <string name="accessibility_qs_edit_tile_add_action" msgid="5051211910345301833">"ടൈൽ, അവസാന ഭാഗത്ത് ചേർക്കുക"</string>
+ <string name="accessibility_qs_edit_tile_start_move" msgid="2009373939914517817">"ടൈൽ നീക്കുക"</string>
+ <string name="accessibility_qs_edit_tile_start_add" msgid="7560798153975555772">"ടൈൽ ചേർക്കുക"</string>
+ <string name="accessibility_qs_edit_tile_move_to_position" msgid="5198161544045930556">"സ്ഥാനം <xliff:g id="POSITION">%1$d</xliff:g>-ലേക്ക് നീക്കുക"</string>
+ <string name="accessibility_qs_edit_tile_add_to_position" msgid="9029163095148274690">"സ്ഥാനം <xliff:g id="POSITION">%1$d</xliff:g>-ൽ ചേർക്കുക"</string>
+ <string name="accessibility_qs_edit_position" msgid="4509277359815711830">"സ്ഥാനം <xliff:g id="POSITION">%1$d</xliff:g>"</string>
<string name="accessibility_desc_quick_settings_edit" msgid="741658939453595297">"ദ്രുത ക്രമീകരണ എഡിറ്റർ."</string>
<string name="accessibility_desc_notification_icon" msgid="7331265967584178674">"<xliff:g id="ID_1">%1$s</xliff:g> അറിയിപ്പ്: <xliff:g id="ID_2">%2$s</xliff:g>"</string>
<string name="accessibility_quick_settings_settings" msgid="7098489591715844713">"ക്രമീകരണം തുറക്കുക."</string>
@@ -1067,4 +1068,11 @@
<string name="controls_added_tooltip" msgid="4842812921719153085">"പുതിയ നിയന്ത്രണങ്ങൾ കാണാൻ പവർ ബട്ടൺ പിടിക്കുക"</string>
<string name="controls_menu_add" msgid="4447246119229920050">"നിയന്ത്രണങ്ങൾ ചേർക്കുക"</string>
<string name="controls_menu_edit" msgid="890623986951347062">"നിയന്ത്രണങ്ങൾ എഡിറ്റ് ചെയ്യുക"</string>
+ <string name="media_output_dialog_add_output" msgid="5642703238877329518">"ഔട്ട്പുട്ടുകൾ ചേർക്കുക"</string>
+ <string name="media_output_dialog_group" msgid="5571251347877452212">"ഗ്രൂപ്പ്"</string>
+ <string name="media_output_dialog_single_device" msgid="3102758980643351058">"ഒരു ഉപകരണം തിരഞ്ഞെടുത്തു"</string>
+ <string name="media_output_dialog_multiple_devices" msgid="1093771040315422350">"<xliff:g id="COUNT">%1$d</xliff:g> ഉപകരണങ്ങൾ തിരഞ്ഞെടുത്തു"</string>
+ <string name="media_output_dialog_disconnected" msgid="1834473104836986046">"<xliff:g id="DEVICE_NAME">%1$s</xliff:g> (വിച്ഛേദിച്ചു)"</string>
+ <string name="media_output_dialog_connect_failed" msgid="3225190634236259010">"കണക്റ്റ് ചെയ്യാനായില്ല. വീണ്ടും ശ്രമിക്കുക."</string>
+ <string name="media_output_dialog_pairing_new" msgid="9099497976087485862">"പുതിയ ഉപകരണവുമായി ജോടിയാക്കുക"</string>
</resources>
diff --git a/packages/SystemUI/res/values-mn/strings.xml b/packages/SystemUI/res/values-mn/strings.xml
index fab7a5531ca1..1c82e48c9ad9 100644
--- a/packages/SystemUI/res/values-mn/strings.xml
+++ b/packages/SystemUI/res/values-mn/strings.xml
@@ -711,7 +711,7 @@
<string name="notification_bubble_title" msgid="8330481035191903164">"Бөмбөлөг"</string>
<string name="notification_automatic_title" msgid="3745465364578762652">"Автомат"</string>
<string name="notification_channel_summary_low" msgid="4860617986908931158">"Дуу эсвэл чичиргээ байхгүй"</string>
- <string name="notification_conversation_summary_low" msgid="1734433426085468009">"Дуу эсвэл чичиргээ байхгүй бөгөөд харицан ярианы хэсгийн доод талд харагдана"</string>
+ <string name="notification_conversation_summary_low" msgid="1734433426085468009">"Дуу эсвэл чичиргээ байхгүй бөгөөд харилцан ярианы хэсгийн доод талд харагдана"</string>
<string name="notification_channel_summary_default" msgid="3282930979307248890">"Утасны тохиргоонд тулгуурлан хонх дуугаргах эсвэл чичирхийлж болзошгүй"</string>
<string name="notification_channel_summary_default_with_bubbles" msgid="1782419896613644568">"Утасны тохиргоонд тулгуурлан хонх дуугаргах эсвэл чичирхийлж болзошгүй. <xliff:g id="APP_NAME">%1$s</xliff:g>-н харилцан яриаг өгөгдмөл тохиргооны дагуу бөмбөлөг болгоно."</string>
<string name="notification_channel_summary_bubble" msgid="7235935211580860537">"Энэ контентын хөвөн гарч ирэх товчлолтойгоор таны анхаарлыг татдаг."</string>
@@ -879,12 +879,13 @@
</string-array>
<string name="tuner_low_priority" msgid="8412666814123009820">"Бага ач холбогдолтой мэдэгдлийн дүрс тэмдгийг харуулах"</string>
<string name="other" msgid="429768510980739978">"Бусад"</string>
- <string name="accessibility_qs_edit_tile_label" msgid="9079791448815232967">"Байршил <xliff:g id="POSITION">%1$d</xliff:g>, <xliff:g id="TILE_NAME">%2$s</xliff:g>. Засахын тулд 2 удаа дарна уу."</string>
- <string name="accessibility_qs_edit_add_tile_label" msgid="8292218072049068613">"<xliff:g id="TILE_NAME">%1$s</xliff:g>. Нэмэхийн тулд 2 удаа дарна уу."</string>
- <string name="accessibility_qs_edit_move_tile" msgid="6027997446473163426">"<xliff:g id="TILE_NAME">%1$s</xliff:g>-г зөөх"</string>
- <string name="accessibility_qs_edit_remove_tile" msgid="3406781901949899624">"<xliff:g id="TILE_NAME">%1$s</xliff:g>-г устгах"</string>
- <string name="accessibility_qs_edit_tile_add" msgid="6289879620154587233">"<xliff:g id="TILE_NAME">%1$s</xliff:g>-г <xliff:g id="POSITION">%2$d</xliff:g> байрлалд нэмэх"</string>
- <string name="accessibility_qs_edit_tile_move" msgid="4841770637244326837">"<xliff:g id="TILE_NAME">%1$s</xliff:g>-г байрлал <xliff:g id="POSITION">%2$d</xliff:g> руу зөөх"</string>
+ <string name="accessibility_qs_edit_remove_tile_action" msgid="775511891457193480">"хавтанг хасна уу"</string>
+ <string name="accessibility_qs_edit_tile_add_action" msgid="5051211910345301833">"дуусгахын тулд хавтан нэмэх"</string>
+ <string name="accessibility_qs_edit_tile_start_move" msgid="2009373939914517817">"Хавтанг зөөх"</string>
+ <string name="accessibility_qs_edit_tile_start_add" msgid="7560798153975555772">"Хавтан нэмэх"</string>
+ <string name="accessibility_qs_edit_tile_move_to_position" msgid="5198161544045930556">"<xliff:g id="POSITION">%1$d</xliff:g> руу зөөнө үү"</string>
+ <string name="accessibility_qs_edit_tile_add_to_position" msgid="9029163095148274690">"<xliff:g id="POSITION">%1$d</xliff:g> байрлалд нэмнэ үү"</string>
+ <string name="accessibility_qs_edit_position" msgid="4509277359815711830">"<xliff:g id="POSITION">%1$d</xliff:g> байрлал"</string>
<string name="accessibility_desc_quick_settings_edit" msgid="741658939453595297">"Түргэн тохиргоо засварлагч."</string>
<string name="accessibility_desc_notification_icon" msgid="7331265967584178674">"<xliff:g id="ID_1">%1$s</xliff:g> мэдэгдэл: <xliff:g id="ID_2">%2$s</xliff:g>"</string>
<string name="accessibility_quick_settings_settings" msgid="7098489591715844713">"Тохиргоог нээнэ үү."</string>
@@ -1067,4 +1068,11 @@
<string name="controls_added_tooltip" msgid="4842812921719153085">"Шинэ хяналтыг харахын тулд асаах товчийг удаан дарна уу"</string>
<string name="controls_menu_add" msgid="4447246119229920050">"Хяналт нэмэх"</string>
<string name="controls_menu_edit" msgid="890623986951347062">"Хяналтыг өөрчлөх"</string>
+ <string name="media_output_dialog_add_output" msgid="5642703238877329518">"Гаралт нэмэх"</string>
+ <string name="media_output_dialog_group" msgid="5571251347877452212">"Бүлэг"</string>
+ <string name="media_output_dialog_single_device" msgid="3102758980643351058">"1 төхөөрөмж сонгосон"</string>
+ <string name="media_output_dialog_multiple_devices" msgid="1093771040315422350">"<xliff:g id="COUNT">%1$d</xliff:g> төхөөрөмж сонгосон"</string>
+ <string name="media_output_dialog_disconnected" msgid="1834473104836986046">"<xliff:g id="DEVICE_NAME">%1$s</xliff:g> (салсан)"</string>
+ <string name="media_output_dialog_connect_failed" msgid="3225190634236259010">"Холбогдож чадсангүй. Дахин оролдоно уу."</string>
+ <string name="media_output_dialog_pairing_new" msgid="9099497976087485862">"Шинэ төхөөрөмж хослуулах"</string>
</resources>
diff --git a/packages/SystemUI/res/values-mr/strings.xml b/packages/SystemUI/res/values-mr/strings.xml
index 31a0a7a8da31..b26f89f63243 100644
--- a/packages/SystemUI/res/values-mr/strings.xml
+++ b/packages/SystemUI/res/values-mr/strings.xml
@@ -879,12 +879,13 @@
</string-array>
<string name="tuner_low_priority" msgid="8412666814123009820">"कमी प्राधान्य सूचना आयकन दर्शवा"</string>
<string name="other" msgid="429768510980739978">"अन्य"</string>
- <string name="accessibility_qs_edit_tile_label" msgid="9079791448815232967">"स्थिती <xliff:g id="POSITION">%1$d</xliff:g>, <xliff:g id="TILE_NAME">%2$s</xliff:g>. संपादित करण्यासाठी दोनदा टॅप करा."</string>
- <string name="accessibility_qs_edit_add_tile_label" msgid="8292218072049068613">"<xliff:g id="TILE_NAME">%1$s</xliff:g> . जोडण्यासाठी दोनदा टॅप करा."</string>
- <string name="accessibility_qs_edit_move_tile" msgid="6027997446473163426">"<xliff:g id="TILE_NAME">%1$s</xliff:g> हलवा"</string>
- <string name="accessibility_qs_edit_remove_tile" msgid="3406781901949899624">"<xliff:g id="TILE_NAME">%1$s</xliff:g> काढा"</string>
- <string name="accessibility_qs_edit_tile_add" msgid="6289879620154587233">"<xliff:g id="TILE_NAME">%1$s</xliff:g> <xliff:g id="POSITION">%2$d</xliff:g> स्थानावर जोडा"</string>
- <string name="accessibility_qs_edit_tile_move" msgid="4841770637244326837">"<xliff:g id="TILE_NAME">%1$s</xliff:g> <xliff:g id="POSITION">%2$d</xliff:g> स्थानावर हलवा"</string>
+ <string name="accessibility_qs_edit_remove_tile_action" msgid="775511891457193480">"टाइल काढून टाका"</string>
+ <string name="accessibility_qs_edit_tile_add_action" msgid="5051211910345301833">"टाइल शेवटच्या स्थानावर जोडा"</string>
+ <string name="accessibility_qs_edit_tile_start_move" msgid="2009373939914517817">"टाइल हलवा"</string>
+ <string name="accessibility_qs_edit_tile_start_add" msgid="7560798153975555772">"टाइल जोडा"</string>
+ <string name="accessibility_qs_edit_tile_move_to_position" msgid="5198161544045930556">"<xliff:g id="POSITION">%1$d</xliff:g> यावर हलवा"</string>
+ <string name="accessibility_qs_edit_tile_add_to_position" msgid="9029163095148274690">"<xliff:g id="POSITION">%1$d</xliff:g> स्थानावर जोडा"</string>
+ <string name="accessibility_qs_edit_position" msgid="4509277359815711830">"स्थान <xliff:g id="POSITION">%1$d</xliff:g>"</string>
<string name="accessibility_desc_quick_settings_edit" msgid="741658939453595297">"द्रुत सेटिंग्ज संपादक."</string>
<string name="accessibility_desc_notification_icon" msgid="7331265967584178674">"<xliff:g id="ID_1">%1$s</xliff:g> सूचना: <xliff:g id="ID_2">%2$s</xliff:g>"</string>
<string name="accessibility_quick_settings_settings" msgid="7098489591715844713">"सेटिंग्ज उघडा."</string>
@@ -1067,4 +1068,11 @@
<string name="controls_added_tooltip" msgid="4842812921719153085">"नवीन नियंत्रणे पाहण्यासाठी पॉवर बटण धरून ठेवा"</string>
<string name="controls_menu_add" msgid="4447246119229920050">"नियंत्रणे जोडा"</string>
<string name="controls_menu_edit" msgid="890623986951347062">"नियंत्रणे व्यवस्थापित करा"</string>
+ <string name="media_output_dialog_add_output" msgid="5642703238877329518">"आउटपुट जोडा"</string>
+ <string name="media_output_dialog_group" msgid="5571251347877452212">"गट"</string>
+ <string name="media_output_dialog_single_device" msgid="3102758980643351058">"एक डिव्हाइस निवडले"</string>
+ <string name="media_output_dialog_multiple_devices" msgid="1093771040315422350">"<xliff:g id="COUNT">%1$d</xliff:g> डिव्हाइस निवडली आहेत"</string>
+ <string name="media_output_dialog_disconnected" msgid="1834473104836986046">"<xliff:g id="DEVICE_NAME">%1$s</xliff:g> (डिस्कनेक्ट केले)"</string>
+ <string name="media_output_dialog_connect_failed" msgid="3225190634236259010">"कनेक्ट करू शकलो नाही. पुन्हा प्रयत्न करा."</string>
+ <string name="media_output_dialog_pairing_new" msgid="9099497976087485862">"नवीन डिव्हाइससोबत पेअर करा"</string>
</resources>
diff --git a/packages/SystemUI/res/values-ms/strings.xml b/packages/SystemUI/res/values-ms/strings.xml
index b147b6e7a023..92bc807e76cc 100644
--- a/packages/SystemUI/res/values-ms/strings.xml
+++ b/packages/SystemUI/res/values-ms/strings.xml
@@ -879,12 +879,13 @@
</string-array>
<string name="tuner_low_priority" msgid="8412666814123009820">"Tunjukkan ikon pemberitahuan keutamaan rendah"</string>
<string name="other" msgid="429768510980739978">"Lain-lain"</string>
- <string name="accessibility_qs_edit_tile_label" msgid="9079791448815232967">"Kedudukan <xliff:g id="POSITION">%1$d</xliff:g>, <xliff:g id="TILE_NAME">%2$s</xliff:g>. Dwiketik untuk mengedit."</string>
- <string name="accessibility_qs_edit_add_tile_label" msgid="8292218072049068613">"<xliff:g id="TILE_NAME">%1$s</xliff:g>. Dwiketik untuk menambah."</string>
- <string name="accessibility_qs_edit_move_tile" msgid="6027997446473163426">"Alihkan <xliff:g id="TILE_NAME">%1$s</xliff:g>"</string>
- <string name="accessibility_qs_edit_remove_tile" msgid="3406781901949899624">"Alih keluar <xliff:g id="TILE_NAME">%1$s</xliff:g>"</string>
- <string name="accessibility_qs_edit_tile_add" msgid="6289879620154587233">"Tambahkan <xliff:g id="TILE_NAME">%1$s</xliff:g> pada kedudukan <xliff:g id="POSITION">%2$d</xliff:g>"</string>
- <string name="accessibility_qs_edit_tile_move" msgid="4841770637244326837">"Alihkan <xliff:g id="TILE_NAME">%1$s</xliff:g> ke kedudukan <xliff:g id="POSITION">%2$d</xliff:g>"</string>
+ <string name="accessibility_qs_edit_remove_tile_action" msgid="775511891457193480">"alih keluar jubin"</string>
+ <string name="accessibility_qs_edit_tile_add_action" msgid="5051211910345301833">"tambahkan jubin pada bahagian hujung"</string>
+ <string name="accessibility_qs_edit_tile_start_move" msgid="2009373939914517817">"Alihkan jubin"</string>
+ <string name="accessibility_qs_edit_tile_start_add" msgid="7560798153975555772">"Tambahkan jubin"</string>
+ <string name="accessibility_qs_edit_tile_move_to_position" msgid="5198161544045930556">"Alih ke <xliff:g id="POSITION">%1$d</xliff:g>"</string>
+ <string name="accessibility_qs_edit_tile_add_to_position" msgid="9029163095148274690">"Tambahkan pada kedudukan <xliff:g id="POSITION">%1$d</xliff:g>"</string>
+ <string name="accessibility_qs_edit_position" msgid="4509277359815711830">"Kedudukan <xliff:g id="POSITION">%1$d</xliff:g>"</string>
<string name="accessibility_desc_quick_settings_edit" msgid="741658939453595297">"Editor tetapan pantas."</string>
<string name="accessibility_desc_notification_icon" msgid="7331265967584178674">"Pemberitahuan <xliff:g id="ID_1">%1$s</xliff:g>: <xliff:g id="ID_2">%2$s</xliff:g>"</string>
<string name="accessibility_quick_settings_settings" msgid="7098489591715844713">"Buka tetapan."</string>
@@ -967,7 +968,7 @@
<string name="auto_saver_enabled_text" msgid="7889491183116752719">"Penjimat Bateri akan dihidupkan secara automatik setelah kuasa bateri kurang daripada <xliff:g id="PERCENTAGE">%d</xliff:g>%%."</string>
<string name="open_saver_setting_action" msgid="2111461909782935190">"Tetapan"</string>
<string name="auto_saver_okay_action" msgid="7815925750741935386">"OK"</string>
- <string name="heap_dump_tile_name" msgid="2464189856478823046">"Longgok Tmbunn SysUI"</string>
+ <string name="heap_dump_tile_name" msgid="2464189856478823046">"DumpSys"</string>
<string name="ongoing_privacy_chip_content_single_app" msgid="2969750601815230385">"<xliff:g id="APP">%1$s</xliff:g> sedang menggunakan <xliff:g id="TYPES_LIST">%2$s</xliff:g> anda."</string>
<string name="ongoing_privacy_chip_content_multiple_apps" msgid="8341216022442383954">"Aplikasi sedang menggunakan <xliff:g id="TYPES_LIST">%s</xliff:g> anda."</string>
<string name="ongoing_privacy_dialog_separator" msgid="1866222499727706187">", "</string>
@@ -1067,4 +1068,11 @@
<string name="controls_added_tooltip" msgid="4842812921719153085">"Tahan butang Kuasa untuk melihat kawalan baharu"</string>
<string name="controls_menu_add" msgid="4447246119229920050">"Tambah kawalan"</string>
<string name="controls_menu_edit" msgid="890623986951347062">"Edit kawalan"</string>
+ <string name="media_output_dialog_add_output" msgid="5642703238877329518">"Tambah output"</string>
+ <string name="media_output_dialog_group" msgid="5571251347877452212">"Kumpulan"</string>
+ <string name="media_output_dialog_single_device" msgid="3102758980643351058">"1 peranti dipilih"</string>
+ <string name="media_output_dialog_multiple_devices" msgid="1093771040315422350">"<xliff:g id="COUNT">%1$d</xliff:g> peranti dipilih"</string>
+ <string name="media_output_dialog_disconnected" msgid="1834473104836986046">"<xliff:g id="DEVICE_NAME">%1$s</xliff:g> (diputuskan sambungan)"</string>
+ <string name="media_output_dialog_connect_failed" msgid="3225190634236259010">"Tidak boleh menyambung. Cuba lagi."</string>
+ <string name="media_output_dialog_pairing_new" msgid="9099497976087485862">"Gandingkan peranti baharu"</string>
</resources>
diff --git a/packages/SystemUI/res/values-my/strings.xml b/packages/SystemUI/res/values-my/strings.xml
index 636e83643ee6..9cc9398979de 100644
--- a/packages/SystemUI/res/values-my/strings.xml
+++ b/packages/SystemUI/res/values-my/strings.xml
@@ -879,12 +879,13 @@
</string-array>
<string name="tuner_low_priority" msgid="8412666814123009820">"အရေးမကြီးသော အကြောင်းကြားချက် သင်္ကေတများ ပြရန်"</string>
<string name="other" msgid="429768510980739978">"အခြား"</string>
- <string name="accessibility_qs_edit_tile_label" msgid="9079791448815232967">"<xliff:g id="POSITION">%1$d</xliff:g>၊ <xliff:g id="TILE_NAME">%2$s</xliff:g> နေရာ။ တည်းဖြတ်ရန် နှစ်ချက်တို့ပါ။"</string>
- <string name="accessibility_qs_edit_add_tile_label" msgid="8292218072049068613">"<xliff:g id="TILE_NAME">%1$s</xliff:g>။ ပေါင်းထည့်ရန် နှစ်ချက်တို့ပါ။"</string>
- <string name="accessibility_qs_edit_move_tile" msgid="6027997446473163426">"<xliff:g id="TILE_NAME">%1$s</xliff:g> ကိုရွှေ့ပါ"</string>
- <string name="accessibility_qs_edit_remove_tile" msgid="3406781901949899624">"<xliff:g id="TILE_NAME">%1$s</xliff:g> ကိုဖယ်ရှားပါ"</string>
- <string name="accessibility_qs_edit_tile_add" msgid="6289879620154587233">"<xliff:g id="TILE_NAME">%1$s</xliff:g> ကို အနေအထား <xliff:g id="POSITION">%2$d</xliff:g> သို့ ပေါင်းထည့်ရန်"</string>
- <string name="accessibility_qs_edit_tile_move" msgid="4841770637244326837">"<xliff:g id="TILE_NAME">%1$s</xliff:g> ကို အနေအထား <xliff:g id="POSITION">%2$d</xliff:g> သို့ ရွှေ့ရန်"</string>
+ <string name="accessibility_qs_edit_remove_tile_action" msgid="775511891457193480">"အကွက်ငယ်ကို ဖယ်ရှားရန်"</string>
+ <string name="accessibility_qs_edit_tile_add_action" msgid="5051211910345301833">"အဆုံးတွင် အကွက်ငယ်ထည့်ရန်"</string>
+ <string name="accessibility_qs_edit_tile_start_move" msgid="2009373939914517817">"အကွက်ငယ်ကို ရွှေ့ရန်"</string>
+ <string name="accessibility_qs_edit_tile_start_add" msgid="7560798153975555772">"အကွက်ငယ်ကို ထည့်ရန်"</string>
+ <string name="accessibility_qs_edit_tile_move_to_position" msgid="5198161544045930556">"<xliff:g id="POSITION">%1$d</xliff:g> သို့ ရွှေ့ရန်"</string>
+ <string name="accessibility_qs_edit_tile_add_to_position" msgid="9029163095148274690">"<xliff:g id="POSITION">%1$d</xliff:g> အနေအထားသို့ ပေါင်းထည့်ရန်"</string>
+ <string name="accessibility_qs_edit_position" msgid="4509277359815711830">"<xliff:g id="POSITION">%1$d</xliff:g> အနေအထား"</string>
<string name="accessibility_desc_quick_settings_edit" msgid="741658939453595297">"မြန်ဆန်သည့် ဆက်တင်တည်းဖြတ်စနစ်"</string>
<string name="accessibility_desc_notification_icon" msgid="7331265967584178674">"<xliff:g id="ID_1">%1$s</xliff:g> အကြောင်းကြားချက် − <xliff:g id="ID_2">%2$s</xliff:g>"</string>
<string name="accessibility_quick_settings_settings" msgid="7098489591715844713">"ဆက်တင်များကို ဖွင့်ပါ။"</string>
@@ -1067,4 +1068,11 @@
<string name="controls_added_tooltip" msgid="4842812921719153085">"ထိန်းချုပ်မှုအသစ်များ ကြည့်ရန် ဖွင့်ပိတ်ခလုတ်ကို ဖိထားပါ"</string>
<string name="controls_menu_add" msgid="4447246119229920050">"ထိန်းချုပ်မှုများ ထည့်ရန်"</string>
<string name="controls_menu_edit" msgid="890623986951347062">"ထိန်းချုပ်မှုများ တည်းဖြတ်ရန်"</string>
+ <string name="media_output_dialog_add_output" msgid="5642703238877329518">"မီဒီယာအထွက်များ ထည့်ရန်"</string>
+ <string name="media_output_dialog_group" msgid="5571251347877452212">"အုပ်စု"</string>
+ <string name="media_output_dialog_single_device" msgid="3102758980643351058">"စက်ပစ္စည်း ၁ ခုကို ရွေးချယ်ထားသည်"</string>
+ <string name="media_output_dialog_multiple_devices" msgid="1093771040315422350">"စက်ပစ္စည်း <xliff:g id="COUNT">%1$d</xliff:g> ခုကို ရွေးချယ်ထားသည်"</string>
+ <string name="media_output_dialog_disconnected" msgid="1834473104836986046">"<xliff:g id="DEVICE_NAME">%1$s</xliff:g> (ချိတ်ဆက်မထားပါ)"</string>
+ <string name="media_output_dialog_connect_failed" msgid="3225190634236259010">"ချိတ်ဆက်၍ မရပါ။ ထပ်စမ်းကြည့်ပါ။"</string>
+ <string name="media_output_dialog_pairing_new" msgid="9099497976087485862">"စက်အသစ် တွဲချိတ်ရန်"</string>
</resources>
diff --git a/packages/SystemUI/res/values-nb/strings.xml b/packages/SystemUI/res/values-nb/strings.xml
index 93ef3af9b037..92ae68943352 100644
--- a/packages/SystemUI/res/values-nb/strings.xml
+++ b/packages/SystemUI/res/values-nb/strings.xml
@@ -879,12 +879,13 @@
</string-array>
<string name="tuner_low_priority" msgid="8412666814123009820">"Vis ikoner for varsler med lav prioritet"</string>
<string name="other" msgid="429768510980739978">"Annet"</string>
- <string name="accessibility_qs_edit_tile_label" msgid="9079791448815232967">"Plassering <xliff:g id="POSITION">%1$d</xliff:g>, <xliff:g id="TILE_NAME">%2$s</xliff:g>. Dobbelttrykk for å endre."</string>
- <string name="accessibility_qs_edit_add_tile_label" msgid="8292218072049068613">"<xliff:g id="TILE_NAME">%1$s</xliff:g>. Dobbelttrykk for å legge til."</string>
- <string name="accessibility_qs_edit_move_tile" msgid="6027997446473163426">"Flytt <xliff:g id="TILE_NAME">%1$s</xliff:g>"</string>
- <string name="accessibility_qs_edit_remove_tile" msgid="3406781901949899624">"Fjern <xliff:g id="TILE_NAME">%1$s</xliff:g>"</string>
- <string name="accessibility_qs_edit_tile_add" msgid="6289879620154587233">"Legg til <xliff:g id="TILE_NAME">%1$s</xliff:g> i posisjon <xliff:g id="POSITION">%2$d</xliff:g>"</string>
- <string name="accessibility_qs_edit_tile_move" msgid="4841770637244326837">"Flytt <xliff:g id="TILE_NAME">%1$s</xliff:g> til posisjon <xliff:g id="POSITION">%2$d</xliff:g>"</string>
+ <string name="accessibility_qs_edit_remove_tile_action" msgid="775511891457193480">"fjerne infobrikken"</string>
+ <string name="accessibility_qs_edit_tile_add_action" msgid="5051211910345301833">"legge til en infobrikke på slutten"</string>
+ <string name="accessibility_qs_edit_tile_start_move" msgid="2009373939914517817">"Flytt infobrikken"</string>
+ <string name="accessibility_qs_edit_tile_start_add" msgid="7560798153975555772">"Legg til en infobrikke"</string>
+ <string name="accessibility_qs_edit_tile_move_to_position" msgid="5198161544045930556">"Flytt til <xliff:g id="POSITION">%1$d</xliff:g>"</string>
+ <string name="accessibility_qs_edit_tile_add_to_position" msgid="9029163095148274690">"Legg til posisjonen <xliff:g id="POSITION">%1$d</xliff:g>"</string>
+ <string name="accessibility_qs_edit_position" msgid="4509277359815711830">"Posisjon <xliff:g id="POSITION">%1$d</xliff:g>"</string>
<string name="accessibility_desc_quick_settings_edit" msgid="741658939453595297">"Redigeringsvindu for hurtiginnstillinger."</string>
<string name="accessibility_desc_notification_icon" msgid="7331265967584178674">"<xliff:g id="ID_1">%1$s</xliff:g>-varsel: <xliff:g id="ID_2">%2$s</xliff:g>"</string>
<string name="accessibility_quick_settings_settings" msgid="7098489591715844713">"Åpne innstillingene."</string>
@@ -1067,4 +1068,11 @@
<string name="controls_added_tooltip" msgid="4842812921719153085">"Hold inne av/på-knappen for å se kontroller"</string>
<string name="controls_menu_add" msgid="4447246119229920050">"Legg til kontroller"</string>
<string name="controls_menu_edit" msgid="890623986951347062">"Endre kontroller"</string>
+ <string name="media_output_dialog_add_output" msgid="5642703238877329518">"Legg til utenheter"</string>
+ <string name="media_output_dialog_group" msgid="5571251347877452212">"Gruppe"</string>
+ <string name="media_output_dialog_single_device" msgid="3102758980643351058">"1 enhet er valgt"</string>
+ <string name="media_output_dialog_multiple_devices" msgid="1093771040315422350">"<xliff:g id="COUNT">%1$d</xliff:g> enheter er valgt"</string>
+ <string name="media_output_dialog_disconnected" msgid="1834473104836986046">"<xliff:g id="DEVICE_NAME">%1$s</xliff:g> (frakoblet)"</string>
+ <string name="media_output_dialog_connect_failed" msgid="3225190634236259010">"Kunne ikke koble til. Prøv på nytt."</string>
+ <string name="media_output_dialog_pairing_new" msgid="9099497976087485862">"Koble til en ny enhet"</string>
</resources>
diff --git a/packages/SystemUI/res/values-ne/strings.xml b/packages/SystemUI/res/values-ne/strings.xml
index d218e3939304..1346874ed08f 100644
--- a/packages/SystemUI/res/values-ne/strings.xml
+++ b/packages/SystemUI/res/values-ne/strings.xml
@@ -43,7 +43,7 @@
<string name="status_bar_settings_mute_label" msgid="914392730086057522">"म्युट गर्नुहोस्"</string>
<string name="status_bar_settings_auto_brightness_label" msgid="2151934479226017725">"स्वतः"</string>
<string name="status_bar_settings_notifications" msgid="5285316949980621438">"सूचनाहरू"</string>
- <string name="bluetooth_tethered" msgid="4171071193052799041">"ब्लुटुथ टेथर भयो"</string>
+ <string name="bluetooth_tethered" msgid="4171071193052799041">"ब्लुटुथ टेदर भयो"</string>
<string name="status_bar_input_method_settings_configure_input_methods" msgid="2972273031043777851">"इनपुट विधिहरू सेटअप गर्नुहोस्"</string>
<string name="status_bar_use_physical_keyboard" msgid="4849251850931213371">"वास्तविक किबोर्ड"</string>
<string name="usb_device_permission_prompt" msgid="4414719028369181772">"<xliff:g id="APPLICATION">%1$s</xliff:g> लाई <xliff:g id="USB_DEVICE">%2$s</xliff:g> माथि पहुँच राख्ने अनुमति दिने हो?"</string>
@@ -879,12 +879,13 @@
</string-array>
<string name="tuner_low_priority" msgid="8412666814123009820">"कम प्राथमिकताका सूचना आइकनहरू देखाउनुहोस्"</string>
<string name="other" msgid="429768510980739978">"अन्य"</string>
- <string name="accessibility_qs_edit_tile_label" msgid="9079791448815232967">"स्थिति <xliff:g id="POSITION">%1$d</xliff:g>, <xliff:g id="TILE_NAME">%2$s</xliff:g>। सम्पादन गर्नाका लागि डबल ट्याप गर्नुहोस्।"</string>
- <string name="accessibility_qs_edit_add_tile_label" msgid="8292218072049068613">"<xliff:g id="TILE_NAME">%1$s</xliff:g>। थप्नका लागि डबल ट्याप गर्नुहोस्।"</string>
- <string name="accessibility_qs_edit_move_tile" msgid="6027997446473163426">"<xliff:g id="TILE_NAME">%1$s</xliff:g> लाई सार्नुहोस्"</string>
- <string name="accessibility_qs_edit_remove_tile" msgid="3406781901949899624">"<xliff:g id="TILE_NAME">%1$s</xliff:g> लाई हटाउनुहोस्"</string>
- <string name="accessibility_qs_edit_tile_add" msgid="6289879620154587233">"<xliff:g id="TILE_NAME">%1$s</xliff:g> लाई <xliff:g id="POSITION">%2$d</xliff:g> स्थितिमा थप्नुहोस्"</string>
- <string name="accessibility_qs_edit_tile_move" msgid="4841770637244326837">"<xliff:g id="TILE_NAME">%1$s</xliff:g> लाई <xliff:g id="POSITION">%2$d</xliff:g> स्थितिमा सार्नुहोस्"</string>
+ <string name="accessibility_qs_edit_remove_tile_action" msgid="775511891457193480">"टाइल हटाउनुहोस्"</string>
+ <string name="accessibility_qs_edit_tile_add_action" msgid="5051211910345301833">"टाइल अन्त्यमा हाल्नुहोस्"</string>
+ <string name="accessibility_qs_edit_tile_start_move" msgid="2009373939914517817">"टाइल सार्नुहोस्"</string>
+ <string name="accessibility_qs_edit_tile_start_add" msgid="7560798153975555772">"टाइल हाल्नुहोस्"</string>
+ <string name="accessibility_qs_edit_tile_move_to_position" msgid="5198161544045930556">"टाइल सारेर <xliff:g id="POSITION">%1$d</xliff:g> मा लैजानुहोस्"</string>
+ <string name="accessibility_qs_edit_tile_add_to_position" msgid="9029163095148274690">"टाइल यो अवस्था <xliff:g id="POSITION">%1$d</xliff:g> मा हाल्नुहोस्"</string>
+ <string name="accessibility_qs_edit_position" msgid="4509277359815711830">"स्थिति <xliff:g id="POSITION">%1$d</xliff:g>"</string>
<string name="accessibility_desc_quick_settings_edit" msgid="741658939453595297">"द्रुत सेटिङ सम्पादक।"</string>
<string name="accessibility_desc_notification_icon" msgid="7331265967584178674">"<xliff:g id="ID_1">%1$s</xliff:g> को सूचना: <xliff:g id="ID_2">%2$s</xliff:g>"</string>
<string name="accessibility_quick_settings_settings" msgid="7098489591715844713">"सेटिङहरूलाई खोल्नुहोस्।"</string>
@@ -1067,4 +1068,11 @@
<string name="controls_added_tooltip" msgid="4842812921719153085">"नयाँ नियन्त्रण सुविधाहरू हेर्न पावर बटन थिचिराख्नुहोस्"</string>
<string name="controls_menu_add" msgid="4447246119229920050">"नियन्त्रण सुविधाहरू थप्नुहोस्"</string>
<string name="controls_menu_edit" msgid="890623986951347062">"नियन्त्रण सुविधाहरू सम्पादन गर्नु…"</string>
+ <string name="media_output_dialog_add_output" msgid="5642703238877329518">"आउटपुट यन्त्रहरू थप्नुहोस्"</string>
+ <string name="media_output_dialog_group" msgid="5571251347877452212">"समूह"</string>
+ <string name="media_output_dialog_single_device" msgid="3102758980643351058">"१ यन्त्र चयन गरियो"</string>
+ <string name="media_output_dialog_multiple_devices" msgid="1093771040315422350">"<xliff:g id="COUNT">%1$d</xliff:g> वटा यन्त्र चयन गरिए"</string>
+ <string name="media_output_dialog_disconnected" msgid="1834473104836986046">"<xliff:g id="DEVICE_NAME">%1$s</xliff:g> (डिस्कनेक्ट गरिएको)"</string>
+ <string name="media_output_dialog_connect_failed" msgid="3225190634236259010">"कनेक्ट गर्न सकिएन। फेरि प्रयास गर्नुहोस्।"</string>
+ <string name="media_output_dialog_pairing_new" msgid="9099497976087485862">"नयाँ यन्त्रको जोडा बनाउनुहोस्"</string>
</resources>
diff --git a/packages/SystemUI/res/values-nl/strings.xml b/packages/SystemUI/res/values-nl/strings.xml
index 3d61ee4adaf7..139b57b7c3bf 100644
--- a/packages/SystemUI/res/values-nl/strings.xml
+++ b/packages/SystemUI/res/values-nl/strings.xml
@@ -711,12 +711,12 @@
<string name="notification_bubble_title" msgid="8330481035191903164">"Bubbel"</string>
<string name="notification_automatic_title" msgid="3745465364578762652">"Automatisch"</string>
<string name="notification_channel_summary_low" msgid="4860617986908931158">"Geen geluid of trilling"</string>
- <string name="notification_conversation_summary_low" msgid="1734433426085468009">"Geen geluid of trilling en wordt op een lagere positie in het gedeelte met gesprekken weergegeven"</string>
+ <string name="notification_conversation_summary_low" msgid="1734433426085468009">"Geen geluid of trilling en wordt lager in het gedeelte met gesprekken weergegeven"</string>
<string name="notification_channel_summary_default" msgid="3282930979307248890">"Kan overgaan of trillen op basis van de telefooninstellingen"</string>
<string name="notification_channel_summary_default_with_bubbles" msgid="1782419896613644568">"Kan overgaan of trillen op basis van de telefooninstellingen. Gesprekken uit <xliff:g id="APP_NAME">%1$s</xliff:g> worden standaard als bubbels weergegeven."</string>
<string name="notification_channel_summary_bubble" msgid="7235935211580860537">"Trekt de aandacht met een zwevende snelkoppeling naar deze content."</string>
<string name="notification_channel_summary_automatic" msgid="5813109268050235275">"Het systeem laten bepalen of deze melding geluid moet maken of moet trillen"</string>
- <string name="notification_channel_summary_priority" msgid="7952654515769021553">"Wordt bovenaan het gedeelte met gesprekken weergegeven, verschijnt als zwevende bubbel, geeft de profielfoto weer op het vergrendelscherm"</string>
+ <string name="notification_channel_summary_priority" msgid="7952654515769021553">"Wordt bovenaan het gespreksgedeelte weergegeven, verschijnt als zwevende bubbel, geeft profielfoto weer op vergrendelscherm"</string>
<string name="notification_conversation_channel_settings" msgid="2409977688430606835">"Instellingen"</string>
<string name="notification_priority_title" msgid="2079708866333537093">"Prioriteit"</string>
<string name="no_shortcut" msgid="8257177117568230126">"<xliff:g id="APP_NAME">%1$s</xliff:g> ondersteunt geen gespreksfuncties"</string>
@@ -879,12 +879,13 @@
</string-array>
<string name="tuner_low_priority" msgid="8412666814123009820">"Pictogrammen voor meldingen met lage prioriteit weergeven"</string>
<string name="other" msgid="429768510980739978">"Overig"</string>
- <string name="accessibility_qs_edit_tile_label" msgid="9079791448815232967">"Positie <xliff:g id="POSITION">%1$d</xliff:g>, <xliff:g id="TILE_NAME">%2$s</xliff:g>. Dubbeltik om te bewerken."</string>
- <string name="accessibility_qs_edit_add_tile_label" msgid="8292218072049068613">"<xliff:g id="TILE_NAME">%1$s</xliff:g>. Dubbeltik om toe te voegen."</string>
- <string name="accessibility_qs_edit_move_tile" msgid="6027997446473163426">"<xliff:g id="TILE_NAME">%1$s</xliff:g> verplaatsen"</string>
- <string name="accessibility_qs_edit_remove_tile" msgid="3406781901949899624">"<xliff:g id="TILE_NAME">%1$s</xliff:g> verwijderen"</string>
- <string name="accessibility_qs_edit_tile_add" msgid="6289879620154587233">"<xliff:g id="TILE_NAME">%1$s</xliff:g> toevoegen aan positie <xliff:g id="POSITION">%2$d</xliff:g>"</string>
- <string name="accessibility_qs_edit_tile_move" msgid="4841770637244326837">"<xliff:g id="TILE_NAME">%1$s</xliff:g> verplaatsen naar positie <xliff:g id="POSITION">%2$d</xliff:g>"</string>
+ <string name="accessibility_qs_edit_remove_tile_action" msgid="775511891457193480">"tegel verwijderen"</string>
+ <string name="accessibility_qs_edit_tile_add_action" msgid="5051211910345301833">"tegel toevoegen aan einde"</string>
+ <string name="accessibility_qs_edit_tile_start_move" msgid="2009373939914517817">"Tegel verplaatsen"</string>
+ <string name="accessibility_qs_edit_tile_start_add" msgid="7560798153975555772">"Tegel toevoegen"</string>
+ <string name="accessibility_qs_edit_tile_move_to_position" msgid="5198161544045930556">"Verplaatsen naar <xliff:g id="POSITION">%1$d</xliff:g>"</string>
+ <string name="accessibility_qs_edit_tile_add_to_position" msgid="9029163095148274690">"Toevoegen aan positie <xliff:g id="POSITION">%1$d</xliff:g>"</string>
+ <string name="accessibility_qs_edit_position" msgid="4509277359815711830">"Positie <xliff:g id="POSITION">%1$d</xliff:g>"</string>
<string name="accessibility_desc_quick_settings_edit" msgid="741658939453595297">"Editor voor \'Snelle instellingen\'."</string>
<string name="accessibility_desc_notification_icon" msgid="7331265967584178674">"<xliff:g id="ID_1">%1$s</xliff:g>-melding: <xliff:g id="ID_2">%2$s</xliff:g>"</string>
<string name="accessibility_quick_settings_settings" msgid="7098489591715844713">"Instellingen openen."</string>
@@ -1067,4 +1068,11 @@
<string name="controls_added_tooltip" msgid="4842812921719153085">"Houd de aan/uit-knop ingedrukt om nieuwe bedieningselementen te bekijken"</string>
<string name="controls_menu_add" msgid="4447246119229920050">"Bedieningselementen toevoegen"</string>
<string name="controls_menu_edit" msgid="890623986951347062">"Bedieningselementen bewerken"</string>
+ <string name="media_output_dialog_add_output" msgid="5642703238877329518">"Uitvoer toevoegen"</string>
+ <string name="media_output_dialog_group" msgid="5571251347877452212">"Groep"</string>
+ <string name="media_output_dialog_single_device" msgid="3102758980643351058">"Eén apparaat geselecteerd"</string>
+ <string name="media_output_dialog_multiple_devices" msgid="1093771040315422350">"<xliff:g id="COUNT">%1$d</xliff:g> apparaten geselecteerd"</string>
+ <string name="media_output_dialog_disconnected" msgid="1834473104836986046">"<xliff:g id="DEVICE_NAME">%1$s</xliff:g> (verbinding verbroken)"</string>
+ <string name="media_output_dialog_connect_failed" msgid="3225190634236259010">"Kan geen verbinding maken. Probeer het nog eens."</string>
+ <string name="media_output_dialog_pairing_new" msgid="9099497976087485862">"Nieuw apparaat koppelen"</string>
</resources>
diff --git a/packages/SystemUI/res/values-or/strings.xml b/packages/SystemUI/res/values-or/strings.xml
index dc38e39e054c..3973734f16d2 100644
--- a/packages/SystemUI/res/values-or/strings.xml
+++ b/packages/SystemUI/res/values-or/strings.xml
@@ -716,7 +716,7 @@
<string name="notification_channel_summary_default_with_bubbles" msgid="1782419896613644568">"ଫୋନ୍ ସେଟିଂସ୍ ଆଧାରରେ ରିଙ୍ଗ କିମ୍ବା ଭାଇବ୍ରେଟ୍ ହୋଇପାରେ। <xliff:g id="APP_NAME">%1$s</xliff:g>ରୁ ବାର୍ତ୍ତାଳାପଗୁଡ଼ିକ ଡିଫଲ୍ଟ ଭାବରେ ବବଲ୍ ହୁଏ।"</string>
<string name="notification_channel_summary_bubble" msgid="7235935211580860537">"ଏହି ବିଷୟବସ୍ତୁ ପାଇଁ ଏକ ଭାସମାନ ସର୍ଟକଟ୍ ସହ ଆପଣଙ୍କର ଧ୍ୟାନ ଦିଅନ୍ତୁ।"</string>
<string name="notification_channel_summary_automatic" msgid="5813109268050235275">"ଏହି ବିଜ୍ଞପ୍ତି ପ୍ରାପ୍ତ ହେବା ସମୟରେ ସାଉଣ୍ଡ ହେବା ଉଚିତ ନା ଭାଇବ୍ରେସନ୍ ତାହା ସିଷ୍ଟମକୁ ସ୍ଥିର କରିବାକୁ ଦିଅନ୍ତୁ"</string>
- <string name="notification_channel_summary_priority" msgid="7952654515769021553">"ବାର୍ତ୍ତାଳାପ ବିଭାଗର ଶୀର୍ଷରେ ଦେଖାଏ, ଭାସମାନ ବବଲ୍ ଭାବେ ଦେଖାଯାଏ, ଲକ୍ ସ୍କ୍ରିନରେ ପ୍ରୋଫାଇଲ୍ ଛବି ଡିସପ୍ଲେ କରେ"</string>
+ <string name="notification_channel_summary_priority" msgid="7952654515769021553">"ବାର୍ତ୍ତାଳାପ ବିଭାଗର ଶୀର୍ଷରେ ଦେଖାଏ, ଫ୍ଲୋଟିଂ ବବଲ୍ ଭାବେ ଦେଖାଯାଏ, ଲକ୍ ସ୍କ୍ରିନରେ ପ୍ରୋଫାଇଲ୍ ଛବି ଡିସପ୍ଲେ କରେ"</string>
<string name="notification_conversation_channel_settings" msgid="2409977688430606835">"ସେଟିଂସ୍"</string>
<string name="notification_priority_title" msgid="2079708866333537093">"ପ୍ରାଥମିକତା"</string>
<string name="no_shortcut" msgid="8257177117568230126">"<xliff:g id="APP_NAME">%1$s</xliff:g> ବାର୍ତ୍ତାଳାପ ଫିଚରଗୁଡ଼ିକୁ ସମର୍ଥନ କରେ ନାହିଁ"</string>
@@ -861,7 +861,7 @@
<string name="right_keycode" msgid="2480715509844798438">"ଡାହାଣ କୀ\'କୋଡ୍‍"</string>
<string name="left_icon" msgid="5036278531966897006">"ବାମ ଆଇକନ୍‍"</string>
<string name="right_icon" msgid="1103955040645237425">"ଡାହାଣ ଆଇକନ୍"</string>
- <string name="drag_to_add_tiles" msgid="8933270127508303672">"ଟାଇଲ୍ ଯୋଡ଼ିବା ପାଇଁ ଦାବିଧରି ଟାଣନ୍ତୁ"</string>
+ <string name="drag_to_add_tiles" msgid="8933270127508303672">"ଟାଇଲ୍ ଯୋଗ କରିବା ପାଇଁ ଦାବିଧରି ଟାଣନ୍ତୁ"</string>
<string name="drag_to_rearrange_tiles" msgid="2143204300089638620">"ଟାଇଲ୍‍ ପୁଣି ସଜାଇବାକୁ ଦାବିଧରି ଟାଣନ୍ତୁ"</string>
<string name="drag_to_remove_tiles" msgid="4682194717573850385">"ବାହାର କରିବାକୁ ଏଠାକୁ ଡ୍ରାଗ୍‍ କରନ୍ତୁ"</string>
<string name="drag_to_remove_disabled" msgid="933046987838658850">"ଆପଣଙ୍କର ଅତିକମ୍‌ରେ <xliff:g id="MIN_NUM_TILES">%1$d</xliff:g>ଟି ଟାଇଲ୍ ଆବଶ୍ୟକ"</string>
@@ -879,12 +879,13 @@
</string-array>
<string name="tuner_low_priority" msgid="8412666814123009820">"କମ୍‍-ଅଗ୍ରାଧିକାର ବିଜ୍ଞପ୍ତି ଆଇକନ୍‍ ଦେଖାନ୍ତୁ"</string>
<string name="other" msgid="429768510980739978">"ଅନ୍ୟାନ୍ୟ"</string>
- <string name="accessibility_qs_edit_tile_label" msgid="9079791448815232967">"ଅବସ୍ଥାନ <xliff:g id="POSITION">%1$d</xliff:g>, <xliff:g id="TILE_NAME">%2$s</xliff:g>। ଏଡିଟ୍ କରିବାକୁ ଡବଲ୍‍-ଟାପ୍‍ କରନ୍ତୁ।"</string>
- <string name="accessibility_qs_edit_add_tile_label" msgid="8292218072049068613">"<xliff:g id="TILE_NAME">%1$s</xliff:g>। ଯୋଡ଼ିବା ପାଇଁ ଡବଲ୍‍-ଟାପ୍‍ କରନ୍ତୁ।"</string>
- <string name="accessibility_qs_edit_move_tile" msgid="6027997446473163426">"<xliff:g id="TILE_NAME">%1$s</xliff:g> ନିଅନ୍ତୁ"</string>
- <string name="accessibility_qs_edit_remove_tile" msgid="3406781901949899624">"<xliff:g id="TILE_NAME">%1$s</xliff:g> ବାହାର କରିଦିଅନ୍ତୁ"</string>
- <string name="accessibility_qs_edit_tile_add" msgid="6289879620154587233">"<xliff:g id="POSITION">%2$d</xliff:g> ଅବସ୍ଥାନକୁ <xliff:g id="TILE_NAME">%1$s</xliff:g> ଯୋଡନ୍ତୁ"</string>
- <string name="accessibility_qs_edit_tile_move" msgid="4841770637244326837">"<xliff:g id="POSITION">%2$d</xliff:g> ଅବସ୍ଥାନକୁ <xliff:g id="TILE_NAME">%1$s</xliff:g> ଘୁଞ୍ଚାନ୍ତୁ"</string>
+ <string name="accessibility_qs_edit_remove_tile_action" msgid="775511891457193480">"ଟାଇଲ୍ କାଢ଼ି ଦିଅନ୍ତୁ"</string>
+ <string name="accessibility_qs_edit_tile_add_action" msgid="5051211910345301833">"ଶେଷରେ ଟାଇଲ୍ ଯୋଗ କରନ୍ତୁ"</string>
+ <string name="accessibility_qs_edit_tile_start_move" msgid="2009373939914517817">"ଟାଇଲ୍ ମୁଭ୍ କରନ୍ତୁ"</string>
+ <string name="accessibility_qs_edit_tile_start_add" msgid="7560798153975555772">"ଟାଇଲ୍ ଯୋଗ କରନ୍ତୁ"</string>
+ <string name="accessibility_qs_edit_tile_move_to_position" msgid="5198161544045930556">"<xliff:g id="POSITION">%1$d</xliff:g>କୁ ମୁଭ୍ କରନ୍ତୁ"</string>
+ <string name="accessibility_qs_edit_tile_add_to_position" msgid="9029163095148274690">"<xliff:g id="POSITION">%1$d</xliff:g> ଅବସ୍ଥିତିରେ ଯୋଗ କରନ୍ତୁ"</string>
+ <string name="accessibility_qs_edit_position" msgid="4509277359815711830">"ଅବସ୍ଥିତି <xliff:g id="POSITION">%1$d</xliff:g>"</string>
<string name="accessibility_desc_quick_settings_edit" msgid="741658939453595297">"ଦ୍ରୁତ ସେଟିଙ୍ଗ ଏଡିଟର୍।"</string>
<string name="accessibility_desc_notification_icon" msgid="7331265967584178674">"<xliff:g id="ID_1">%1$s</xliff:g> ବିଜ୍ଞପ୍ତି: <xliff:g id="ID_2">%2$s</xliff:g>"</string>
<string name="accessibility_quick_settings_settings" msgid="7098489591715844713">"ସେଟିଂସ୍ ଖୋଲନ୍ତୁ।"</string>
@@ -1067,4 +1068,11 @@
<string name="controls_added_tooltip" msgid="4842812921719153085">"ନୂଆ ନିୟନ୍ତ୍ରଣଗୁଡ଼ିକୁ ଦେଖିବା ପାଇଁ ପାୱାର ବଟନକୁ ଧରି ରଖନ୍ତୁ"</string>
<string name="controls_menu_add" msgid="4447246119229920050">"ନିୟନ୍ତ୍ରଣଗୁଡ଼ିକ ଯୋଗ କରନ୍ତୁ"</string>
<string name="controls_menu_edit" msgid="890623986951347062">"ନିୟନ୍ତ୍ରଣଗୁଡ଼ିକ ସମ୍ପାଦନ କରନ୍ତୁ"</string>
+ <string name="media_output_dialog_add_output" msgid="5642703238877329518">"ଆଉଟପୁଟ୍ ଯୋଗ କରନ୍ତୁ"</string>
+ <string name="media_output_dialog_group" msgid="5571251347877452212">"ଗୋଷ୍ଠୀ"</string>
+ <string name="media_output_dialog_single_device" msgid="3102758980643351058">"1ଟି ଡିଭାଇସ୍ ଚୟନ କରାଯାଇଛି"</string>
+ <string name="media_output_dialog_multiple_devices" msgid="1093771040315422350">"<xliff:g id="COUNT">%1$d</xliff:g>ଟି ଡିଭାଇସ୍ ଚୟନ କରାଯାଇଛି"</string>
+ <string name="media_output_dialog_disconnected" msgid="1834473104836986046">"<xliff:g id="DEVICE_NAME">%1$s</xliff:g> (ବିଚ୍ଛିନ୍ନ କରାଯାଇଛି)"</string>
+ <string name="media_output_dialog_connect_failed" msgid="3225190634236259010">"ସଂଯୋଗ କରାଯାଇପାରିଲା ନାହିଁ। ପୁଣି ଚେଷ୍ଟା କରନ୍ତୁ।"</string>
+ <string name="media_output_dialog_pairing_new" msgid="9099497976087485862">"ନୂଆ ଡିଭାଇସକୁ ପେୟାର୍ କରନ୍ତୁ"</string>
</resources>
diff --git a/packages/SystemUI/res/values-pa/strings.xml b/packages/SystemUI/res/values-pa/strings.xml
index 8d15d176ead4..e727f48a1a3e 100644
--- a/packages/SystemUI/res/values-pa/strings.xml
+++ b/packages/SystemUI/res/values-pa/strings.xml
@@ -879,12 +879,13 @@
</string-array>
<string name="tuner_low_priority" msgid="8412666814123009820">"ਘੱਟ ਤਰਜੀਹ ਵਾਲੇ ਸੂਚਨਾ ਪ੍ਰਤੀਕਾਂ ਨੂੰ ਦਿਖਾਓ"</string>
<string name="other" msgid="429768510980739978">"ਹੋਰ"</string>
- <string name="accessibility_qs_edit_tile_label" msgid="9079791448815232967">"ਸਥਿਤੀ <xliff:g id="POSITION">%1$d</xliff:g>, <xliff:g id="TILE_NAME">%2$s</xliff:g>। ਸੰਪਾਦਨ ਲਈ ਦੋ ਵਾਰ ਟੈਪ ਕਰੋ।"</string>
- <string name="accessibility_qs_edit_add_tile_label" msgid="8292218072049068613">"<xliff:g id="TILE_NAME">%1$s</xliff:g>। ਸ਼ਾਮਲ ਕਰਨ ਲਈ ਦੋ ਵਾਰ ਟੈਪ ਕਰੋ।"</string>
- <string name="accessibility_qs_edit_move_tile" msgid="6027997446473163426">"<xliff:g id="TILE_NAME">%1$s</xliff:g> ਨੂੰ ਤਬਦੀਲ ਕਰੋ"</string>
- <string name="accessibility_qs_edit_remove_tile" msgid="3406781901949899624">"<xliff:g id="TILE_NAME">%1$s</xliff:g> ਹਟਾਓ"</string>
- <string name="accessibility_qs_edit_tile_add" msgid="6289879620154587233">"<xliff:g id="TILE_NAME">%1$s</xliff:g> ਨੂੰ <xliff:g id="POSITION">%2$d</xliff:g> ਸਥਾਨ \'ਤੇ ਸ਼ਾਮਲ ਕਰੋ"</string>
- <string name="accessibility_qs_edit_tile_move" msgid="4841770637244326837">"<xliff:g id="TILE_NAME">%1$s</xliff:g> ਨੂੰ <xliff:g id="POSITION">%2$d</xliff:g> ਸਥਾਨ \'ਤੇ ਲਿਜਾਓ"</string>
+ <string name="accessibility_qs_edit_remove_tile_action" msgid="775511891457193480">"ਟਾਇਲ ਹਟਾਓ"</string>
+ <string name="accessibility_qs_edit_tile_add_action" msgid="5051211910345301833">"ਟਾਇਲ ਨੂੰ ਅੰਤ ਵਿੱਚ ਸ਼ਾਮਲ ਕਰੋ"</string>
+ <string name="accessibility_qs_edit_tile_start_move" msgid="2009373939914517817">"ਟਾਇਲ ਨੂੰ ਲਿਜਾਓ"</string>
+ <string name="accessibility_qs_edit_tile_start_add" msgid="7560798153975555772">"ਟਾਇਲ ਸ਼ਾਮਲ ਕਰੋ"</string>
+ <string name="accessibility_qs_edit_tile_move_to_position" msgid="5198161544045930556">"<xliff:g id="POSITION">%1$d</xliff:g> \'ਤੇ ਲਿਜਾਓ"</string>
+ <string name="accessibility_qs_edit_tile_add_to_position" msgid="9029163095148274690">"<xliff:g id="POSITION">%1$d</xliff:g> ਸਥਾਨ \'ਤੇ ਸ਼ਾਮਲ ਕਰੋ"</string>
+ <string name="accessibility_qs_edit_position" msgid="4509277359815711830">"ਸਥਾਨ <xliff:g id="POSITION">%1$d</xliff:g>"</string>
<string name="accessibility_desc_quick_settings_edit" msgid="741658939453595297">"ਤਤਕਾਲ ਸੈਟਿੰਗਾਂ ਸੰਪਾਦਕ।"</string>
<string name="accessibility_desc_notification_icon" msgid="7331265967584178674">"<xliff:g id="ID_1">%1$s</xliff:g> ਸੂਚਨਾ: <xliff:g id="ID_2">%2$s</xliff:g>"</string>
<string name="accessibility_quick_settings_settings" msgid="7098489591715844713">"ਸੈਟਿੰਗਾਂ ਖੋਲ੍ਹੋ।"</string>
@@ -1067,4 +1068,11 @@
<string name="controls_added_tooltip" msgid="4842812921719153085">"ਨਵੇਂ ਕੰਟਰੋਲ ਦੇਖਣ ਲਈ ਪਾਵਰ ਬਟਨ ਦਬਾਈ ਰੱਖੋ"</string>
<string name="controls_menu_add" msgid="4447246119229920050">"ਕੰਟਰੋਲ ਸ਼ਾਮਲ ਕਰੋ"</string>
<string name="controls_menu_edit" msgid="890623986951347062">"ਕੰਟਰੋਲਾਂ ਦਾ ਸੰਪਾਦਨ ਕਰੋ"</string>
+ <string name="media_output_dialog_add_output" msgid="5642703238877329518">"ਆਊਟਪੁੱਟ ਸ਼ਾਮਲ ਕਰੋ"</string>
+ <string name="media_output_dialog_group" msgid="5571251347877452212">"ਗਰੁੱਪ"</string>
+ <string name="media_output_dialog_single_device" msgid="3102758980643351058">"1 ਡੀਵਾਈਸ ਨੂੰ ਚੁਣਿਆ ਗਿਆ"</string>
+ <string name="media_output_dialog_multiple_devices" msgid="1093771040315422350">"<xliff:g id="COUNT">%1$d</xliff:g> ਡੀਵਾਈਸਾਂ ਨੂੰ ਚੁਣਿਆ ਗਿਆ"</string>
+ <string name="media_output_dialog_disconnected" msgid="1834473104836986046">"<xliff:g id="DEVICE_NAME">%1$s</xliff:g> (ਡਿਸਕਨੈਕਟ ਹੋਇਆ)"</string>
+ <string name="media_output_dialog_connect_failed" msgid="3225190634236259010">"ਕਨੈਕਟ ਨਹੀਂ ਕੀਤਾ ਜਾ ਸਕਿਆ। ਦੁਬਾਰਾ ਕੋਸ਼ਿਸ਼ ਕਰੋ।"</string>
+ <string name="media_output_dialog_pairing_new" msgid="9099497976087485862">"ਨਵਾਂ ਡੀਵਾਈਸ ਜੋੜਾਬੱਧ ਕਰੋ"</string>
</resources>
diff --git a/packages/SystemUI/res/values-pl/strings.xml b/packages/SystemUI/res/values-pl/strings.xml
index b531be0ff724..f188aaa1ca76 100644
--- a/packages/SystemUI/res/values-pl/strings.xml
+++ b/packages/SystemUI/res/values-pl/strings.xml
@@ -889,12 +889,13 @@
</string-array>
<string name="tuner_low_priority" msgid="8412666814123009820">"Pokazuj ikony powiadomień o niskim priorytecie"</string>
<string name="other" msgid="429768510980739978">"Inne"</string>
- <string name="accessibility_qs_edit_tile_label" msgid="9079791448815232967">"Położenie <xliff:g id="POSITION">%1$d</xliff:g>, kafelek <xliff:g id="TILE_NAME">%2$s</xliff:g>. Kliknij dwukrotnie, by edytować."</string>
- <string name="accessibility_qs_edit_add_tile_label" msgid="8292218072049068613">"Kafelek <xliff:g id="TILE_NAME">%1$s</xliff:g>. Kliknij dwukrotnie, by dodać."</string>
- <string name="accessibility_qs_edit_move_tile" msgid="6027997446473163426">"Przenieś kafelek <xliff:g id="TILE_NAME">%1$s</xliff:g>"</string>
- <string name="accessibility_qs_edit_remove_tile" msgid="3406781901949899624">"Usuń kafelek <xliff:g id="TILE_NAME">%1$s</xliff:g>"</string>
- <string name="accessibility_qs_edit_tile_add" msgid="6289879620154587233">"Dodaj kafelek <xliff:g id="TILE_NAME">%1$s</xliff:g> w położeniu <xliff:g id="POSITION">%2$d</xliff:g>"</string>
- <string name="accessibility_qs_edit_tile_move" msgid="4841770637244326837">"Przenieś kafelek <xliff:g id="TILE_NAME">%1$s</xliff:g> w położenie <xliff:g id="POSITION">%2$d</xliff:g>"</string>
+ <string name="accessibility_qs_edit_remove_tile_action" msgid="775511891457193480">"usunąć kartę"</string>
+ <string name="accessibility_qs_edit_tile_add_action" msgid="5051211910345301833">"dodać kartę na końcu"</string>
+ <string name="accessibility_qs_edit_tile_start_move" msgid="2009373939914517817">"Przenieś kartę"</string>
+ <string name="accessibility_qs_edit_tile_start_add" msgid="7560798153975555772">"Dodaj kartę"</string>
+ <string name="accessibility_qs_edit_tile_move_to_position" msgid="5198161544045930556">"Przenieś do pozycji <xliff:g id="POSITION">%1$d</xliff:g>"</string>
+ <string name="accessibility_qs_edit_tile_add_to_position" msgid="9029163095148274690">"Dodaj w pozycji <xliff:g id="POSITION">%1$d</xliff:g>"</string>
+ <string name="accessibility_qs_edit_position" msgid="4509277359815711830">"Pozycja <xliff:g id="POSITION">%1$d</xliff:g>"</string>
<string name="accessibility_desc_quick_settings_edit" msgid="741658939453595297">"Edytor szybkich ustawień."</string>
<string name="accessibility_desc_notification_icon" msgid="7331265967584178674">"Powiadomienie z aplikacji <xliff:g id="ID_1">%1$s</xliff:g>: <xliff:g id="ID_2">%2$s</xliff:g>"</string>
<string name="accessibility_quick_settings_settings" msgid="7098489591715844713">"Otwórz ustawienia."</string>
@@ -1079,4 +1080,11 @@
<string name="controls_added_tooltip" msgid="4842812921719153085">"Przytrzymaj przycisk zasilania, by zobaczyć nowe elementy sterujące"</string>
<string name="controls_menu_add" msgid="4447246119229920050">"Dodaj elementy sterujące"</string>
<string name="controls_menu_edit" msgid="890623986951347062">"Edytuj elementy sterujące"</string>
+ <string name="media_output_dialog_add_output" msgid="5642703238877329518">"Dodaj urządzenia wyjściowe"</string>
+ <string name="media_output_dialog_group" msgid="5571251347877452212">"Grupa"</string>
+ <string name="media_output_dialog_single_device" msgid="3102758980643351058">"Wybrano 1 urządzenie"</string>
+ <string name="media_output_dialog_multiple_devices" msgid="1093771040315422350">"Wybrane urządzenia: <xliff:g id="COUNT">%1$d</xliff:g>"</string>
+ <string name="media_output_dialog_disconnected" msgid="1834473104836986046">"<xliff:g id="DEVICE_NAME">%1$s</xliff:g> (rozłączono)"</string>
+ <string name="media_output_dialog_connect_failed" msgid="3225190634236259010">"Nie udało się połączyć. Spróbuj ponownie."</string>
+ <string name="media_output_dialog_pairing_new" msgid="9099497976087485862">"Sparuj nowe urządzenie"</string>
</resources>
diff --git a/packages/SystemUI/res/values-pt-rBR/strings.xml b/packages/SystemUI/res/values-pt-rBR/strings.xml
index 67f161151d47..8c8adae62e82 100644
--- a/packages/SystemUI/res/values-pt-rBR/strings.xml
+++ b/packages/SystemUI/res/values-pt-rBR/strings.xml
@@ -712,13 +712,13 @@
<string name="notification_automatic_title" msgid="3745465364578762652">"Automática"</string>
<string name="notification_channel_summary_low" msgid="4860617986908931158">"Som e vibração desativados"</string>
<string name="notification_conversation_summary_low" msgid="1734433426085468009">"O som e a vibração estão desativados, e o balão aparece na parte inferior da seção de conversa"</string>
- <string name="notification_channel_summary_default" msgid="3282930979307248890">"Pode vibrar ou tocar com base nas configurações do smartphone"</string>
+ <string name="notification_channel_summary_default" msgid="3282930979307248890">"Podem vibrar ou tocar com base nas configurações do smartphone"</string>
<string name="notification_channel_summary_default_with_bubbles" msgid="1782419896613644568">"Pode vibrar ou tocar com base nas configurações do smartphone. As conversas do app <xliff:g id="APP_NAME">%1$s</xliff:g> aparecem em balões por padrão."</string>
<string name="notification_channel_summary_bubble" msgid="7235935211580860537">"Mantém sua atenção com um atalho flutuante para esse conteúdo."</string>
<string name="notification_channel_summary_automatic" msgid="5813109268050235275">"Faça com que o sistema determine se a notificação resultará em som ou vibração"</string>
- <string name="notification_channel_summary_priority" msgid="7952654515769021553">"Aparece na parte superior de uma seção de conversa, em forma de balão, mostrando a foto do perfil na tela de bloqueio"</string>
+ <string name="notification_channel_summary_priority" msgid="7952654515769021553">"Aparecem na parte superior de uma seção de conversa, em forma de balões, mostrando a foto do perfil na tela de bloqueio"</string>
<string name="notification_conversation_channel_settings" msgid="2409977688430606835">"Configurações"</string>
- <string name="notification_priority_title" msgid="2079708866333537093">"Prioridade"</string>
+ <string name="notification_priority_title" msgid="2079708866333537093">"Prioritárias"</string>
<string name="no_shortcut" msgid="8257177117568230126">"O app <xliff:g id="APP_NAME">%1$s</xliff:g> não é compatível com recursos de conversa"</string>
<string name="bubble_overflow_empty_title" msgid="3120029421991510842">"Nenhum balão recente"</string>
<string name="bubble_overflow_empty_subtitle" msgid="2030874469510497397">"Os balões recentes e dispensados aparecerão aqui"</string>
@@ -879,12 +879,13 @@
</string-array>
<string name="tuner_low_priority" msgid="8412666814123009820">"Mostrar ícones de notificações de baixa prioridade"</string>
<string name="other" msgid="429768510980739978">"Outros"</string>
- <string name="accessibility_qs_edit_tile_label" msgid="9079791448815232967">"Posição <xliff:g id="POSITION">%1$d</xliff:g>, <xliff:g id="TILE_NAME">%2$s</xliff:g>. Toque duas vezes para editar."</string>
- <string name="accessibility_qs_edit_add_tile_label" msgid="8292218072049068613">"<xliff:g id="TILE_NAME">%1$s</xliff:g>. Toque duas vezes para adicionar."</string>
- <string name="accessibility_qs_edit_move_tile" msgid="6027997446473163426">"Move <xliff:g id="TILE_NAME">%1$s</xliff:g>"</string>
- <string name="accessibility_qs_edit_remove_tile" msgid="3406781901949899624">"Remove <xliff:g id="TILE_NAME">%1$s</xliff:g>"</string>
- <string name="accessibility_qs_edit_tile_add" msgid="6289879620154587233">"Adicionar <xliff:g id="TILE_NAME">%1$s</xliff:g> à posição <xliff:g id="POSITION">%2$d</xliff:g>"</string>
- <string name="accessibility_qs_edit_tile_move" msgid="4841770637244326837">"Mover <xliff:g id="TILE_NAME">%1$s</xliff:g> para a posição <xliff:g id="POSITION">%2$d</xliff:g>"</string>
+ <string name="accessibility_qs_edit_remove_tile_action" msgid="775511891457193480">"remover o bloco"</string>
+ <string name="accessibility_qs_edit_tile_add_action" msgid="5051211910345301833">"adicionar o bloco ao final"</string>
+ <string name="accessibility_qs_edit_tile_start_move" msgid="2009373939914517817">"Mover bloco"</string>
+ <string name="accessibility_qs_edit_tile_start_add" msgid="7560798153975555772">"Adicionar bloco"</string>
+ <string name="accessibility_qs_edit_tile_move_to_position" msgid="5198161544045930556">"Mover para <xliff:g id="POSITION">%1$d</xliff:g>"</string>
+ <string name="accessibility_qs_edit_tile_add_to_position" msgid="9029163095148274690">"Adicionar à posição <xliff:g id="POSITION">%1$d</xliff:g>"</string>
+ <string name="accessibility_qs_edit_position" msgid="4509277359815711830">"Posição <xliff:g id="POSITION">%1$d</xliff:g>"</string>
<string name="accessibility_desc_quick_settings_edit" msgid="741658939453595297">"Editor de configurações rápidas."</string>
<string name="accessibility_desc_notification_icon" msgid="7331265967584178674">"Notificação do <xliff:g id="ID_1">%1$s</xliff:g>: <xliff:g id="ID_2">%2$s</xliff:g>"</string>
<string name="accessibility_quick_settings_settings" msgid="7098489591715844713">"Abrir configurações."</string>
@@ -1067,4 +1068,11 @@
<string name="controls_added_tooltip" msgid="4842812921719153085">"Mantenha o botão liga/desliga pressionado para ver os novos controles"</string>
<string name="controls_menu_add" msgid="4447246119229920050">"Adicionar controles"</string>
<string name="controls_menu_edit" msgid="890623986951347062">"Editar controles"</string>
+ <string name="media_output_dialog_add_output" msgid="5642703238877329518">"Adicionar saídas"</string>
+ <string name="media_output_dialog_group" msgid="5571251347877452212">"Grupo"</string>
+ <string name="media_output_dialog_single_device" msgid="3102758980643351058">"1 dispositivo selecionado"</string>
+ <string name="media_output_dialog_multiple_devices" msgid="1093771040315422350">"<xliff:g id="COUNT">%1$d</xliff:g> dispositivos selecionados"</string>
+ <string name="media_output_dialog_disconnected" msgid="1834473104836986046">"<xliff:g id="DEVICE_NAME">%1$s</xliff:g> (desconectado)"</string>
+ <string name="media_output_dialog_connect_failed" msgid="3225190634236259010">"Não foi possível conectar. Tente novamente."</string>
+ <string name="media_output_dialog_pairing_new" msgid="9099497976087485862">"Parear novo dispositivo"</string>
</resources>
diff --git a/packages/SystemUI/res/values-pt-rPT/strings.xml b/packages/SystemUI/res/values-pt-rPT/strings.xml
index 6e32ca4b940b..082e14ec6fc3 100644
--- a/packages/SystemUI/res/values-pt-rPT/strings.xml
+++ b/packages/SystemUI/res/values-pt-rPT/strings.xml
@@ -716,7 +716,7 @@
<string name="notification_channel_summary_default_with_bubbles" msgid="1782419896613644568">"Pode tocar ou vibrar com base nas definições do telemóvel. As conversas da app <xliff:g id="APP_NAME">%1$s</xliff:g> aparecem como um balão por predefinição."</string>
<string name="notification_channel_summary_bubble" msgid="7235935211580860537">"Mantém a sua atenção com um atalho flutuante para este conteúdo."</string>
<string name="notification_channel_summary_automatic" msgid="5813109268050235275">"Faça com que o sistema determine se esta notificação deve emitir um som ou uma vibração"</string>
- <string name="notification_channel_summary_priority" msgid="7952654515769021553">"Aparece na parte superior da secção de conversas, surge como um balão flutuante e apresenta a imagem do perfil no ecrã de bloqueio."</string>
+ <string name="notification_channel_summary_priority" msgid="7952654515769021553">"Aparece no topo da secção de conversas, surge como balão flutuante e apresenta a imagem do perfil no ecrã de bloqueio."</string>
<string name="notification_conversation_channel_settings" msgid="2409977688430606835">"Definições"</string>
<string name="notification_priority_title" msgid="2079708866333537093">"Prioridade"</string>
<string name="no_shortcut" msgid="8257177117568230126">"A app <xliff:g id="APP_NAME">%1$s</xliff:g> não suporta funcionalidades de conversa."</string>
@@ -861,7 +861,7 @@
<string name="right_keycode" msgid="2480715509844798438">"Código de tecla direito"</string>
<string name="left_icon" msgid="5036278531966897006">"Ícone esquerdo"</string>
<string name="right_icon" msgid="1103955040645237425">"Ícone direito"</string>
- <string name="drag_to_add_tiles" msgid="8933270127508303672">"Toque sem soltar e arraste para adicionar mosaicos."</string>
+ <string name="drag_to_add_tiles" msgid="8933270127508303672">"Tocar sem soltar e arrastar para adicionar mosaicos"</string>
<string name="drag_to_rearrange_tiles" msgid="2143204300089638620">"Tocar sem soltar e arrastar para reorganizar os mosaicos"</string>
<string name="drag_to_remove_tiles" msgid="4682194717573850385">"Arrastar para aqui para remover"</string>
<string name="drag_to_remove_disabled" msgid="933046987838658850">"Necessita de, pelo menos, <xliff:g id="MIN_NUM_TILES">%1$d</xliff:g> cartões"</string>
@@ -879,12 +879,13 @@
</string-array>
<string name="tuner_low_priority" msgid="8412666814123009820">"Mostrar ícones de notificações de prioridade baixa"</string>
<string name="other" msgid="429768510980739978">"Outro"</string>
- <string name="accessibility_qs_edit_tile_label" msgid="9079791448815232967">"Posição <xliff:g id="POSITION">%1$d</xliff:g>, <xliff:g id="TILE_NAME">%2$s</xliff:g>. Toque duas vezes para editar."</string>
- <string name="accessibility_qs_edit_add_tile_label" msgid="8292218072049068613">"<xliff:g id="TILE_NAME">%1$s</xliff:g>. Toque duas vezes para adicionar."</string>
- <string name="accessibility_qs_edit_move_tile" msgid="6027997446473163426">"Mover <xliff:g id="TILE_NAME">%1$s</xliff:g>"</string>
- <string name="accessibility_qs_edit_remove_tile" msgid="3406781901949899624">"Remover <xliff:g id="TILE_NAME">%1$s</xliff:g>"</string>
- <string name="accessibility_qs_edit_tile_add" msgid="6289879620154587233">"Adicionar <xliff:g id="TILE_NAME">%1$s</xliff:g> à posição <xliff:g id="POSITION">%2$d</xliff:g>"</string>
- <string name="accessibility_qs_edit_tile_move" msgid="4841770637244326837">"Mover <xliff:g id="TILE_NAME">%1$s</xliff:g> para a posição <xliff:g id="POSITION">%2$d</xliff:g>"</string>
+ <string name="accessibility_qs_edit_remove_tile_action" msgid="775511891457193480">"remover o cartão"</string>
+ <string name="accessibility_qs_edit_tile_add_action" msgid="5051211910345301833">"adicionar o cartão ao final"</string>
+ <string name="accessibility_qs_edit_tile_start_move" msgid="2009373939914517817">"Mover cartão"</string>
+ <string name="accessibility_qs_edit_tile_start_add" msgid="7560798153975555772">"Adicionar cartão"</string>
+ <string name="accessibility_qs_edit_tile_move_to_position" msgid="5198161544045930556">"Mova para <xliff:g id="POSITION">%1$d</xliff:g>"</string>
+ <string name="accessibility_qs_edit_tile_add_to_position" msgid="9029163095148274690">"Adicione à posição <xliff:g id="POSITION">%1$d</xliff:g>"</string>
+ <string name="accessibility_qs_edit_position" msgid="4509277359815711830">"Posição <xliff:g id="POSITION">%1$d</xliff:g>"</string>
<string name="accessibility_desc_quick_settings_edit" msgid="741658939453595297">"Editor de definições rápidas."</string>
<string name="accessibility_desc_notification_icon" msgid="7331265967584178674">"Notificação do <xliff:g id="ID_1">%1$s</xliff:g>: <xliff:g id="ID_2">%2$s</xliff:g>"</string>
<string name="accessibility_quick_settings_settings" msgid="7098489591715844713">"Abrir as definições."</string>
@@ -1067,4 +1068,11 @@
<string name="controls_added_tooltip" msgid="4842812921719153085">"Mantenha premido o botão ligar/desligar para ver os novos controlos."</string>
<string name="controls_menu_add" msgid="4447246119229920050">"Adicionar controlos"</string>
<string name="controls_menu_edit" msgid="890623986951347062">"Editar controlos"</string>
+ <string name="media_output_dialog_add_output" msgid="5642703238877329518">"Adicione saídas"</string>
+ <string name="media_output_dialog_group" msgid="5571251347877452212">"Grupo"</string>
+ <string name="media_output_dialog_single_device" msgid="3102758980643351058">"1 dispositivo selecionado"</string>
+ <string name="media_output_dialog_multiple_devices" msgid="1093771040315422350">"<xliff:g id="COUNT">%1$d</xliff:g> dispositivos selecionados"</string>
+ <string name="media_output_dialog_disconnected" msgid="1834473104836986046">"<xliff:g id="DEVICE_NAME">%1$s</xliff:g> (desligado)"</string>
+ <string name="media_output_dialog_connect_failed" msgid="3225190634236259010">"Não foi possível ligar. Tente novamente."</string>
+ <string name="media_output_dialog_pairing_new" msgid="9099497976087485862">"Sincronize o novo dispositivo"</string>
</resources>
diff --git a/packages/SystemUI/res/values-pt/strings.xml b/packages/SystemUI/res/values-pt/strings.xml
index 67f161151d47..8c8adae62e82 100644
--- a/packages/SystemUI/res/values-pt/strings.xml
+++ b/packages/SystemUI/res/values-pt/strings.xml
@@ -712,13 +712,13 @@
<string name="notification_automatic_title" msgid="3745465364578762652">"Automática"</string>
<string name="notification_channel_summary_low" msgid="4860617986908931158">"Som e vibração desativados"</string>
<string name="notification_conversation_summary_low" msgid="1734433426085468009">"O som e a vibração estão desativados, e o balão aparece na parte inferior da seção de conversa"</string>
- <string name="notification_channel_summary_default" msgid="3282930979307248890">"Pode vibrar ou tocar com base nas configurações do smartphone"</string>
+ <string name="notification_channel_summary_default" msgid="3282930979307248890">"Podem vibrar ou tocar com base nas configurações do smartphone"</string>
<string name="notification_channel_summary_default_with_bubbles" msgid="1782419896613644568">"Pode vibrar ou tocar com base nas configurações do smartphone. As conversas do app <xliff:g id="APP_NAME">%1$s</xliff:g> aparecem em balões por padrão."</string>
<string name="notification_channel_summary_bubble" msgid="7235935211580860537">"Mantém sua atenção com um atalho flutuante para esse conteúdo."</string>
<string name="notification_channel_summary_automatic" msgid="5813109268050235275">"Faça com que o sistema determine se a notificação resultará em som ou vibração"</string>
- <string name="notification_channel_summary_priority" msgid="7952654515769021553">"Aparece na parte superior de uma seção de conversa, em forma de balão, mostrando a foto do perfil na tela de bloqueio"</string>
+ <string name="notification_channel_summary_priority" msgid="7952654515769021553">"Aparecem na parte superior de uma seção de conversa, em forma de balões, mostrando a foto do perfil na tela de bloqueio"</string>
<string name="notification_conversation_channel_settings" msgid="2409977688430606835">"Configurações"</string>
- <string name="notification_priority_title" msgid="2079708866333537093">"Prioridade"</string>
+ <string name="notification_priority_title" msgid="2079708866333537093">"Prioritárias"</string>
<string name="no_shortcut" msgid="8257177117568230126">"O app <xliff:g id="APP_NAME">%1$s</xliff:g> não é compatível com recursos de conversa"</string>
<string name="bubble_overflow_empty_title" msgid="3120029421991510842">"Nenhum balão recente"</string>
<string name="bubble_overflow_empty_subtitle" msgid="2030874469510497397">"Os balões recentes e dispensados aparecerão aqui"</string>
@@ -879,12 +879,13 @@
</string-array>
<string name="tuner_low_priority" msgid="8412666814123009820">"Mostrar ícones de notificações de baixa prioridade"</string>
<string name="other" msgid="429768510980739978">"Outros"</string>
- <string name="accessibility_qs_edit_tile_label" msgid="9079791448815232967">"Posição <xliff:g id="POSITION">%1$d</xliff:g>, <xliff:g id="TILE_NAME">%2$s</xliff:g>. Toque duas vezes para editar."</string>
- <string name="accessibility_qs_edit_add_tile_label" msgid="8292218072049068613">"<xliff:g id="TILE_NAME">%1$s</xliff:g>. Toque duas vezes para adicionar."</string>
- <string name="accessibility_qs_edit_move_tile" msgid="6027997446473163426">"Move <xliff:g id="TILE_NAME">%1$s</xliff:g>"</string>
- <string name="accessibility_qs_edit_remove_tile" msgid="3406781901949899624">"Remove <xliff:g id="TILE_NAME">%1$s</xliff:g>"</string>
- <string name="accessibility_qs_edit_tile_add" msgid="6289879620154587233">"Adicionar <xliff:g id="TILE_NAME">%1$s</xliff:g> à posição <xliff:g id="POSITION">%2$d</xliff:g>"</string>
- <string name="accessibility_qs_edit_tile_move" msgid="4841770637244326837">"Mover <xliff:g id="TILE_NAME">%1$s</xliff:g> para a posição <xliff:g id="POSITION">%2$d</xliff:g>"</string>
+ <string name="accessibility_qs_edit_remove_tile_action" msgid="775511891457193480">"remover o bloco"</string>
+ <string name="accessibility_qs_edit_tile_add_action" msgid="5051211910345301833">"adicionar o bloco ao final"</string>
+ <string name="accessibility_qs_edit_tile_start_move" msgid="2009373939914517817">"Mover bloco"</string>
+ <string name="accessibility_qs_edit_tile_start_add" msgid="7560798153975555772">"Adicionar bloco"</string>
+ <string name="accessibility_qs_edit_tile_move_to_position" msgid="5198161544045930556">"Mover para <xliff:g id="POSITION">%1$d</xliff:g>"</string>
+ <string name="accessibility_qs_edit_tile_add_to_position" msgid="9029163095148274690">"Adicionar à posição <xliff:g id="POSITION">%1$d</xliff:g>"</string>
+ <string name="accessibility_qs_edit_position" msgid="4509277359815711830">"Posição <xliff:g id="POSITION">%1$d</xliff:g>"</string>
<string name="accessibility_desc_quick_settings_edit" msgid="741658939453595297">"Editor de configurações rápidas."</string>
<string name="accessibility_desc_notification_icon" msgid="7331265967584178674">"Notificação do <xliff:g id="ID_1">%1$s</xliff:g>: <xliff:g id="ID_2">%2$s</xliff:g>"</string>
<string name="accessibility_quick_settings_settings" msgid="7098489591715844713">"Abrir configurações."</string>
@@ -1067,4 +1068,11 @@
<string name="controls_added_tooltip" msgid="4842812921719153085">"Mantenha o botão liga/desliga pressionado para ver os novos controles"</string>
<string name="controls_menu_add" msgid="4447246119229920050">"Adicionar controles"</string>
<string name="controls_menu_edit" msgid="890623986951347062">"Editar controles"</string>
+ <string name="media_output_dialog_add_output" msgid="5642703238877329518">"Adicionar saídas"</string>
+ <string name="media_output_dialog_group" msgid="5571251347877452212">"Grupo"</string>
+ <string name="media_output_dialog_single_device" msgid="3102758980643351058">"1 dispositivo selecionado"</string>
+ <string name="media_output_dialog_multiple_devices" msgid="1093771040315422350">"<xliff:g id="COUNT">%1$d</xliff:g> dispositivos selecionados"</string>
+ <string name="media_output_dialog_disconnected" msgid="1834473104836986046">"<xliff:g id="DEVICE_NAME">%1$s</xliff:g> (desconectado)"</string>
+ <string name="media_output_dialog_connect_failed" msgid="3225190634236259010">"Não foi possível conectar. Tente novamente."</string>
+ <string name="media_output_dialog_pairing_new" msgid="9099497976087485862">"Parear novo dispositivo"</string>
</resources>
diff --git a/packages/SystemUI/res/values-ro/strings.xml b/packages/SystemUI/res/values-ro/strings.xml
index 691fd835b563..4128c21eaa4e 100644
--- a/packages/SystemUI/res/values-ro/strings.xml
+++ b/packages/SystemUI/res/values-ro/strings.xml
@@ -884,12 +884,13 @@
</string-array>
<string name="tuner_low_priority" msgid="8412666814123009820">"Afișați pictogramele de notificare cu prioritate redusă"</string>
<string name="other" msgid="429768510980739978">"Altele"</string>
- <string name="accessibility_qs_edit_tile_label" msgid="9079791448815232967">"Poziția <xliff:g id="POSITION">%1$d</xliff:g>, <xliff:g id="TILE_NAME">%2$s</xliff:g>. Atingeți de două ori pentru a edita."</string>
- <string name="accessibility_qs_edit_add_tile_label" msgid="8292218072049068613">"<xliff:g id="TILE_NAME">%1$s</xliff:g>. Atingeți de două ori pentru a adăuga."</string>
- <string name="accessibility_qs_edit_move_tile" msgid="6027997446473163426">"Mutați <xliff:g id="TILE_NAME">%1$s</xliff:g>"</string>
- <string name="accessibility_qs_edit_remove_tile" msgid="3406781901949899624">"Eliminați <xliff:g id="TILE_NAME">%1$s</xliff:g>"</string>
- <string name="accessibility_qs_edit_tile_add" msgid="6289879620154587233">"Adăugați <xliff:g id="TILE_NAME">%1$s</xliff:g> pe poziția <xliff:g id="POSITION">%2$d</xliff:g>"</string>
- <string name="accessibility_qs_edit_tile_move" msgid="4841770637244326837">"Mutați <xliff:g id="TILE_NAME">%1$s</xliff:g> pe poziția <xliff:g id="POSITION">%2$d</xliff:g>"</string>
+ <string name="accessibility_qs_edit_remove_tile_action" msgid="775511891457193480">"eliminați cardul"</string>
+ <string name="accessibility_qs_edit_tile_add_action" msgid="5051211910345301833">"adăugați cardul la sfârșit"</string>
+ <string name="accessibility_qs_edit_tile_start_move" msgid="2009373939914517817">"Mutați cardul"</string>
+ <string name="accessibility_qs_edit_tile_start_add" msgid="7560798153975555772">"Adăugați un card"</string>
+ <string name="accessibility_qs_edit_tile_move_to_position" msgid="5198161544045930556">"Mutați pe poziția <xliff:g id="POSITION">%1$d</xliff:g>"</string>
+ <string name="accessibility_qs_edit_tile_add_to_position" msgid="9029163095148274690">"Adăugați pe poziția <xliff:g id="POSITION">%1$d</xliff:g>"</string>
+ <string name="accessibility_qs_edit_position" msgid="4509277359815711830">"Poziția <xliff:g id="POSITION">%1$d</xliff:g>"</string>
<string name="accessibility_desc_quick_settings_edit" msgid="741658939453595297">"Editorul pentru setări rapide."</string>
<string name="accessibility_desc_notification_icon" msgid="7331265967584178674">"Notificare <xliff:g id="ID_1">%1$s</xliff:g>: <xliff:g id="ID_2">%2$s</xliff:g>"</string>
<string name="accessibility_quick_settings_settings" msgid="7098489591715844713">"Deschideți setările."</string>
@@ -1073,4 +1074,11 @@
<string name="controls_added_tooltip" msgid="4842812921719153085">"Apăsați butonul de alimentare pentru a vedea noile comenzi"</string>
<string name="controls_menu_add" msgid="4447246119229920050">"Adăugați comenzi"</string>
<string name="controls_menu_edit" msgid="890623986951347062">"Editați comenzile"</string>
+ <string name="media_output_dialog_add_output" msgid="5642703238877329518">"Adăugați ieșiri"</string>
+ <string name="media_output_dialog_group" msgid="5571251347877452212">"Grup"</string>
+ <string name="media_output_dialog_single_device" msgid="3102758980643351058">"S-a selectat un dispozitiv"</string>
+ <string name="media_output_dialog_multiple_devices" msgid="1093771040315422350">"S-au selectat <xliff:g id="COUNT">%1$d</xliff:g> dispozitive"</string>
+ <string name="media_output_dialog_disconnected" msgid="1834473104836986046">"<xliff:g id="DEVICE_NAME">%1$s</xliff:g> (s-a deconectat)"</string>
+ <string name="media_output_dialog_connect_failed" msgid="3225190634236259010">"Nu s-a putut conecta. Reîncercați."</string>
+ <string name="media_output_dialog_pairing_new" msgid="9099497976087485862">"Asociați un nou dispozitiv"</string>
</resources>
diff --git a/packages/SystemUI/res/values-ru/strings.xml b/packages/SystemUI/res/values-ru/strings.xml
index 3ccafda2bbfe..66e3ec3ba78a 100644
--- a/packages/SystemUI/res/values-ru/strings.xml
+++ b/packages/SystemUI/res/values-ru/strings.xml
@@ -722,7 +722,7 @@
<string name="notification_channel_summary_default_with_bubbles" msgid="1782419896613644568">"Звонок или вибрация в зависимости от настроек телефона. Разговоры из приложения \"<xliff:g id="APP_NAME">%1$s</xliff:g>\" по умолчанию появляются в виде всплывающего чата."</string>
<string name="notification_channel_summary_bubble" msgid="7235935211580860537">"Привлекает ваше внимание к контенту с помощью плавающего ярлыка"</string>
<string name="notification_channel_summary_automatic" msgid="5813109268050235275">"Система будет сама определять, включать ли звуковой сигнал или вибрацию для уведомления"</string>
- <string name="notification_channel_summary_priority" msgid="7952654515769021553">"Появляется в верхней части списка разговоров и как всплывающий чат, а также показывает фото профиля на заблокированном экране"</string>
+ <string name="notification_channel_summary_priority" msgid="7952654515769021553">"Появляется в верхней части списка разговоров и как всплывающий чат, фото профиля показывается на заблок. экране"</string>
<string name="notification_conversation_channel_settings" msgid="2409977688430606835">"Настройки"</string>
<string name="notification_priority_title" msgid="2079708866333537093">"Приоритет"</string>
<string name="no_shortcut" msgid="8257177117568230126">"Приложение \"<xliff:g id="APP_NAME">%1$s</xliff:g>\" не поддерживает функции разговоров."</string>
@@ -889,12 +889,13 @@
</string-array>
<string name="tuner_low_priority" msgid="8412666814123009820">"Показывать значки уведомлений с низким приоритетом"</string>
<string name="other" msgid="429768510980739978">"Другое"</string>
- <string name="accessibility_qs_edit_tile_label" msgid="9079791448815232967">"Позиция <xliff:g id="POSITION">%1$d</xliff:g>, кнопка \"<xliff:g id="TILE_NAME">%2$s</xliff:g>\". Чтобы изменить, нажмите дважды."</string>
- <string name="accessibility_qs_edit_add_tile_label" msgid="8292218072049068613">"Кнопка \"<xliff:g id="TILE_NAME">%1$s</xliff:g>\". Чтобы добавить, нажмите дважды."</string>
- <string name="accessibility_qs_edit_move_tile" msgid="6027997446473163426">"Переместить кнопку \"<xliff:g id="TILE_NAME">%1$s</xliff:g>\""</string>
- <string name="accessibility_qs_edit_remove_tile" msgid="3406781901949899624">"Удалить кнопку \"<xliff:g id="TILE_NAME">%1$s</xliff:g>\""</string>
- <string name="accessibility_qs_edit_tile_add" msgid="6289879620154587233">"Добавить значок <xliff:g id="TILE_NAME">%1$s</xliff:g> на позицию <xliff:g id="POSITION">%2$d</xliff:g>"</string>
- <string name="accessibility_qs_edit_tile_move" msgid="4841770637244326837">"Переместить значок <xliff:g id="TILE_NAME">%1$s</xliff:g> на позицию <xliff:g id="POSITION">%2$d</xliff:g>"</string>
+ <string name="accessibility_qs_edit_remove_tile_action" msgid="775511891457193480">"удалить кнопку быстрого доступа"</string>
+ <string name="accessibility_qs_edit_tile_add_action" msgid="5051211910345301833">"добавить кнопку быстрого доступа в конец"</string>
+ <string name="accessibility_qs_edit_tile_start_move" msgid="2009373939914517817">"Переместить кнопку быстрого доступа"</string>
+ <string name="accessibility_qs_edit_tile_start_add" msgid="7560798153975555772">"Добавить кнопку быстрого доступа"</string>
+ <string name="accessibility_qs_edit_tile_move_to_position" msgid="5198161544045930556">"Переместить на позицию <xliff:g id="POSITION">%1$d</xliff:g>"</string>
+ <string name="accessibility_qs_edit_tile_add_to_position" msgid="9029163095148274690">"Добавить на позицию <xliff:g id="POSITION">%1$d</xliff:g>"</string>
+ <string name="accessibility_qs_edit_position" msgid="4509277359815711830">"Позиция <xliff:g id="POSITION">%1$d</xliff:g>"</string>
<string name="accessibility_desc_quick_settings_edit" msgid="741658939453595297">"Редактор быстрых настроек."</string>
<string name="accessibility_desc_notification_icon" msgid="7331265967584178674">"Уведомление <xliff:g id="ID_1">%1$s</xliff:g>: <xliff:g id="ID_2">%2$s</xliff:g>"</string>
<string name="accessibility_quick_settings_settings" msgid="7098489591715844713">"Открыть настройки."</string>
@@ -1079,4 +1080,11 @@
<string name="controls_added_tooltip" msgid="4842812921719153085">"Удерживайте кнопку питания, чтобы увидеть новые элементы управления"</string>
<string name="controls_menu_add" msgid="4447246119229920050">"Добавить виджеты"</string>
<string name="controls_menu_edit" msgid="890623986951347062">"Изменить виджеты"</string>
+ <string name="media_output_dialog_add_output" msgid="5642703238877329518">"Добавление устройств вывода"</string>
+ <string name="media_output_dialog_group" msgid="5571251347877452212">"Группа"</string>
+ <string name="media_output_dialog_single_device" msgid="3102758980643351058">"Выбрано 1 устройство"</string>
+ <string name="media_output_dialog_multiple_devices" msgid="1093771040315422350">"Выбрано устройств: <xliff:g id="COUNT">%1$d</xliff:g>"</string>
+ <string name="media_output_dialog_disconnected" msgid="1834473104836986046">"<xliff:g id="DEVICE_NAME">%1$s</xliff:g> (отключено)"</string>
+ <string name="media_output_dialog_connect_failed" msgid="3225190634236259010">"Не удалось подключиться. Повторите попытку."</string>
+ <string name="media_output_dialog_pairing_new" msgid="9099497976087485862">"Подключить новое устройство"</string>
</resources>
diff --git a/packages/SystemUI/res/values-si/strings.xml b/packages/SystemUI/res/values-si/strings.xml
index 14528c078a33..98e8d1fd9d63 100644
--- a/packages/SystemUI/res/values-si/strings.xml
+++ b/packages/SystemUI/res/values-si/strings.xml
@@ -879,12 +879,13 @@
</string-array>
<string name="tuner_low_priority" msgid="8412666814123009820">"අඩු ප්‍රමුඛතා දැනුම්දීම් අයිකන පෙන්වන්න"</string>
<string name="other" msgid="429768510980739978">"වෙනත්"</string>
- <string name="accessibility_qs_edit_tile_label" msgid="9079791448815232967">"ස්ථානය <xliff:g id="POSITION">%1$d</xliff:g>, <xliff:g id="TILE_NAME">%2$s</xliff:g>. වෙනස් කිරීමට දෙවරක් තට්ටු කරන්න."</string>
- <string name="accessibility_qs_edit_add_tile_label" msgid="8292218072049068613">"<xliff:g id="TILE_NAME">%1$s</xliff:g>. එක් කිරීමට දෙවරක් තට්ටු කරන්න."</string>
- <string name="accessibility_qs_edit_move_tile" msgid="6027997446473163426">"<xliff:g id="TILE_NAME">%1$s</xliff:g> ගෙන යන්න"</string>
- <string name="accessibility_qs_edit_remove_tile" msgid="3406781901949899624">"<xliff:g id="TILE_NAME">%1$s</xliff:g> ඉවත් කරන්න"</string>
- <string name="accessibility_qs_edit_tile_add" msgid="6289879620154587233">"<xliff:g id="TILE_NAME">%1$s</xliff:g> තත්ත්වයට <xliff:g id="POSITION">%2$d</xliff:g> එක් කරන්න"</string>
- <string name="accessibility_qs_edit_tile_move" msgid="4841770637244326837">"<xliff:g id="TILE_NAME">%1$s</xliff:g> තත්ත්වයට <xliff:g id="POSITION">%2$d</xliff:g> ගෙන යන්න"</string>
+ <string name="accessibility_qs_edit_remove_tile_action" msgid="775511891457193480">"ටයිල් ඉවත් කරන්න"</string>
+ <string name="accessibility_qs_edit_tile_add_action" msgid="5051211910345301833">"අගට ටයිල් එක් කරන්න"</string>
+ <string name="accessibility_qs_edit_tile_start_move" msgid="2009373939914517817">"ටයිල් ගෙන යන්න"</string>
+ <string name="accessibility_qs_edit_tile_start_add" msgid="7560798153975555772">"ටයිල් එක් කරන්න"</string>
+ <string name="accessibility_qs_edit_tile_move_to_position" msgid="5198161544045930556">"<xliff:g id="POSITION">%1$d</xliff:g> වෙත ගෙන යන්න"</string>
+ <string name="accessibility_qs_edit_tile_add_to_position" msgid="9029163095148274690">"<xliff:g id="POSITION">%1$d</xliff:g> ස්ථානයට එක් කරන්න"</string>
+ <string name="accessibility_qs_edit_position" msgid="4509277359815711830">"ස්ථානය <xliff:g id="POSITION">%1$d</xliff:g>"</string>
<string name="accessibility_desc_quick_settings_edit" msgid="741658939453595297">"ඉක්මන් සැකසුම් සංස්කාරකය."</string>
<string name="accessibility_desc_notification_icon" msgid="7331265967584178674">"<xliff:g id="ID_1">%1$s</xliff:g> දැනුම්දීම: <xliff:g id="ID_2">%2$s</xliff:g>"</string>
<string name="accessibility_quick_settings_settings" msgid="7098489591715844713">"සැකසීම් විවෘත කරන්න."</string>
@@ -1067,4 +1068,11 @@
<string name="controls_added_tooltip" msgid="4842812921719153085">"නව පාලන බැලීමට බල බොත්තම අල්ලාගෙන සිටින්න"</string>
<string name="controls_menu_add" msgid="4447246119229920050">"පාලන එක් කරන්න"</string>
<string name="controls_menu_edit" msgid="890623986951347062">"පාලන සංස්කරණය කරන්න"</string>
+ <string name="media_output_dialog_add_output" msgid="5642703238877329518">"ප්‍රතිදාන එක් කරන්න"</string>
+ <string name="media_output_dialog_group" msgid="5571251347877452212">"සමූහය"</string>
+ <string name="media_output_dialog_single_device" msgid="3102758980643351058">"උපාංග 1ක් තෝරන ලදී"</string>
+ <string name="media_output_dialog_multiple_devices" msgid="1093771040315422350">"උපාංග <xliff:g id="COUNT">%1$d</xliff:g>ක් තෝරන ලදී"</string>
+ <string name="media_output_dialog_disconnected" msgid="1834473104836986046">"<xliff:g id="DEVICE_NAME">%1$s</xliff:g> (විසන්ධි විය)"</string>
+ <string name="media_output_dialog_connect_failed" msgid="3225190634236259010">"සම්බන්ධ වීමට නොහැකි විය. නැවත උත්සාහ කරන්න."</string>
+ <string name="media_output_dialog_pairing_new" msgid="9099497976087485862">"නව උපාංගය යුගල කරන්න"</string>
</resources>
diff --git a/packages/SystemUI/res/values-sk/strings.xml b/packages/SystemUI/res/values-sk/strings.xml
index 3c405128788a..d9c6279ec42e 100644
--- a/packages/SystemUI/res/values-sk/strings.xml
+++ b/packages/SystemUI/res/values-sk/strings.xml
@@ -889,12 +889,13 @@
</string-array>
<string name="tuner_low_priority" msgid="8412666814123009820">"Zobraziť ikony upozornení s nízkou prioritou"</string>
<string name="other" msgid="429768510980739978">"Ďalšie"</string>
- <string name="accessibility_qs_edit_tile_label" msgid="9079791448815232967">"Pozícia <xliff:g id="POSITION">%1$d</xliff:g>, <xliff:g id="TILE_NAME">%2$s</xliff:g>. Upravíte ju dvojitým klepnutím."</string>
- <string name="accessibility_qs_edit_add_tile_label" msgid="8292218072049068613">"<xliff:g id="TILE_NAME">%1$s</xliff:g>. Pridáte ju dvojitým klepnutím."</string>
- <string name="accessibility_qs_edit_move_tile" msgid="6027997446473163426">"Presunúť dlaždicu <xliff:g id="TILE_NAME">%1$s</xliff:g>"</string>
- <string name="accessibility_qs_edit_remove_tile" msgid="3406781901949899624">"Odstrániť dlaždicu <xliff:g id="TILE_NAME">%1$s</xliff:g>"</string>
- <string name="accessibility_qs_edit_tile_add" msgid="6289879620154587233">"Pridať <xliff:g id="TILE_NAME">%1$s</xliff:g> na pozíciu <xliff:g id="POSITION">%2$d</xliff:g>"</string>
- <string name="accessibility_qs_edit_tile_move" msgid="4841770637244326837">"Presunúť <xliff:g id="TILE_NAME">%1$s</xliff:g> na pozíciu <xliff:g id="POSITION">%2$d</xliff:g>"</string>
+ <string name="accessibility_qs_edit_remove_tile_action" msgid="775511891457193480">"odstrániť kartu"</string>
+ <string name="accessibility_qs_edit_tile_add_action" msgid="5051211910345301833">"pridať kartu na koniec"</string>
+ <string name="accessibility_qs_edit_tile_start_move" msgid="2009373939914517817">"Presunúť kartu"</string>
+ <string name="accessibility_qs_edit_tile_start_add" msgid="7560798153975555772">"Pridať kartu"</string>
+ <string name="accessibility_qs_edit_tile_move_to_position" msgid="5198161544045930556">"Presunúť na <xliff:g id="POSITION">%1$d</xliff:g>. pozíciu"</string>
+ <string name="accessibility_qs_edit_tile_add_to_position" msgid="9029163095148274690">"Pridať na <xliff:g id="POSITION">%1$d</xliff:g>. pozíciu"</string>
+ <string name="accessibility_qs_edit_position" msgid="4509277359815711830">"<xliff:g id="POSITION">%1$d</xliff:g>. pozícia"</string>
<string name="accessibility_desc_quick_settings_edit" msgid="741658939453595297">"Editor rýchlych nastavení"</string>
<string name="accessibility_desc_notification_icon" msgid="7331265967584178674">"Upozornenie <xliff:g id="ID_1">%1$s</xliff:g>: <xliff:g id="ID_2">%2$s</xliff:g>"</string>
<string name="accessibility_quick_settings_settings" msgid="7098489591715844713">"Otvoriť nastavenia"</string>
@@ -1079,4 +1080,11 @@
<string name="controls_added_tooltip" msgid="4842812921719153085">"Pridržaním vypínača zobrazíte nové ovládacie prvky"</string>
<string name="controls_menu_add" msgid="4447246119229920050">"Pridať ovládače"</string>
<string name="controls_menu_edit" msgid="890623986951347062">"Upraviť ovládače"</string>
+ <string name="media_output_dialog_add_output" msgid="5642703238877329518">"Pridanie výstupov"</string>
+ <string name="media_output_dialog_group" msgid="5571251347877452212">"Skupina"</string>
+ <string name="media_output_dialog_single_device" msgid="3102758980643351058">"1 vybrané zariadenie"</string>
+ <string name="media_output_dialog_multiple_devices" msgid="1093771040315422350">"Počet vybraných zariadení: <xliff:g id="COUNT">%1$d</xliff:g>"</string>
+ <string name="media_output_dialog_disconnected" msgid="1834473104836986046">"<xliff:g id="DEVICE_NAME">%1$s</xliff:g> (odpojené)"</string>
+ <string name="media_output_dialog_connect_failed" msgid="3225190634236259010">"Nepodarilo sa pripojiť. Skúste to znova."</string>
+ <string name="media_output_dialog_pairing_new" msgid="9099497976087485862">"Spárovať nové zariadenie"</string>
</resources>
diff --git a/packages/SystemUI/res/values-sl/strings.xml b/packages/SystemUI/res/values-sl/strings.xml
index 4c68eeb15800..778938c77374 100644
--- a/packages/SystemUI/res/values-sl/strings.xml
+++ b/packages/SystemUI/res/values-sl/strings.xml
@@ -95,7 +95,7 @@
<string name="screenrecord_description" msgid="1123231719680353736">"Med snemanjem lahko sistem Android zajame morebitne občutljive podatke, ki so prikazani na zaslonu ali se predvajajo v napravi. To vključuje gesla, podatke za plačilo, fotografije, sporočila in zvok."</string>
<string name="screenrecord_audio_label" msgid="6183558856175159629">"Snemanje zvoka"</string>
<string name="screenrecord_device_audio_label" msgid="9016927171280567791">"Zvok v napravi"</string>
- <string name="screenrecord_device_audio_description" msgid="4922694220572186193">"Zvok v napravi, kot so glasba, klici in toni zvonjenja"</string>
+ <string name="screenrecord_device_audio_description" msgid="4922694220572186193">"Zvoki v napravi, kot so glasba, klici in toni zvonjenja"</string>
<string name="screenrecord_mic_label" msgid="2111264835791332350">"Mikrofon"</string>
<string name="screenrecord_device_audio_and_mic_label" msgid="1831323771978646841">"Zvok v napravi in mikrofon"</string>
<string name="screenrecord_start" msgid="330991441575775004">"Začni"</string>
@@ -889,12 +889,13 @@
</string-array>
<string name="tuner_low_priority" msgid="8412666814123009820">"Pokaži ikone obvestil z nizko stopnjo prednosti"</string>
<string name="other" msgid="429768510980739978">"Drugo"</string>
- <string name="accessibility_qs_edit_tile_label" msgid="9079791448815232967">"Položaj <xliff:g id="POSITION">%1$d</xliff:g>, <xliff:g id="TILE_NAME">%2$s</xliff:g>. Če želite urediti, se dvakrat dotaknite."</string>
- <string name="accessibility_qs_edit_add_tile_label" msgid="8292218072049068613">"<xliff:g id="TILE_NAME">%1$s</xliff:g>. Če želite dodati, se dvakrat dotaknite."</string>
- <string name="accessibility_qs_edit_move_tile" msgid="6027997446473163426">"Premik tega: <xliff:g id="TILE_NAME">%1$s</xliff:g>"</string>
- <string name="accessibility_qs_edit_remove_tile" msgid="3406781901949899624">"Odstranitev tega: <xliff:g id="TILE_NAME">%1$s</xliff:g>"</string>
- <string name="accessibility_qs_edit_tile_add" msgid="6289879620154587233">"Dodaj ploščico <xliff:g id="TILE_NAME">%1$s</xliff:g> na položaj <xliff:g id="POSITION">%2$d</xliff:g>"</string>
- <string name="accessibility_qs_edit_tile_move" msgid="4841770637244326837">"Premakni ploščico <xliff:g id="TILE_NAME">%1$s</xliff:g> na položaj <xliff:g id="POSITION">%2$d</xliff:g>"</string>
+ <string name="accessibility_qs_edit_remove_tile_action" msgid="775511891457193480">"odstranitev ploščice"</string>
+ <string name="accessibility_qs_edit_tile_add_action" msgid="5051211910345301833">"dodajanje ploščice na konec"</string>
+ <string name="accessibility_qs_edit_tile_start_move" msgid="2009373939914517817">"Premik ploščice"</string>
+ <string name="accessibility_qs_edit_tile_start_add" msgid="7560798153975555772">"Dodajanje ploščice"</string>
+ <string name="accessibility_qs_edit_tile_move_to_position" msgid="5198161544045930556">"Premik na položaj <xliff:g id="POSITION">%1$d</xliff:g>"</string>
+ <string name="accessibility_qs_edit_tile_add_to_position" msgid="9029163095148274690">"Dodajanje na položaj <xliff:g id="POSITION">%1$d</xliff:g>"</string>
+ <string name="accessibility_qs_edit_position" msgid="4509277359815711830">"Položaj <xliff:g id="POSITION">%1$d</xliff:g>"</string>
<string name="accessibility_desc_quick_settings_edit" msgid="741658939453595297">"Urejevalnik hitrih nastavitev."</string>
<string name="accessibility_desc_notification_icon" msgid="7331265967584178674">"Obvestilo za <xliff:g id="ID_1">%1$s</xliff:g>: <xliff:g id="ID_2">%2$s</xliff:g>"</string>
<string name="accessibility_quick_settings_settings" msgid="7098489591715844713">"Odpri nastavitve."</string>
@@ -1079,4 +1080,11 @@
<string name="controls_added_tooltip" msgid="4842812921719153085">"Za ogled novih kontrolnikov pridržite gumb za vklop"</string>
<string name="controls_menu_add" msgid="4447246119229920050">"Dodaj kontrolnike"</string>
<string name="controls_menu_edit" msgid="890623986951347062">"Uredi kontrolnike"</string>
+ <string name="media_output_dialog_add_output" msgid="5642703238877329518">"Dodajanje izhodov"</string>
+ <string name="media_output_dialog_group" msgid="5571251347877452212">"Skupina"</string>
+ <string name="media_output_dialog_single_device" msgid="3102758980643351058">"Izbrana je ena naprava"</string>
+ <string name="media_output_dialog_multiple_devices" msgid="1093771040315422350">"Izbranih je toliko naprav: <xliff:g id="COUNT">%1$d</xliff:g>"</string>
+ <string name="media_output_dialog_disconnected" msgid="1834473104836986046">"<xliff:g id="DEVICE_NAME">%1$s</xliff:g> (povezava prekinjena)"</string>
+ <string name="media_output_dialog_connect_failed" msgid="3225190634236259010">"Povezave ni bilo mogoče vzpostaviti. Poskusite znova."</string>
+ <string name="media_output_dialog_pairing_new" msgid="9099497976087485862">"Seznanitev nove naprave"</string>
</resources>
diff --git a/packages/SystemUI/res/values-sq/strings.xml b/packages/SystemUI/res/values-sq/strings.xml
index 681fa3fbeaf1..abcde4286190 100644
--- a/packages/SystemUI/res/values-sq/strings.xml
+++ b/packages/SystemUI/res/values-sq/strings.xml
@@ -879,12 +879,20 @@
</string-array>
<string name="tuner_low_priority" msgid="8412666814123009820">"Shfaq ikonat e njoftimeve me përparësi të ulët"</string>
<string name="other" msgid="429768510980739978">"Të tjera"</string>
- <string name="accessibility_qs_edit_tile_label" msgid="9079791448815232967">"Pozicioni <xliff:g id="POSITION">%1$d</xliff:g>, <xliff:g id="TILE_NAME">%2$s</xliff:g>. Trokit dy herë për ta redaktuar."</string>
- <string name="accessibility_qs_edit_add_tile_label" msgid="8292218072049068613">"<xliff:g id="TILE_NAME">%1$s</xliff:g>. Trokit dy herë për ta shtuar."</string>
- <string name="accessibility_qs_edit_move_tile" msgid="6027997446473163426">"Zhvendose <xliff:g id="TILE_NAME">%1$s</xliff:g>"</string>
- <string name="accessibility_qs_edit_remove_tile" msgid="3406781901949899624">"Hiqe <xliff:g id="TILE_NAME">%1$s</xliff:g>"</string>
- <string name="accessibility_qs_edit_tile_add" msgid="6289879620154587233">"Shto <xliff:g id="TILE_NAME">%1$s</xliff:g> te pozicioni <xliff:g id="POSITION">%2$d</xliff:g>"</string>
- <string name="accessibility_qs_edit_tile_move" msgid="4841770637244326837">"Zhvendos <xliff:g id="TILE_NAME">%1$s</xliff:g> te pozicioni <xliff:g id="POSITION">%2$d</xliff:g>"</string>
+ <!-- no translation found for accessibility_qs_edit_remove_tile_action (775511891457193480) -->
+ <skip />
+ <!-- no translation found for accessibility_qs_edit_tile_add_action (5051211910345301833) -->
+ <skip />
+ <!-- no translation found for accessibility_qs_edit_tile_start_move (2009373939914517817) -->
+ <skip />
+ <!-- no translation found for accessibility_qs_edit_tile_start_add (7560798153975555772) -->
+ <skip />
+ <!-- no translation found for accessibility_qs_edit_tile_move_to_position (5198161544045930556) -->
+ <skip />
+ <!-- no translation found for accessibility_qs_edit_tile_add_to_position (9029163095148274690) -->
+ <skip />
+ <!-- no translation found for accessibility_qs_edit_position (4509277359815711830) -->
+ <skip />
<string name="accessibility_desc_quick_settings_edit" msgid="741658939453595297">"Redaktori i cilësimeve të shpejta."</string>
<string name="accessibility_desc_notification_icon" msgid="7331265967584178674">"Njoftim nga <xliff:g id="ID_1">%1$s</xliff:g>: <xliff:g id="ID_2">%2$s</xliff:g>"</string>
<string name="accessibility_quick_settings_settings" msgid="7098489591715844713">"Hap cilësimet."</string>
@@ -1067,4 +1075,11 @@
<string name="controls_added_tooltip" msgid="4842812921719153085">"Mbaj shtypur butonin e energjisë për të parë kontrollet e reja"</string>
<string name="controls_menu_add" msgid="4447246119229920050">"Shto kontrollet"</string>
<string name="controls_menu_edit" msgid="890623986951347062">"Modifiko kontrollet"</string>
+ <string name="media_output_dialog_add_output" msgid="5642703238877329518">"Shto daljet"</string>
+ <string name="media_output_dialog_group" msgid="5571251347877452212">"Grupi"</string>
+ <string name="media_output_dialog_single_device" msgid="3102758980643351058">"1 pajisje e zgjedhur"</string>
+ <string name="media_output_dialog_multiple_devices" msgid="1093771040315422350">"<xliff:g id="COUNT">%1$d</xliff:g> pajisje të zgjedhura"</string>
+ <string name="media_output_dialog_disconnected" msgid="1834473104836986046">"<xliff:g id="DEVICE_NAME">%1$s</xliff:g> (e shkëputur)"</string>
+ <string name="media_output_dialog_connect_failed" msgid="3225190634236259010">"Nuk mund të lidhej. Provo sërish."</string>
+ <string name="media_output_dialog_pairing_new" msgid="9099497976087485862">"Çifto pajisjen e re"</string>
</resources>
diff --git a/packages/SystemUI/res/values-sr/strings.xml b/packages/SystemUI/res/values-sr/strings.xml
index 15190d4860bf..90f1c5adf7be 100644
--- a/packages/SystemUI/res/values-sr/strings.xml
+++ b/packages/SystemUI/res/values-sr/strings.xml
@@ -884,12 +884,13 @@
</string-array>
<string name="tuner_low_priority" msgid="8412666814123009820">"Прикажи иконе обавештења ниског приоритета"</string>
<string name="other" msgid="429768510980739978">"Друго"</string>
- <string name="accessibility_qs_edit_tile_label" msgid="9079791448815232967">"<xliff:g id="POSITION">%1$d</xliff:g>. позиција, <xliff:g id="TILE_NAME">%2$s</xliff:g>. Двапут додирните да бисте изменили."</string>
- <string name="accessibility_qs_edit_add_tile_label" msgid="8292218072049068613">"<xliff:g id="TILE_NAME">%1$s</xliff:g>. Двапут додирните да бисте додали."</string>
- <string name="accessibility_qs_edit_move_tile" msgid="6027997446473163426">"Премести плочицу <xliff:g id="TILE_NAME">%1$s</xliff:g>"</string>
- <string name="accessibility_qs_edit_remove_tile" msgid="3406781901949899624">"Уклони плочицу <xliff:g id="TILE_NAME">%1$s</xliff:g>"</string>
- <string name="accessibility_qs_edit_tile_add" msgid="6289879620154587233">"Додајте „<xliff:g id="TILE_NAME">%1$s</xliff:g>“ на позицију <xliff:g id="POSITION">%2$d</xliff:g>"</string>
- <string name="accessibility_qs_edit_tile_move" msgid="4841770637244326837">"Преместите „<xliff:g id="TILE_NAME">%1$s</xliff:g>“ на позицију <xliff:g id="POSITION">%2$d</xliff:g>"</string>
+ <string name="accessibility_qs_edit_remove_tile_action" msgid="775511891457193480">"уклонили плочицу"</string>
+ <string name="accessibility_qs_edit_tile_add_action" msgid="5051211910345301833">"додали плочицу на крај"</string>
+ <string name="accessibility_qs_edit_tile_start_move" msgid="2009373939914517817">"Преместите плочицу"</string>
+ <string name="accessibility_qs_edit_tile_start_add" msgid="7560798153975555772">"Додајте плочицу"</string>
+ <string name="accessibility_qs_edit_tile_move_to_position" msgid="5198161544045930556">"Преместите на <xliff:g id="POSITION">%1$d</xliff:g>. позицију"</string>
+ <string name="accessibility_qs_edit_tile_add_to_position" msgid="9029163095148274690">"Додајте на <xliff:g id="POSITION">%1$d</xliff:g>. позицију"</string>
+ <string name="accessibility_qs_edit_position" msgid="4509277359815711830">"<xliff:g id="POSITION">%1$d</xliff:g>. позиција"</string>
<string name="accessibility_desc_quick_settings_edit" msgid="741658939453595297">"Уређивач за Брза подешавања."</string>
<string name="accessibility_desc_notification_icon" msgid="7331265967584178674">"Обавештења за <xliff:g id="ID_1">%1$s</xliff:g>: <xliff:g id="ID_2">%2$s</xliff:g>"</string>
<string name="accessibility_quick_settings_settings" msgid="7098489591715844713">"Отвори Подешавања."</string>
@@ -1073,4 +1074,11 @@
<string name="controls_added_tooltip" msgid="4842812921719153085">"Задржите дугме за укључивање да бисте видели нове контроле"</string>
<string name="controls_menu_add" msgid="4447246119229920050">"Додај контроле"</string>
<string name="controls_menu_edit" msgid="890623986951347062">"Измени контроле"</string>
+ <string name="media_output_dialog_add_output" msgid="5642703238877329518">"Додајте излазе"</string>
+ <string name="media_output_dialog_group" msgid="5571251347877452212">"Група"</string>
+ <string name="media_output_dialog_single_device" msgid="3102758980643351058">"Изабран је 1 уређај"</string>
+ <string name="media_output_dialog_multiple_devices" msgid="1093771040315422350">"Изабраних уређаја: <xliff:g id="COUNT">%1$d</xliff:g>"</string>
+ <string name="media_output_dialog_disconnected" msgid="1834473104836986046">"<xliff:g id="DEVICE_NAME">%1$s</xliff:g> (веза је прекинута)"</string>
+ <string name="media_output_dialog_connect_failed" msgid="3225190634236259010">"Повезивање није успело. Пробајте поново."</string>
+ <string name="media_output_dialog_pairing_new" msgid="9099497976087485862">"Упари нови уређај"</string>
</resources>
diff --git a/packages/SystemUI/res/values-sv/strings.xml b/packages/SystemUI/res/values-sv/strings.xml
index 6fc976ea9d36..8918f519b020 100644
--- a/packages/SystemUI/res/values-sv/strings.xml
+++ b/packages/SystemUI/res/values-sv/strings.xml
@@ -879,12 +879,13 @@
</string-array>
<string name="tuner_low_priority" msgid="8412666814123009820">"Visa ikoner för aviseringar med låg prioritet"</string>
<string name="other" msgid="429768510980739978">"Annat"</string>
- <string name="accessibility_qs_edit_tile_label" msgid="9079791448815232967">"Position <xliff:g id="POSITION">%1$d</xliff:g>, <xliff:g id="TILE_NAME">%2$s</xliff:g>. Tryck snabbt två gånger för att redigera positionen."</string>
- <string name="accessibility_qs_edit_add_tile_label" msgid="8292218072049068613">"<xliff:g id="TILE_NAME">%1$s</xliff:g>. Lägg till genom att trycka snabbt två gånger."</string>
- <string name="accessibility_qs_edit_move_tile" msgid="6027997446473163426">"Flytta <xliff:g id="TILE_NAME">%1$s</xliff:g>"</string>
- <string name="accessibility_qs_edit_remove_tile" msgid="3406781901949899624">"Ta bort <xliff:g id="TILE_NAME">%1$s</xliff:g>"</string>
- <string name="accessibility_qs_edit_tile_add" msgid="6289879620154587233">"Lägg till <xliff:g id="TILE_NAME">%1$s</xliff:g> på position <xliff:g id="POSITION">%2$d</xliff:g>"</string>
- <string name="accessibility_qs_edit_tile_move" msgid="4841770637244326837">"Flytta <xliff:g id="TILE_NAME">%1$s</xliff:g> till position <xliff:g id="POSITION">%2$d</xliff:g>"</string>
+ <string name="accessibility_qs_edit_remove_tile_action" msgid="775511891457193480">"ta bort ruta"</string>
+ <string name="accessibility_qs_edit_tile_add_action" msgid="5051211910345301833">"lägg till ruta i slutet"</string>
+ <string name="accessibility_qs_edit_tile_start_move" msgid="2009373939914517817">"Flytta ruta"</string>
+ <string name="accessibility_qs_edit_tile_start_add" msgid="7560798153975555772">"Lägg till ruta"</string>
+ <string name="accessibility_qs_edit_tile_move_to_position" msgid="5198161544045930556">"Flytta till <xliff:g id="POSITION">%1$d</xliff:g>"</string>
+ <string name="accessibility_qs_edit_tile_add_to_position" msgid="9029163095148274690">"Lägg till på position <xliff:g id="POSITION">%1$d</xliff:g>"</string>
+ <string name="accessibility_qs_edit_position" msgid="4509277359815711830">"Position <xliff:g id="POSITION">%1$d</xliff:g>"</string>
<string name="accessibility_desc_quick_settings_edit" msgid="741658939453595297">"Redigerare för snabbinställningar."</string>
<string name="accessibility_desc_notification_icon" msgid="7331265967584178674">"<xliff:g id="ID_1">%1$s</xliff:g>-avisering: <xliff:g id="ID_2">%2$s</xliff:g>"</string>
<string name="accessibility_quick_settings_settings" msgid="7098489591715844713">"Öppna inställningarna."</string>
@@ -1067,4 +1068,11 @@
<string name="controls_added_tooltip" msgid="4842812921719153085">"De nya snabbkontrollerna visas om du håller strömbrytaren nedtryckt"</string>
<string name="controls_menu_add" msgid="4447246119229920050">"Lägg till snabbkontroller"</string>
<string name="controls_menu_edit" msgid="890623986951347062">"Redigera snabbkontroller"</string>
+ <string name="media_output_dialog_add_output" msgid="5642703238877329518">"Lägg till utgångar"</string>
+ <string name="media_output_dialog_group" msgid="5571251347877452212">"Grupp"</string>
+ <string name="media_output_dialog_single_device" msgid="3102758980643351058">"1 enhet har valts"</string>
+ <string name="media_output_dialog_multiple_devices" msgid="1093771040315422350">"<xliff:g id="COUNT">%1$d</xliff:g> enheter har valts"</string>
+ <string name="media_output_dialog_disconnected" msgid="1834473104836986046">"<xliff:g id="DEVICE_NAME">%1$s</xliff:g> (frånkopplad)"</string>
+ <string name="media_output_dialog_connect_failed" msgid="3225190634236259010">"Det gick inte att ansluta. Försök igen."</string>
+ <string name="media_output_dialog_pairing_new" msgid="9099497976087485862">"Parkoppla en ny enhet"</string>
</resources>
diff --git a/packages/SystemUI/res/values-sw/strings.xml b/packages/SystemUI/res/values-sw/strings.xml
index 67d5f3404269..3949f2df6e40 100644
--- a/packages/SystemUI/res/values-sw/strings.xml
+++ b/packages/SystemUI/res/values-sw/strings.xml
@@ -879,12 +879,13 @@
</string-array>
<string name="tuner_low_priority" msgid="8412666814123009820">"Onyesha aikoni za arifa zisizo muhimu"</string>
<string name="other" msgid="429768510980739978">"Nyingine"</string>
- <string name="accessibility_qs_edit_tile_label" msgid="9079791448815232967">"Nafasi ya <xliff:g id="POSITION">%1$d</xliff:g>, <xliff:g id="TILE_NAME">%2$s</xliff:g>. Gusa mara mbili ili ubadilishe."</string>
- <string name="accessibility_qs_edit_add_tile_label" msgid="8292218072049068613">"<xliff:g id="TILE_NAME">%1$s</xliff:g>. Gusa mara mbili ili uongeze."</string>
- <string name="accessibility_qs_edit_move_tile" msgid="6027997446473163426">"Hamisha <xliff:g id="TILE_NAME">%1$s</xliff:g>"</string>
- <string name="accessibility_qs_edit_remove_tile" msgid="3406781901949899624">"Ondoa <xliff:g id="TILE_NAME">%1$s</xliff:g>"</string>
- <string name="accessibility_qs_edit_tile_add" msgid="6289879620154587233">"Ongeza <xliff:g id="TILE_NAME">%1$s</xliff:g> kwenye nafasi ya <xliff:g id="POSITION">%2$d</xliff:g>"</string>
- <string name="accessibility_qs_edit_tile_move" msgid="4841770637244326837">"Sogeza <xliff:g id="TILE_NAME">%1$s</xliff:g> kwenye nafasi ya <xliff:g id="POSITION">%2$d</xliff:g>"</string>
+ <string name="accessibility_qs_edit_remove_tile_action" msgid="775511891457193480">"ondoa kigae"</string>
+ <string name="accessibility_qs_edit_tile_add_action" msgid="5051211910345301833">"ongeza kigae mwishoni"</string>
+ <string name="accessibility_qs_edit_tile_start_move" msgid="2009373939914517817">"Hamisha kigae"</string>
+ <string name="accessibility_qs_edit_tile_start_add" msgid="7560798153975555772">"Ongeza kigae"</string>
+ <string name="accessibility_qs_edit_tile_move_to_position" msgid="5198161544045930556">"Hamishia kwenye <xliff:g id="POSITION">%1$d</xliff:g>"</string>
+ <string name="accessibility_qs_edit_tile_add_to_position" msgid="9029163095148274690">"Ongeza kwenye nafasi ya <xliff:g id="POSITION">%1$d</xliff:g>"</string>
+ <string name="accessibility_qs_edit_position" msgid="4509277359815711830">"Nafasi ya <xliff:g id="POSITION">%1$d</xliff:g>"</string>
<string name="accessibility_desc_quick_settings_edit" msgid="741658939453595297">"Kihariri cha Mipangilio ya haraka."</string>
<string name="accessibility_desc_notification_icon" msgid="7331265967584178674">"Arifa kutoka <xliff:g id="ID_1">%1$s</xliff:g>: <xliff:g id="ID_2">%2$s</xliff:g>"</string>
<string name="accessibility_quick_settings_settings" msgid="7098489591715844713">"Fungua mipangilio."</string>
@@ -1067,4 +1068,11 @@
<string name="controls_added_tooltip" msgid="4842812921719153085">"Shikilia kitufe cha kuwasha/kuzima ili uone vidhibiti vipya"</string>
<string name="controls_menu_add" msgid="4447246119229920050">"Weka vidhibiti"</string>
<string name="controls_menu_edit" msgid="890623986951347062">"Badilisha vidhibiti"</string>
+ <string name="media_output_dialog_add_output" msgid="5642703238877329518">"Weka vifaa vya kutoa sauti"</string>
+ <string name="media_output_dialog_group" msgid="5571251347877452212">"Kikundi"</string>
+ <string name="media_output_dialog_single_device" msgid="3102758980643351058">"Umechagua kifaa 1"</string>
+ <string name="media_output_dialog_multiple_devices" msgid="1093771040315422350">"Umechagua vifaa <xliff:g id="COUNT">%1$d</xliff:g>"</string>
+ <string name="media_output_dialog_disconnected" msgid="1834473104836986046">"<xliff:g id="DEVICE_NAME">%1$s</xliff:g> (hakijaunganishwa)"</string>
+ <string name="media_output_dialog_connect_failed" msgid="3225190634236259010">"Imeshindwa kuunganisha. Jaribu tena."</string>
+ <string name="media_output_dialog_pairing_new" msgid="9099497976087485862">"Oanisha kifaa kipya"</string>
</resources>
diff --git a/packages/SystemUI/res/values-ta/strings.xml b/packages/SystemUI/res/values-ta/strings.xml
index 8f2e296301d7..9d5c54fc7890 100644
--- a/packages/SystemUI/res/values-ta/strings.xml
+++ b/packages/SystemUI/res/values-ta/strings.xml
@@ -879,12 +879,20 @@
</string-array>
<string name="tuner_low_priority" msgid="8412666814123009820">"குறைந்த முன்னுரிமை உள்ள அறிவிப்பு ஐகான்களைக் காட்டு"</string>
<string name="other" msgid="429768510980739978">"மற்றவை"</string>
- <string name="accessibility_qs_edit_tile_label" msgid="9079791448815232967">"நிலை <xliff:g id="POSITION">%1$d</xliff:g>, <xliff:g id="TILE_NAME">%2$s</xliff:g>. திருத்த, இருமுறை தட்டவும்."</string>
- <string name="accessibility_qs_edit_add_tile_label" msgid="8292218072049068613">"<xliff:g id="TILE_NAME">%1$s</xliff:g>. சேர்க்க, இருமுறை தட்டவும்."</string>
- <string name="accessibility_qs_edit_move_tile" msgid="6027997446473163426">"<xliff:g id="TILE_NAME">%1$s</xliff:g>ஐ நகர்த்தவும்"</string>
- <string name="accessibility_qs_edit_remove_tile" msgid="3406781901949899624">"<xliff:g id="TILE_NAME">%1$s</xliff:g>ஐ அகற்றவும்"</string>
- <string name="accessibility_qs_edit_tile_add" msgid="6289879620154587233">"நிலைப்பாடு <xliff:g id="POSITION">%2$d</xliff:g> இல் <xliff:g id="TILE_NAME">%1$s</xliff:g>ஐச் சேர்க்கும்"</string>
- <string name="accessibility_qs_edit_tile_move" msgid="4841770637244326837">"நிலைப்பாடு <xliff:g id="POSITION">%2$d</xliff:g>க்கு <xliff:g id="TILE_NAME">%1$s</xliff:g>ஐ நகர்த்தும்"</string>
+ <!-- no translation found for accessibility_qs_edit_remove_tile_action (775511891457193480) -->
+ <skip />
+ <!-- no translation found for accessibility_qs_edit_tile_add_action (5051211910345301833) -->
+ <skip />
+ <!-- no translation found for accessibility_qs_edit_tile_start_move (2009373939914517817) -->
+ <skip />
+ <!-- no translation found for accessibility_qs_edit_tile_start_add (7560798153975555772) -->
+ <skip />
+ <!-- no translation found for accessibility_qs_edit_tile_move_to_position (5198161544045930556) -->
+ <skip />
+ <!-- no translation found for accessibility_qs_edit_tile_add_to_position (9029163095148274690) -->
+ <skip />
+ <!-- no translation found for accessibility_qs_edit_position (4509277359815711830) -->
+ <skip />
<string name="accessibility_desc_quick_settings_edit" msgid="741658939453595297">"விரைவு அமைப்புகள் திருத்தி."</string>
<string name="accessibility_desc_notification_icon" msgid="7331265967584178674">"<xliff:g id="ID_1">%1$s</xliff:g> அறிவிப்பு: <xliff:g id="ID_2">%2$s</xliff:g>"</string>
<string name="accessibility_quick_settings_settings" msgid="7098489591715844713">"அமைப்புகளைத் திற."</string>
@@ -1067,4 +1075,11 @@
<string name="controls_added_tooltip" msgid="4842812921719153085">"புதிய கட்டுப்பாடுகளைப் பார்க்க பவர் பட்டனைப் பிடித்திருக்கவும்"</string>
<string name="controls_menu_add" msgid="4447246119229920050">"கட்டுப்பாடுகளைச் சேர்த்தல்"</string>
<string name="controls_menu_edit" msgid="890623986951347062">"கட்டுப்பாடுகளை மாற்றுதல்"</string>
+ <string name="media_output_dialog_add_output" msgid="5642703238877329518">"அவுட்புட்களைச் சேர்த்தல்"</string>
+ <string name="media_output_dialog_group" msgid="5571251347877452212">"குழு"</string>
+ <string name="media_output_dialog_single_device" msgid="3102758980643351058">"1 சாதனம் தேர்ந்தெடுக்கப்பட்டுள்ளது"</string>
+ <string name="media_output_dialog_multiple_devices" msgid="1093771040315422350">"<xliff:g id="COUNT">%1$d</xliff:g> சாதனங்கள் தேர்ந்தெடுக்கப்பட்டுள்ளன"</string>
+ <string name="media_output_dialog_disconnected" msgid="1834473104836986046">"<xliff:g id="DEVICE_NAME">%1$s</xliff:g> (இணைப்பு துண்டிக்கப்பட்டது)"</string>
+ <string name="media_output_dialog_connect_failed" msgid="3225190634236259010">"இணைக்க முடியவில்லை. மீண்டும் முயலவும்."</string>
+ <string name="media_output_dialog_pairing_new" msgid="9099497976087485862">"புதிய சாதனத்தை இணைத்தல்"</string>
</resources>
diff --git a/packages/SystemUI/res/values-te/strings.xml b/packages/SystemUI/res/values-te/strings.xml
index 8a15d7615140..d7a27b86a99a 100644
--- a/packages/SystemUI/res/values-te/strings.xml
+++ b/packages/SystemUI/res/values-te/strings.xml
@@ -693,7 +693,7 @@
<string name="notification_channel_unsilenced" msgid="94878840742161152">"ఈ నోటిఫికేషన్‌లు మిమ్మల్ని హెచ్చరిస్తాయి"</string>
<string name="inline_blocking_helper" msgid="2891486013649543452">"మీరు సాధారణంగా ఈ నోటిఫికేషన్‌లను విస్మరిస్తారు. \nవాటి ప్రదర్శనను కొనసాగించాలా?"</string>
<string name="inline_done_button" msgid="6043094985588909584">"పూర్తయింది"</string>
- <string name="inline_ok_button" msgid="603075490581280343">"అప్లై చేయి"</string>
+ <string name="inline_ok_button" msgid="603075490581280343">"అప్లయి చేయి"</string>
<string name="inline_keep_showing" msgid="8736001253507073497">"ఈ నోటిఫికేషన్‌లను చూపిస్తూ ఉండాలా?"</string>
<string name="inline_stop_button" msgid="2453460935438696090">"నోటిఫికేషన్‌లను ఆపివేయి"</string>
<string name="inline_deliver_silently_button" msgid="2714314213321223286">"నిశ్శబ్దంగా బట్వాడా చేయండి"</string>
@@ -879,12 +879,13 @@
</string-array>
<string name="tuner_low_priority" msgid="8412666814123009820">"తక్కువ ప్రాధాన్యత నోటిఫికేషన్ చిహ్నాలను చూపించు"</string>
<string name="other" msgid="429768510980739978">"ఇతరం"</string>
- <string name="accessibility_qs_edit_tile_label" msgid="9079791448815232967">"స్థానం <xliff:g id="POSITION">%1$d</xliff:g>, <xliff:g id="TILE_NAME">%2$s</xliff:g>. సవరించడానికి రెండుసార్లు నొక్కండి."</string>
- <string name="accessibility_qs_edit_add_tile_label" msgid="8292218072049068613">"<xliff:g id="TILE_NAME">%1$s</xliff:g>. జోడించడానికి రెండుసార్లు నొక్కండి."</string>
- <string name="accessibility_qs_edit_move_tile" msgid="6027997446473163426">"<xliff:g id="TILE_NAME">%1$s</xliff:g>ని తరలిస్తుంది"</string>
- <string name="accessibility_qs_edit_remove_tile" msgid="3406781901949899624">"<xliff:g id="TILE_NAME">%1$s</xliff:g>ని తీసివేస్తుంది"</string>
- <string name="accessibility_qs_edit_tile_add" msgid="6289879620154587233">"స్థానం <xliff:g id="POSITION">%2$d</xliff:g>కి <xliff:g id="TILE_NAME">%1$s</xliff:g>ని జోడించండి"</string>
- <string name="accessibility_qs_edit_tile_move" msgid="4841770637244326837">"స్థానం <xliff:g id="POSITION">%2$d</xliff:g>కి <xliff:g id="TILE_NAME">%1$s</xliff:g>ని తరలించండి"</string>
+ <string name="accessibility_qs_edit_remove_tile_action" msgid="775511891457193480">"టైల్‌ను తీసివేయండి"</string>
+ <string name="accessibility_qs_edit_tile_add_action" msgid="5051211910345301833">"ముగించడానికి టైల్‌ను జోడించండి"</string>
+ <string name="accessibility_qs_edit_tile_start_move" msgid="2009373939914517817">"టైల్‌ను తరలించండి"</string>
+ <string name="accessibility_qs_edit_tile_start_add" msgid="7560798153975555772">"టైల్‌ను జోడించండి"</string>
+ <string name="accessibility_qs_edit_tile_move_to_position" msgid="5198161544045930556">"<xliff:g id="POSITION">%1$d</xliff:g>కు తరలించండి"</string>
+ <string name="accessibility_qs_edit_tile_add_to_position" msgid="9029163095148274690">"<xliff:g id="POSITION">%1$d</xliff:g> స్థానానికి జోడించండి"</string>
+ <string name="accessibility_qs_edit_position" msgid="4509277359815711830">"స్థానం <xliff:g id="POSITION">%1$d</xliff:g>"</string>
<string name="accessibility_desc_quick_settings_edit" msgid="741658939453595297">"శీఘ్ర సెట్టింగ్‌ల ఎడిటర్."</string>
<string name="accessibility_desc_notification_icon" msgid="7331265967584178674">"<xliff:g id="ID_1">%1$s</xliff:g> నోటిఫికేషన్: <xliff:g id="ID_2">%2$s</xliff:g>"</string>
<string name="accessibility_quick_settings_settings" msgid="7098489591715844713">"సెట్టింగ్‌లను తెరవండి."</string>
@@ -1067,4 +1068,11 @@
<string name="controls_added_tooltip" msgid="4842812921719153085">"కొత్త నియంత్రణలను చూడడానికి పవర్ బటన్‌ని నొక్కి పట్టుకోండి"</string>
<string name="controls_menu_add" msgid="4447246119229920050">"నియంత్రణలను జోడించండి"</string>
<string name="controls_menu_edit" msgid="890623986951347062">"నియంత్రణలను ఎడిట్ చేయండి"</string>
+ <string name="media_output_dialog_add_output" msgid="5642703238877329518">"అవుట్‌పుట్‌లను జోడించండి"</string>
+ <string name="media_output_dialog_group" msgid="5571251347877452212">"గ్రూప్"</string>
+ <string name="media_output_dialog_single_device" msgid="3102758980643351058">"1 పరికరం ఎంచుకోబడింది"</string>
+ <string name="media_output_dialog_multiple_devices" msgid="1093771040315422350">"<xliff:g id="COUNT">%1$d</xliff:g> పరికరాలు ఎంచుకోబడ్డాయి"</string>
+ <string name="media_output_dialog_disconnected" msgid="1834473104836986046">"<xliff:g id="DEVICE_NAME">%1$s</xliff:g> (డిస్‌కనెక్ట్ అయ్యింది)"</string>
+ <string name="media_output_dialog_connect_failed" msgid="3225190634236259010">"కనెక్ట్ చేయడం సాధ్యపడలేదు. మళ్లీ ట్రై చేయండి."</string>
+ <string name="media_output_dialog_pairing_new" msgid="9099497976087485862">"కొత్త పరికరాన్ని పెయిర్ చేయండి"</string>
</resources>
diff --git a/packages/SystemUI/res/values-th/strings.xml b/packages/SystemUI/res/values-th/strings.xml
index 16593b4c245f..cb6089fa0344 100644
--- a/packages/SystemUI/res/values-th/strings.xml
+++ b/packages/SystemUI/res/values-th/strings.xml
@@ -92,7 +92,7 @@
<string name="screenrecord_background_processing_label" msgid="7244617554884238898">"กำลังประมวลผลการอัดหน้าจอ"</string>
<string name="screenrecord_channel_description" msgid="4147077128486138351">"การแจ้งเตือนต่อเนื่องสำหรับเซสชันการบันทึกหน้าจอ"</string>
<string name="screenrecord_start_label" msgid="1750350278888217473">"เริ่มบันทึกเลยไหม"</string>
- <string name="screenrecord_description" msgid="1123231719680353736">"ขณะบันทึก ระบบ Android อาจบันทึกข้อมูลที่ละเอียดอ่อนที่ปรากฏบนหน้าจอหรือเล่นในอุปกรณ์ได้ ซึ่งรวมถึงรหัสผ่าน ข้อมูลการชำระเงิน รูปภาพ ข้อความ และเสียง"</string>
+ <string name="screenrecord_description" msgid="1123231719680353736">"ขณะบันทึก ระบบ Android อาจบันทึกข้อมูลที่ละเอียดอ่อนซึ่งปรากฏบนหน้าจอหรือเล่นในอุปกรณ์ได้ ซึ่งรวมถึงรหัสผ่าน ข้อมูลการชำระเงิน รูปภาพ ข้อความ และเสียง"</string>
<string name="screenrecord_audio_label" msgid="6183558856175159629">"บันทึกเสียง"</string>
<string name="screenrecord_device_audio_label" msgid="9016927171280567791">"เสียงจากอุปกรณ์"</string>
<string name="screenrecord_device_audio_description" msgid="4922694220572186193">"เสียงจากอุปกรณ์ เช่น เพลง การโทร และเสียงเรียกเข้า"</string>
@@ -337,7 +337,7 @@
<string name="accessibility_rotation_lock_on_landscape_changed" msgid="5785739044300729592">"ขณะนี้หน้าจอล็อกอยู่ในแนวนอน"</string>
<string name="accessibility_rotation_lock_on_portrait_changed" msgid="5580170829728987989">"ขณะนี้หน้าจอล็อกอยู่ในแนวตั้ง"</string>
<string name="dessert_case" msgid="9104973640704357717">"ชั้นแสดงของหวาน"</string>
- <string name="start_dreams" msgid="9131802557946276718">"โปรแกรมรักษาหน้าจอ"</string>
+ <string name="start_dreams" msgid="9131802557946276718">"โปรแกรมรักษาจอภาพ"</string>
<string name="ethernet_label" msgid="2203544727007463351">"อีเทอร์เน็ต"</string>
<string name="quick_settings_header_onboarding_text" msgid="1918085351115504765">"แตะไอคอนค้างไว้เพื่อดูตัวเลือกอื่นๆ"</string>
<string name="quick_settings_dnd_label" msgid="7728690179108024338">"ห้ามรบกวน"</string>
@@ -509,7 +509,7 @@
<string name="manage_notifications_text" msgid="6885645344647733116">"จัดการ"</string>
<string name="manage_notifications_history_text" msgid="57055985396576230">"ประวัติ"</string>
<string name="notification_section_header_incoming" msgid="850925217908095197">"ใหม่"</string>
- <string name="notification_section_header_gentle" msgid="6804099527336337197">"เงียบ"</string>
+ <string name="notification_section_header_gentle" msgid="6804099527336337197">"ปิดเสียง"</string>
<string name="notification_section_header_alerting" msgid="5581175033680477651">"การแจ้งเตือน"</string>
<string name="notification_section_header_conversations" msgid="821834744538345661">"การสนทนา"</string>
<string name="accessibility_notification_section_header_gentle_clear_all" msgid="6490207897764933919">"ล้างการแจ้งเตือนแบบไม่มีเสียงทั้งหมด"</string>
@@ -706,7 +706,7 @@
<string name="inline_silent_button_keep_alerting" msgid="6577845442184724992">"แจ้งเตือนต่อไป"</string>
<string name="inline_turn_off_notifications" msgid="8543989584403106071">"ปิดการแจ้งเตือน"</string>
<string name="inline_keep_showing_app" msgid="4393429060390649757">"แสดงการแจ้งเตือนจากแอปนี้ต่อไปไหม"</string>
- <string name="notification_silence_title" msgid="8608090968400832335">"เงียบ"</string>
+ <string name="notification_silence_title" msgid="8608090968400832335">"ปิดเสียง"</string>
<string name="notification_alert_title" msgid="3656229781017543655">"ค่าเริ่มต้น"</string>
<string name="notification_bubble_title" msgid="8330481035191903164">"บับเบิล"</string>
<string name="notification_automatic_title" msgid="3745465364578762652">"อัตโนมัติ"</string>
@@ -862,7 +862,7 @@
<string name="left_icon" msgid="5036278531966897006">"ไอคอนทางซ้าย"</string>
<string name="right_icon" msgid="1103955040645237425">"ไอคอนทางขวา"</string>
<string name="drag_to_add_tiles" msgid="8933270127508303672">"กดค้างแล้วลากเพื่อเพิ่มการ์ด"</string>
- <string name="drag_to_rearrange_tiles" msgid="2143204300089638620">"กดการ์ดค้างไว้แล้วลากเพื่อจัดเรียงใหม่"</string>
+ <string name="drag_to_rearrange_tiles" msgid="2143204300089638620">"กดค้างแล้วลากเพื่อจัดเรียงการ์ดใหม่"</string>
<string name="drag_to_remove_tiles" msgid="4682194717573850385">"ลากมาที่นี่เพื่อนำออก"</string>
<string name="drag_to_remove_disabled" msgid="933046987838658850">"คุณต้องมีการ์ดอย่างน้อย <xliff:g id="MIN_NUM_TILES">%1$d</xliff:g> รายการ"</string>
<string name="qs_edit" msgid="5583565172803472437">"แก้ไข"</string>
@@ -879,12 +879,13 @@
</string-array>
<string name="tuner_low_priority" msgid="8412666814123009820">"แสดงไอคอนการแจ้งเตือนลำดับความสำคัญต่ำ"</string>
<string name="other" msgid="429768510980739978">"อื่นๆ"</string>
- <string name="accessibility_qs_edit_tile_label" msgid="9079791448815232967">"ตำแหน่ง <xliff:g id="POSITION">%1$d</xliff:g> <xliff:g id="TILE_NAME">%2$s</xliff:g> แตะ 2 ครั้งเพื่อแก้ไข"</string>
- <string name="accessibility_qs_edit_add_tile_label" msgid="8292218072049068613">"<xliff:g id="TILE_NAME">%1$s</xliff:g> แตะ 2 ครั้งเพื่อเพิ่ม"</string>
- <string name="accessibility_qs_edit_move_tile" msgid="6027997446473163426">"ย้าย <xliff:g id="TILE_NAME">%1$s</xliff:g>"</string>
- <string name="accessibility_qs_edit_remove_tile" msgid="3406781901949899624">"นำ <xliff:g id="TILE_NAME">%1$s</xliff:g> ออก"</string>
- <string name="accessibility_qs_edit_tile_add" msgid="6289879620154587233">"เพิ่ม <xliff:g id="TILE_NAME">%1$s</xliff:g> ไปยังตำแหน่ง <xliff:g id="POSITION">%2$d</xliff:g>"</string>
- <string name="accessibility_qs_edit_tile_move" msgid="4841770637244326837">"ย้าย <xliff:g id="TILE_NAME">%1$s</xliff:g> ไปยังตำแหน่ง <xliff:g id="POSITION">%2$d</xliff:g>"</string>
+ <string name="accessibility_qs_edit_remove_tile_action" msgid="775511891457193480">"นำชิ้นส่วนออก"</string>
+ <string name="accessibility_qs_edit_tile_add_action" msgid="5051211910345301833">"เพิ่มชิ้นส่วนต่อท้าย"</string>
+ <string name="accessibility_qs_edit_tile_start_move" msgid="2009373939914517817">"ย้ายชิ้นส่วน"</string>
+ <string name="accessibility_qs_edit_tile_start_add" msgid="7560798153975555772">"เพิ่มชิ้นส่วน"</string>
+ <string name="accessibility_qs_edit_tile_move_to_position" msgid="5198161544045930556">"ย้ายไปที่ <xliff:g id="POSITION">%1$d</xliff:g>"</string>
+ <string name="accessibility_qs_edit_tile_add_to_position" msgid="9029163095148274690">"เพิ่มไปยังตำแหน่ง <xliff:g id="POSITION">%1$d</xliff:g>"</string>
+ <string name="accessibility_qs_edit_position" msgid="4509277359815711830">"ตำแหน่ง <xliff:g id="POSITION">%1$d</xliff:g>"</string>
<string name="accessibility_desc_quick_settings_edit" msgid="741658939453595297">"ตัวแก้ไขการตั้งค่าด่วน"</string>
<string name="accessibility_desc_notification_icon" msgid="7331265967584178674">"<xliff:g id="ID_1">%1$s</xliff:g> การแจ้งเตือน: <xliff:g id="ID_2">%2$s</xliff:g>"</string>
<string name="accessibility_quick_settings_settings" msgid="7098489591715844713">"เปิดการตั้งค่า"</string>
@@ -1067,4 +1068,11 @@
<string name="controls_added_tooltip" msgid="4842812921719153085">"กดปุ่มเปิด/ปิดค้างไว้เพื่อดูตัวควบคุมใหม่ๆ"</string>
<string name="controls_menu_add" msgid="4447246119229920050">"เพิ่มตัวควบคุม"</string>
<string name="controls_menu_edit" msgid="890623986951347062">"แก้ไขตัวควบคุม"</string>
+ <string name="media_output_dialog_add_output" msgid="5642703238877329518">"เพิ่มเอาต์พุต"</string>
+ <string name="media_output_dialog_group" msgid="5571251347877452212">"กลุ่ม"</string>
+ <string name="media_output_dialog_single_device" msgid="3102758980643351058">"เลือกอุปกรณ์ไว้ 1 รายการ"</string>
+ <string name="media_output_dialog_multiple_devices" msgid="1093771040315422350">"เลือกอุปกรณ์ไว้ <xliff:g id="COUNT">%1$d</xliff:g> รายการ"</string>
+ <string name="media_output_dialog_disconnected" msgid="1834473104836986046">"<xliff:g id="DEVICE_NAME">%1$s</xliff:g> (ยกเลิกการเชื่อมต่อแล้ว)"</string>
+ <string name="media_output_dialog_connect_failed" msgid="3225190634236259010">"เชื่อมต่อไม่ได้ ลองใหม่"</string>
+ <string name="media_output_dialog_pairing_new" msgid="9099497976087485862">"จับคู่อุปกรณ์ใหม่"</string>
</resources>
diff --git a/packages/SystemUI/res/values-tl/strings.xml b/packages/SystemUI/res/values-tl/strings.xml
index 283a6d54ef0f..7e6aa629ea63 100644
--- a/packages/SystemUI/res/values-tl/strings.xml
+++ b/packages/SystemUI/res/values-tl/strings.xml
@@ -879,12 +879,13 @@
</string-array>
<string name="tuner_low_priority" msgid="8412666814123009820">"Ipakita ang mga icon ng notification na may mababang priority"</string>
<string name="other" msgid="429768510980739978">"Iba pa"</string>
- <string name="accessibility_qs_edit_tile_label" msgid="9079791448815232967">"Posisyon <xliff:g id="POSITION">%1$d</xliff:g>, <xliff:g id="TILE_NAME">%2$s</xliff:g>. I-double tap upang i-edit."</string>
- <string name="accessibility_qs_edit_add_tile_label" msgid="8292218072049068613">"<xliff:g id="TILE_NAME">%1$s</xliff:g>. I-double tap upang idagdag."</string>
- <string name="accessibility_qs_edit_move_tile" msgid="6027997446473163426">"Ilipat ang <xliff:g id="TILE_NAME">%1$s</xliff:g>"</string>
- <string name="accessibility_qs_edit_remove_tile" msgid="3406781901949899624">"Alisin ang <xliff:g id="TILE_NAME">%1$s</xliff:g>"</string>
- <string name="accessibility_qs_edit_tile_add" msgid="6289879620154587233">"Idagdag ang <xliff:g id="TILE_NAME">%1$s</xliff:g> sa posisyong <xliff:g id="POSITION">%2$d</xliff:g>"</string>
- <string name="accessibility_qs_edit_tile_move" msgid="4841770637244326837">"Ilipat ang <xliff:g id="TILE_NAME">%1$s</xliff:g> sa posisyong <xliff:g id="POSITION">%2$d</xliff:g>"</string>
+ <string name="accessibility_qs_edit_remove_tile_action" msgid="775511891457193480">"alisin ang tile"</string>
+ <string name="accessibility_qs_edit_tile_add_action" msgid="5051211910345301833">"idagdag ang tile sa dulo"</string>
+ <string name="accessibility_qs_edit_tile_start_move" msgid="2009373939914517817">"Ilipat ang tile"</string>
+ <string name="accessibility_qs_edit_tile_start_add" msgid="7560798153975555772">"Magdagdag ng tile"</string>
+ <string name="accessibility_qs_edit_tile_move_to_position" msgid="5198161544045930556">"Ilipat sa <xliff:g id="POSITION">%1$d</xliff:g>"</string>
+ <string name="accessibility_qs_edit_tile_add_to_position" msgid="9029163095148274690">"Idagdag sa posisyong <xliff:g id="POSITION">%1$d</xliff:g>"</string>
+ <string name="accessibility_qs_edit_position" msgid="4509277359815711830">"Posisyon <xliff:g id="POSITION">%1$d</xliff:g>"</string>
<string name="accessibility_desc_quick_settings_edit" msgid="741658939453595297">"Editor ng Mga mabilisang setting."</string>
<string name="accessibility_desc_notification_icon" msgid="7331265967584178674">"Notification sa <xliff:g id="ID_1">%1$s</xliff:g>: <xliff:g id="ID_2">%2$s</xliff:g>"</string>
<string name="accessibility_quick_settings_settings" msgid="7098489591715844713">"Buksan ang mga setting."</string>
@@ -1067,4 +1068,11 @@
<string name="controls_added_tooltip" msgid="4842812921719153085">"Pindutin nang matagal ang Power button para makita ang mga bagong kontrol"</string>
<string name="controls_menu_add" msgid="4447246119229920050">"Magdagdag ng mga kontrol"</string>
<string name="controls_menu_edit" msgid="890623986951347062">"Mag-edit ng mga kontrol"</string>
+ <string name="media_output_dialog_add_output" msgid="5642703238877329518">"Magdagdag ng mga output"</string>
+ <string name="media_output_dialog_group" msgid="5571251347877452212">"Grupo"</string>
+ <string name="media_output_dialog_single_device" msgid="3102758980643351058">"1 device ang napili"</string>
+ <string name="media_output_dialog_multiple_devices" msgid="1093771040315422350">"<xliff:g id="COUNT">%1$d</xliff:g> (na) device ang napili"</string>
+ <string name="media_output_dialog_disconnected" msgid="1834473104836986046">"<xliff:g id="DEVICE_NAME">%1$s</xliff:g> (nakadiskonekta)"</string>
+ <string name="media_output_dialog_connect_failed" msgid="3225190634236259010">"Hindi makakonekta. Subukan ulit."</string>
+ <string name="media_output_dialog_pairing_new" msgid="9099497976087485862">"Magpares ng bagong device"</string>
</resources>
diff --git a/packages/SystemUI/res/values-tr/strings.xml b/packages/SystemUI/res/values-tr/strings.xml
index 514f4ee57ce3..dac352f79d8b 100644
--- a/packages/SystemUI/res/values-tr/strings.xml
+++ b/packages/SystemUI/res/values-tr/strings.xml
@@ -879,12 +879,13 @@
</string-array>
<string name="tuner_low_priority" msgid="8412666814123009820">"Düşük öncelikli bildirim simgelerini göster"</string>
<string name="other" msgid="429768510980739978">"Diğer"</string>
- <string name="accessibility_qs_edit_tile_label" msgid="9079791448815232967">"<xliff:g id="POSITION">%1$d</xliff:g>. konum, <xliff:g id="TILE_NAME">%2$s</xliff:g>. Düzenlemek için iki kez dokunun."</string>
- <string name="accessibility_qs_edit_add_tile_label" msgid="8292218072049068613">"<xliff:g id="TILE_NAME">%1$s</xliff:g>. Eklemek için iki kez dokunun."</string>
- <string name="accessibility_qs_edit_move_tile" msgid="6027997446473163426">"<xliff:g id="TILE_NAME">%1$s</xliff:g> kutusunu taşı"</string>
- <string name="accessibility_qs_edit_remove_tile" msgid="3406781901949899624">"<xliff:g id="TILE_NAME">%1$s</xliff:g> kutusunu kaldır"</string>
- <string name="accessibility_qs_edit_tile_add" msgid="6289879620154587233">"<xliff:g id="TILE_NAME">%1$s</xliff:g> öğesini <xliff:g id="POSITION">%2$d</xliff:g> konumuna ekle"</string>
- <string name="accessibility_qs_edit_tile_move" msgid="4841770637244326837">"<xliff:g id="TILE_NAME">%1$s</xliff:g> öğesini <xliff:g id="POSITION">%2$d</xliff:g> konumuna taşı"</string>
+ <string name="accessibility_qs_edit_remove_tile_action" msgid="775511891457193480">"Karoyu kaldırmak için"</string>
+ <string name="accessibility_qs_edit_tile_add_action" msgid="5051211910345301833">"Sona karo eklemek için"</string>
+ <string name="accessibility_qs_edit_tile_start_move" msgid="2009373939914517817">"Karoyu taşı"</string>
+ <string name="accessibility_qs_edit_tile_start_add" msgid="7560798153975555772">"Karo ekle"</string>
+ <string name="accessibility_qs_edit_tile_move_to_position" msgid="5198161544045930556">"<xliff:g id="POSITION">%1$d</xliff:g> konumuna taşı"</string>
+ <string name="accessibility_qs_edit_tile_add_to_position" msgid="9029163095148274690">"<xliff:g id="POSITION">%1$d</xliff:g> konumuna ekle"</string>
+ <string name="accessibility_qs_edit_position" msgid="4509277359815711830">"Konum: <xliff:g id="POSITION">%1$d</xliff:g>"</string>
<string name="accessibility_desc_quick_settings_edit" msgid="741658939453595297">"Hızlı ayar düzenleyicisi."</string>
<string name="accessibility_desc_notification_icon" msgid="7331265967584178674">"<xliff:g id="ID_1">%1$s</xliff:g> bildirimi: <xliff:g id="ID_2">%2$s</xliff:g>"</string>
<string name="accessibility_quick_settings_settings" msgid="7098489591715844713">"Ayarları aç."</string>
@@ -1067,4 +1068,11 @@
<string name="controls_added_tooltip" msgid="4842812921719153085">"Yeni kontrolleri görmek için Güç düğmesini basılı tutun"</string>
<string name="controls_menu_add" msgid="4447246119229920050">"Denetim ekle"</string>
<string name="controls_menu_edit" msgid="890623986951347062">"Denetimleri düzenle"</string>
+ <string name="media_output_dialog_add_output" msgid="5642703238877329518">"Çıkışlar ekleyin"</string>
+ <string name="media_output_dialog_group" msgid="5571251347877452212">"Grup"</string>
+ <string name="media_output_dialog_single_device" msgid="3102758980643351058">"1 cihaz seçildi"</string>
+ <string name="media_output_dialog_multiple_devices" msgid="1093771040315422350">"<xliff:g id="COUNT">%1$d</xliff:g> cihaz seçildi"</string>
+ <string name="media_output_dialog_disconnected" msgid="1834473104836986046">"<xliff:g id="DEVICE_NAME">%1$s</xliff:g> (bağlı değil)"</string>
+ <string name="media_output_dialog_connect_failed" msgid="3225190634236259010">"Bağlanılamadı. Tekrar deneyin."</string>
+ <string name="media_output_dialog_pairing_new" msgid="9099497976087485862">"Yeni cihaz eşle"</string>
</resources>
diff --git a/packages/SystemUI/res/values-uk/strings.xml b/packages/SystemUI/res/values-uk/strings.xml
index 565907ce9106..eeedac603ab5 100644
--- a/packages/SystemUI/res/values-uk/strings.xml
+++ b/packages/SystemUI/res/values-uk/strings.xml
@@ -889,12 +889,13 @@
</string-array>
<string name="tuner_low_priority" msgid="8412666814123009820">"Показувати значки сповіщень із низьким пріоритетом"</string>
<string name="other" msgid="429768510980739978">"Інше"</string>
- <string name="accessibility_qs_edit_tile_label" msgid="9079791448815232967">"Позиція <xliff:g id="POSITION">%1$d</xliff:g>, <xliff:g id="TILE_NAME">%2$s</xliff:g>. Двічі торкніться, щоб змінити."</string>
- <string name="accessibility_qs_edit_add_tile_label" msgid="8292218072049068613">"<xliff:g id="TILE_NAME">%1$s</xliff:g>. Двічі торкніться, щоб додати."</string>
- <string name="accessibility_qs_edit_move_tile" msgid="6027997446473163426">"Перемістити <xliff:g id="TILE_NAME">%1$s</xliff:g>"</string>
- <string name="accessibility_qs_edit_remove_tile" msgid="3406781901949899624">"Видалити <xliff:g id="TILE_NAME">%1$s</xliff:g>"</string>
- <string name="accessibility_qs_edit_tile_add" msgid="6289879620154587233">"Додати <xliff:g id="TILE_NAME">%1$s</xliff:g> на позицію <xliff:g id="POSITION">%2$d</xliff:g>"</string>
- <string name="accessibility_qs_edit_tile_move" msgid="4841770637244326837">"Перемістити <xliff:g id="TILE_NAME">%1$s</xliff:g> на позицію <xliff:g id="POSITION">%2$d</xliff:g>"</string>
+ <string name="accessibility_qs_edit_remove_tile_action" msgid="775511891457193480">"вилучити значок"</string>
+ <string name="accessibility_qs_edit_tile_add_action" msgid="5051211910345301833">"додати значок у кінець"</string>
+ <string name="accessibility_qs_edit_tile_start_move" msgid="2009373939914517817">"Перемістити значок"</string>
+ <string name="accessibility_qs_edit_tile_start_add" msgid="7560798153975555772">"Додати значок"</string>
+ <string name="accessibility_qs_edit_tile_move_to_position" msgid="5198161544045930556">"Перемістити на позицію <xliff:g id="POSITION">%1$d</xliff:g>"</string>
+ <string name="accessibility_qs_edit_tile_add_to_position" msgid="9029163095148274690">"Додати на позицію <xliff:g id="POSITION">%1$d</xliff:g>"</string>
+ <string name="accessibility_qs_edit_position" msgid="4509277359815711830">"Позиція <xliff:g id="POSITION">%1$d</xliff:g>"</string>
<string name="accessibility_desc_quick_settings_edit" msgid="741658939453595297">"Редактор швидких налаштувань."</string>
<string name="accessibility_desc_notification_icon" msgid="7331265967584178674">"Сповіщення <xliff:g id="ID_1">%1$s</xliff:g>: <xliff:g id="ID_2">%2$s</xliff:g>"</string>
<string name="accessibility_quick_settings_settings" msgid="7098489591715844713">"Відкрити налаштування."</string>
@@ -1079,4 +1080,11 @@
<string name="controls_added_tooltip" msgid="4842812921719153085">"Утримуйте кнопку живлення, щоб переглянути нові елементи керування"</string>
<string name="controls_menu_add" msgid="4447246119229920050">"Додати елементи керування"</string>
<string name="controls_menu_edit" msgid="890623986951347062">"Змінити елементи керування"</string>
+ <string name="media_output_dialog_add_output" msgid="5642703238877329518">"Додати пристрої виводу"</string>
+ <string name="media_output_dialog_group" msgid="5571251347877452212">"Група"</string>
+ <string name="media_output_dialog_single_device" msgid="3102758980643351058">"Вибрано 1 пристрій"</string>
+ <string name="media_output_dialog_multiple_devices" msgid="1093771040315422350">"Вибрано пристроїв: <xliff:g id="COUNT">%1$d</xliff:g>"</string>
+ <string name="media_output_dialog_disconnected" msgid="1834473104836986046">"<xliff:g id="DEVICE_NAME">%1$s</xliff:g> (відключено)"</string>
+ <string name="media_output_dialog_connect_failed" msgid="3225190634236259010">"Не вдалося підключитися. Повторіть спробу."</string>
+ <string name="media_output_dialog_pairing_new" msgid="9099497976087485862">"Підключити новий пристрій"</string>
</resources>
diff --git a/packages/SystemUI/res/values-ur/strings.xml b/packages/SystemUI/res/values-ur/strings.xml
index d5f90195a4d9..4bd192fd63f0 100644
--- a/packages/SystemUI/res/values-ur/strings.xml
+++ b/packages/SystemUI/res/values-ur/strings.xml
@@ -879,12 +879,13 @@
</string-array>
<string name="tuner_low_priority" msgid="8412666814123009820">"کم ترجیحی اطلاع کے آئیکنز دکھائیں"</string>
<string name="other" msgid="429768510980739978">"دیگر"</string>
- <string name="accessibility_qs_edit_tile_label" msgid="9079791448815232967">"پوزیشن <xliff:g id="POSITION">%1$d</xliff:g>، <xliff:g id="TILE_NAME">%2$s</xliff:g>۔ ترمیم کرنے کیلئے دو بار تھپتھپائیں۔"</string>
- <string name="accessibility_qs_edit_add_tile_label" msgid="8292218072049068613">"<xliff:g id="TILE_NAME">%1$s</xliff:g>۔ شامل کرنے کیلئے دو بار تھپتھپائیں۔"</string>
- <string name="accessibility_qs_edit_move_tile" msgid="6027997446473163426">"<xliff:g id="TILE_NAME">%1$s</xliff:g> کو منتقل کریں"</string>
- <string name="accessibility_qs_edit_remove_tile" msgid="3406781901949899624">"<xliff:g id="TILE_NAME">%1$s</xliff:g> ہٹائیں"</string>
- <string name="accessibility_qs_edit_tile_add" msgid="6289879620154587233">"پوزیشن <xliff:g id="TILE_NAME">%1$s</xliff:g> میں <xliff:g id="POSITION">%2$d</xliff:g> شامل کریں"</string>
- <string name="accessibility_qs_edit_tile_move" msgid="4841770637244326837">"<xliff:g id="TILE_NAME">%1$s</xliff:g> کو پوزیشن <xliff:g id="POSITION">%2$d</xliff:g> میں منتقل کریں"</string>
+ <string name="accessibility_qs_edit_remove_tile_action" msgid="775511891457193480">"ٹائل ہٹائیں"</string>
+ <string name="accessibility_qs_edit_tile_add_action" msgid="5051211910345301833">"ختم کرنے کے لیے ٹائل شامل کریں"</string>
+ <string name="accessibility_qs_edit_tile_start_move" msgid="2009373939914517817">"ٹائل منتقل کریں"</string>
+ <string name="accessibility_qs_edit_tile_start_add" msgid="7560798153975555772">"ٹائل شامل کریں"</string>
+ <string name="accessibility_qs_edit_tile_move_to_position" msgid="5198161544045930556">"<xliff:g id="POSITION">%1$d</xliff:g> میں منتقل کریں"</string>
+ <string name="accessibility_qs_edit_tile_add_to_position" msgid="9029163095148274690">"پوزیشن <xliff:g id="POSITION">%1$d</xliff:g> میں شامل کریں"</string>
+ <string name="accessibility_qs_edit_position" msgid="4509277359815711830">"پوزیشن <xliff:g id="POSITION">%1$d</xliff:g>"</string>
<string name="accessibility_desc_quick_settings_edit" msgid="741658939453595297">"فوری ترتیبات کا ایڈیٹر۔"</string>
<string name="accessibility_desc_notification_icon" msgid="7331265967584178674">"<xliff:g id="ID_1">%1$s</xliff:g> اطلاع: <xliff:g id="ID_2">%2$s</xliff:g>"</string>
<string name="accessibility_quick_settings_settings" msgid="7098489591715844713">"ترتیبات کھولیں۔"</string>
@@ -1067,4 +1068,11 @@
<string name="controls_added_tooltip" msgid="4842812921719153085">"نئے کنٹرولز دیکھنے کے لیے پاور بٹن کو دبائے رکھیں"</string>
<string name="controls_menu_add" msgid="4447246119229920050">"کنٹرولز شامل کریں"</string>
<string name="controls_menu_edit" msgid="890623986951347062">"کنٹرولز میں ترمیم کریں"</string>
+ <string name="media_output_dialog_add_output" msgid="5642703238877329518">"آؤٹ پٹس شامل کریں"</string>
+ <string name="media_output_dialog_group" msgid="5571251347877452212">"گروپ"</string>
+ <string name="media_output_dialog_single_device" msgid="3102758980643351058">"1 آلہ منتخب کیا گیا"</string>
+ <string name="media_output_dialog_multiple_devices" msgid="1093771040315422350">"<xliff:g id="COUNT">%1$d</xliff:g> آلات منتخب کیے گئے"</string>
+ <string name="media_output_dialog_disconnected" msgid="1834473104836986046">"<xliff:g id="DEVICE_NAME">%1$s</xliff:g> (غیر منسلک ہو گیا)"</string>
+ <string name="media_output_dialog_connect_failed" msgid="3225190634236259010">"منسلک نہیں ہو سکا۔ پھر کوشش کریں۔"</string>
+ <string name="media_output_dialog_pairing_new" msgid="9099497976087485862">"نئے آلہ کا جوڑا بنائیں"</string>
</resources>
diff --git a/packages/SystemUI/res/values-uz/strings.xml b/packages/SystemUI/res/values-uz/strings.xml
index 71703e05ea29..7c3728abab78 100644
--- a/packages/SystemUI/res/values-uz/strings.xml
+++ b/packages/SystemUI/res/values-uz/strings.xml
@@ -879,12 +879,13 @@
</string-array>
<string name="tuner_low_priority" msgid="8412666814123009820">"Muhim boʻlmagan bildirishnoma ikonkalarini koʻrsatish"</string>
<string name="other" msgid="429768510980739978">"Boshqa"</string>
- <string name="accessibility_qs_edit_tile_label" msgid="9079791448815232967">"<xliff:g id="POSITION">%1$d</xliff:g>-joy, “<xliff:g id="TILE_NAME">%2$s</xliff:g>” tugmasi. Tahrirlash uchun ustiga ikki marta bosing."</string>
- <string name="accessibility_qs_edit_add_tile_label" msgid="8292218072049068613">"“<xliff:g id="TILE_NAME">%1$s</xliff:g>” tugmasi. Qo‘shish uchun ustiga ikki marta bosing."</string>
- <string name="accessibility_qs_edit_move_tile" msgid="6027997446473163426">"“<xliff:g id="TILE_NAME">%1$s</xliff:g>” tugmasini ko‘chirish"</string>
- <string name="accessibility_qs_edit_remove_tile" msgid="3406781901949899624">"<xliff:g id="TILE_NAME">%1$s</xliff:g> tugmasini olib tashlash"</string>
- <string name="accessibility_qs_edit_tile_add" msgid="6289879620154587233">"<xliff:g id="POSITION">%2$d</xliff:g>-joyga buni qo‘shish: <xliff:g id="TILE_NAME">%1$s</xliff:g>"</string>
- <string name="accessibility_qs_edit_tile_move" msgid="4841770637244326837">"<xliff:g id="POSITION">%2$d</xliff:g>-joyga buni ko‘chirish: <xliff:g id="TILE_NAME">%1$s</xliff:g>"</string>
+ <string name="accessibility_qs_edit_remove_tile_action" msgid="775511891457193480">"katakchani olib tashlash"</string>
+ <string name="accessibility_qs_edit_tile_add_action" msgid="5051211910345301833">"oxiriga katakcha kiritish"</string>
+ <string name="accessibility_qs_edit_tile_start_move" msgid="2009373939914517817">"Katakchani boshqa joyga olish"</string>
+ <string name="accessibility_qs_edit_tile_start_add" msgid="7560798153975555772">"Katakcha kiritish"</string>
+ <string name="accessibility_qs_edit_tile_move_to_position" msgid="5198161544045930556">"Bu joyga olish: <xliff:g id="POSITION">%1$d</xliff:g>"</string>
+ <string name="accessibility_qs_edit_tile_add_to_position" msgid="9029163095148274690">"Bu joyga kiritish: <xliff:g id="POSITION">%1$d</xliff:g>"</string>
+ <string name="accessibility_qs_edit_position" msgid="4509277359815711830">"Joylashuv: <xliff:g id="POSITION">%1$d</xliff:g>"</string>
<string name="accessibility_desc_quick_settings_edit" msgid="741658939453595297">"Tezkor sozlamalar muharriri"</string>
<string name="accessibility_desc_notification_icon" msgid="7331265967584178674">"<xliff:g id="ID_1">%1$s</xliff:g> bildirishnomasi: <xliff:g id="ID_2">%2$s</xliff:g>"</string>
<string name="accessibility_quick_settings_settings" msgid="7098489591715844713">"Sozlamalarni ochish."</string>
@@ -1067,4 +1068,11 @@
<string name="controls_added_tooltip" msgid="4842812921719153085">"Yangi boshqaruv elementlari bilan tanishish uchun quvvat tugmasini bosib turing"</string>
<string name="controls_menu_add" msgid="4447246119229920050">"Element kiritish"</string>
<string name="controls_menu_edit" msgid="890623986951347062">"Elementlarni tahrirlash"</string>
+ <string name="media_output_dialog_add_output" msgid="5642703238877329518">"Chiquvchi qurilmani kiritish"</string>
+ <string name="media_output_dialog_group" msgid="5571251347877452212">"Guruh"</string>
+ <string name="media_output_dialog_single_device" msgid="3102758980643351058">"1 ta qurilma tanlandi"</string>
+ <string name="media_output_dialog_multiple_devices" msgid="1093771040315422350">"<xliff:g id="COUNT">%1$d</xliff:g> ta qurilma tanlandi"</string>
+ <string name="media_output_dialog_disconnected" msgid="1834473104836986046">"<xliff:g id="DEVICE_NAME">%1$s</xliff:g> (uzilgan)"</string>
+ <string name="media_output_dialog_connect_failed" msgid="3225190634236259010">"Ulanmadi. Qayta urining."</string>
+ <string name="media_output_dialog_pairing_new" msgid="9099497976087485862">"Yangi qurilmani ulash"</string>
</resources>
diff --git a/packages/SystemUI/res/values-vi/strings.xml b/packages/SystemUI/res/values-vi/strings.xml
index 12eb5e12f2a6..f23f7424a073 100644
--- a/packages/SystemUI/res/values-vi/strings.xml
+++ b/packages/SystemUI/res/values-vi/strings.xml
@@ -879,12 +879,13 @@
</string-array>
<string name="tuner_low_priority" msgid="8412666814123009820">"Hiển thị biểu tượng thông báo có mức ưu tiên thấp"</string>
<string name="other" msgid="429768510980739978">"Khác"</string>
- <string name="accessibility_qs_edit_tile_label" msgid="9079791448815232967">"Vị trí <xliff:g id="POSITION">%1$d</xliff:g>, <xliff:g id="TILE_NAME">%2$s</xliff:g>. Nhấn đúp để chỉnh sửa."</string>
- <string name="accessibility_qs_edit_add_tile_label" msgid="8292218072049068613">"<xliff:g id="TILE_NAME">%1$s</xliff:g>. Nhấn đúp để thêm."</string>
- <string name="accessibility_qs_edit_move_tile" msgid="6027997446473163426">"Di chuyển <xliff:g id="TILE_NAME">%1$s</xliff:g>"</string>
- <string name="accessibility_qs_edit_remove_tile" msgid="3406781901949899624">"Xóa <xliff:g id="TILE_NAME">%1$s</xliff:g>"</string>
- <string name="accessibility_qs_edit_tile_add" msgid="6289879620154587233">"Thêm <xliff:g id="TILE_NAME">%1$s</xliff:g> vào vị trí <xliff:g id="POSITION">%2$d</xliff:g>"</string>
- <string name="accessibility_qs_edit_tile_move" msgid="4841770637244326837">"Di chuyển <xliff:g id="TILE_NAME">%1$s</xliff:g> đến vị trí <xliff:g id="POSITION">%2$d</xliff:g>"</string>
+ <string name="accessibility_qs_edit_remove_tile_action" msgid="775511891457193480">"xóa ô"</string>
+ <string name="accessibility_qs_edit_tile_add_action" msgid="5051211910345301833">"thêm ô vào cuối"</string>
+ <string name="accessibility_qs_edit_tile_start_move" msgid="2009373939914517817">"Di chuyển ô"</string>
+ <string name="accessibility_qs_edit_tile_start_add" msgid="7560798153975555772">"Thêm ô"</string>
+ <string name="accessibility_qs_edit_tile_move_to_position" msgid="5198161544045930556">"Di chuyển tới <xliff:g id="POSITION">%1$d</xliff:g>"</string>
+ <string name="accessibility_qs_edit_tile_add_to_position" msgid="9029163095148274690">"Thêm vào vị trí <xliff:g id="POSITION">%1$d</xliff:g>"</string>
+ <string name="accessibility_qs_edit_position" msgid="4509277359815711830">"Vị trí <xliff:g id="POSITION">%1$d</xliff:g>"</string>
<string name="accessibility_desc_quick_settings_edit" msgid="741658939453595297">"Trình chỉnh sửa cài đặt nhanh."</string>
<string name="accessibility_desc_notification_icon" msgid="7331265967584178674">"Thông báo của <xliff:g id="ID_1">%1$s</xliff:g>: <xliff:g id="ID_2">%2$s</xliff:g>"</string>
<string name="accessibility_quick_settings_settings" msgid="7098489591715844713">"Mở phần cài đặt."</string>
@@ -1067,4 +1068,11 @@
<string name="controls_added_tooltip" msgid="4842812921719153085">"Giữ nút Nguồn để xem các tùy chọn điều khiển mới"</string>
<string name="controls_menu_add" msgid="4447246119229920050">"Thêm các tùy chọn điều khiển"</string>
<string name="controls_menu_edit" msgid="890623986951347062">"Chỉnh sửa tùy chọn điều khiển"</string>
+ <string name="media_output_dialog_add_output" msgid="5642703238877329518">"Thêm thiết bị đầu ra"</string>
+ <string name="media_output_dialog_group" msgid="5571251347877452212">"Nhóm"</string>
+ <string name="media_output_dialog_single_device" msgid="3102758980643351058">"Đã chọn 1 thiết bị"</string>
+ <string name="media_output_dialog_multiple_devices" msgid="1093771040315422350">"Đã chọn <xliff:g id="COUNT">%1$d</xliff:g> thiết bị"</string>
+ <string name="media_output_dialog_disconnected" msgid="1834473104836986046">"<xliff:g id="DEVICE_NAME">%1$s</xliff:g> (đã ngắt kết nối)"</string>
+ <string name="media_output_dialog_connect_failed" msgid="3225190634236259010">"Không thể kết nối. Hãy thử lại."</string>
+ <string name="media_output_dialog_pairing_new" msgid="9099497976087485862">"Ghép nối thiết bị mới"</string>
</resources>
diff --git a/packages/SystemUI/res/values-zh-rCN/strings.xml b/packages/SystemUI/res/values-zh-rCN/strings.xml
index f886ecb6cf39..9b81cb04db1a 100644
--- a/packages/SystemUI/res/values-zh-rCN/strings.xml
+++ b/packages/SystemUI/res/values-zh-rCN/strings.xml
@@ -879,12 +879,13 @@
</string-array>
<string name="tuner_low_priority" msgid="8412666814123009820">"显示低优先级的通知图标"</string>
<string name="other" msgid="429768510980739978">"其他"</string>
- <string name="accessibility_qs_edit_tile_label" msgid="9079791448815232967">"位置 <xliff:g id="POSITION">%1$d</xliff:g>,<xliff:g id="TILE_NAME">%2$s</xliff:g>。点按两次即可修改。"</string>
- <string name="accessibility_qs_edit_add_tile_label" msgid="8292218072049068613">"<xliff:g id="TILE_NAME">%1$s</xliff:g>。点按两次即可添加。"</string>
- <string name="accessibility_qs_edit_move_tile" msgid="6027997446473163426">"移动<xliff:g id="TILE_NAME">%1$s</xliff:g>"</string>
- <string name="accessibility_qs_edit_remove_tile" msgid="3406781901949899624">"移除<xliff:g id="TILE_NAME">%1$s</xliff:g>"</string>
- <string name="accessibility_qs_edit_tile_add" msgid="6289879620154587233">"将“<xliff:g id="TILE_NAME">%1$s</xliff:g>”添加到位置 <xliff:g id="POSITION">%2$d</xliff:g>"</string>
- <string name="accessibility_qs_edit_tile_move" msgid="4841770637244326837">"将“<xliff:g id="TILE_NAME">%1$s</xliff:g>”移动到位置 <xliff:g id="POSITION">%2$d</xliff:g>"</string>
+ <string name="accessibility_qs_edit_remove_tile_action" msgid="775511891457193480">"移除图块"</string>
+ <string name="accessibility_qs_edit_tile_add_action" msgid="5051211910345301833">"将图块添加到末尾"</string>
+ <string name="accessibility_qs_edit_tile_start_move" msgid="2009373939914517817">"移动图块"</string>
+ <string name="accessibility_qs_edit_tile_start_add" msgid="7560798153975555772">"添加图块"</string>
+ <string name="accessibility_qs_edit_tile_move_to_position" msgid="5198161544045930556">"移至 <xliff:g id="POSITION">%1$d</xliff:g>"</string>
+ <string name="accessibility_qs_edit_tile_add_to_position" msgid="9029163095148274690">"添加到位置 <xliff:g id="POSITION">%1$d</xliff:g>"</string>
+ <string name="accessibility_qs_edit_position" msgid="4509277359815711830">"位置 <xliff:g id="POSITION">%1$d</xliff:g>"</string>
<string name="accessibility_desc_quick_settings_edit" msgid="741658939453595297">"快捷设置编辑器。"</string>
<string name="accessibility_desc_notification_icon" msgid="7331265967584178674">"<xliff:g id="ID_1">%1$s</xliff:g>通知:<xliff:g id="ID_2">%2$s</xliff:g>"</string>
<string name="accessibility_quick_settings_settings" msgid="7098489591715844713">"打开设置。"</string>
@@ -1067,4 +1068,11 @@
<string name="controls_added_tooltip" msgid="4842812921719153085">"按住电源按钮即可查看新控件"</string>
<string name="controls_menu_add" msgid="4447246119229920050">"添加控件"</string>
<string name="controls_menu_edit" msgid="890623986951347062">"修改控件"</string>
+ <string name="media_output_dialog_add_output" msgid="5642703238877329518">"添加输出设备"</string>
+ <string name="media_output_dialog_group" msgid="5571251347877452212">"群组"</string>
+ <string name="media_output_dialog_single_device" msgid="3102758980643351058">"已选择 1 个设备"</string>
+ <string name="media_output_dialog_multiple_devices" msgid="1093771040315422350">"已选择 <xliff:g id="COUNT">%1$d</xliff:g> 个设备"</string>
+ <string name="media_output_dialog_disconnected" msgid="1834473104836986046">"<xliff:g id="DEVICE_NAME">%1$s</xliff:g>(已断开连接)"</string>
+ <string name="media_output_dialog_connect_failed" msgid="3225190634236259010">"无法连接。请重试。"</string>
+ <string name="media_output_dialog_pairing_new" msgid="9099497976087485862">"与新设备配对"</string>
</resources>
diff --git a/packages/SystemUI/res/values-zh-rHK/strings.xml b/packages/SystemUI/res/values-zh-rHK/strings.xml
index 744d341deb08..facb47a3bdfc 100644
--- a/packages/SystemUI/res/values-zh-rHK/strings.xml
+++ b/packages/SystemUI/res/values-zh-rHK/strings.xml
@@ -716,7 +716,7 @@
<string name="notification_channel_summary_default_with_bubbles" msgid="1782419896613644568">"可能會根據手機設定發出鈴聲或震動。「<xliff:g id="APP_NAME">%1$s</xliff:g>」的對話會預設以對話氣泡顯示。"</string>
<string name="notification_channel_summary_bubble" msgid="7235935211580860537">"為此內容建立浮動捷徑以保持注意力。"</string>
<string name="notification_channel_summary_automatic" msgid="5813109268050235275">"由系統判斷是否要讓此通知發出音效或震動"</string>
- <string name="notification_channel_summary_priority" msgid="7952654515769021553">"在對話部分的頂部以浮動對話氣泡顯示,並在上鎖畫面顯示個人檔案相片"</string>
+ <string name="notification_channel_summary_priority" msgid="7952654515769021553">"以浮動對話泡顯示在對話部分的頂部,並在上鎖畫面顯示個人檔案相片"</string>
<string name="notification_conversation_channel_settings" msgid="2409977688430606835">"設定"</string>
<string name="notification_priority_title" msgid="2079708866333537093">"重要"</string>
<string name="no_shortcut" msgid="8257177117568230126">"「<xliff:g id="APP_NAME">%1$s</xliff:g>」不支援對話功能"</string>
@@ -879,12 +879,13 @@
</string-array>
<string name="tuner_low_priority" msgid="8412666814123009820">"顯示低優先順序通知圖示"</string>
<string name="other" msgid="429768510980739978">"其他"</string>
- <string name="accessibility_qs_edit_tile_label" msgid="9079791448815232967">"位置 <xliff:g id="POSITION">%1$d</xliff:g>,<xliff:g id="TILE_NAME">%2$s</xliff:g>。輕按兩下即可編輯。"</string>
- <string name="accessibility_qs_edit_add_tile_label" msgid="8292218072049068613">"<xliff:g id="TILE_NAME">%1$s</xliff:g>。輕按兩下即可新增。"</string>
- <string name="accessibility_qs_edit_move_tile" msgid="6027997446473163426">"移動 <xliff:g id="TILE_NAME">%1$s</xliff:g>"</string>
- <string name="accessibility_qs_edit_remove_tile" msgid="3406781901949899624">"移除 <xliff:g id="TILE_NAME">%1$s</xliff:g>"</string>
- <string name="accessibility_qs_edit_tile_add" msgid="6289879620154587233">"將「<xliff:g id="TILE_NAME">%1$s</xliff:g>」加去位置 <xliff:g id="POSITION">%2$d</xliff:g>"</string>
- <string name="accessibility_qs_edit_tile_move" msgid="4841770637244326837">"將「<xliff:g id="TILE_NAME">%1$s</xliff:g>」移去位置 <xliff:g id="POSITION">%2$d</xliff:g>"</string>
+ <string name="accessibility_qs_edit_remove_tile_action" msgid="775511891457193480">"移除圖塊"</string>
+ <string name="accessibility_qs_edit_tile_add_action" msgid="5051211910345301833">"將圖塊加到結尾"</string>
+ <string name="accessibility_qs_edit_tile_start_move" msgid="2009373939914517817">"移圖塊"</string>
+ <string name="accessibility_qs_edit_tile_start_add" msgid="7560798153975555772">"加圖塊"</string>
+ <string name="accessibility_qs_edit_tile_move_to_position" msgid="5198161544045930556">"移去 <xliff:g id="POSITION">%1$d</xliff:g>"</string>
+ <string name="accessibility_qs_edit_tile_add_to_position" msgid="9029163095148274690">"加去位置 <xliff:g id="POSITION">%1$d</xliff:g>"</string>
+ <string name="accessibility_qs_edit_position" msgid="4509277359815711830">"位置 <xliff:g id="POSITION">%1$d</xliff:g>"</string>
<string name="accessibility_desc_quick_settings_edit" msgid="741658939453595297">"快速設定編輯工具。"</string>
<string name="accessibility_desc_notification_icon" msgid="7331265967584178674">"<xliff:g id="ID_1">%1$s</xliff:g> 通知:<xliff:g id="ID_2">%2$s</xliff:g>"</string>
<string name="accessibility_quick_settings_settings" msgid="7098489591715844713">"開啟設定。"</string>
@@ -967,7 +968,7 @@
<string name="auto_saver_enabled_text" msgid="7889491183116752719">"省電模式將會在電量低於 <xliff:g id="PERCENTAGE">%d</xliff:g>%% 時自動開啟。"</string>
<string name="open_saver_setting_action" msgid="2111461909782935190">"設定"</string>
<string name="auto_saver_okay_action" msgid="7815925750741935386">"知道了"</string>
- <string name="heap_dump_tile_name" msgid="2464189856478823046">"轉儲 SysUI 堆"</string>
+ <string name="heap_dump_tile_name" msgid="2464189856478823046">"Dump SysUI Heap"</string>
<string name="ongoing_privacy_chip_content_single_app" msgid="2969750601815230385">"「<xliff:g id="APP">%1$s</xliff:g>」正在使用<xliff:g id="TYPES_LIST">%2$s</xliff:g>。"</string>
<string name="ongoing_privacy_chip_content_multiple_apps" msgid="8341216022442383954">"有多個應用程式正在使用<xliff:g id="TYPES_LIST">%s</xliff:g>。"</string>
<string name="ongoing_privacy_dialog_separator" msgid="1866222499727706187">"、 "</string>
@@ -1067,4 +1068,11 @@
<string name="controls_added_tooltip" msgid="4842812921719153085">"按住「開關」按鈕以查看新控制項"</string>
<string name="controls_menu_add" msgid="4447246119229920050">"新增控制項"</string>
<string name="controls_menu_edit" msgid="890623986951347062">"編輯控制項"</string>
+ <string name="media_output_dialog_add_output" msgid="5642703238877329518">"新增輸出裝置"</string>
+ <string name="media_output_dialog_group" msgid="5571251347877452212">"群組"</string>
+ <string name="media_output_dialog_single_device" msgid="3102758980643351058">"已選取 1 部裝置"</string>
+ <string name="media_output_dialog_multiple_devices" msgid="1093771040315422350">"已選取 <xliff:g id="COUNT">%1$d</xliff:g> 部裝置"</string>
+ <string name="media_output_dialog_disconnected" msgid="1834473104836986046">"<xliff:g id="DEVICE_NAME">%1$s</xliff:g> (已中斷連線)"</string>
+ <string name="media_output_dialog_connect_failed" msgid="3225190634236259010">"無法連線,請再試一次。"</string>
+ <string name="media_output_dialog_pairing_new" msgid="9099497976087485862">"配對新裝置"</string>
</resources>
diff --git a/packages/SystemUI/res/values-zh-rTW/strings.xml b/packages/SystemUI/res/values-zh-rTW/strings.xml
index 49d530f0a7af..4e4db70ddd67 100644
--- a/packages/SystemUI/res/values-zh-rTW/strings.xml
+++ b/packages/SystemUI/res/values-zh-rTW/strings.xml
@@ -711,7 +711,7 @@
<string name="notification_bubble_title" msgid="8330481035191903164">"泡泡"</string>
<string name="notification_automatic_title" msgid="3745465364578762652">"自動"</string>
<string name="notification_channel_summary_low" msgid="4860617986908931158">"不震動或發出聲音"</string>
- <string name="notification_conversation_summary_low" msgid="1734433426085468009">"不震動或發出聲音,並顯示在對話部分的下方"</string>
+ <string name="notification_conversation_summary_low" msgid="1734433426085468009">"不震動或發出聲音,並調整排序到其他對話下方"</string>
<string name="notification_channel_summary_default" msgid="3282930979307248890">"根據手機的設定響鈴或震動"</string>
<string name="notification_channel_summary_default_with_bubbles" msgid="1782419896613644568">"可能會根據手機的設定響鈴或震動。根據預設,來自「<xliff:g id="APP_NAME">%1$s</xliff:g>」的對話會以對話框形式顯示。"</string>
<string name="notification_channel_summary_bubble" msgid="7235935211580860537">"利用浮動式捷徑快速存取這項內容。"</string>
@@ -879,12 +879,13 @@
</string-array>
<string name="tuner_low_priority" msgid="8412666814123009820">"顯示低優先順序通知圖示"</string>
<string name="other" msgid="429768510980739978">"其他"</string>
- <string name="accessibility_qs_edit_tile_label" msgid="9079791448815232967">"位置 <xliff:g id="POSITION">%1$d</xliff:g>,<xliff:g id="TILE_NAME">%2$s</xliff:g>。輕觸兩下即可編輯。"</string>
- <string name="accessibility_qs_edit_add_tile_label" msgid="8292218072049068613">"<xliff:g id="TILE_NAME">%1$s</xliff:g>。輕觸兩下即可新增。"</string>
- <string name="accessibility_qs_edit_move_tile" msgid="6027997446473163426">"移動 <xliff:g id="TILE_NAME">%1$s</xliff:g>"</string>
- <string name="accessibility_qs_edit_remove_tile" msgid="3406781901949899624">"移除 <xliff:g id="TILE_NAME">%1$s</xliff:g>"</string>
- <string name="accessibility_qs_edit_tile_add" msgid="6289879620154587233">"將 <xliff:g id="TILE_NAME">%1$s</xliff:g> 新增到位置 <xliff:g id="POSITION">%2$d</xliff:g>"</string>
- <string name="accessibility_qs_edit_tile_move" msgid="4841770637244326837">"將 <xliff:g id="TILE_NAME">%1$s</xliff:g> 移到位置 <xliff:g id="POSITION">%2$d</xliff:g>"</string>
+ <string name="accessibility_qs_edit_remove_tile_action" msgid="775511891457193480">"移除圖塊"</string>
+ <string name="accessibility_qs_edit_tile_add_action" msgid="5051211910345301833">"將圖塊加到結尾處"</string>
+ <string name="accessibility_qs_edit_tile_start_move" msgid="2009373939914517817">"移動圖塊"</string>
+ <string name="accessibility_qs_edit_tile_start_add" msgid="7560798153975555772">"新增圖塊"</string>
+ <string name="accessibility_qs_edit_tile_move_to_position" msgid="5198161544045930556">"移至 <xliff:g id="POSITION">%1$d</xliff:g>"</string>
+ <string name="accessibility_qs_edit_tile_add_to_position" msgid="9029163095148274690">"新增到位置 <xliff:g id="POSITION">%1$d</xliff:g>"</string>
+ <string name="accessibility_qs_edit_position" msgid="4509277359815711830">"位置 <xliff:g id="POSITION">%1$d</xliff:g>"</string>
<string name="accessibility_desc_quick_settings_edit" msgid="741658939453595297">"快速設定編輯器。"</string>
<string name="accessibility_desc_notification_icon" msgid="7331265967584178674">"<xliff:g id="ID_1">%1$s</xliff:g> 通知:<xliff:g id="ID_2">%2$s</xliff:g>"</string>
<string name="accessibility_quick_settings_settings" msgid="7098489591715844713">"開啟設定。"</string>
@@ -967,7 +968,7 @@
<string name="auto_saver_enabled_text" msgid="7889491183116752719">"省電模式會在電量低於 <xliff:g id="PERCENTAGE">%d</xliff:g>%% 時自動開啟。"</string>
<string name="open_saver_setting_action" msgid="2111461909782935190">"設定"</string>
<string name="auto_saver_okay_action" msgid="7815925750741935386">"我知道了"</string>
- <string name="heap_dump_tile_name" msgid="2464189856478823046">"傾印 SysUI 記憶體快照"</string>
+ <string name="heap_dump_tile_name" msgid="2464189856478823046">"Dump SysUI Heap"</string>
<string name="ongoing_privacy_chip_content_single_app" msgid="2969750601815230385">"「<xliff:g id="APP">%1$s</xliff:g>」正在使用<xliff:g id="TYPES_LIST">%2$s</xliff:g>。"</string>
<string name="ongoing_privacy_chip_content_multiple_apps" msgid="8341216022442383954">"有多個應用程式正在使用<xliff:g id="TYPES_LIST">%s</xliff:g>。"</string>
<string name="ongoing_privacy_dialog_separator" msgid="1866222499727706187">"、 "</string>
@@ -1067,4 +1068,11 @@
<string name="controls_added_tooltip" msgid="4842812921719153085">"按住電源按鈕即可查看新的控制項"</string>
<string name="controls_menu_add" msgid="4447246119229920050">"新增控制項"</string>
<string name="controls_menu_edit" msgid="890623986951347062">"編輯控制項"</string>
+ <string name="media_output_dialog_add_output" msgid="5642703238877329518">"新增輸出裝置"</string>
+ <string name="media_output_dialog_group" msgid="5571251347877452212">"群組"</string>
+ <string name="media_output_dialog_single_device" msgid="3102758980643351058">"已選取 1 部裝置"</string>
+ <string name="media_output_dialog_multiple_devices" msgid="1093771040315422350">"已選取 <xliff:g id="COUNT">%1$d</xliff:g> 部裝置"</string>
+ <string name="media_output_dialog_disconnected" msgid="1834473104836986046">"<xliff:g id="DEVICE_NAME">%1$s</xliff:g> (已中斷連線)"</string>
+ <string name="media_output_dialog_connect_failed" msgid="3225190634236259010">"無法連線,請再試一次。"</string>
+ <string name="media_output_dialog_pairing_new" msgid="9099497976087485862">"配對新裝置"</string>
</resources>
diff --git a/packages/SystemUI/res/values-zu/strings.xml b/packages/SystemUI/res/values-zu/strings.xml
index abc45c84ccd0..65e9a1c6a5e0 100644
--- a/packages/SystemUI/res/values-zu/strings.xml
+++ b/packages/SystemUI/res/values-zu/strings.xml
@@ -879,12 +879,13 @@
</string-array>
<string name="tuner_low_priority" msgid="8412666814123009820">"Bonisa izithonjana zesaziso zokubaluleka okuncane"</string>
<string name="other" msgid="429768510980739978">"Okunye"</string>
- <string name="accessibility_qs_edit_tile_label" msgid="9079791448815232967">"Isimo esingu-<xliff:g id="POSITION">%1$d</xliff:g>, <xliff:g id="TILE_NAME">%2$s</xliff:g>. Thepha kabili ukuze uhlele."</string>
- <string name="accessibility_qs_edit_add_tile_label" msgid="8292218072049068613">"<xliff:g id="TILE_NAME">%1$s</xliff:g>. Thepha kabili ukuze ungeze."</string>
- <string name="accessibility_qs_edit_move_tile" msgid="6027997446473163426">"Hambisa i-<xliff:g id="TILE_NAME">%1$s</xliff:g>"</string>
- <string name="accessibility_qs_edit_remove_tile" msgid="3406781901949899624">"Susa i-<xliff:g id="TILE_NAME">%1$s</xliff:g>"</string>
- <string name="accessibility_qs_edit_tile_add" msgid="6289879620154587233">"Engeza i-<xliff:g id="TILE_NAME">%1$s</xliff:g> ukuze ubeke i-<xliff:g id="POSITION">%2$d</xliff:g>"</string>
- <string name="accessibility_qs_edit_tile_move" msgid="4841770637244326837">"Hambisa i-<xliff:g id="TILE_NAME">%1$s</xliff:g> ukuze ubeke i-<xliff:g id="POSITION">%2$d</xliff:g>"</string>
+ <string name="accessibility_qs_edit_remove_tile_action" msgid="775511891457193480">"susa ithayela"</string>
+ <string name="accessibility_qs_edit_tile_add_action" msgid="5051211910345301833">"engeza ithayela ekugcineni"</string>
+ <string name="accessibility_qs_edit_tile_start_move" msgid="2009373939914517817">"Hambisa ithayela"</string>
+ <string name="accessibility_qs_edit_tile_start_add" msgid="7560798153975555772">"Engeza ithayela"</string>
+ <string name="accessibility_qs_edit_tile_move_to_position" msgid="5198161544045930556">"Hambisa ku-<xliff:g id="POSITION">%1$d</xliff:g>"</string>
+ <string name="accessibility_qs_edit_tile_add_to_position" msgid="9029163095148274690">"Engeza kusikhundla se-<xliff:g id="POSITION">%1$d</xliff:g>"</string>
+ <string name="accessibility_qs_edit_position" msgid="4509277359815711830">"Isikhundla se-<xliff:g id="POSITION">%1$d</xliff:g>"</string>
<string name="accessibility_desc_quick_settings_edit" msgid="741658939453595297">"Isihleli sezilungiselelo ezisheshayo."</string>
<string name="accessibility_desc_notification_icon" msgid="7331265967584178674">"<xliff:g id="ID_1">%1$s</xliff:g> isaziso: <xliff:g id="ID_2">%2$s</xliff:g>"</string>
<string name="accessibility_quick_settings_settings" msgid="7098489591715844713">"Vula izilungiselelo."</string>
@@ -1067,4 +1068,11 @@
<string name="controls_added_tooltip" msgid="4842812921719153085">"Bamba Inkinobho yamandla ukuze ubone izilawuli ezintsha"</string>
<string name="controls_menu_add" msgid="4447246119229920050">"Engeza Izilawuli"</string>
<string name="controls_menu_edit" msgid="890623986951347062">"Hlela izilawuli"</string>
+ <string name="media_output_dialog_add_output" msgid="5642703238877329518">"Engeza okukhiphayo"</string>
+ <string name="media_output_dialog_group" msgid="5571251347877452212">"Iqembu"</string>
+ <string name="media_output_dialog_single_device" msgid="3102758980643351058">"idivayisi ekhethiwe e-1"</string>
+ <string name="media_output_dialog_multiple_devices" msgid="1093771040315422350">"amadivayisi akhethiwe angu-<xliff:g id="COUNT">%1$d</xliff:g>"</string>
+ <string name="media_output_dialog_disconnected" msgid="1834473104836986046">"<xliff:g id="DEVICE_NAME">%1$s</xliff:g> (inqamukile)"</string>
+ <string name="media_output_dialog_connect_failed" msgid="3225190634236259010">"Ayikwazanga ukuxhumeka. Zama futhi."</string>
+ <string name="media_output_dialog_pairing_new" msgid="9099497976087485862">"Bhanqa idivayisi entsha"</string>
</resources>
diff --git a/packages/SystemUI/res/values/colors_tv.xml b/packages/SystemUI/res/values/colors_tv.xml
index 22b09b31b867..23ca24a6c9a1 100644
--- a/packages/SystemUI/res/values/colors_tv.xml
+++ b/packages/SystemUI/res/values/colors_tv.xml
@@ -22,8 +22,6 @@
<color name="recents_tv_dismiss_text_color">#7FEEEEEE</color>
<color name="recents_tv_text_shadow_color">#7F000000</color>
- <!-- Background color for audio recording indicator (G800) -->
- <color name="tv_audio_recording_indicator_background">#FF3C4043</color>
<color name="tv_audio_recording_indicator_icon_background">#CC000000</color>
<color name="tv_audio_recording_indicator_stroke">#33FFFFFF</color>
diff --git a/packages/SystemUI/res/values/dimens.xml b/packages/SystemUI/res/values/dimens.xml
index 98e8cde40275..d5362baaacf0 100644
--- a/packages/SystemUI/res/values/dimens.xml
+++ b/packages/SystemUI/res/values/dimens.xml
@@ -226,6 +226,8 @@
<dimen name="notification_guts_conversation_action_text_padding_start">32dp</dimen>
<dimen name="conversation_onboarding_bullet_gap_width">6dp</dimen>
+ <dimen name="notification_guts_header_top_padding">11dp</dimen>
+
<!-- The height of the header in inline settings -->
<dimen name="notification_guts_header_height">24dp</dimen>
@@ -1368,9 +1370,10 @@
<dimen name="config_rounded_mask_size_bottom">@*android:dimen/rounded_corner_radius_bottom</dimen>
<!-- Output switcher panel related dimensions -->
- <dimen name="media_output_dialog_padding_top">11dp</dimen>
+ <dimen name="media_output_dialog_list_margin">12dp</dimen>
<dimen name="media_output_dialog_list_max_height">364dp</dimen>
<dimen name="media_output_dialog_header_album_icon_size">52dp</dimen>
<dimen name="media_output_dialog_header_back_icon_size">36dp</dimen>
+ <dimen name="media_output_dialog_header_icon_padding">16dp</dimen>
<dimen name="media_output_dialog_icon_corner_radius">16dp</dimen>
</resources>
diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/system/WindowManagerWrapper.java b/packages/SystemUI/shared/src/com/android/systemui/shared/system/WindowManagerWrapper.java
index 067ac9ec7b1e..e5f2ad5b7586 100644
--- a/packages/SystemUI/shared/src/com/android/systemui/shared/system/WindowManagerWrapper.java
+++ b/packages/SystemUI/shared/src/com/android/systemui/shared/system/WindowManagerWrapper.java
@@ -33,7 +33,6 @@ import android.view.WindowManagerGlobal;
import com.android.systemui.shared.recents.view.AppTransitionAnimationSpecsFuture;
import com.android.systemui.shared.recents.view.RecentsTransition;
-import com.android.systemui.shared.system.PinnedStackListenerForwarder.PinnedStackListener;
public class WindowManagerWrapper {
@@ -75,7 +74,7 @@ public class WindowManagerWrapper {
WindowConfiguration.WINDOWING_MODE_FULLSCREEN;
public static final int WINDOWING_MODE_MULTI_WINDOW =
WindowConfiguration.WINDOWING_MODE_MULTI_WINDOW;
- public static final int WINDOWING_MODE_PINNED = WindowConfiguration.WINDOWING_MODE_PINNED;
+
public static final int WINDOWING_MODE_SPLIT_SCREEN_PRIMARY =
WindowConfiguration.WINDOWING_MODE_SPLIT_SCREEN_PRIMARY;
public static final int WINDOWING_MODE_SPLIT_SCREEN_SECONDARY =
@@ -84,13 +83,6 @@ public class WindowManagerWrapper {
private static final WindowManagerWrapper sInstance = new WindowManagerWrapper();
- /**
- * Forwarder to which we can add multiple pinned stack listeners. Each listener will receive
- * updates from the window manager service.
- */
- private PinnedStackListenerForwarder mPinnedStackListenerForwarder =
- new PinnedStackListenerForwarder();
-
public static WindowManagerWrapper getInstance() {
return sInstance;
}
@@ -188,23 +180,6 @@ public class WindowManagerWrapper {
}
/**
- * Adds a pinned stack listener, which will receive updates from the window manager service
- * along with any other pinned stack listeners that were added via this method.
- */
- public void addPinnedStackListener(PinnedStackListener listener) throws RemoteException {
- mPinnedStackListenerForwarder.addListener(listener);
- WindowManagerGlobal.getWindowManagerService().registerPinnedStackListener(
- DEFAULT_DISPLAY, mPinnedStackListenerForwarder);
- }
-
- /**
- * Removes a pinned stack listener.
- */
- public void removePinnedStackListener(PinnedStackListener listener) {
- mPinnedStackListenerForwarder.removeListener(listener);
- }
-
- /**
* Mirrors a specified display. The SurfaceControl returned is the root of the mirrored
* hierarchy.
*
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityContainer.java b/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityContainer.java
index 05172279c4ed..81d37a830f8f 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityContainer.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityContainer.java
@@ -15,8 +15,6 @@
*/
package com.android.keyguard;
-import static android.view.ViewRootImpl.NEW_INSETS_MODE_FULL;
-import static android.view.ViewRootImpl.sNewInsetsMode;
import static android.view.WindowInsets.Type.ime;
import static android.view.WindowInsets.Type.systemBars;
import static android.view.WindowInsetsAnimation.Callback.DISPATCH_MODE_STOP;
@@ -516,14 +514,9 @@ public class KeyguardSecurityContainer extends FrameLayout implements KeyguardSe
public WindowInsets onApplyWindowInsets(WindowInsets insets) {
// Consume bottom insets because we're setting the padding locally (for IME and navbar.)
- int inset;
- if (sNewInsetsMode == NEW_INSETS_MODE_FULL) {
- int bottomInset = insets.getInsetsIgnoringVisibility(systemBars()).bottom;
- int imeInset = insets.getInsets(ime()).bottom;
- inset = max(bottomInset, imeInset);
- } else {
- inset = insets.getSystemWindowInsetBottom();
- }
+ int bottomInset = insets.getInsetsIgnoringVisibility(systemBars()).bottom;
+ int imeInset = insets.getInsets(ime()).bottom;
+ int inset = max(bottomInset, imeInset);
setPadding(getPaddingLeft(), getPaddingTop(), getPaddingRight(), inset);
return insets.inset(0, 0, 0, inset);
}
diff --git a/packages/SystemUI/src/com/android/systemui/Dependency.java b/packages/SystemUI/src/com/android/systemui/Dependency.java
index ed78c94d45f9..832edf719dbb 100644
--- a/packages/SystemUI/src/com/android/systemui/Dependency.java
+++ b/packages/SystemUI/src/com/android/systemui/Dependency.java
@@ -80,7 +80,6 @@ import com.android.systemui.statusbar.notification.NotificationFilter;
import com.android.systemui.statusbar.notification.collection.legacy.NotificationGroupManagerLegacy;
import com.android.systemui.statusbar.notification.collection.legacy.VisualStabilityManager;
import com.android.systemui.statusbar.notification.logging.NotificationLogger;
-import com.android.systemui.statusbar.notification.row.NotificationBlockingHelperManager;
import com.android.systemui.statusbar.notification.row.NotificationGutsManager;
import com.android.systemui.statusbar.phone.AutoHideController;
import com.android.systemui.statusbar.phone.DozeParameters;
@@ -280,7 +279,6 @@ public class Dependency {
@Inject Lazy<VisualStabilityManager> mVisualStabilityManager;
@Inject Lazy<NotificationGutsManager> mNotificationGutsManager;
@Inject Lazy<NotificationMediaManager> mNotificationMediaManager;
- @Inject Lazy<NotificationBlockingHelperManager> mNotificationBlockingHelperManager;
@Inject Lazy<NotificationRemoteInputManager> mNotificationRemoteInputManager;
@Inject Lazy<SmartReplyConstants> mSmartReplyConstants;
@Inject Lazy<NotificationListener> mNotificationListener;
@@ -473,8 +471,6 @@ public class Dependency {
mNotificationGroupAlertTransferHelper::get);
mProviders.put(NotificationMediaManager.class, mNotificationMediaManager::get);
mProviders.put(NotificationGutsManager.class, mNotificationGutsManager::get);
- mProviders.put(NotificationBlockingHelperManager.class,
- mNotificationBlockingHelperManager::get);
mProviders.put(NotificationRemoteInputManager.class,
mNotificationRemoteInputManager::get);
mProviders.put(SmartReplyConstants.class, mSmartReplyConstants::get);
diff --git a/packages/SystemUI/src/com/android/systemui/SystemUIFactory.java b/packages/SystemUI/src/com/android/systemui/SystemUIFactory.java
index f15949977754..80253b424335 100644
--- a/packages/SystemUI/src/com/android/systemui/SystemUIFactory.java
+++ b/packages/SystemUI/src/com/android/systemui/SystemUIFactory.java
@@ -17,6 +17,7 @@
package com.android.systemui;
import android.content.Context;
+import android.content.res.AssetManager;
import android.content.res.Resources;
import android.os.Handler;
import android.util.Log;
@@ -26,6 +27,7 @@ import com.android.systemui.dagger.DaggerGlobalRootComponent;
import com.android.systemui.dagger.GlobalRootComponent;
import com.android.systemui.dagger.SysUIComponent;
import com.android.systemui.dagger.WMComponent;
+import com.android.systemui.navigationbar.gestural.BackGestureTfClassifierProvider;
import com.android.systemui.screenshot.ScreenshotNotificationSmartActionsProvider;
import java.util.concurrent.ExecutionException;
@@ -110,12 +112,16 @@ public class SystemUIFactory {
return mSysUIComponent;
}
- /** Returns the list of system UI components that should be started. */
+ /**
+ * Returns the list of system UI components that should be started.
+ */
public String[] getSystemUIServiceComponents(Resources resources) {
return resources.getStringArray(R.array.config_systemUIServiceComponents);
}
- /** Returns the list of system UI components that should be started per user. */
+ /**
+ * Returns the list of system UI components that should be started per user.
+ */
public String[] getSystemUIServiceComponentsPerUser(Resources resources) {
return resources.getStringArray(R.array.config_systemUIServiceComponentsPerUser);
}
@@ -125,9 +131,17 @@ public class SystemUIFactory {
* This method is overridden in vendor specific implementation of Sys UI.
*/
public ScreenshotNotificationSmartActionsProvider
- createScreenshotNotificationSmartActionsProvider(Context context,
- Executor executor,
- Handler uiHandler) {
+ createScreenshotNotificationSmartActionsProvider(
+ Context context, Executor executor, Handler uiHandler) {
return new ScreenshotNotificationSmartActionsProvider();
}
-}
+
+ /**
+ * Creates an instance of BackGestureTfClassifierProvider.
+ * This method is overridden in vendor specific implementation of Sys UI.
+ */
+ public BackGestureTfClassifierProvider createBackGestureTfClassifierProvider(
+ AssetManager am) {
+ return new BackGestureTfClassifierProvider();
+ }
+} \ No newline at end of file
diff --git a/packages/SystemUI/src/com/android/systemui/accessibility/WindowMagnificationController.java b/packages/SystemUI/src/com/android/systemui/accessibility/WindowMagnificationController.java
index 494a0f64cea4..714095631fdb 100644
--- a/packages/SystemUI/src/com/android/systemui/accessibility/WindowMagnificationController.java
+++ b/packages/SystemUI/src/com/android/systemui/accessibility/WindowMagnificationController.java
@@ -48,6 +48,7 @@ import android.view.View;
import android.view.WindowManager;
import android.view.WindowManagerGlobal;
+import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.graphics.SfVsyncFrameCallbackProvider;
import com.android.systemui.R;
import com.android.systemui.shared.system.WindowManagerWrapper;
@@ -65,7 +66,8 @@ class WindowMagnificationController implements View.OnTouchListener, SurfaceHold
private final Point mDisplaySize = new Point();
private final int mDisplayId;
@Surface.Rotation
- private int mRotation;
+ @VisibleForTesting
+ int mRotation;
private final Rect mMagnificationFrame = new Rect();
private final SurfaceControl.Transaction mTransaction;
@@ -97,6 +99,7 @@ class WindowMagnificationController implements View.OnTouchListener, SurfaceHold
private SurfaceView mMirrorSurfaceView;
private int mMirrorSurfaceMargin;
private int mBorderDragSize;
+ private int mDragViewSize;
private int mOuterBorderSize;
// The boundary of magnification frame.
private final Rect mMagnificationFrameBoundary = new Rect();
@@ -109,7 +112,7 @@ class WindowMagnificationController implements View.OnTouchListener, SurfaceHold
WindowMagnificationController(Context context, @NonNull Handler handler,
SfVsyncFrameCallbackProvider sfVsyncFrameProvider,
- MirrorWindowControl mirrorWindowControl, SurfaceControl.Transaction transaction,
+ MirrorWindowControl mirrorWindowControl, SurfaceControl.Transaction transaction,
@NonNull WindowMagnifierCallback callback) {
mContext = context;
mHandler = handler;
@@ -168,6 +171,8 @@ class WindowMagnificationController implements View.OnTouchListener, SurfaceHold
R.dimen.magnification_mirror_surface_margin);
mBorderDragSize = mResources.getDimensionPixelSize(
R.dimen.magnification_border_drag_size);
+ mDragViewSize = mResources.getDimensionPixelSize(
+ R.dimen.magnification_drag_view_size);
mOuterBorderSize = mResources.getDimensionPixelSize(
R.dimen.magnification_outer_border_margin);
}
@@ -203,13 +208,12 @@ class WindowMagnificationController implements View.OnTouchListener, SurfaceHold
* @param configDiff a bit mask of the differences between the configurations
*/
void onConfigurationChanged(int configDiff) {
- if (!isWindowVisible()) {
- return;
- }
if ((configDiff & ActivityInfo.CONFIG_DENSITY) != 0) {
updateDimensions();
- mWm.removeView(mMirrorView);
- createMirrorWindow();
+ if (isWindowVisible()) {
+ mWm.removeView(mMirrorView);
+ createMirrorWindow();
+ }
} else if ((configDiff & ActivityInfo.CONFIG_ORIENTATION) != 0) {
onRotate();
}
@@ -217,16 +221,20 @@ class WindowMagnificationController implements View.OnTouchListener, SurfaceHold
/** Handles MirrorWindow position when the device rotation changed. */
private void onRotate() {
- Display display = mContext.getDisplay();
+ final Display display = mContext.getDisplay();
+ final int oldRotation = mRotation;
display.getRealSize(mDisplaySize);
setMagnificationFrameBoundary();
+ mRotation = display.getRotation();
+ if (!isWindowVisible()) {
+ return;
+ }
// Keep MirrorWindow position on the screen unchanged when device rotates 90°
// clockwise or anti-clockwise.
- final int rotationDegree = getDegreeFromRotation(display.getRotation(), mRotation);
+ final int rotationDegree = getDegreeFromRotation(mRotation, oldRotation);
final Matrix matrix = new Matrix();
matrix.setRotate(rotationDegree);
- mRotation = display.getRotation();
if (rotationDegree == 90) {
matrix.postTranslate(mDisplaySize.x, 0);
} else if (rotationDegree == 270) {
@@ -307,6 +315,10 @@ class WindowMagnificationController implements View.OnTouchListener, SurfaceHold
Region regionInsideDragBorder = new Region(mBorderDragSize, mBorderDragSize,
mMirrorView.getWidth() - mBorderDragSize,
mMirrorView.getHeight() - mBorderDragSize);
+ Rect dragArea = new Rect(mMirrorView.getWidth() - mDragViewSize - mBorderDragSize,
+ mMirrorView.getHeight() - mDragViewSize - mBorderDragSize,
+ mMirrorView.getWidth(), mMirrorView.getHeight());
+ regionInsideDragBorder.op(dragArea, Region.Op.DIFFERENCE);
return regionInsideDragBorder;
}
@@ -555,6 +567,7 @@ class WindowMagnificationController implements View.OnTouchListener, SurfaceHold
/**
* Gets the scale.
+ *
* @return {@link Float#NaN} if the window is invisible.
*/
float getScale() {
diff --git a/packages/SystemUI/src/com/android/systemui/appops/AppOpsControllerImpl.java b/packages/SystemUI/src/com/android/systemui/appops/AppOpsControllerImpl.java
index a3339f6fc051..0fd47654ebb9 100644
--- a/packages/SystemUI/src/com/android/systemui/appops/AppOpsControllerImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/appops/AppOpsControllerImpl.java
@@ -16,8 +16,13 @@
package com.android.systemui.appops;
+import static android.media.AudioManager.ACTION_MICROPHONE_MUTE_CHANGED;
+
import android.app.AppOpsManager;
+import android.content.BroadcastReceiver;
import android.content.Context;
+import android.content.Intent;
+import android.content.IntentFilter;
import android.content.pm.PackageManager;
import android.location.LocationManager;
import android.media.AudioManager;
@@ -34,6 +39,7 @@ import androidx.annotation.WorkerThread;
import com.android.internal.annotations.GuardedBy;
import com.android.internal.annotations.VisibleForTesting;
import com.android.systemui.Dumpable;
+import com.android.systemui.broadcast.BroadcastDispatcher;
import com.android.systemui.dagger.SysUISingleton;
import com.android.systemui.dagger.qualifiers.Background;
import com.android.systemui.dump.DumpManager;
@@ -54,7 +60,7 @@ import javax.inject.Inject;
* NotificationPresenter to be displayed to the user.
*/
@SysUISingleton
-public class AppOpsControllerImpl implements AppOpsController,
+public class AppOpsControllerImpl extends BroadcastReceiver implements AppOpsController,
AppOpsManager.OnOpActiveChangedInternalListener,
AppOpsManager.OnOpNotedListener, Dumpable {
@@ -65,6 +71,7 @@ public class AppOpsControllerImpl implements AppOpsController,
private static final String TAG = "AppOpsControllerImpl";
private static final boolean DEBUG = false;
+ private final BroadcastDispatcher mDispatcher;
private final AppOpsManager mAppOps;
private final AudioManager mAudioManager;
private final LocationManager mLocationManager;
@@ -79,6 +86,7 @@ public class AppOpsControllerImpl implements AppOpsController,
private final SparseArray<Set<Callback>> mCallbacksByCode = new SparseArray<>();
private final PermissionFlagsCache mFlagsCache;
private boolean mListening;
+ private boolean mMicMuted;
@GuardedBy("mActiveItems")
private final List<AppOpItem> mActiveItems = new ArrayList<>();
@@ -105,8 +113,10 @@ public class AppOpsControllerImpl implements AppOpsController,
@Background Looper bgLooper,
DumpManager dumpManager,
PermissionFlagsCache cache,
- AudioManager audioManager
+ AudioManager audioManager,
+ BroadcastDispatcher dispatcher
) {
+ mDispatcher = dispatcher;
mAppOps = (AppOpsManager) context.getSystemService(Context.APP_OPS_SERVICE);
mFlagsCache = cache;
mBGHandler = new H(bgLooper);
@@ -115,6 +125,7 @@ public class AppOpsControllerImpl implements AppOpsController,
mCallbacksByCode.put(OPS[i], new ArraySet<>());
}
mAudioManager = audioManager;
+ mMicMuted = audioManager.isMicrophoneMute();
mLocationManager = context.getSystemService(LocationManager.class);
dumpManager.registerDumpable(TAG, this);
}
@@ -133,6 +144,8 @@ public class AppOpsControllerImpl implements AppOpsController,
mAudioManager.registerAudioRecordingCallback(mAudioRecordingCallback, mBGHandler);
mBGHandler.post(() -> mAudioRecordingCallback.onRecordingConfigChanged(
mAudioManager.getActiveRecordingConfigurations()));
+ mDispatcher.registerReceiverWithHandler(this,
+ new IntentFilter(ACTION_MICROPHONE_MUTE_CHANGED), mBGHandler);
} else {
mAppOps.stopWatchingActive(this);
@@ -140,6 +153,7 @@ public class AppOpsControllerImpl implements AppOpsController,
mAudioManager.unregisterAudioRecordingCallback(mAudioRecordingCallback);
mBGHandler.removeCallbacksAndMessages(null); // null removes all
+ mDispatcher.unregisterReceiver(this);
synchronized (mActiveItems) {
mActiveItems.clear();
mRecordingsByUid.clear();
@@ -468,6 +482,9 @@ public class AppOpsControllerImpl implements AppOpsController,
}
private boolean isAnyRecordingPausedLocked(int uid) {
+ if (mMicMuted) {
+ return true;
+ }
List<AudioRecordingConfiguration> configs = mRecordingsByUid.get(uid);
if (configs == null) return false;
int configsNum = configs.size();
@@ -522,6 +539,12 @@ public class AppOpsControllerImpl implements AppOpsController,
}
};
+ @Override
+ public void onReceive(Context context, Intent intent) {
+ mMicMuted = mAudioManager.isMicrophoneMute();
+ updateRecordingPausedStatus();
+ }
+
protected class H extends Handler {
H(Looper looper) {
super(looper);
diff --git a/packages/SystemUI/src/com/android/systemui/assist/AssistHandleReminderExpBehavior.java b/packages/SystemUI/src/com/android/systemui/assist/AssistHandleReminderExpBehavior.java
index 1b2e4c6a595e..02a672b587d8 100644
--- a/packages/SystemUI/src/com/android/systemui/assist/AssistHandleReminderExpBehavior.java
+++ b/packages/SystemUI/src/com/android/systemui/assist/AssistHandleReminderExpBehavior.java
@@ -26,6 +26,8 @@ import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
import android.content.pm.ResolveInfo;
+import android.database.ContentObserver;
+import android.net.Uri;
import android.os.Handler;
import android.provider.Settings;
@@ -66,8 +68,10 @@ import dagger.Lazy;
@SysUISingleton
final class AssistHandleReminderExpBehavior implements BehaviorController {
- private static final String LEARNING_TIME_ELAPSED_KEY = "reminder_exp_learning_time_elapsed";
- private static final String LEARNING_EVENT_COUNT_KEY = "reminder_exp_learning_event_count";
+ private static final Uri LEARNING_TIME_ELAPSED_URI =
+ Settings.Secure.getUriFor(Settings.Secure.ASSIST_HANDLES_LEARNING_TIME_ELAPSED_MILLIS);
+ private static final Uri LEARNING_EVENT_COUNT_URI =
+ Settings.Secure.getUriFor(Settings.Secure.ASSIST_HANDLES_LEARNING_EVENT_COUNT);
private static final String LEARNED_HINT_LAST_SHOWN_KEY =
"reminder_exp_learned_hint_last_shown";
private static final long DEFAULT_LEARNING_TIME_MS = TimeUnit.DAYS.toMillis(10);
@@ -181,6 +185,7 @@ final class AssistHandleReminderExpBehavior implements BehaviorController {
private boolean mIsNavBarHidden;
private boolean mIsLauncherShowing;
private int mConsecutiveTaskSwitches;
+ @Nullable private ContentObserver mSettingObserver;
/** Whether user has learned the gesture. */
private boolean mIsLearned;
@@ -248,9 +253,22 @@ final class AssistHandleReminderExpBehavior implements BehaviorController {
mWakefulnessLifecycle.get().addObserver(mWakefulnessLifecycleObserver);
mLearningTimeElapsed = Settings.Secure.getLong(
- context.getContentResolver(), LEARNING_TIME_ELAPSED_KEY, /* default = */ 0);
+ context.getContentResolver(),
+ Settings.Secure.ASSIST_HANDLES_LEARNING_TIME_ELAPSED_MILLIS,
+ /* default = */ 0);
mLearningCount = Settings.Secure.getInt(
- context.getContentResolver(), LEARNING_EVENT_COUNT_KEY, /* default = */ 0);
+ context.getContentResolver(),
+ Settings.Secure.ASSIST_HANDLES_LEARNING_EVENT_COUNT,
+ /* default = */ 0);
+ mSettingObserver = new SettingsObserver(context, mHandler);
+ context.getContentResolver().registerContentObserver(
+ LEARNING_TIME_ELAPSED_URI,
+ /* notifyForDescendants = */ true,
+ mSettingObserver);
+ context.getContentResolver().registerContentObserver(
+ LEARNING_EVENT_COUNT_URI,
+ /* notifyForDescendants = */ true,
+ mSettingObserver);
mLearnedHintLastShownEpochDay = Settings.Secure.getLong(
context.getContentResolver(), LEARNED_HINT_LAST_SHOWN_KEY, /* default = */ 0);
mLastLearningTimestamp = mClock.currentTimeMillis();
@@ -264,8 +282,20 @@ final class AssistHandleReminderExpBehavior implements BehaviorController {
if (mContext != null) {
mBroadcastDispatcher.get().unregisterReceiver(mDefaultHomeBroadcastReceiver);
mBootCompleteCache.get().removeListener(mBootCompleteListener);
- Settings.Secure.putLong(mContext.getContentResolver(), LEARNING_TIME_ELAPSED_KEY, 0);
- Settings.Secure.putInt(mContext.getContentResolver(), LEARNING_EVENT_COUNT_KEY, 0);
+ mContext.getContentResolver().unregisterContentObserver(mSettingObserver);
+ mSettingObserver = null;
+ // putString in order to use overrideableByRestore
+ Settings.Secure.putString(
+ mContext.getContentResolver(),
+ Settings.Secure.ASSIST_HANDLES_LEARNING_TIME_ELAPSED_MILLIS,
+ Long.toString(0L),
+ /* overrideableByRestore = */ true);
+ // putString in order to use overrideableByRestore
+ Settings.Secure.putString(
+ mContext.getContentResolver(),
+ Settings.Secure.ASSIST_HANDLES_LEARNING_EVENT_COUNT,
+ Integer.toString(0),
+ /* overrideableByRestore = */ true);
Settings.Secure.putLong(mContext.getContentResolver(), LEARNED_HINT_LAST_SHOWN_KEY, 0);
mContext = null;
}
@@ -282,8 +312,12 @@ final class AssistHandleReminderExpBehavior implements BehaviorController {
return;
}
- Settings.Secure.putLong(
- mContext.getContentResolver(), LEARNING_EVENT_COUNT_KEY, ++mLearningCount);
+ // putString in order to use overrideableByRestore
+ Settings.Secure.putString(
+ mContext.getContentResolver(),
+ Settings.Secure.ASSIST_HANDLES_LEARNING_EVENT_COUNT,
+ Integer.toString(++mLearningCount),
+ /* overrideableByRestore = */ true);
}
@Override
@@ -460,8 +494,12 @@ final class AssistHandleReminderExpBehavior implements BehaviorController {
mIsLearned =
mLearningCount >= getLearningCount() || mLearningTimeElapsed >= getLearningTimeMs();
- mHandler.post(() -> Settings.Secure.putLong(
- mContext.getContentResolver(), LEARNING_TIME_ELAPSED_KEY, mLearningTimeElapsed));
+ // putString in order to use overrideableByRestore
+ mHandler.post(() -> Settings.Secure.putString(
+ mContext.getContentResolver(),
+ Settings.Secure.ASSIST_HANDLES_LEARNING_TIME_ELAPSED_MILLIS,
+ Long.toString(mLearningTimeElapsed),
+ /* overrideableByRestore = */ true));
}
private void resetConsecutiveTaskSwitches() {
@@ -589,4 +627,32 @@ final class AssistHandleReminderExpBehavior implements BehaviorController {
+ "="
+ getShowWhenTaught());
}
+
+ private final class SettingsObserver extends ContentObserver {
+
+ private final Context mContext;
+
+ SettingsObserver(Context context, Handler handler) {
+ super(handler);
+ mContext = context;
+ }
+
+ @Override
+ public void onChange(boolean selfChange, @Nullable Uri uri) {
+ if (LEARNING_TIME_ELAPSED_URI.equals(uri)) {
+ mLastLearningTimestamp = mClock.currentTimeMillis();
+ mLearningTimeElapsed = Settings.Secure.getLong(
+ mContext.getContentResolver(),
+ Settings.Secure.ASSIST_HANDLES_LEARNING_TIME_ELAPSED_MILLIS,
+ /* default = */ 0);
+ } else if (LEARNING_EVENT_COUNT_URI.equals(uri)) {
+ mLearningCount = Settings.Secure.getInt(
+ mContext.getContentResolver(),
+ Settings.Secure.ASSIST_HANDLES_LEARNING_EVENT_COUNT,
+ /* default = */ 0);
+ }
+
+ super.onChange(selfChange, uri);
+ }
+ }
}
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/AuthController.java b/packages/SystemUI/src/com/android/systemui/biometrics/AuthController.java
index 77afa27b5cca..38a191c8fb06 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/AuthController.java
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/AuthController.java
@@ -52,6 +52,7 @@ import com.android.systemui.dagger.SysUISingleton;
import com.android.systemui.doze.DozeReceiver;
import com.android.systemui.plugins.statusbar.StatusBarStateController;
import com.android.systemui.statusbar.CommandQueue;
+import com.android.systemui.statusbar.phone.KeyguardBouncer;
import java.util.List;
@@ -64,7 +65,7 @@ import javax.inject.Provider;
*/
@SysUISingleton
public class AuthController extends SystemUI implements CommandQueue.Callbacks,
- AuthDialogCallback, DozeReceiver {
+ AuthDialogCallback, DozeReceiver, KeyguardBouncer.BouncerExpansionCallback {
private static final String TAG = "AuthController";
private static final boolean DEBUG = true;
@@ -308,7 +309,7 @@ public class AuthController extends SystemUI implements CommandQueue.Callbacks,
final List<FingerprintSensorProperties> fingerprintSensorProperties =
mFingerprintManager.getSensorProperties();
for (FingerprintSensorProperties props : fingerprintSensorProperties) {
- if (props.sensorType == FingerprintSensorProperties.TYPE_UDFPS) {
+ if (props.isAnyUdfpsType()) {
mUdfpsController = mUdfpsControllerFactory.get();
break;
}
@@ -426,6 +427,35 @@ public class AuthController extends SystemUI implements CommandQueue.Callbacks,
mCurrentDialog = null;
}
+ /** See {@link KeyguardBouncer.BouncerExpansionCallback#onFullyShown}. */
+ @Override
+ public void onFullyShown() {
+ if (mUdfpsController != null) {
+ mUdfpsController.setBouncerVisibility(true);
+ }
+ }
+
+ /** See {@link KeyguardBouncer.BouncerExpansionCallback#onStartingToHide}. */
+ @Override
+ public void onStartingToHide() {
+ }
+
+ /** See {@link KeyguardBouncer.BouncerExpansionCallback#onStartingToShow}. */
+ @Override
+ public void onStartingToShow() {
+ if (mUdfpsController != null) {
+ mUdfpsController.setBouncerVisibility(true);
+ }
+ }
+
+ /** See {@link KeyguardBouncer.BouncerExpansionCallback#onFullyHidden}. */
+ @Override
+ public void onFullyHidden() {
+ if (mUdfpsController != null) {
+ mUdfpsController.setBouncerVisibility(false);
+ }
+ }
+
private void showDialog(SomeArgs args, boolean skipAnimation, Bundle savedState) {
mCurrentDialogArgs = args;
final @BiometricAuthenticator.Modality int type = args.argi1;
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsController.java b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsController.java
index 06c190f1964c..d79c96ea4774 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsController.java
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsController.java
@@ -16,17 +16,16 @@
package com.android.systemui.biometrics;
-import android.annotation.NonNull;
+import static com.android.internal.util.Preconditions.checkNotNull;
+
import android.annotation.SuppressLint;
-import android.content.ContentResolver;
import android.content.Context;
+import android.content.res.Resources;
import android.content.res.TypedArray;
import android.graphics.PixelFormat;
import android.graphics.Point;
import android.hardware.fingerprint.FingerprintManager;
import android.hardware.fingerprint.IUdfpsOverlayController;
-import android.os.Handler;
-import android.os.Looper;
import android.os.PowerManager;
import android.os.UserHandle;
import android.provider.Settings;
@@ -38,10 +37,16 @@ import android.view.LayoutInflater;
import android.view.MotionEvent;
import android.view.WindowManager;
+import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
+
import com.android.internal.BrightnessSynchronizer;
import com.android.systemui.R;
+import com.android.systemui.dagger.qualifiers.Main;
import com.android.systemui.doze.DozeReceiver;
import com.android.systemui.plugins.statusbar.StatusBarStateController;
+import com.android.systemui.util.concurrency.DelayableExecutor;
+import com.android.systemui.util.settings.SystemSettings;
import java.io.FileWriter;
import java.io.IOException;
@@ -60,8 +65,8 @@ class UdfpsController implements DozeReceiver {
private final FingerprintManager mFingerprintManager;
private final WindowManager mWindowManager;
- private final ContentResolver mContentResolver;
- private final Handler mHandler;
+ private final SystemSettings mSystemSettings;
+ private final DelayableExecutor mFgExecutor;
private final WindowManager.LayoutParams mLayoutParams;
private final UdfpsView mView;
// Debugfs path to control the high-brightness mode.
@@ -81,24 +86,30 @@ class UdfpsController implements DozeReceiver {
// Default non-HBM backlight value normalized to the range [0, 1.0]. Used as a fallback when the
// actual brightness value cannot be retrieved.
private final float mDefaultBrightness;
+ // Indicates whether the overlay is currently showing. Even if it has been requested, it might
+ // not be showing.
private boolean mIsOverlayShowing;
+ // Indicates whether the overlay has been requested.
+ private boolean mIsOverlayRequested;
+ // Indicates whether the bouncer is showing. When it is showing, the overlay needs to be hidden.
+ private boolean mIsBouncerShowing;
// The fingerprint AOD trigger doesn't provide an ACTION_UP/ACTION_CANCEL event to tell us when
// to turn off high brightness mode. To get around this limitation, the state of the AOD
// interrupt is being tracked and a timeout is used as a last resort to turn off high brightness
// mode.
private boolean mIsAodInterruptActive;
- private final Runnable mAodInterruptTimeoutAction = this::onCancelAodInterrupt;
+ @Nullable private Runnable mCancelAodTimeoutAction;
public class UdfpsOverlayController extends IUdfpsOverlayController.Stub {
@Override
public void showUdfpsOverlay() {
- UdfpsController.this.showUdfpsOverlay();
+ UdfpsController.this.setShowOverlay(true);
}
@Override
public void hideUdfpsOverlay() {
- UdfpsController.this.hideUdfpsOverlay();
+ UdfpsController.this.setShowOverlay(false);
}
@Override
@@ -138,18 +149,27 @@ class UdfpsController implements DozeReceiver {
@Inject
UdfpsController(@NonNull Context context,
- @NonNull StatusBarStateController statusBarStateController) {
- mFingerprintManager = context.getSystemService(FingerprintManager.class);
- mWindowManager = context.getSystemService(WindowManager.class);
- mContentResolver = context.getContentResolver();
- mHandler = new Handler(Looper.getMainLooper());
+ @Main Resources resources,
+ LayoutInflater inflater,
+ @Nullable FingerprintManager fingerprintManager,
+ PowerManager powerManager,
+ WindowManager windowManager,
+ SystemSettings systemSettings,
+ @NonNull StatusBarStateController statusBarStateController,
+ @Main DelayableExecutor fgExecutor) {
+ // The fingerprint manager is queried for UDFPS before this class is constructed, so the
+ // fingerprint manager should never be null.
+ mFingerprintManager = checkNotNull(fingerprintManager);
+ mWindowManager = windowManager;
+ mSystemSettings = systemSettings;
+ mFgExecutor = fgExecutor;
mLayoutParams = createLayoutParams(context);
- mView = (UdfpsView) LayoutInflater.from(context).inflate(R.layout.udfps_view, null, false);
+ mView = (UdfpsView) inflater.inflate(R.layout.udfps_view, null, false);
- mHbmPath = context.getResources().getString(R.string.udfps_hbm_sysfs_path);
- mHbmEnableCommand = context.getResources().getString(R.string.udfps_hbm_enable_command);
- mHbmDisableCommand = context.getResources().getString(R.string.udfps_hbm_disable_command);
+ mHbmPath = resources.getString(R.string.udfps_hbm_sysfs_path);
+ mHbmEnableCommand = resources.getString(R.string.udfps_hbm_enable_command);
+ mHbmDisableCommand = resources.getString(R.string.udfps_hbm_disable_command);
mHbmSupported = !TextUtils.isEmpty(mHbmPath);
mView.setHbmSupported(mHbmSupported);
@@ -157,11 +177,11 @@ class UdfpsController implements DozeReceiver {
// This range only consists of the minimum and maximum values, which only cover
// non-high-brightness mode.
- float[] nitsRange = toFloatArray(context.getResources().obtainTypedArray(
+ float[] nitsRange = toFloatArray(resources.obtainTypedArray(
com.android.internal.R.array.config_screenBrightnessNits));
// The last value of this range corresponds to the high-brightness mode.
- float[] nitsAutoBrightnessValues = toFloatArray(context.getResources().obtainTypedArray(
+ float[] nitsAutoBrightnessValues = toFloatArray(resources.obtainTypedArray(
com.android.internal.R.array.config_autoBrightnessDisplayValuesNits));
mHbmNits = nitsAutoBrightnessValues[nitsAutoBrightnessValues.length - 1];
@@ -170,12 +190,12 @@ class UdfpsController implements DozeReceiver {
// This range only consists of the minimum and maximum backlight values, which only apply
// in non-high-brightness mode.
float[] normalizedBacklightRange = normalizeBacklightRange(
- context.getResources().getIntArray(
+ resources.getIntArray(
com.android.internal.R.array.config_screenBrightnessBacklight));
mBacklightToNitsSpline = Spline.createSpline(normalizedBacklightRange, nitsRange);
mNitsToHbmBacklightSpline = Spline.createSpline(hbmNitsRange, normalizedBacklightRange);
- mDefaultBrightness = obtainDefaultBrightness(context);
+ mDefaultBrightness = obtainDefaultBrightness(powerManager);
// TODO(b/160025856): move to the "dump" method.
Log.v(TAG, String.format("ctor | mNitsRange: [%f, %f]", nitsRange[0], nitsRange[1]));
@@ -193,8 +213,37 @@ class UdfpsController implements DozeReceiver {
mView.dozeTimeTick();
}
+ private void setShowOverlay(boolean show) {
+ if (show == mIsOverlayRequested) {
+ return;
+ }
+ mIsOverlayRequested = show;
+ updateOverlay();
+ }
+
+ /**
+ * Call when the visibility of the bouncer changes.
+ *
+ * @param isShowing Whether or not the bouncer is showing
+ */
+ void setBouncerVisibility(boolean isShowing) {
+ if (isShowing == mIsBouncerShowing) {
+ return;
+ }
+ mIsBouncerShowing = isShowing;
+ updateOverlay();
+ }
+
+ private void updateOverlay() {
+ if (mIsOverlayRequested && !mIsBouncerShowing) {
+ showUdfpsOverlay();
+ } else {
+ hideUdfpsOverlay();
+ }
+ }
+
private void showUdfpsOverlay() {
- mHandler.post(() -> {
+ mFgExecutor.execute(() -> {
if (!mIsOverlayShowing) {
try {
Log.v(TAG, "showUdfpsOverlay | adding window");
@@ -211,7 +260,7 @@ class UdfpsController implements DozeReceiver {
}
private void hideUdfpsOverlay() {
- mHandler.post(() -> {
+ mFgExecutor.execute(() -> {
if (mIsOverlayShowing) {
Log.v(TAG, "hideUdfpsOverlay | removing window");
mView.setOnTouchListener(null);
@@ -228,7 +277,7 @@ class UdfpsController implements DozeReceiver {
// Returns a value in the range of [0, 255].
private int computeScrimOpacity() {
// Backlight setting can be NaN, -1.0f, and [0.0f, 1.0f].
- float backlightSetting = Settings.System.getFloatForUser(mContentResolver,
+ float backlightSetting = mSystemSettings.getFloatForUser(
Settings.System.SCREEN_BRIGHTNESS_FLOAT, mDefaultBrightness,
UserHandle.USER_CURRENT);
@@ -265,7 +314,8 @@ class UdfpsController implements DozeReceiver {
// Since the sensor that triggers the AOD interrupt doesn't provide ACTION_UP/ACTION_CANCEL,
// we need to be careful about not letting the screen accidentally remain in high brightness
// mode. As a mitigation, queue a call to cancel the fingerprint scan.
- mHandler.postDelayed(mAodInterruptTimeoutAction, AOD_INTERRUPT_TIMEOUT_MILLIS);
+ mCancelAodTimeoutAction = mFgExecutor.executeDelayed(this::onCancelAodInterrupt,
+ AOD_INTERRUPT_TIMEOUT_MILLIS);
// using a hard-coded value for major and minor until it is available from the sensor
onFingerDown(screenX, screenY, 13.0f, 13.0f);
}
@@ -280,7 +330,10 @@ class UdfpsController implements DozeReceiver {
if (!mIsAodInterruptActive) {
return;
}
- mHandler.removeCallbacks(mAodInterruptTimeoutAction);
+ if (mCancelAodTimeoutAction != null) {
+ mCancelAodTimeoutAction.run();
+ mCancelAodTimeoutAction = null;
+ }
mIsAodInterruptActive = false;
onFingerUp();
}
@@ -338,8 +391,7 @@ class UdfpsController implements DozeReceiver {
return lp;
}
- private static float obtainDefaultBrightness(Context context) {
- PowerManager powerManager = context.getSystemService(PowerManager.class);
+ private static float obtainDefaultBrightness(PowerManager powerManager) {
if (powerManager == null) {
Log.e(TAG, "PowerManager is unavailable. Can't obtain default brightness.");
return 0f;
diff --git a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleController.java b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleController.java
index b6106025a17a..90b1f55ac514 100644
--- a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleController.java
+++ b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleController.java
@@ -33,7 +33,6 @@ import static android.view.View.INVISIBLE;
import static android.view.View.VISIBLE;
import static android.view.WindowManager.LayoutParams.LAYOUT_IN_DISPLAY_CUTOUT_MODE_ALWAYS;
-import static com.android.systemui.bubbles.BubbleDebugConfig.DEBUG_BUBBLE_CONTROLLER;
import static com.android.systemui.bubbles.BubbleDebugConfig.TAG_BUBBLES;
import static com.android.systemui.bubbles.BubbleDebugConfig.TAG_WITH_CLASS_NAME;
import static com.android.systemui.statusbar.StatusBarState.SHADE;
@@ -91,7 +90,6 @@ import com.android.systemui.plugins.statusbar.StatusBarStateController;
import com.android.systemui.shared.system.ActivityManagerWrapper;
import com.android.systemui.shared.system.PinnedStackListenerForwarder;
import com.android.systemui.shared.system.TaskStackChangeListener;
-import com.android.systemui.shared.system.WindowManagerWrapper;
import com.android.systemui.statusbar.FeatureFlags;
import com.android.systemui.statusbar.NotificationLockscreenUserManager;
import com.android.systemui.statusbar.NotificationRemoveInterceptor;
@@ -115,6 +113,7 @@ import com.android.systemui.statusbar.phone.StatusBar;
import com.android.systemui.statusbar.policy.ConfigurationController;
import com.android.systemui.statusbar.policy.ZenModeController;
import com.android.systemui.util.FloatingContentCoordinator;
+import com.android.systemui.wmshell.WindowManagerShellWrapper;
import java.io.FileDescriptor;
import java.io.PrintWriter;
@@ -366,6 +365,7 @@ public class BubbleController implements ConfigurationController.ConfigurationLi
INotificationManager notificationManager,
@Nullable IStatusBarService statusBarService,
WindowManager windowManager,
+ WindowManagerShellWrapper windowManagerShellWrapper,
LauncherApps launcherApps) {
dumpManager.registerDumpable(TAG, this);
mContext = context;
@@ -441,7 +441,7 @@ public class BubbleController implements ConfigurationController.ConfigurationLi
ActivityManagerWrapper.getInstance().registerTaskStackListener(mTaskStackListener);
try {
- WindowManagerWrapper.getInstance().addPinnedStackListener(new BubblesImeListener());
+ windowManagerShellWrapper.addPinnedStackListener(new BubblesImeListener());
} catch (RemoteException e) {
e.printStackTrace();
}
diff --git a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleExpandedView.java b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleExpandedView.java
index f98bb333b6b1..ec60cbd175d0 100644
--- a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleExpandedView.java
+++ b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleExpandedView.java
@@ -16,15 +16,12 @@
package com.android.systemui.bubbles;
-import static android.app.WindowConfiguration.WINDOWING_MODE_MULTI_WINDOW;
import static android.content.Intent.FLAG_ACTIVITY_MULTIPLE_TASK;
import static android.content.Intent.FLAG_ACTIVITY_NEW_DOCUMENT;
import static android.graphics.PixelFormat.TRANSPARENT;
import static android.view.Display.INVALID_DISPLAY;
import static android.view.InsetsState.ITYPE_IME;
import static android.view.ViewGroup.LayoutParams.WRAP_CONTENT;
-import static android.view.ViewRootImpl.NEW_INSETS_MODE_FULL;
-import static android.view.ViewRootImpl.sNewInsetsMode;
import static android.view.WindowManager.LayoutParams.FLAG_LAYOUT_NO_LIMITS;
import static android.view.WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE;
import static android.view.WindowManager.LayoutParams.FLAG_NOT_TOUCHABLE;
@@ -48,7 +45,6 @@ import android.content.res.Configuration;
import android.content.res.Resources;
import android.content.res.TypedArray;
import android.graphics.Color;
-import android.graphics.Insets;
import android.graphics.Outline;
import android.graphics.Point;
import android.graphics.Rect;
@@ -471,11 +467,7 @@ public class BubbleExpandedView extends LinearLayout {
mKeyboardVisible = false;
mNeedsNewHeight = false;
if (mActivityView != null) {
- if (sNewInsetsMode == NEW_INSETS_MODE_FULL) {
- setImeWindowToDisplay(0, 0);
- } else {
- mActivityView.setForwardedInsets(Insets.of(0, 0, 0, 0));
- }
+ setImeWindowToDisplay(0, 0);
}
if (DEBUG_BUBBLE_EXPANDED_VIEW) {
Log.d(TAG, "onDetachedFromWindow: bubble=" + getBubbleKey());
@@ -525,13 +517,7 @@ public class BubbleExpandedView extends LinearLayout {
insets.getDisplayCutout() != null
? insets.getDisplayCutout().getSafeInsetBottom()
: 0);
- final int insetsBottom = Math.max(activityViewBottom - keyboardTop, 0);
-
- if (sNewInsetsMode == NEW_INSETS_MODE_FULL) {
- setImeWindowToDisplay(getWidth(), insetsBottom);
- } else {
- mActivityView.setForwardedInsets(Insets.of(0, 0, 0, insetsBottom));
- }
+ setImeWindowToDisplay(getWidth(), Math.max(activityViewBottom - keyboardTop, 0));
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleLoggerImpl.java b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleLoggerImpl.java
index 2d90c8626a0e..ea612af3d4a4 100644
--- a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleLoggerImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleLoggerImpl.java
@@ -33,7 +33,7 @@ public class BubbleLoggerImpl extends UiEventLoggerImpl implements BubbleLogger
* @param e UI event
*/
public void log(Bubble b, UiEventEnum e) {
- super.log(e, b.getUser().getIdentifier(), b.getPackageName());
+ logWithInstanceId(e, b.getAppUid(), b.getPackageName(), b.getInstanceId());
}
/**
diff --git a/packages/SystemUI/src/com/android/systemui/bubbles/dagger/BubbleModule.java b/packages/SystemUI/src/com/android/systemui/bubbles/dagger/BubbleModule.java
index 9efc3c20f55a..5bf105368997 100644
--- a/packages/SystemUI/src/com/android/systemui/bubbles/dagger/BubbleModule.java
+++ b/packages/SystemUI/src/com/android/systemui/bubbles/dagger/BubbleModule.java
@@ -40,6 +40,7 @@ import com.android.systemui.statusbar.phone.ShadeController;
import com.android.systemui.statusbar.policy.ConfigurationController;
import com.android.systemui.statusbar.policy.ZenModeController;
import com.android.systemui.util.FloatingContentCoordinator;
+import com.android.systemui.wmshell.WindowManagerShellWrapper;
import dagger.Module;
import dagger.Provides;
@@ -73,6 +74,7 @@ public interface BubbleModule {
INotificationManager notifManager,
IStatusBarService statusBarService,
WindowManager windowManager,
+ WindowManagerShellWrapper windowManagerShellWrapper,
LauncherApps launcherApps) {
return new BubbleController(
context,
@@ -96,6 +98,7 @@ public interface BubbleModule {
notifManager,
statusBarService,
windowManager,
+ windowManagerShellWrapper,
launcherApps);
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/dagger/DefaultBroadcastReceiverBinder.java b/packages/SystemUI/src/com/android/systemui/dagger/DefaultBroadcastReceiverBinder.java
index 307362fe790e..25d02a66d56d 100644
--- a/packages/SystemUI/src/com/android/systemui/dagger/DefaultBroadcastReceiverBinder.java
+++ b/packages/SystemUI/src/com/android/systemui/dagger/DefaultBroadcastReceiverBinder.java
@@ -18,7 +18,7 @@ package com.android.systemui.dagger;
import android.content.BroadcastReceiver;
-import com.android.systemui.media.dialog.MediaOutDialogReceiver;
+import com.android.systemui.media.dialog.MediaOutputDialogReceiver;
import com.android.systemui.screenshot.ActionProxyReceiver;
import com.android.systemui.screenshot.DeleteScreenshotReceiver;
import com.android.systemui.screenshot.SmartActionsReceiver;
@@ -65,8 +65,8 @@ public abstract class DefaultBroadcastReceiverBinder {
*/
@Binds
@IntoMap
- @ClassKey(MediaOutDialogReceiver.class)
- public abstract BroadcastReceiver bindMediaOutDialogReceiver(
- MediaOutDialogReceiver broadcastReceiver);
+ @ClassKey(MediaOutputDialogReceiver.class)
+ public abstract BroadcastReceiver bindMediaOutputDialogReceiver(
+ MediaOutputDialogReceiver broadcastReceiver);
}
diff --git a/packages/SystemUI/src/com/android/systemui/dagger/DependencyProvider.java b/packages/SystemUI/src/com/android/systemui/dagger/DependencyProvider.java
index 38e12a6ed5f8..c90e6b1360df 100644
--- a/packages/SystemUI/src/com/android/systemui/dagger/DependencyProvider.java
+++ b/packages/SystemUI/src/com/android/systemui/dagger/DependencyProvider.java
@@ -66,6 +66,7 @@ import com.android.systemui.shared.plugins.PluginManager;
import com.android.systemui.shared.plugins.PluginManagerImpl;
import com.android.systemui.shared.system.ActivityManagerWrapper;
import com.android.systemui.shared.system.DevicePolicyManagerWrapper;
+import com.android.systemui.shared.system.WindowManagerWrapper;
import com.android.systemui.statusbar.CommandQueue;
import com.android.systemui.statusbar.NotificationRemoteInputManager;
import com.android.systemui.statusbar.phone.AutoHideController;
@@ -312,6 +313,13 @@ public class DependencyProvider {
/** */
@Provides
+ public WindowManagerWrapper providesWindowManagerWrapper() {
+ return WindowManagerWrapper.getInstance();
+ }
+
+ /** */
+ @Provides
+ @SysUISingleton
public SystemActions providesSystemActions(Context context) {
return new SystemActions(context);
}
diff --git a/packages/SystemUI/src/com/android/systemui/doze/DozeLog.java b/packages/SystemUI/src/com/android/systemui/doze/DozeLog.java
index 424a8246b278..6154a4ecb85c 100644
--- a/packages/SystemUI/src/com/android/systemui/doze/DozeLog.java
+++ b/packages/SystemUI/src/com/android/systemui/doze/DozeLog.java
@@ -198,6 +198,22 @@ public class DozeLog implements Dumpable {
}
/**
+ * Appends doze state changed sent to all DozeMachine parts event to the logs
+ * @param state new DozeMachine state
+ */
+ public void traceDozeStateSendComplete(DozeMachine.State state) {
+ mLogger.logStateChangedSent(state);
+ }
+
+ /**
+ * Appends display state changed event to the logs
+ * @param displayState new DozeMachine state
+ */
+ public void traceDisplayState(int displayState) {
+ mLogger.logDisplayStateChanged(displayState);
+ }
+
+ /**
* Appends wake-display event to the logs.
* @param wake if we're waking up or sleeping.
*/
diff --git a/packages/SystemUI/src/com/android/systemui/doze/DozeLogger.kt b/packages/SystemUI/src/com/android/systemui/doze/DozeLogger.kt
index 732745a1158b..46cec95a967a 100644
--- a/packages/SystemUI/src/com/android/systemui/doze/DozeLogger.kt
+++ b/packages/SystemUI/src/com/android/systemui/doze/DozeLogger.kt
@@ -143,6 +143,22 @@ class DozeLogger @Inject constructor(
})
}
+ fun logStateChangedSent(state: DozeMachine.State) {
+ buffer.log(TAG, INFO, {
+ str1 = state.name
+ }, {
+ "Doze state sent to all DozeMachineParts stateSent=$str1"
+ })
+ }
+
+ fun logDisplayStateChanged(displayState: Int) {
+ buffer.log(TAG, INFO, {
+ int1 = displayState
+ }, {
+ "Display state changed to $int1"
+ })
+ }
+
fun logWakeDisplay(isAwake: Boolean) {
buffer.log(TAG, DEBUG, {
bool1 = isAwake
diff --git a/packages/SystemUI/src/com/android/systemui/doze/DozeMachine.java b/packages/SystemUI/src/com/android/systemui/doze/DozeMachine.java
index 1ef806c8bd68..1e0460bea5be 100644
--- a/packages/SystemUI/src/com/android/systemui/doze/DozeMachine.java
+++ b/packages/SystemUI/src/com/android/systemui/doze/DozeMachine.java
@@ -205,6 +205,7 @@ public class DozeMachine {
}
void onScreenState(int state) {
+ mDozeLog.traceDisplayState(state);
for (Part part : mParts) {
part.onScreenState(state);
}
@@ -308,6 +309,7 @@ public class DozeMachine {
for (Part p : mParts) {
p.transitionTo(oldState, newState);
}
+ mDozeLog.traceDozeStateSendComplete(newState);
switch (newState) {
case FINISH:
@@ -432,8 +434,12 @@ public class DozeMachine {
/** Give the Part a chance to clean itself up. */
default void destroy() {}
- /** Alerts that the screenstate is being changed. */
- default void onScreenState(int state) {}
+ /**
+ * Alerts that the screenstate is being changed.
+ * Note: This may be called from within a call to transitionTo, so local DozeState may not
+ * be accurate nor match with the new displayState.
+ */
+ default void onScreenState(int displayState) {}
/** Sets the {@link DozeMachine} when this Part is associated with one. */
default void setDozeMachine(DozeMachine dozeMachine) {}
diff --git a/packages/SystemUI/src/com/android/systemui/doze/DozeScreenBrightness.java b/packages/SystemUI/src/com/android/systemui/doze/DozeScreenBrightness.java
index b872b1a4219a..92494cf5b546 100644
--- a/packages/SystemUI/src/com/android/systemui/doze/DozeScreenBrightness.java
+++ b/packages/SystemUI/src/com/android/systemui/doze/DozeScreenBrightness.java
@@ -35,6 +35,7 @@ import com.android.systemui.doze.dagger.DozeScope;
import com.android.systemui.doze.dagger.WrappedService;
import com.android.systemui.util.sensors.AsyncSensorManager;
+import java.io.PrintWriter;
import java.util.Optional;
import javax.inject.Inject;
@@ -73,7 +74,6 @@ public class DozeScreenBrightness extends BroadcastReceiver implements DozeMachi
* --ei brightness_bucket 1}
*/
private int mDebugBrightnessBucket = -1;
- private DozeMachine.State mState;
@Inject
public DozeScreenBrightness(Context context, @WrappedService DozeMachine.Service service,
@@ -94,7 +94,6 @@ public class DozeScreenBrightness extends BroadcastReceiver implements DozeMachi
@Override
public void transitionTo(DozeMachine.State oldState, DozeMachine.State newState) {
- mState = newState;
switch (newState) {
case INITIALIZED:
case DOZE:
@@ -112,10 +111,7 @@ public class DozeScreenBrightness extends BroadcastReceiver implements DozeMachi
@Override
public void onScreenState(int state) {
- if (!mScreenOff
- && (mState == DozeMachine.State.DOZE_AOD
- || mState == DozeMachine.State.DOZE_AOD_DOCKED)
- && (state == Display.STATE_DOZE || state == Display.STATE_DOZE_SUSPEND)) {
+ if (state == Display.STATE_DOZE || state == Display.STATE_DOZE_SUSPEND) {
setLightSensorEnabled(true);
} else {
setLightSensorEnabled(false);
@@ -226,4 +222,9 @@ public class DozeScreenBrightness extends BroadcastReceiver implements DozeMachi
mDebugBrightnessBucket = intent.getIntExtra(BRIGHTNESS_BUCKET, -1);
updateBrightnessAndReady(false /* force */);
}
+
+ /** Dump current state */
+ public void dump(PrintWriter pw) {
+ pw.println("DozeScreenBrightnessSensorRegistered=" + mRegistered);
+ }
}
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/Lifecycle.java b/packages/SystemUI/src/com/android/systemui/keyguard/Lifecycle.java
index 1b20cfbc4e55..3da6caf31968 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/Lifecycle.java
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/Lifecycle.java
@@ -16,7 +16,10 @@
package com.android.systemui.keyguard;
+import androidx.annotation.NonNull;
+
import java.util.ArrayList;
+import java.util.Objects;
import java.util.function.Consumer;
/**
@@ -26,8 +29,8 @@ public class Lifecycle<T> {
private ArrayList<T> mObservers = new ArrayList<>();
- public void addObserver(T observer) {
- mObservers.add(observer);
+ public void addObserver(@NonNull T observer) {
+ mObservers.add(Objects.requireNonNull(observer));
}
public void removeObserver(T observer) {
diff --git a/packages/SystemUI/src/com/android/systemui/media/MediaCarouselController.kt b/packages/SystemUI/src/com/android/systemui/media/MediaCarouselController.kt
index f150381f4070..636f42089743 100644
--- a/packages/SystemUI/src/com/android/systemui/media/MediaCarouselController.kt
+++ b/packages/SystemUI/src/com/android/systemui/media/MediaCarouselController.kt
@@ -274,7 +274,7 @@ class MediaCarouselController @Inject constructor(
}
}
- private fun removePlayer(key: String) {
+ private fun removePlayer(key: String, dismissMediaData: Boolean = true) {
val removed = MediaPlayerData.removeMediaPlayer(key)
removed?.apply {
mediaCarouselScrollHandler.onPrePlayerRemoved(removed)
@@ -283,13 +283,16 @@ class MediaCarouselController @Inject constructor(
mediaCarouselScrollHandler.onPlayersChanged()
updatePageIndicator()
- // Inform the media manager of a potentially late dismissal
- mediaManager.dismissMediaData(key, 0L)
+ if (dismissMediaData) {
+ // Inform the media manager of a potentially late dismissal
+ mediaManager.dismissMediaData(key, 0L)
+ }
}
}
private fun recreatePlayers() {
MediaPlayerData.mediaData().forEach { (key, data) ->
+ removePlayer(key, dismissMediaData = false)
addOrUpdatePlayer(key = key, oldKey = null, data = data)
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/media/MediaDeviceManager.kt b/packages/SystemUI/src/com/android/systemui/media/MediaDeviceManager.kt
index 2bc908be055c..a993d00df01e 100644
--- a/packages/SystemUI/src/com/android/systemui/media/MediaDeviceManager.kt
+++ b/packages/SystemUI/src/com/android/systemui/media/MediaDeviceManager.kt
@@ -16,7 +16,6 @@
package com.android.systemui.media
-import android.content.Context
import android.media.MediaRouter2Manager
import android.media.session.MediaController
import androidx.annotation.AnyThread
@@ -25,7 +24,6 @@ import androidx.annotation.WorkerThread
import com.android.settingslib.media.LocalMediaManager
import com.android.settingslib.media.MediaDevice
import com.android.systemui.Dumpable
-import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.dagger.qualifiers.Background
import com.android.systemui.dagger.qualifiers.Main
import com.android.systemui.dump.DumpManager
@@ -34,11 +32,13 @@ import java.io.PrintWriter
import java.util.concurrent.Executor
import javax.inject.Inject
+private const val PLAYBACK_TYPE_UNKNOWN = 0
+
/**
* Provides information about the route (ie. device) where playback is occurring.
*/
class MediaDeviceManager @Inject constructor(
- private val context: Context,
+ private val controllerFactory: MediaControllerFactory,
private val localMediaManagerFactory: LocalMediaManagerFactory,
private val mr2manager: MediaRouter2Manager,
@Main private val fgExecutor: Executor,
@@ -72,7 +72,7 @@ class MediaDeviceManager @Inject constructor(
if (entry == null || entry?.token != data.token) {
entry?.stop()
val controller = data.token?.let {
- MediaController(context, it)
+ controllerFactory.create(it)
}
entry = Entry(key, oldKey, controller,
localMediaManagerFactory.create(data.packageName))
@@ -123,11 +123,12 @@ class MediaDeviceManager @Inject constructor(
val oldKey: String?,
val controller: MediaController?,
val localMediaManager: LocalMediaManager
- ) : LocalMediaManager.DeviceCallback {
+ ) : LocalMediaManager.DeviceCallback, MediaController.Callback() {
val token
get() = controller?.sessionToken
private var started = false
+ private var playbackType = PLAYBACK_TYPE_UNKNOWN
private var current: MediaDevice? = null
set(value) {
if (!started || value != field) {
@@ -142,6 +143,8 @@ class MediaDeviceManager @Inject constructor(
fun start() = bgExecutor.execute {
localMediaManager.registerCallback(this)
localMediaManager.startScan()
+ playbackType = controller?.playbackInfo?.playbackType ?: PLAYBACK_TYPE_UNKNOWN
+ controller?.registerCallback(this)
updateCurrent()
started = true
}
@@ -149,22 +152,37 @@ class MediaDeviceManager @Inject constructor(
@AnyThread
fun stop() = bgExecutor.execute {
started = false
+ controller?.unregisterCallback(this)
localMediaManager.stopScan()
localMediaManager.unregisterCallback(this)
}
fun dump(fd: FileDescriptor, pw: PrintWriter, args: Array<String>) {
- val route = controller?.let {
+ val routingSession = controller?.let {
mr2manager.getRoutingSessionForMediaController(it)
}
+ val selectedRoutes = routingSession?.let {
+ mr2manager.getSelectedRoutes(it)
+ }
with(pw) {
println(" current device is ${current?.name}")
val type = controller?.playbackInfo?.playbackType
- println(" PlaybackType=$type (1 for local, 2 for remote)")
- println(" route=$route")
+ println(" PlaybackType=$type (1 for local, 2 for remote) cached=$playbackType")
+ println(" routingSession=$routingSession")
+ println(" selectedRoutes=$selectedRoutes")
}
}
+ @WorkerThread
+ override fun onAudioInfoChanged(info: MediaController.PlaybackInfo?) {
+ val newPlaybackType = info?.playbackType ?: PLAYBACK_TYPE_UNKNOWN
+ if (newPlaybackType == playbackType) {
+ return
+ }
+ playbackType = newPlaybackType
+ updateCurrent()
+ }
+
override fun onDeviceListUpdate(devices: List<MediaDevice>?) = bgExecutor.execute {
updateCurrent()
}
diff --git a/packages/SystemUI/src/com/android/systemui/media/MediaTimeoutListener.kt b/packages/SystemUI/src/com/android/systemui/media/MediaTimeoutListener.kt
index 6bd5274fa331..51dbfa733541 100644
--- a/packages/SystemUI/src/com/android/systemui/media/MediaTimeoutListener.kt
+++ b/packages/SystemUI/src/com/android/systemui/media/MediaTimeoutListener.kt
@@ -126,6 +126,7 @@ class MediaTimeoutListener @Inject constructor(
fun destroy() {
mediaController?.unregisterCallback(this)
+ cancellation?.run()
}
override fun onPlaybackStateChanged(state: PlaybackState?) {
@@ -182,4 +183,4 @@ class MediaTimeoutListener @Inject constructor(
cancellation = null
}
}
-} \ No newline at end of file
+}
diff --git a/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputAdapter.java b/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputAdapter.java
index 9fc64d51cdf7..9b6a9ea80ebe 100644
--- a/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputAdapter.java
+++ b/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputAdapter.java
@@ -42,7 +42,7 @@ import java.util.List;
public class MediaOutputAdapter extends MediaOutputBaseAdapter {
private static final String TAG = "MediaOutputAdapter";
- private static final int PAIR_NEW = 1;
+ private static final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG);
public MediaOutputAdapter(MediaOutputController controller) {
super(controller);
@@ -58,11 +58,14 @@ public class MediaOutputAdapter extends MediaOutputBaseAdapter {
@Override
public void onBindViewHolder(@NonNull MediaDeviceBaseViewHolder viewHolder, int position) {
- if (mController.isZeroMode() && position == (mController.getMediaDevices().size())) {
- viewHolder.onBind(PAIR_NEW);
- } else if (position < (mController.getMediaDevices().size())) {
- viewHolder.onBind(((List<MediaDevice>) (mController.getMediaDevices())).get(position));
- } else {
+ final int size = mController.getMediaDevices().size();
+ if (mController.isZeroMode() && position == size) {
+ viewHolder.onBind(CUSTOMIZED_ITEM_PAIR_NEW, false /* topMargin */,
+ true /* bottomMargin */);
+ } else if (position < size) {
+ viewHolder.onBind(((List<MediaDevice>) (mController.getMediaDevices())).get(position),
+ position == 0 /* topMargin */, position == (size - 1) /* bottomMargin */);
+ } else if (DEBUG) {
Log.d(TAG, "Incorrect position: " + position);
}
}
@@ -83,7 +86,7 @@ public class MediaOutputAdapter extends MediaOutputBaseAdapter {
}
void onItemClick(int customizedItem) {
- if (customizedItem == PAIR_NEW) {
+ if (customizedItem == CUSTOMIZED_ITEM_PAIR_NEW) {
mController.launchBluetoothPairing();
}
}
@@ -112,51 +115,49 @@ public class MediaOutputAdapter extends MediaOutputBaseAdapter {
}
@Override
- void onBind(MediaDevice device) {
- super.onBind(device);
+ void onBind(MediaDevice device, boolean topMargin, boolean bottomMargin) {
+ super.onBind(device, topMargin, bottomMargin);
if (mController.isTransferring()) {
if (device.getState() == MediaDeviceState.STATE_CONNECTING
&& !mController.hasAdjustVolumeUserRestriction()) {
- setTwoLineLayout(device, true);
- mProgressBar.setVisibility(View.VISIBLE);
- mSeekBar.setVisibility(View.GONE);
- mSubTitleText.setVisibility(View.GONE);
+ setTwoLineLayout(device, null /* title */, true /* bFocused */,
+ false /* showSeekBar*/, true /* showProgressBar */,
+ false /* showSubtitle */);
} else {
- setSingleLineLayout(getItemTitle(device), false);
+ setSingleLineLayout(getItemTitle(device), false /* bFocused */);
}
} else {
// Set different layout for each device
if (device.getState() == MediaDeviceState.STATE_CONNECTING_FAILED) {
- setTwoLineLayout(device, false);
- mSubTitleText.setVisibility(View.VISIBLE);
- mSeekBar.setVisibility(View.GONE);
- mProgressBar.setVisibility(View.GONE);
+ setTwoLineLayout(device, null /* title */, false /* bFocused */,
+ false /* showSeekBar*/, false /* showProgressBar */,
+ true /* showSubtitle */);
mSubTitleText.setText(R.string.media_output_dialog_connect_failed);
mFrameLayout.setOnClickListener(v -> onItemClick(device));
} else if (!mController.hasAdjustVolumeUserRestriction()
&& isCurrentConnected(device)) {
- setTwoLineLayout(device, true);
- mSeekBar.setVisibility(View.VISIBLE);
- mProgressBar.setVisibility(View.GONE);
- mSubTitleText.setVisibility(View.GONE);
+ setTwoLineLayout(device, null /* title */, true /* bFocused */,
+ true /* showSeekBar*/, false /* showProgressBar */,
+ false /* showSubtitle */);
initSeekbar(device);
} else {
- setSingleLineLayout(getItemTitle(device), false);
+ setSingleLineLayout(getItemTitle(device), false /* bFocused */);
mFrameLayout.setOnClickListener(v -> onItemClick(device));
}
}
}
@Override
- void onBind(int customizedItem) {
- if (customizedItem == PAIR_NEW) {
+ void onBind(int customizedItem, boolean topMargin, boolean bottomMargin) {
+ super.onBind(customizedItem, topMargin, bottomMargin);
+ if (customizedItem == CUSTOMIZED_ITEM_PAIR_NEW) {
setSingleLineLayout(mContext.getText(R.string.media_output_dialog_pairing_new),
- false);
+ false /* bFocused */);
final Drawable d = mContext.getDrawable(R.drawable.ic_add);
d.setColorFilter(new PorterDuffColorFilter(
Utils.getColorAccentDefaultColor(mContext), PorterDuff.Mode.SRC_IN));
mTitleIcon.setImageDrawable(d);
- mFrameLayout.setOnClickListener(v -> onItemClick(PAIR_NEW));
+ mFrameLayout.setOnClickListener(v -> onItemClick(CUSTOMIZED_ITEM_PAIR_NEW));
}
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputBaseAdapter.java b/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputBaseAdapter.java
index 7579c25b030a..01dc6c4b71da 100644
--- a/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputBaseAdapter.java
+++ b/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputBaseAdapter.java
@@ -44,9 +44,12 @@ public abstract class MediaOutputBaseAdapter extends
private static final String FONT_SELECTED_TITLE = "sans-serif-medium";
private static final String FONT_TITLE = "sans-serif";
+ static final int CUSTOMIZED_ITEM_PAIR_NEW = 1;
+
final MediaOutputController mController;
private boolean mIsDragging;
+ private int mMargin;
Context mContext;
View mHolderView;
@@ -60,6 +63,8 @@ public abstract class MediaOutputBaseAdapter extends
public MediaDeviceBaseViewHolder onCreateViewHolder(@NonNull ViewGroup viewGroup,
int viewType) {
mContext = viewGroup.getContext();
+ mMargin = mContext.getResources().getDimensionPixelSize(
+ R.dimen.media_output_dialog_list_margin);
mHolderView = LayoutInflater.from(mContext).inflate(R.layout.media_output_list_item,
viewGroup, false);
@@ -106,12 +111,26 @@ public abstract class MediaOutputBaseAdapter extends
mSeekBar = view.requireViewById(R.id.volume_seekbar);
}
- void onBind(MediaDevice device) {
+ void onBind(MediaDevice device, boolean topMargin, boolean bottomMargin) {
mTitleIcon.setImageIcon(mController.getDeviceIconCompat(device).toIcon(mContext));
+ setMargin(topMargin, bottomMargin);
}
- void onBind(int customizedItem) { }
+ void onBind(int customizedItem, boolean topMargin, boolean bottomMargin) {
+ setMargin(topMargin, bottomMargin);
+ }
+ private void setMargin(boolean topMargin, boolean bottomMargin) {
+ ViewGroup.MarginLayoutParams params = (ViewGroup.MarginLayoutParams) mFrameLayout
+ .getLayoutParams();
+ if (topMargin) {
+ params.topMargin = mMargin;
+ }
+ if (bottomMargin) {
+ params.bottomMargin = mMargin;
+ }
+ mFrameLayout.setLayoutParams(params);
+ }
void setSingleLineLayout(CharSequence title, boolean bFocused) {
mTitleText.setVisibility(View.VISIBLE);
mTwoLineLayout.setVisibility(View.GONE);
@@ -123,10 +142,19 @@ public abstract class MediaOutputBaseAdapter extends
}
}
- void setTwoLineLayout(MediaDevice device, boolean bFocused) {
+ void setTwoLineLayout(MediaDevice device, CharSequence title, boolean bFocused,
+ boolean showSeekBar, boolean showProgressBar, boolean showSubtitle) {
mTitleText.setVisibility(View.GONE);
mTwoLineLayout.setVisibility(View.VISIBLE);
- mTwoLineTitleText.setText(getItemTitle(device));
+ mSeekBar.setVisibility(showSeekBar ? View.VISIBLE : View.GONE);
+ mProgressBar.setVisibility(showProgressBar ? View.VISIBLE : View.GONE);
+ mSubTitleText.setVisibility(showSubtitle ? View.VISIBLE : View.GONE);
+ if (device == null) {
+ mTwoLineTitleText.setText(title);
+ } else {
+ mTwoLineTitleText.setText(getItemTitle(device));
+ }
+
if (bFocused) {
mTwoLineTitleText.setTypeface(Typeface.create(FONT_SELECTED_TITLE,
Typeface.NORMAL));
diff --git a/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputBaseDialog.java b/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputBaseDialog.java
index 781bf8d74d88..ebca8a735ad5 100644
--- a/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputBaseDialog.java
+++ b/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputBaseDialog.java
@@ -33,7 +33,6 @@ import android.view.Window;
import android.view.WindowInsets;
import android.view.WindowManager;
import android.widget.Button;
-import android.widget.FrameLayout;
import android.widget.ImageView;
import android.widget.LinearLayout;
import android.widget.TextView;
@@ -43,7 +42,7 @@ import androidx.core.graphics.drawable.IconCompat;
import androidx.recyclerview.widget.LinearLayoutManager;
import androidx.recyclerview.widget.RecyclerView;
-import com.android.settingslib.R;
+import com.android.systemui.R;
import com.android.systemui.statusbar.phone.SystemUIDialog;
/**
@@ -69,12 +68,9 @@ public abstract class MediaOutputBaseDialog extends SystemUIDialog implements
private LinearLayout mDeviceListLayout;
private Button mDoneButton;
private Button mStopButton;
- private View mListBottomPadding;
private int mListMaxHeight;
MediaOutputBaseAdapter mAdapter;
- FrameLayout mGroupItemController;
- View mGroupDivider;
private final ViewTreeObserver.OnGlobalLayoutListener mDeviceListLayoutListener = () -> {
// Set max height for list
@@ -114,12 +110,9 @@ public abstract class MediaOutputBaseDialog extends SystemUIDialog implements
mHeaderSubtitle = mDialogView.requireViewById(R.id.header_subtitle);
mHeaderIcon = mDialogView.requireViewById(R.id.header_icon);
mDevicesRecyclerView = mDialogView.requireViewById(R.id.list_result);
- mGroupItemController = mDialogView.requireViewById(R.id.group_item_controller);
- mGroupDivider = mDialogView.requireViewById(R.id.group_item_divider);
mDeviceListLayout = mDialogView.requireViewById(R.id.device_list);
mDoneButton = mDialogView.requireViewById(R.id.done);
mStopButton = mDialogView.requireViewById(R.id.stop);
- mListBottomPadding = mDialogView.requireViewById(R.id.list_bottom_padding);
mDeviceListLayout.getViewTreeObserver().addOnGlobalLayoutListener(
mDeviceListLayoutListener);
@@ -162,7 +155,9 @@ public abstract class MediaOutputBaseDialog extends SystemUIDialog implements
}
if (mHeaderIcon.getVisibility() == View.VISIBLE) {
final int size = getHeaderIconSize();
- mHeaderIcon.setLayoutParams(new LinearLayout.LayoutParams(size, size));
+ final int padding = mContext.getResources().getDimensionPixelSize(
+ R.dimen.media_output_dialog_header_icon_padding);
+ mHeaderIcon.setLayoutParams(new LinearLayout.LayoutParams(size + padding, size));
}
// Update title and subtitle
mHeaderTitle.setText(getHeaderText());
@@ -178,12 +173,8 @@ public abstract class MediaOutputBaseDialog extends SystemUIDialog implements
if (!mAdapter.isDragging()) {
mAdapter.notifyDataSetChanged();
}
- // Add extra padding when device amount is less than 6
- if (mMediaOutputController.getMediaDevices().size() < 6) {
- mListBottomPadding.setVisibility(View.VISIBLE);
- } else {
- mListBottomPadding.setVisibility(View.GONE);
- }
+ // Show when remote media session is available
+ mStopButton.setVisibility(getStopButtonVisibility());
}
abstract int getHeaderIconRes();
@@ -196,6 +187,8 @@ public abstract class MediaOutputBaseDialog extends SystemUIDialog implements
abstract CharSequence getHeaderSubtitle();
+ abstract int getStopButtonVisibility();
+
@Override
public void onMediaChanged() {
mMainThreadHandler.post(() -> refresh());
diff --git a/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputController.java b/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputController.java
index 64d20a273931..b1f1bda25961 100644
--- a/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputController.java
+++ b/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputController.java
@@ -24,6 +24,7 @@ import android.graphics.Canvas;
import android.graphics.drawable.BitmapDrawable;
import android.graphics.drawable.Drawable;
import android.media.MediaMetadata;
+import android.media.MediaRoute2Info;
import android.media.RoutingSessionInfo;
import android.media.session.MediaController;
import android.media.session.MediaSessionManager;
@@ -63,7 +64,7 @@ import javax.inject.Inject;
public class MediaOutputController implements LocalMediaManager.DeviceCallback{
private static final String TAG = "MediaOutputController";
- private static final boolean DEBUG = false;
+ private static final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG);
private final String mPackageName;
private final Context mContext;
@@ -406,6 +407,14 @@ public class MediaOutputController implements LocalMediaManager.DeviceCallback{
mActivityStarter.dismissKeyguardThenExecute(postKeyguardAction, null, true);
}
+ boolean isActiveRemoteDevice(@NonNull MediaDevice device) {
+ final List<String> features = device.getFeatures();
+ return (features.contains(MediaRoute2Info.FEATURE_REMOTE_PLAYBACK)
+ || features.contains(MediaRoute2Info.FEATURE_REMOTE_AUDIO_PLAYBACK)
+ || features.contains(MediaRoute2Info.FEATURE_REMOTE_VIDEO_PLAYBACK)
+ || features.contains(MediaRoute2Info.FEATURE_REMOTE_GROUP_PLAYBACK));
+ }
+
private final MediaController.Callback mCb = new MediaController.Callback() {
@Override
public void onMetadataChanged(MediaMetadata metadata) {
diff --git a/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputDialog.java b/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputDialog.java
index ac9d8ce52d88..a892a12f387b 100644
--- a/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputDialog.java
+++ b/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputDialog.java
@@ -45,8 +45,6 @@ public class MediaOutputDialog extends MediaOutputBaseDialog {
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
- mGroupItemController.setVisibility(View.GONE);
- mGroupDivider.setVisibility(View.GONE);
}
@Override
@@ -74,4 +72,10 @@ public class MediaOutputDialog extends MediaOutputBaseDialog {
CharSequence getHeaderSubtitle() {
return mMediaOutputController.getHeaderSubTitle();
}
+
+ @Override
+ int getStopButtonVisibility() {
+ return mMediaOutputController.isActiveRemoteDevice(
+ mMediaOutputController.getCurrentConnectedMediaDevice()) ? View.VISIBLE : View.GONE;
+ }
}
diff --git a/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputDialogFactory.kt b/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputDialogFactory.kt
index bc1dca58990d..4cdca4cbcf1e 100644
--- a/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputDialogFactory.kt
+++ b/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputDialogFactory.kt
@@ -33,10 +33,22 @@ class MediaOutputDialogFactory @Inject constructor(
private val shadeController: ShadeController,
private val starter: ActivityStarter
) {
+ companion object {
+ var mediaOutputDialog: MediaOutputDialog? = null
+ }
+
/** Creates a [MediaOutputDialog] for the given package. */
fun create(packageName: String, aboveStatusBar: Boolean) {
- MediaOutputController(context, packageName, mediaSessionManager, lbm, shadeController,
- starter).run {
+ mediaOutputDialog?.dismiss()
+
+ mediaOutputDialog = MediaOutputController(context, packageName, mediaSessionManager, lbm,
+ shadeController, starter).run {
MediaOutputDialog(context, aboveStatusBar, this) }
}
+
+ /** dismiss [MediaOutputDialog] if exist. */
+ fun dismiss() {
+ mediaOutputDialog?.dismiss()
+ mediaOutputDialog = null
+ }
}
diff --git a/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutDialogReceiver.kt b/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputDialogReceiver.kt
index d60771394ded..0ce0c020ccde 100644
--- a/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutDialogReceiver.kt
+++ b/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputDialogReceiver.kt
@@ -26,8 +26,8 @@ import javax.inject.Inject
/**
* BroadcastReceiver for handling media output intent
*/
-class MediaOutDialogReceiver @Inject constructor(
- private var mediaOutputDialogFactory: MediaOutputDialogFactory
+class MediaOutputDialogReceiver @Inject constructor(
+ private val mediaOutputDialogFactory: MediaOutputDialogFactory
) : BroadcastReceiver() {
override fun onReceive(context: Context, intent: Intent) {
if (TextUtils.equals(MediaOutputSliceConstants.ACTION_LAUNCH_MEDIA_OUTPUT_DIALOG,
diff --git a/packages/SystemUI/src/com/android/systemui/navigationbar/buttons/KeyButtonDrawable.java b/packages/SystemUI/src/com/android/systemui/navigationbar/buttons/KeyButtonDrawable.java
index fc2016913292..702be72ff4ed 100644
--- a/packages/SystemUI/src/com/android/systemui/navigationbar/buttons/KeyButtonDrawable.java
+++ b/packages/SystemUI/src/com/android/systemui/navigationbar/buttons/KeyButtonDrawable.java
@@ -171,6 +171,24 @@ public class KeyButtonDrawable extends Drawable {
}
@Override
+ public boolean setVisible(boolean visible, boolean restart) {
+ boolean changed = super.setVisible(visible, restart);
+ if (changed) {
+ // End any existing animations when the visibility changes
+ jumpToCurrentState();
+ }
+ return changed;
+ }
+
+ @Override
+ public void jumpToCurrentState() {
+ super.jumpToCurrentState();
+ if (mAnimatedDrawable != null) {
+ mAnimatedDrawable.jumpToCurrentState();
+ }
+ }
+
+ @Override
public void setAlpha(int alpha) {
mState.mAlpha = alpha;
mIconPaint.setAlpha(alpha);
diff --git a/packages/SystemUI/src/com/android/systemui/navigationbar/buttons/KeyButtonRipple.java b/packages/SystemUI/src/com/android/systemui/navigationbar/buttons/KeyButtonRipple.java
index 72cd4f1343e6..cf45f52e3367 100644
--- a/packages/SystemUI/src/com/android/systemui/navigationbar/buttons/KeyButtonRipple.java
+++ b/packages/SystemUI/src/com/android/systemui/navigationbar/buttons/KeyButtonRipple.java
@@ -225,6 +225,16 @@ public class KeyButtonRipple extends Drawable {
}
@Override
+ public boolean setVisible(boolean visible, boolean restart) {
+ boolean changed = super.setVisible(visible, restart);
+ if (changed) {
+ // End any existing animations when the visibility changes
+ jumpToCurrentState();
+ }
+ return changed;
+ }
+
+ @Override
public void jumpToCurrentState() {
endAnimations("jumpToCurrentState", false /* cancel */);
}
diff --git a/packages/SystemUI/src/com/android/systemui/navigationbar/gestural/BackGestureTfClassifierProvider.java b/packages/SystemUI/src/com/android/systemui/navigationbar/gestural/BackGestureTfClassifierProvider.java
new file mode 100644
index 000000000000..fbed18389c40
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/navigationbar/gestural/BackGestureTfClassifierProvider.java
@@ -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.navigationbar.gestural;
+
+import android.content.res.AssetManager;
+
+import java.util.HashMap;
+import java.util.Map;
+
+/**
+ * This class can be overridden by a vendor-specific sys UI implementation,
+ * in order to provide classification models for the Back Gesture.
+ */
+public class BackGestureTfClassifierProvider {
+ private static final String TAG = "BackGestureTfClassifierProvider";
+
+ /**
+ * Default implementation that returns an empty map.
+ * This method is overridden in vendor-specific Sys UI implementation.
+ *
+ * @param am An AssetManager to get the vocab file.
+ */
+ public Map<String, Integer> loadVocab(AssetManager am) {
+ return new HashMap<String, Integer>();
+ }
+
+ /**
+ * This method is overridden in vendor-specific Sys UI implementation.
+ *
+ * @param featuresVector List of input features.
+ *
+ */
+ public float predict(Object[] featuresVector) {
+ return -1;
+ }
+
+ /**
+ * Interpreter owns resources. This method releases the resources after
+ * use to avoid memory leak.
+ * This method is overridden in vendor-specific Sys UI implementation.
+ *
+ */
+ public void release() {}
+
+ /**
+ * Returns whether to use the ML model for Back Gesture.
+ * This method is overridden in vendor-specific Sys UI implementation.
+ *
+ */
+ public boolean isActive() {
+ return false;
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/navigationbar/gestural/EdgeBackGestureHandler.java b/packages/SystemUI/src/com/android/systemui/navigationbar/gestural/EdgeBackGestureHandler.java
index dfc82f120c90..56943604e9b3 100644
--- a/packages/SystemUI/src/com/android/systemui/navigationbar/gestural/EdgeBackGestureHandler.java
+++ b/packages/SystemUI/src/com/android/systemui/navigationbar/gestural/EdgeBackGestureHandler.java
@@ -40,7 +40,6 @@ import android.util.Log;
import android.util.TypedValue;
import android.view.Choreographer;
import android.view.ISystemGestureExclusionListener;
-import android.view.InputChannel;
import android.view.InputDevice;
import android.view.InputEvent;
import android.view.InputMonitor;
@@ -56,6 +55,7 @@ import com.android.internal.config.sysui.SystemUiDeviceConfigFlags;
import com.android.internal.policy.GestureNavigationSettingsObserver;
import com.android.systemui.Dependency;
import com.android.systemui.R;
+import com.android.systemui.SystemUIFactory;
import com.android.systemui.broadcast.BroadcastDispatcher;
import com.android.systemui.bubbles.BubbleController;
import com.android.systemui.model.SysUiState;
@@ -79,6 +79,7 @@ import com.android.systemui.tracing.nano.SystemUiTraceProto;
import java.io.PrintWriter;
import java.util.ArrayList;
import java.util.List;
+import java.util.Map;
import java.util.concurrent.Executor;
/**
@@ -120,8 +121,31 @@ public class EdgeBackGestureHandler extends CurrentUserTracker implements Displa
public void onTaskStackChanged() {
mGestureBlockingActivityRunning = isGestureBlockingActivityRunning();
}
+ @Override
+ public void onTaskCreated(int taskId, ComponentName componentName) {
+ if (componentName != null) {
+ mPackageName = componentName.getPackageName();
+ } else {
+ mPackageName = "_UNKNOWN";
+ }
+ }
};
+ private DeviceConfig.OnPropertiesChangedListener mOnPropertiesChangedListener =
+ new DeviceConfig.OnPropertiesChangedListener() {
+ @Override
+ public void onPropertiesChanged(DeviceConfig.Properties properties) {
+ if (DeviceConfig.NAMESPACE_SYSTEMUI.equals(properties.getNamespace())
+ && (properties.getKeyset().contains(
+ SystemUiDeviceConfigFlags.BACK_GESTURE_ML_MODEL_THRESHOLD)
+ || properties.getKeyset().contains(
+ SystemUiDeviceConfigFlags.USE_BACK_GESTURE_ML_MODEL))) {
+ updateMLModelState();
+ }
+ }
+ };
+
+
private final Context mContext;
private final OverviewProxyService mOverviewProxyService;
private final SysUiState mSysUiState;
@@ -177,6 +201,13 @@ public class EdgeBackGestureHandler extends CurrentUserTracker implements Displa
private int mRightInset;
private int mSysUiFlags;
+ // For Tf-Lite model.
+ private BackGestureTfClassifierProvider mBackGestureTfClassifierProvider;
+ private Map<String, Integer> mVocab;
+ private boolean mUseMLModel;
+ private float mMLModelThreshold;
+ private String mPackageName;
+
private final GestureNavigationSettingsObserver mGestureNavigationSettingsObserver;
private final NavigationEdgeBackPlugin.BackCallback mBackCallback =
@@ -242,7 +273,6 @@ public class EdgeBackGestureHandler extends CurrentUserTracker implements Displa
Log.e(TAG, "Failed to add gesture blocking activities", e);
}
}
-
mLongPressTimeout = Math.min(MAX_LONG_PRESS_TIMEOUT,
ViewConfiguration.getLongPressTimeout());
@@ -357,6 +387,7 @@ public class EdgeBackGestureHandler extends CurrentUserTracker implements Displa
mContext.getSystemService(DisplayManager.class).unregisterDisplayListener(this);
mPluginManager.removePluginListener(this);
ActivityManagerWrapper.getInstance().unregisterTaskStackListener(mTaskStackListener);
+ DeviceConfig.removeOnPropertiesChangedListener(mOnPropertiesChangedListener);
try {
WindowManagerGlobal.getWindowManagerService()
@@ -372,6 +403,9 @@ public class EdgeBackGestureHandler extends CurrentUserTracker implements Displa
mContext.getSystemService(DisplayManager.class).registerDisplayListener(this,
mContext.getMainThreadHandler());
ActivityManagerWrapper.getInstance().registerTaskStackListener(mTaskStackListener);
+ DeviceConfig.addOnPropertiesChangedListener(DeviceConfig.NAMESPACE_SYSTEMUI,
+ runnable -> (mContext.getMainThreadHandler()).post(runnable),
+ mOnPropertiesChangedListener);
try {
WindowManagerGlobal.getWindowManagerService()
@@ -393,6 +427,8 @@ public class EdgeBackGestureHandler extends CurrentUserTracker implements Displa
mPluginManager.addPluginListener(
this, NavigationEdgeBackPlugin.class, /*allowMultiple=*/ false);
}
+ // Update the ML model resources.
+ updateMLModelState();
}
@Override
@@ -445,26 +481,87 @@ public class EdgeBackGestureHandler extends CurrentUserTracker implements Displa
}
}
- private boolean isWithinTouchRegion(int x, int y) {
- // Disallow if we are in the bottom gesture area
- if (y >= (mDisplaySize.y - mBottomGestureHeight)) {
- return false;
+ private void updateMLModelState() {
+ boolean newState = mIsEnabled && DeviceConfig.getBoolean(DeviceConfig.NAMESPACE_SYSTEMUI,
+ SystemUiDeviceConfigFlags.USE_BACK_GESTURE_ML_MODEL, false);
+
+ if (newState == mUseMLModel) {
+ return;
}
- // If the point is way too far (twice the margin), it is
- // not interesting to us for logging purposes, nor we
- // should process it. Simply return false and keep
- // mLogGesture = false.
- if (x > 2 * (mEdgeWidthLeft + mLeftInset)
- && x < (mDisplaySize.x - 2 * (mEdgeWidthRight + mRightInset))) {
- return false;
+ if (newState) {
+ mBackGestureTfClassifierProvider = SystemUIFactory.getInstance()
+ .createBackGestureTfClassifierProvider(mContext.getAssets());
+ mMLModelThreshold = DeviceConfig.getFloat(DeviceConfig.NAMESPACE_SYSTEMUI,
+ SystemUiDeviceConfigFlags.BACK_GESTURE_ML_MODEL_THRESHOLD, 0.9f);
+ if (mBackGestureTfClassifierProvider.isActive()) {
+ mVocab = mBackGestureTfClassifierProvider.loadVocab(mContext.getAssets());
+ mUseMLModel = true;
+ return;
+ }
}
- // Denotes whether we should proceed with the gesture.
- // Even if it is false, we may want to log it assuming
- // it is not invalid due to exclusion.
- boolean withinRange = x <= mEdgeWidthLeft + mLeftInset
- || x >= (mDisplaySize.x - mEdgeWidthRight - mRightInset);
+ mUseMLModel = false;
+ if (mBackGestureTfClassifierProvider != null) {
+ mBackGestureTfClassifierProvider.release();
+ mBackGestureTfClassifierProvider = null;
+ }
+ }
+
+ private float getBackGesturePredictionsCategory(int x, int y) {
+ if (!mVocab.containsKey(mPackageName)) {
+ return -1;
+ }
+
+ int distanceFromEdge;
+ int location;
+ if (x <= mDisplaySize.x / 2.0) {
+ location = 1; // left
+ distanceFromEdge = x;
+ } else {
+ location = 2; // right
+ distanceFromEdge = mDisplaySize.x - x;
+ }
+
+ Object[] featuresVector = {
+ new long[]{(long) mDisplaySize.x},
+ new long[]{(long) distanceFromEdge},
+ new long[]{(long) location},
+ new long[]{(long) mVocab.get(mPackageName)},
+ new long[]{(long) y},
+ };
+
+ final float results = mBackGestureTfClassifierProvider.predict(featuresVector);
+ if (results == -1) return -1;
+
+ return results >= mMLModelThreshold ? 1 : 0;
+ }
+
+ private boolean isWithinTouchRegion(int x, int y) {
+ boolean withinRange = false;
+ float results = -1;
+
+ if (mUseMLModel && (results = getBackGesturePredictionsCategory(x, y)) != -1) {
+ withinRange = results == 1 ? true : false;
+ } else {
+ // Disallow if we are in the bottom gesture area
+ if (y >= (mDisplaySize.y - mBottomGestureHeight)) {
+ return false;
+ }
+ // If the point is way too far (twice the margin), it is
+ // not interesting to us for logging purposes, nor we
+ // should process it. Simply return false and keep
+ // mLogGesture = false.
+ if (x > 2 * (mEdgeWidthLeft + mLeftInset)
+ && x < (mDisplaySize.x - 2 * (mEdgeWidthRight + mRightInset))) {
+ return false;
+ }
+ // Denotes whether we should proceed with the gesture.
+ // Even if it is false, we may want to log it assuming
+ // it is not invalid due to exclusion.
+ withinRange = x <= mEdgeWidthLeft + mLeftInset
+ || x >= (mDisplaySize.x - mEdgeWidthRight - mRightInset);
+ }
// Always allow if the user is in a transient sticky immersive state
if (mIsNavBarShownTransiently) {
@@ -666,6 +763,11 @@ public class EdgeBackGestureHandler extends CurrentUserTracker implements Displa
ActivityManager.RunningTaskInfo runningTask =
ActivityManagerWrapper.getInstance().getRunningTask();
ComponentName topActivity = runningTask == null ? null : runningTask.topActivity;
+ if (topActivity != null) {
+ mPackageName = topActivity.getPackageName();
+ } else {
+ mPackageName = "_UNKNOWN";
+ }
return topActivity != null && mGestureBlockingActivities.contains(topActivity);
}
diff --git a/packages/SystemUI/src/com/android/systemui/pip/Pip.java b/packages/SystemUI/src/com/android/systemui/pip/Pip.java
index b068370da9e3..2b115508e525 100644
--- a/packages/SystemUI/src/com/android/systemui/pip/Pip.java
+++ b/packages/SystemUI/src/com/android/systemui/pip/Pip.java
@@ -16,24 +16,21 @@
package com.android.systemui.pip;
-import android.content.res.Configuration;
+import android.app.ActivityManager;
+import android.content.ComponentName;
import android.media.session.MediaController;
+import com.android.systemui.pip.phone.PipTouchHandler;
import com.android.systemui.pip.tv.PipController;
-import com.android.systemui.shared.recents.IPinnedStackAnimationListener;
import java.io.PrintWriter;
+import java.util.function.Consumer;
/**
* Interface to engage picture in picture feature.
*/
public interface Pip {
/**
- * Called when showing Pip menu.
- */
- void showPictureInPictureMenu();
-
- /**
* Registers {@link com.android.systemui.pip.tv.PipController.Listener} that gets called.
* whenever receiving notification on changes in PIP.
*/
@@ -53,6 +50,14 @@ public interface Pip {
}
/**
+ * Dump the current state and information if need.
+ *
+ * @param pw The stream to dump information to.
+ */
+ default void dump(PrintWriter pw) {
+ }
+
+ /**
* Expand PIP, it's possible that specific request to activate the window via Alt-tab.
*/
default void expandPip() {
@@ -64,7 +69,11 @@ public interface Pip {
* @return The state of defined in PipController.
*/
default int getPlaybackState() {
- return 0;
+ return -1;
+ }
+
+ default PipTouchHandler getPipTouchHandler() {
+ return null;
}
/**
@@ -95,9 +104,61 @@ public interface Pip {
}
/**
- * Called when configuration change invoked.
+ * Called whenever an Activity is moved to the pinned stack from another stack.
+ */
+ default void onActivityPinned(String packageName) {
+ }
+
+ /**
+ * Called whenever an Activity is moved from the pinned stack to another stack
+ */
+ default void onActivityUnpinned(ComponentName topActivity) {
+ }
+
+ /**
+ * Called whenever IActivityManager.startActivity is called on an activity that is already
+ * running, but the task is either brought to the front or a new Intent is delivered to it.
+ *
+ * @param task information about the task the activity was relaunched into
+ * @param clearedTask whether or not the launch activity also cleared the task as a part of
+ * starting
+ */
+ default void onActivityRestartAttempt(ActivityManager.RunningTaskInfo task,
+ boolean clearedTask) {
+ }
+
+ /**
+ * Called when display size or font size of settings changed
+ */
+ default void onDensityOrFontScaleChanged() {
+ }
+
+ /**
+ * Called when overlay package change invoked.
*/
- void onConfigurationChanged(Configuration newConfig);
+ default void onOverlayChanged() {
+ }
+
+ /**
+ * Registers the session listener for the current user.
+ */
+ default void registerSessionListenerForCurrentUser() {
+ }
+
+ /**
+ * Called when SysUI state changed.
+ *
+ * @param isSysUiStateValid Is SysUI state valid or not.
+ * @param flag Current SysUI state.
+ */
+ default void onSystemUiStateChanged(boolean isSysUiStateValid, int flag) {
+ }
+
+ /**
+ * Called when task stack changed.
+ */
+ default void onTaskStackChanged() {
+ }
/**
* Removes a {@link PipController.Listener} from PipController.
@@ -137,6 +198,14 @@ public interface Pip {
}
/**
+ * Registers the pinned stack animation listener.
+ *
+ * @param callback The callback of pinned stack animation.
+ */
+ default void setPinnedStackAnimationListener(Consumer<Boolean> callback) {
+ }
+
+ /**
* Set the pinned stack with {@link PipAnimationController.AnimationType}
*
* @param animationType The pre-defined {@link PipAnimationController.AnimationType}
@@ -145,12 +214,9 @@ public interface Pip {
}
/**
- * Registers the pinned stack animation listener.
- *
- * @param listener The listener of pinned stack animation.
+ * Called when showing Pip menu.
*/
- default void setPinnedStackAnimationListener(IPinnedStackAnimationListener listener) {
- }
+ void showPictureInPictureMenu();
/**
* Suspends resizing operation on the Pip until {@link #resumePipResizing} is called.
@@ -159,12 +225,4 @@ public interface Pip {
*/
default void suspendPipResizing(int reason) {
}
-
- /**
- * Dump the current state and information if need.
- *
- * @param pw The stream to dump information to.
- */
- default void dump(PrintWriter pw) {
- }
}
diff --git a/packages/SystemUI/src/com/android/systemui/pip/PipAnimationController.java b/packages/SystemUI/src/com/android/systemui/pip/PipAnimationController.java
index fd8ca8044acf..52ce7fe210ec 100644
--- a/packages/SystemUI/src/com/android/systemui/pip/PipAnimationController.java
+++ b/packages/SystemUI/src/com/android/systemui/pip/PipAnimationController.java
@@ -109,10 +109,11 @@ public class PipAnimationController {
@SuppressWarnings("unchecked")
PipTransitionAnimator getAnimator(SurfaceControl leash, Rect startBounds, Rect endBounds,
- Rect sourceHintRect) {
+ Rect sourceHintRect, @PipAnimationController.TransitionDirection int direction) {
if (mCurrentAnimator == null) {
mCurrentAnimator = setupPipTransitionAnimator(
- PipTransitionAnimator.ofBounds(leash, startBounds, endBounds, sourceHintRect));
+ PipTransitionAnimator.ofBounds(leash, startBounds, endBounds, sourceHintRect,
+ direction));
} else if (mCurrentAnimator.getAnimationType() == ANIM_TYPE_ALPHA
&& mCurrentAnimator.isRunning()) {
// If we are still animating the fade into pip, then just move the surface and ensure
@@ -127,7 +128,8 @@ public class PipAnimationController {
} else {
mCurrentAnimator.cancel();
mCurrentAnimator = setupPipTransitionAnimator(
- PipTransitionAnimator.ofBounds(leash, startBounds, endBounds, sourceHintRect));
+ PipTransitionAnimator.ofBounds(leash, startBounds, endBounds, sourceHintRect,
+ direction));
}
return mCurrentAnimator;
}
@@ -217,7 +219,7 @@ public class PipAnimationController {
public void onAnimationEnd(Animator animation) {
mCurrentValue = mEndValue;
final SurfaceControl.Transaction tx = newSurfaceControlTransaction();
- onEndTransaction(mLeash, tx);
+ onEndTransaction(mLeash, tx, mTransitionDirection);
if (mPipAnimationCallback != null) {
mPipAnimationCallback.onPipAnimationEnd(tx, this);
}
@@ -281,7 +283,8 @@ public class PipAnimationController {
boolean inScaleTransition() {
if (mAnimationType != ANIM_TYPE_BOUNDS) return false;
- return !isInPipDirection(getTransitionDirection());
+ final int direction = getTransitionDirection();
+ return !isInPipDirection(direction) && !isOutPipDirection(direction);
}
/**
@@ -316,7 +319,8 @@ public class PipAnimationController {
void onStartTransaction(SurfaceControl leash, SurfaceControl.Transaction tx) {}
- void onEndTransaction(SurfaceControl leash, SurfaceControl.Transaction tx) {}
+ void onEndTransaction(SurfaceControl leash, SurfaceControl.Transaction tx,
+ @TransitionDirection int transitionDirection) {}
abstract void applySurfaceControlTransaction(SurfaceControl leash,
SurfaceControl.Transaction tx, float fraction);
@@ -357,16 +361,26 @@ public class PipAnimationController {
}
static PipTransitionAnimator<Rect> ofBounds(SurfaceControl leash,
- Rect startValue, Rect endValue, Rect sourceHintRect) {
+ Rect startValue, Rect endValue, Rect sourceHintRect,
+ @PipAnimationController.TransitionDirection int direction) {
// Just for simplicity we'll interpolate between the source rect hint insets and empty
// insets to calculate the window crop
- final Rect initialStartValue = new Rect(startValue);
- final Rect sourceHintRectInsets = sourceHintRect != null
- ? new Rect(sourceHintRect.left - startValue.left,
- sourceHintRect.top - startValue.top,
- startValue.right - sourceHintRect.right,
- startValue.bottom - sourceHintRect.bottom)
- : null;
+ final Rect initialSourceValue;
+ if (isOutPipDirection(direction)) {
+ initialSourceValue = new Rect(endValue);
+ } else {
+ initialSourceValue = new Rect(startValue);
+ }
+
+ final Rect sourceHintRectInsets;
+ if (sourceHintRect == null) {
+ sourceHintRectInsets = null;
+ } else {
+ sourceHintRectInsets = new Rect(sourceHintRect.left - initialSourceValue.left,
+ sourceHintRect.top - initialSourceValue.top,
+ initialSourceValue.right - sourceHintRect.right,
+ initialSourceValue.bottom - sourceHintRect.bottom);
+ }
final Rect sourceInsets = new Rect(0, 0, 0, 0);
// construct new Rect instances in case they are recycled
@@ -382,21 +396,23 @@ public class PipAnimationController {
final Rect end = getEndValue();
Rect bounds = mRectEvaluator.evaluate(fraction, start, end);
setCurrentValue(bounds);
- if (inScaleTransition()) {
- if (isOutPipDirection(getTransitionDirection())) {
+ if (inScaleTransition() || sourceHintRect == null) {
+ if (isOutPipDirection(direction)) {
getSurfaceTransactionHelper().scale(tx, leash, end, bounds);
} else {
getSurfaceTransactionHelper().scale(tx, leash, start, bounds);
}
} else {
- if (sourceHintRectInsets != null) {
- Rect insets = mInsetsEvaluator.evaluate(fraction, sourceInsets,
- sourceHintRectInsets);
- getSurfaceTransactionHelper().scaleAndCrop(tx, leash, initialStartValue,
- bounds, insets);
+ final Rect insets;
+ if (isOutPipDirection(direction)) {
+ insets = mInsetsEvaluator.evaluate(fraction, sourceHintRectInsets,
+ sourceInsets);
} else {
- getSurfaceTransactionHelper().scale(tx, leash, start, bounds);
+ insets = mInsetsEvaluator.evaluate(fraction, sourceInsets,
+ sourceHintRectInsets);
}
+ getSurfaceTransactionHelper().scaleAndCrop(tx, leash,
+ initialSourceValue, bounds, insets);
}
tx.apply();
}
@@ -411,13 +427,20 @@ public class PipAnimationController {
}
@Override
- void onEndTransaction(SurfaceControl leash, SurfaceControl.Transaction tx) {
+ void onEndTransaction(SurfaceControl leash, SurfaceControl.Transaction tx,
+ int transitionDirection) {
// NOTE: intentionally does not apply the transaction here.
// this end transaction should get executed synchronously with the final
// WindowContainerTransaction in task organizer
- getSurfaceTransactionHelper()
- .resetScale(tx, leash, getDestinationBounds())
- .crop(tx, leash, getDestinationBounds());
+ final Rect destBounds = getDestinationBounds();
+ getSurfaceTransactionHelper().resetScale(tx, leash, destBounds);
+ if (transitionDirection == TRANSITION_DIRECTION_LEAVE_PIP) {
+ // Leaving to fullscreen, reset crop to null.
+ tx.setPosition(leash, destBounds.left, destBounds.top);
+ tx.setWindowCrop(leash, 0, 0);
+ } else {
+ getSurfaceTransactionHelper().crop(tx, leash, destBounds);
+ }
}
@Override
diff --git a/packages/SystemUI/src/com/android/systemui/pip/PipSurfaceTransactionHelper.java b/packages/SystemUI/src/com/android/systemui/pip/PipSurfaceTransactionHelper.java
index 3e98169c5b2b..fc724cb539dc 100644
--- a/packages/SystemUI/src/com/android/systemui/pip/PipSurfaceTransactionHelper.java
+++ b/packages/SystemUI/src/com/android/systemui/pip/PipSurfaceTransactionHelper.java
@@ -24,16 +24,14 @@ import android.graphics.RectF;
import android.view.SurfaceControl;
import com.android.systemui.dagger.SysUISingleton;
-import com.android.systemui.statusbar.policy.ConfigurationController;
import com.android.wm.shell.R;
/**
* Abstracts the common operations on {@link SurfaceControl.Transaction} for PiP transition.
*/
@SysUISingleton
-public class PipSurfaceTransactionHelper implements ConfigurationController.ConfigurationListener {
+public class PipSurfaceTransactionHelper {
- private final Context mContext;
private final boolean mEnableCornerRadius;
private int mCornerRadius;
@@ -44,17 +42,21 @@ public class PipSurfaceTransactionHelper implements ConfigurationController.Conf
private final RectF mTmpDestinationRectF = new RectF();
private final Rect mTmpDestinationRect = new Rect();
- public PipSurfaceTransactionHelper(Context context, ConfigurationController configController) {
+ public PipSurfaceTransactionHelper(Context context) {
final Resources res = context.getResources();
- mContext = context;
mEnableCornerRadius = res.getBoolean(R.bool.config_pipEnableRoundCorner);
- configController.addCallback(this);
}
- @Override
- public void onDensityOrFontScaleChanged() {
- final Resources res = mContext.getResources();
- mCornerRadius = res.getDimensionPixelSize(R.dimen.pip_corner_radius);
+ /**
+ * Called when display size or font size of settings changed
+ *
+ * @param context the current context
+ */
+ public void onDensityOrFontScaleChanged(Context context) {
+ if (mEnableCornerRadius) {
+ final Resources res = context.getResources();
+ mCornerRadius = res.getDimensionPixelSize(R.dimen.pip_corner_radius);
+ }
}
/**
diff --git a/packages/SystemUI/src/com/android/systemui/pip/PipTaskOrganizer.java b/packages/SystemUI/src/com/android/systemui/pip/PipTaskOrganizer.java
index 9ca5f2a0dfd1..9cf2751df8ee 100644
--- a/packages/SystemUI/src/com/android/systemui/pip/PipTaskOrganizer.java
+++ b/packages/SystemUI/src/com/android/systemui/pip/PipTaskOrganizer.java
@@ -62,6 +62,7 @@ import com.android.internal.os.SomeArgs;
import com.android.systemui.dagger.SysUISingleton;
import com.android.systemui.pip.phone.PipMenuActivityController;
import com.android.systemui.pip.phone.PipUpdateThread;
+import com.android.systemui.pip.phone.PipUtils;
import com.android.wm.shell.R;
import com.android.wm.shell.ShellTaskOrganizer;
import com.android.wm.shell.common.DisplayController;
@@ -129,7 +130,6 @@ public class PipTaskOrganizer extends TaskOrganizer implements ShellTaskOrganize
}
}
- private final Context mContext;
private final Handler mMainHandler;
private final Handler mUpdateHandler;
private final PipBoundsHandler mPipBoundsHandler;
@@ -248,7 +248,6 @@ public class PipTaskOrganizer extends TaskOrganizer implements ShellTaskOrganize
@NonNull DisplayController displayController,
@NonNull PipUiEventLogger pipUiEventLogger,
@NonNull ShellTaskOrganizer shellTaskOrganizer) {
- mContext = context;
mMainHandler = new Handler(Looper.getMainLooper());
mUpdateHandler = new Handler(PipUpdateThread.get().getLooper(), mUpdateCallbacks);
mPipBoundsHandler = boundsHandler;
@@ -260,6 +259,11 @@ public class PipTaskOrganizer extends TaskOrganizer implements ShellTaskOrganize
mSurfaceControlTransactionFactory = SurfaceControl.Transaction::new;
mSplitScreenOptional = splitScreenOptional;
mTaskOrganizer = shellTaskOrganizer;
+
+ if (!PipUtils.hasSystemFeature(context)) {
+ Log.w(TAG, "Device not support PIP feature");
+ return;
+ }
mTaskOrganizer.addListener(this, WINDOWING_MODE_PINNED);
displayController.addDisplayWindowListener(this);
}
@@ -360,8 +364,8 @@ public class PipTaskOrganizer extends TaskOrganizer implements ShellTaskOrganize
public void onTransactionReady(int id, SurfaceControl.Transaction t) {
t.apply();
scheduleAnimateResizePip(mLastReportedBounds, destinationBounds,
- null /* sourceHintRect */, direction, animationDurationMs,
- null /* updateBoundsCallback */);
+ getValidSourceHintRect(mTaskInfo, destinationBounds), direction,
+ animationDurationMs, null /* updateBoundsCallback */);
mState = State.EXITING_PIP;
}
});
@@ -559,8 +563,8 @@ public class PipTaskOrganizer extends TaskOrganizer implements ShellTaskOrganize
if (Looper.getMainLooper() != Looper.myLooper()) {
throw new RuntimeException("PipMenuView needs to be attached on the main thread.");
}
-
- mPipViewHost = new SurfaceControlViewHost(mContext, mContext.getDisplay(),
+ final Context context = menuView.getContext();
+ mPipViewHost = new SurfaceControlViewHost(context, context.getDisplay(),
(android.os.Binder) null);
mPipMenuSurface = mPipViewHost.getSurfacePackage().getSurfaceControl();
SurfaceControl.Transaction transaction = new SurfaceControl.Transaction();
@@ -653,6 +657,13 @@ public class PipTaskOrganizer extends TaskOrganizer implements ShellTaskOrganize
}
/**
+ * Called when display size or font size of settings changed
+ */
+ public void onDensityOrFontScaleChanged(Context context) {
+ mSurfaceTransactionHelper.onDensityOrFontScaleChanged(context);
+ }
+
+ /**
* TODO(b/152809058): consolidate the display info handling logic in SysUI
*
* @param destinationBoundsOut the current destination bounds will be populated to this param
@@ -998,7 +1009,7 @@ public class PipTaskOrganizer extends TaskOrganizer implements ShellTaskOrganize
return;
}
mPipAnimationController
- .getAnimator(mLeash, currentBounds, destinationBounds, sourceHintRect)
+ .getAnimator(mLeash, currentBounds, destinationBounds, sourceHintRect, direction)
.setTransitionDirection(direction)
.setPipAnimationCallback(mPipAnimationCallback)
.setDuration(durationMs)
diff --git a/packages/SystemUI/src/com/android/systemui/pip/phone/PipController.java b/packages/SystemUI/src/com/android/systemui/pip/phone/PipController.java
index 4f225e2f9590..625304371d5b 100644
--- a/packages/SystemUI/src/com/android/systemui/pip/phone/PipController.java
+++ b/packages/SystemUI/src/com/android/systemui/pip/phone/PipController.java
@@ -16,57 +16,37 @@
package com.android.systemui.pip.phone;
-import static android.app.WindowConfiguration.ACTIVITY_TYPE_UNDEFINED;
import static android.app.WindowConfiguration.WINDOWING_MODE_PINNED;
-import static android.content.pm.PackageManager.FEATURE_PICTURE_IN_PICTURE;
import static com.android.systemui.pip.PipAnimationController.isOutPipDirection;
import android.annotation.Nullable;
import android.app.ActivityManager;
-import android.app.ActivityTaskManager;
-import android.app.ActivityTaskManager.RootTaskInfo;
-import android.app.IActivityManager;
import android.app.RemoteAction;
import android.content.ComponentName;
import android.content.Context;
-import android.content.pm.PackageManager;
import android.content.pm.ParceledListSlice;
-import android.content.res.Configuration;
import android.graphics.Rect;
import android.os.Handler;
import android.os.RemoteException;
import android.os.UserHandle;
import android.os.UserManager;
import android.util.Log;
-import android.util.Pair;
import android.view.DisplayInfo;
import android.view.IPinnedStackController;
import android.window.WindowContainerTransaction;
-import com.android.systemui.Dependency;
-import com.android.systemui.UiOffloadThread;
-import com.android.systemui.broadcast.BroadcastDispatcher;
import com.android.systemui.dagger.SysUISingleton;
-import com.android.systemui.model.SysUiState;
import com.android.systemui.pip.Pip;
import com.android.systemui.pip.PipBoundsHandler;
-import com.android.systemui.pip.PipSurfaceTransactionHelper;
import com.android.systemui.pip.PipTaskOrganizer;
-import com.android.systemui.pip.PipUiEventLogger;
-import com.android.systemui.shared.recents.IPinnedStackAnimationListener;
-import com.android.systemui.shared.system.ActivityManagerWrapper;
-import com.android.systemui.shared.system.InputConsumerController;
-import com.android.systemui.shared.system.PinnedStackListenerForwarder.PinnedStackListener;
-import com.android.systemui.shared.system.TaskStackChangeListener;
-import com.android.systemui.shared.system.WindowManagerWrapper;
-import com.android.systemui.statusbar.policy.ConfigurationController;
-import com.android.systemui.util.DeviceConfigProxy;
-import com.android.systemui.util.FloatingContentCoordinator;
+import com.android.systemui.shared.system.PinnedStackListenerForwarder;
+import com.android.systemui.wmshell.WindowManagerShellWrapper;
import com.android.wm.shell.common.DisplayChangeController;
import com.android.wm.shell.common.DisplayController;
import java.io.PrintWriter;
+import java.util.function.Consumer;
/**
* Manages the picture-in-picture (PIP) UI and states for Phones.
@@ -76,7 +56,6 @@ public class PipController implements Pip, PipTaskOrganizer.PipTransitionCallbac
private static final String TAG = "PipController";
private Context mContext;
- private IActivityManager mActivityManager;
private Handler mHandler = new Handler();
private final DisplayInfo mTmpDisplayInfo = new DisplayInfo();
@@ -85,13 +64,13 @@ public class PipController implements Pip, PipTaskOrganizer.PipTransitionCallbac
protected final Rect mReentryBounds = new Rect();
private DisplayController mDisplayController;
- private InputConsumerController mInputConsumerController;
private PipAppOpsListener mAppOpsListener;
private PipBoundsHandler mPipBoundsHandler;
private PipMediaController mMediaController;
private PipTouchHandler mTouchHandler;
- private PipSurfaceTransactionHelper mPipSurfaceTransactionHelper;
- private IPinnedStackAnimationListener mPinnedStackAnimationRecentsListener;
+ private Consumer<Boolean> mPinnedStackAnimationRecentsCallback;
+ private WindowManagerShellWrapper mWindowManagerShellWrapper;
+
private boolean mIsInFixedRotation;
protected PipMenuActivityController mMenuController;
@@ -158,50 +137,10 @@ public class PipController implements Pip, PipTaskOrganizer.PipTransitionCallbac
};
/**
- * Handler for system task stack changes.
- */
- private final TaskStackChangeListener mTaskStackListener = new TaskStackChangeListener() {
- @Override
- public void onActivityPinned(String packageName, int userId, int taskId, int stackId) {
- mTouchHandler.onActivityPinned();
- mMediaController.onActivityPinned();
- mMenuController.onActivityPinned();
- mAppOpsListener.onActivityPinned(packageName);
-
- Dependency.get(UiOffloadThread.class).execute(() -> {
- WindowManagerWrapper.getInstance().setPipVisibility(true);
- });
- }
-
- @Override
- public void onActivityUnpinned() {
- final Pair<ComponentName, Integer> topPipActivityInfo = PipUtils.getTopPipActivity(
- mContext, mActivityManager);
- final ComponentName topActivity = topPipActivityInfo.first;
- mMenuController.onActivityUnpinned();
- mTouchHandler.onActivityUnpinned(topActivity);
- mAppOpsListener.onActivityUnpinned();
-
- Dependency.get(UiOffloadThread.class).execute(() -> {
- WindowManagerWrapper.getInstance().setPipVisibility(topActivity != null);
- });
- }
-
- @Override
- public void onActivityRestartAttempt(ActivityManager.RunningTaskInfo task,
- boolean homeTaskVisible, boolean clearedTask, boolean wasVisible) {
- if (task.configuration.windowConfiguration.getWindowingMode()
- != WINDOWING_MODE_PINNED) {
- return;
- }
- mTouchHandler.getMotionHelper().expandLeavePip(clearedTask /* skipAnimation */);
- }
- };
-
- /**
* Handler for messages from the PIP controller.
*/
- private class PipControllerPinnedStackListener extends PinnedStackListener {
+ private class PipControllerPinnedStackListener extends
+ PinnedStackListenerForwarder.PinnedStackListener {
@Override
public void onListenerRegistered(IPinnedStackController controller) {
mHandler.post(() -> mTouchHandler.setPinnedStackController(controller));
@@ -239,7 +178,10 @@ public class PipController implements Pip, PipTaskOrganizer.PipTransitionCallbac
@Override
public void onConfigurationChanged() {
- mHandler.post(() -> mPipBoundsHandler.onConfigurationChanged(mContext));
+ mHandler.post(() -> {
+ mPipBoundsHandler.onConfigurationChanged(mContext);
+ mTouchHandler.onConfigurationChanged();
+ });
}
@Override
@@ -251,39 +193,36 @@ public class PipController implements Pip, PipTaskOrganizer.PipTransitionCallbac
}
}
- public ConfigurationController.ConfigurationListener mOverlayChangedListener =
- new ConfigurationController.ConfigurationListener() {
- @Override
- public void onOverlayChanged() {
- mHandler.post(() -> {
- mPipBoundsHandler.onOverlayChanged(mContext, mContext.getDisplay());
- updateMovementBounds(null /* toBounds */,
- false /* fromRotation */, false /* fromImeAdjustment */,
- false /* fromShelfAdjustment */,
- null /* windowContainerTransaction */);
- });
- }
- };
-
- public PipController(Context context, BroadcastDispatcher broadcastDispatcher,
- ConfigurationController configController,
- DeviceConfigProxy deviceConfig,
+ public PipController(Context context,
DisplayController displayController,
- FloatingContentCoordinator floatingContentCoordinator,
- SysUiState sysUiState,
+ PipAppOpsListener pipAppOpsListener,
PipBoundsHandler pipBoundsHandler,
- PipSurfaceTransactionHelper pipSurfaceTransactionHelper,
+ PipMediaController pipMediaController,
+ PipMenuActivityController pipMenuActivityController,
PipTaskOrganizer pipTaskOrganizer,
- PipUiEventLogger pipUiEventLogger) {
+ PipTouchHandler pipTouchHandler,
+ WindowManagerShellWrapper windowManagerShellWrapper
+ ) {
mContext = context;
- mActivityManager = ActivityManager.getService();
- PackageManager pm = context.getPackageManager();
- boolean supportsPip = pm.hasSystemFeature(FEATURE_PICTURE_IN_PICTURE);
- if (!supportsPip) {
+ if (PipUtils.hasSystemFeature(mContext)) {
+ initController(context, displayController, pipAppOpsListener, pipBoundsHandler,
+ pipMediaController, pipMenuActivityController, pipTaskOrganizer,
+ pipTouchHandler, windowManagerShellWrapper);
+ } else {
Log.w(TAG, "Device not support PIP feature");
- return;
}
+ }
+
+ private void initController(Context context,
+ DisplayController displayController,
+ PipAppOpsListener pipAppOpsListener,
+ PipBoundsHandler pipBoundsHandler,
+ PipMediaController pipMediaController,
+ PipMenuActivityController pipMenuActivityController,
+ PipTaskOrganizer pipTaskOrganizer,
+ PipTouchHandler pipTouchHandler,
+ WindowManagerShellWrapper windowManagerShellWrapper) {
// Ensure that we are the primary user's SystemUI.
final int processUser = UserManager.get(context).getUserHandle();
@@ -291,28 +230,15 @@ public class PipController implements Pip, PipTaskOrganizer.PipTransitionCallbac
throw new IllegalStateException("Non-primary Pip component not currently supported.");
}
- try {
- WindowManagerWrapper.getInstance().addPinnedStackListener(
- new PipControllerPinnedStackListener());
- } catch (RemoteException e) {
- Log.e(TAG, "Failed to register pinned stack listener", e);
- }
- ActivityManagerWrapper.getInstance().registerTaskStackListener(mTaskStackListener);
-
+ mWindowManagerShellWrapper = windowManagerShellWrapper;
mDisplayController = displayController;
mPipBoundsHandler = pipBoundsHandler;
- mPipSurfaceTransactionHelper = pipSurfaceTransactionHelper;
mPipTaskOrganizer = pipTaskOrganizer;
mPipTaskOrganizer.registerPipTransitionCallback(this);
- mInputConsumerController = InputConsumerController.getPipInputConsumer();
- mMediaController = new PipMediaController(context, mActivityManager, broadcastDispatcher);
- mMenuController = new PipMenuActivityController(context,
- mMediaController, mInputConsumerController, mPipTaskOrganizer);
- mTouchHandler = new PipTouchHandler(context, mActivityManager,
- mMenuController, mInputConsumerController, mPipBoundsHandler, mPipTaskOrganizer,
- floatingContentCoordinator, deviceConfig, sysUiState, pipUiEventLogger);
- mAppOpsListener = new PipAppOpsListener(context, mActivityManager,
- mTouchHandler.getMotionHelper());
+ mMediaController = pipMediaController;
+ mMenuController = pipMenuActivityController;
+ mTouchHandler = pipTouchHandler;
+ mAppOpsListener = pipAppOpsListener;
displayController.addDisplayChangingController(mRotationController);
displayController.addDisplayWindowListener(mFixedRotationListener);
@@ -322,26 +248,69 @@ public class PipController implements Pip, PipTaskOrganizer.PipTransitionCallbac
context.getDisplay().getDisplayInfo(displayInfo);
mPipBoundsHandler.onDisplayInfoChanged(displayInfo);
- configController.addCallback(mOverlayChangedListener);
-
try {
- RootTaskInfo taskInfo = ActivityTaskManager.getService().getRootTaskInfo(
- WINDOWING_MODE_PINNED, ACTIVITY_TYPE_UNDEFINED);
- if (taskInfo != null) {
- // If SystemUI restart, and it already existed a pinned stack,
- // register the pip input consumer to ensure touch can send to it.
- mInputConsumerController.registerInputConsumer(true /* withSfVsync */);
- }
- } catch (RemoteException | UnsupportedOperationException e) {
- e.printStackTrace();
+ mWindowManagerShellWrapper.addPinnedStackListener(
+ new PipControllerPinnedStackListener());
+ } catch (RemoteException e) {
+ Log.e(TAG, "Failed to register pinned stack listener", e);
}
}
- /**
- * Updates the PIP per configuration changed.
- */
- public void onConfigurationChanged(Configuration newConfig) {
- mTouchHandler.onConfigurationChanged();
+ @Override
+ public void onDensityOrFontScaleChanged() {
+ mHandler.post(() -> {
+ mPipTaskOrganizer.onDensityOrFontScaleChanged(mContext);
+ });
+ }
+
+ @Override
+ public void onActivityPinned(String packageName) {
+ mHandler.post(() -> {
+ mTouchHandler.onActivityPinned();
+ mMediaController.onActivityPinned();
+ mMenuController.onActivityPinned();
+ mAppOpsListener.onActivityPinned(packageName);
+ });
+ }
+
+ @Override
+ public void onActivityUnpinned(ComponentName topActivity) {
+ mHandler.post(() -> {
+ mMenuController.onActivityUnpinned();
+ mTouchHandler.onActivityUnpinned(topActivity);
+ mAppOpsListener.onActivityUnpinned();
+ });
+ }
+
+ @Override
+ public void onActivityRestartAttempt(ActivityManager.RunningTaskInfo task,
+ boolean clearedTask) {
+ if (task.configuration.windowConfiguration.getWindowingMode()
+ != WINDOWING_MODE_PINNED) {
+ return;
+ }
+ mTouchHandler.getMotionHelper().expandLeavePip(clearedTask /* skipAnimation */);
+ }
+
+ @Override
+ public void onOverlayChanged() {
+ mHandler.post(() -> {
+ mPipBoundsHandler.onOverlayChanged(mContext, mContext.getDisplay());
+ updateMovementBounds(null /* toBounds */,
+ false /* fromRotation */, false /* fromImeAdjustment */,
+ false /* fromShelfAdjustment */,
+ null /* windowContainerTransaction */);
+ });
+ }
+
+ @Override
+ public void registerSessionListenerForCurrentUser() {
+ mMediaController.registerSessionListenerForCurrentUser();
+ }
+
+ @Override
+ public void onSystemUiStateChanged(boolean isValidState, int flag) {
+ mTouchHandler.onSystemUiStateChanged(isValidState);
}
/**
@@ -352,6 +321,11 @@ public class PipController implements Pip, PipTaskOrganizer.PipTransitionCallbac
mTouchHandler.getMotionHelper().expandLeavePip(false /* skipAnimation */);
}
+ @Override
+ public PipTouchHandler getPipTouchHandler() {
+ return mTouchHandler;
+ }
+
/**
* Hides the PIP menu.
*/
@@ -397,8 +371,8 @@ public class PipController implements Pip, PipTaskOrganizer.PipTransitionCallbac
}
@Override
- public void setPinnedStackAnimationListener(IPinnedStackAnimationListener listener) {
- mHandler.post(() -> mPinnedStackAnimationRecentsListener = listener);
+ public void setPinnedStackAnimationListener(Consumer<Boolean> callback) {
+ mHandler.post(() -> mPinnedStackAnimationRecentsCallback = callback);
}
@Override
@@ -410,12 +384,8 @@ public class PipController implements Pip, PipTaskOrganizer.PipTransitionCallbac
}
// Disable touches while the animation is running
mTouchHandler.setTouchEnabled(false);
- if (mPinnedStackAnimationRecentsListener != null) {
- try {
- mPinnedStackAnimationRecentsListener.onPinnedStackAnimationStarted();
- } catch (RemoteException e) {
- Log.e(TAG, "Failed to callback recents", e);
- }
+ if (mPinnedStackAnimationRecentsCallback != null) {
+ mPinnedStackAnimationRecentsCallback.accept(true);
}
}
@@ -466,7 +436,6 @@ public class PipController implements Pip, PipTaskOrganizer.PipTransitionCallbac
public void dump(PrintWriter pw) {
final String innerPrefix = " ";
pw.println(TAG);
- mInputConsumerController.dump(pw, innerPrefix);
mMenuController.dump(pw, innerPrefix);
mTouchHandler.dump(pw, innerPrefix);
mPipBoundsHandler.dump(pw, innerPrefix);
diff --git a/packages/SystemUI/src/com/android/systemui/pip/phone/PipMediaController.java b/packages/SystemUI/src/com/android/systemui/pip/phone/PipMediaController.java
index 361aafacdf76..a5b5092ead8c 100644
--- a/packages/SystemUI/src/com/android/systemui/pip/phone/PipMediaController.java
+++ b/packages/SystemUI/src/com/android/systemui/pip/phone/PipMediaController.java
@@ -26,19 +26,14 @@ import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
-import android.graphics.drawable.Drawable;
import android.graphics.drawable.Icon;
import android.media.session.MediaController;
import android.media.session.MediaSession;
import android.media.session.MediaSessionManager;
-import android.media.session.MediaSessionManager.OnActiveSessionsChangedListener;
import android.media.session.PlaybackState;
import android.os.UserHandle;
-import com.android.systemui.Dependency;
import com.android.systemui.R;
-import com.android.systemui.broadcast.BroadcastDispatcher;
-import com.android.systemui.statusbar.policy.UserInfoController;
import java.util.ArrayList;
import java.util.Collections;
@@ -101,17 +96,11 @@ public class PipMediaController {
};
private final MediaSessionManager.OnActiveSessionsChangedListener mSessionsChangedListener =
- new OnActiveSessionsChangedListener() {
- @Override
- public void onActiveSessionsChanged(List<MediaController> controllers) {
- resolveActiveMediaController(controllers);
- }
- };
+ controllers -> resolveActiveMediaController(controllers);
private ArrayList<ActionListener> mListeners = new ArrayList<>();
- public PipMediaController(Context context, IActivityManager activityManager,
- BroadcastDispatcher broadcastDispatcher) {
+ public PipMediaController(Context context, IActivityManager activityManager) {
mContext = context;
mActivityManager = activityManager;
IntentFilter mediaControlFilter = new IntentFilter();
@@ -119,16 +108,12 @@ public class PipMediaController {
mediaControlFilter.addAction(ACTION_PAUSE);
mediaControlFilter.addAction(ACTION_NEXT);
mediaControlFilter.addAction(ACTION_PREV);
- broadcastDispatcher.registerReceiver(mPlayPauseActionReceiver, mediaControlFilter);
+ mContext.registerReceiver(mPlayPauseActionReceiver, mediaControlFilter,
+ UserHandle.USER_ALL);
createMediaActions();
mMediaSessionManager =
(MediaSessionManager) context.getSystemService(Context.MEDIA_SESSION_SERVICE);
-
- // The media session listener needs to be re-registered when switching users
- UserInfoController userInfoController = Dependency.get(UserInfoController.class);
- userInfoController.addCallback((String name, Drawable picture, String userAccount) ->
- registerSessionListenerForCurrentUser());
}
/**
@@ -220,7 +205,7 @@ public class PipMediaController {
/**
* Re-registers the session listener for the current user.
*/
- private void registerSessionListenerForCurrentUser() {
+ public void registerSessionListenerForCurrentUser() {
mMediaSessionManager.removeOnActiveSessionsChangedListener(mSessionsChangedListener);
mMediaSessionManager.addOnActiveSessionsChangedListener(mSessionsChangedListener, null,
UserHandle.USER_CURRENT, null);
diff --git a/packages/SystemUI/src/com/android/systemui/pip/phone/PipMenuActivityController.java b/packages/SystemUI/src/com/android/systemui/pip/phone/PipMenuActivityController.java
index 5b07db6c91a1..4c86ea3aac0c 100644
--- a/packages/SystemUI/src/com/android/systemui/pip/phone/PipMenuActivityController.java
+++ b/packages/SystemUI/src/com/android/systemui/pip/phone/PipMenuActivityController.java
@@ -34,7 +34,6 @@ import android.view.WindowManager;
import com.android.systemui.pip.PipTaskOrganizer;
import com.android.systemui.pip.phone.PipMediaController.ActionListener;
-import com.android.systemui.shared.system.InputConsumerController;
import java.io.PrintWriter;
import java.util.ArrayList;
@@ -86,7 +85,6 @@ public class PipMenuActivityController {
private Context mContext;
private PipTaskOrganizer mPipTaskOrganizer;
private PipMediaController mMediaController;
- private InputConsumerController mInputConsumerController;
private ArrayList<Listener> mListeners = new ArrayList<>();
private ParceledListSlice<RemoteAction> mAppActions;
@@ -104,12 +102,9 @@ public class PipMenuActivityController {
};
public PipMenuActivityController(Context context,
- PipMediaController mediaController, InputConsumerController inputConsumerController,
- PipTaskOrganizer pipTaskOrganizer
- ) {
+ PipMediaController mediaController, PipTaskOrganizer pipTaskOrganizer) {
mContext = context;
mMediaController = mediaController;
- mInputConsumerController = inputConsumerController;
mPipTaskOrganizer = pipTaskOrganizer;
}
@@ -119,12 +114,10 @@ public class PipMenuActivityController {
public void onActivityPinned() {
attachPipMenuView();
- mInputConsumerController.registerInputConsumer(true /* withSfVsync */);
}
public void onActivityUnpinned() {
hideMenu();
- mInputConsumerController.unregisterInputConsumer();
mPipTaskOrganizer.detachPipMenuViewHost();
mPipMenuView = null;
}
@@ -254,7 +247,9 @@ public class PipMenuActivityController {
if (isMenuVisible()) {
// If the menu is visible in either the closed or full state, then hide the menu and
// trigger the animation trigger afterwards
- onStartCallback.run();
+ if (onStartCallback != null) {
+ onStartCallback.run();
+ }
mPipMenuView.hideMenu(onEndCallback);
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/pip/phone/PipMenuView.java b/packages/SystemUI/src/com/android/systemui/pip/phone/PipMenuView.java
index c66f442c4c0d..1c38ab338969 100644
--- a/packages/SystemUI/src/com/android/systemui/pip/phone/PipMenuView.java
+++ b/packages/SystemUI/src/com/android/systemui/pip/phone/PipMenuView.java
@@ -316,7 +316,7 @@ public class PipMenuView extends FrameLayout {
}
void hideMenu(Runnable animationEndCallback) {
- hideMenu(animationEndCallback, true /* notifyMenuVisibility */, false);
+ hideMenu(animationEndCallback, true /* notifyMenuVisibility */, true /* animate */);
}
private void hideMenu(final Runnable animationFinishedRunnable, boolean notifyMenuVisibility,
@@ -394,8 +394,10 @@ public class PipMenuView extends FrameLayout {
// TODO: Check if the action drawable has changed before we reload it
action.getIcon().loadDrawableAsync(mContext, d -> {
- d.setTint(Color.WHITE);
- actionView.setImageDrawable(d);
+ if (d != null) {
+ d.setTint(Color.WHITE);
+ actionView.setImageDrawable(d);
+ }
}, mHandler);
actionView.setContentDescription(action.getContentDescription());
if (action.isEnabled()) {
diff --git a/packages/SystemUI/src/com/android/systemui/pip/phone/PipResizeGestureHandler.java b/packages/SystemUI/src/com/android/systemui/pip/phone/PipResizeGestureHandler.java
index 08d9b2ae21b0..f911b4912e7a 100644
--- a/packages/SystemUI/src/com/android/systemui/pip/phone/PipResizeGestureHandler.java
+++ b/packages/SystemUI/src/com/android/systemui/pip/phone/PipResizeGestureHandler.java
@@ -21,13 +21,6 @@ import static com.android.internal.policy.TaskResizingAlgorithm.CTRL_LEFT;
import static com.android.internal.policy.TaskResizingAlgorithm.CTRL_NONE;
import static com.android.internal.policy.TaskResizingAlgorithm.CTRL_RIGHT;
import static com.android.internal.policy.TaskResizingAlgorithm.CTRL_TOP;
-import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_BOUNCER_SHOWING;
-import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_BUBBLES_EXPANDED;
-import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_GLOBAL_ACTIONS_SHOWING;
-import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_NOTIFICATION_PANEL_EXPANDED;
-import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_QUICK_SETTINGS_EXPANDED;
-import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_STATUS_BAR_KEYGUARD_SHOWING;
-import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_STATUS_BAR_KEYGUARD_SHOWING_OCCLUDED;
import android.content.Context;
import android.content.res.Resources;
@@ -50,11 +43,9 @@ import android.view.ScaleGestureDetector;
import android.view.ViewConfiguration;
import com.android.internal.policy.TaskResizingAlgorithm;
-import com.android.systemui.model.SysUiState;
import com.android.systemui.pip.PipBoundsHandler;
import com.android.systemui.pip.PipTaskOrganizer;
import com.android.systemui.pip.PipUiEventLogger;
-import com.android.systemui.util.DeviceConfigProxy;
import com.android.wm.shell.R;
import java.io.PrintWriter;
@@ -71,21 +62,11 @@ public class PipResizeGestureHandler {
private static final float PINCH_THRESHOLD = 0.05f;
private static final float STARTING_SCALE_FACTOR = 1.0f;
- private static final int INVALID_SYSUI_STATE_MASK =
- SYSUI_STATE_GLOBAL_ACTIONS_SHOWING
- | SYSUI_STATE_STATUS_BAR_KEYGUARD_SHOWING
- | SYSUI_STATE_STATUS_BAR_KEYGUARD_SHOWING_OCCLUDED
- | SYSUI_STATE_BOUNCER_SHOWING
- | SYSUI_STATE_NOTIFICATION_PANEL_EXPANDED
- | SYSUI_STATE_BUBBLES_EXPANDED
- | SYSUI_STATE_QUICK_SETTINGS_EXPANDED;
-
private final Context mContext;
private final PipBoundsHandler mPipBoundsHandler;
private final PipMotionHelper mMotionHelper;
private final int mDisplayId;
private final Executor mMainExecutor;
- private final SysUiState mSysUiState;
private final ScaleGestureDetector mScaleGestureDetector;
private final Region mTmpRegion = new Region();
@@ -110,6 +91,7 @@ public class PipResizeGestureHandler {
private boolean mIsAttached;
private boolean mIsEnabled;
private boolean mEnablePinchResize;
+ private boolean mIsSysUiStateValid;
private boolean mThresholdCrossed;
private boolean mUsingPinchToZoom = false;
private float mScaleFactor = STARTING_SCALE_FACTOR;
@@ -123,9 +105,8 @@ public class PipResizeGestureHandler {
private int mCtrlType;
public PipResizeGestureHandler(Context context, PipBoundsHandler pipBoundsHandler,
- PipMotionHelper motionHelper, DeviceConfigProxy deviceConfig,
- PipTaskOrganizer pipTaskOrganizer, Function<Rect, Rect> movementBoundsSupplier,
- Runnable updateMovementBoundsRunnable, SysUiState sysUiState,
+ PipMotionHelper motionHelper, PipTaskOrganizer pipTaskOrganizer,
+ Function<Rect, Rect> movementBoundsSupplier, Runnable updateMovementBoundsRunnable,
PipUiEventLogger pipUiEventLogger, PipMenuActivityController menuActivityController) {
mContext = context;
mDisplayId = context.getDisplayId();
@@ -135,7 +116,6 @@ public class PipResizeGestureHandler {
mPipTaskOrganizer = pipTaskOrganizer;
mMovementBoundsSupplier = movementBoundsSupplier;
mUpdateMovementBoundsRunnable = updateMovementBoundsRunnable;
- mSysUiState = sysUiState;
mPipMenuActivityController = menuActivityController;
mPipUiEventLogger = pipUiEventLogger;
@@ -202,7 +182,7 @@ public class PipResizeGestureHandler {
DeviceConfig.NAMESPACE_SYSTEMUI,
PIP_PINCH_RESIZE,
/* defaultValue = */ false);
- deviceConfig.addOnPropertiesChangedListener(DeviceConfig.NAMESPACE_SYSTEMUI, mMainExecutor,
+ DeviceConfig.addOnPropertiesChangedListener(DeviceConfig.NAMESPACE_SYSTEMUI, mMainExecutor,
new DeviceConfig.OnPropertiesChangedListener() {
@Override
public void onPropertiesChanged(DeviceConfig.Properties properties) {
@@ -218,6 +198,15 @@ public class PipResizeGestureHandler {
reloadResources();
}
+ /**
+ * Called when SysUI state changed.
+ *
+ * @param isSysUiStateValid Is SysUI valid or not.
+ */
+ public void onSystemUiStateChanged(boolean isSysUiStateValid) {
+ mIsSysUiStateValid = isSysUiStateValid;
+ }
+
private void reloadResources() {
final Resources res = mContext.getResources();
mDelta = res.getDimensionPixelSize(R.dimen.pip_resize_edge_size);
@@ -401,7 +390,7 @@ public class PipResizeGestureHandler {
}
private boolean isInValidSysUiState() {
- return (mSysUiState.getFlags() & INVALID_SYSUI_STATE_MASK) == 0;
+ return mIsSysUiStateValid;
}
private void onDragCornerResize(MotionEvent ev) {
diff --git a/packages/SystemUI/src/com/android/systemui/pip/phone/PipTouchHandler.java b/packages/SystemUI/src/com/android/systemui/pip/phone/PipTouchHandler.java
index 858683c4e2d4..6e2c046d8fb3 100644
--- a/packages/SystemUI/src/com/android/systemui/pip/phone/PipTouchHandler.java
+++ b/packages/SystemUI/src/com/android/systemui/pip/phone/PipTouchHandler.java
@@ -23,7 +23,6 @@ import static com.android.systemui.pip.phone.PipMenuActivityController.MENU_STAT
import static com.android.systemui.pip.phone.PipMenuActivityController.MENU_STATE_NONE;
import android.annotation.SuppressLint;
-import android.app.IActivityManager;
import android.content.ComponentName;
import android.content.Context;
import android.content.res.Resources;
@@ -57,13 +56,10 @@ import androidx.dynamicanimation.animation.SpringForce;
import com.android.internal.annotations.VisibleForTesting;
import com.android.systemui.R;
-import com.android.systemui.model.SysUiState;
import com.android.systemui.pip.PipAnimationController;
import com.android.systemui.pip.PipBoundsHandler;
import com.android.systemui.pip.PipTaskOrganizer;
import com.android.systemui.pip.PipUiEventLogger;
-import com.android.systemui.shared.system.InputConsumerController;
-import com.android.systemui.util.DeviceConfigProxy;
import com.android.systemui.util.DismissCircleView;
import com.android.systemui.util.FloatingContentCoordinator;
import com.android.systemui.util.animation.PhysicsAnimator;
@@ -92,7 +88,6 @@ public class PipTouchHandler {
private final boolean mEnableResize;
private final Context mContext;
private final WindowManager mWindowManager;
- private final IActivityManager mActivityManager;
private final PipBoundsHandler mPipBoundsHandler;
private final PipUiEventLogger mPipUiEventLogger;
@@ -216,18 +211,14 @@ public class PipTouchHandler {
}
@SuppressLint("InflateParams")
- public PipTouchHandler(Context context, IActivityManager activityManager,
+ public PipTouchHandler(Context context,
PipMenuActivityController menuController,
- InputConsumerController inputConsumerController,
PipBoundsHandler pipBoundsHandler,
PipTaskOrganizer pipTaskOrganizer,
FloatingContentCoordinator floatingContentCoordinator,
- DeviceConfigProxy deviceConfig,
- SysUiState sysUiState,
PipUiEventLogger pipUiEventLogger) {
// Initialize the Pip input consumer
mContext = context;
- mActivityManager = activityManager;
mAccessibilityManager = context.getSystemService(AccessibilityManager.class);
mPipBoundsHandler = pipBoundsHandler;
mWindowManager = (WindowManager) mContext.getSystemService(Context.WINDOW_SERVICE);
@@ -238,22 +229,18 @@ public class PipTouchHandler {
mPipBoundsHandler.getSnapAlgorithm(), floatingContentCoordinator);
mPipResizeGestureHandler =
new PipResizeGestureHandler(context, pipBoundsHandler, mMotionHelper,
- deviceConfig, pipTaskOrganizer, this::getMovementBounds,
- this::updateMovementBounds, sysUiState, pipUiEventLogger, menuController);
+ pipTaskOrganizer, this::getMovementBounds,
+ this::updateMovementBounds, pipUiEventLogger, menuController);
mTouchState = new PipTouchState(ViewConfiguration.get(context), mHandler,
() -> mMenuController.showMenuWithDelay(MENU_STATE_FULL, mMotionHelper.getBounds(),
true /* allowMenuTimeout */, willResizeMenu(), shouldShowResizeHandle()),
- menuController::hideMenu);
+ menuController::hideMenu);
Resources res = context.getResources();
mEnableDismissDragToEdge = res.getBoolean(R.bool.config_pipEnableDismissDragToEdge);
mEnableResize = res.getBoolean(R.bool.config_pipEnableResizeForMenu);
reloadResources();
- // Register the listener for input consumer touch events
- inputConsumerController.setInputListener(this::handleTouchEvent);
- inputConsumerController.setRegistrationListener(this::onRegistrationChanged);
-
mFloatingContentCoordinator = floatingContentCoordinator;
mConnection = new PipAccessibilityInteractionConnection(mContext, mMotionHelper,
pipTaskOrganizer, mPipBoundsHandler.getSnapAlgorithm(),
@@ -320,15 +307,12 @@ public class PipTouchHandler {
DeviceConfig.NAMESPACE_SYSTEMUI,
PIP_STASHING,
/* defaultValue = */ false);
- deviceConfig.addOnPropertiesChangedListener(DeviceConfig.NAMESPACE_SYSTEMUI,
+ DeviceConfig.addOnPropertiesChangedListener(DeviceConfig.NAMESPACE_SYSTEMUI,
context.getMainExecutor(),
- new DeviceConfig.OnPropertiesChangedListener() {
- @Override
- public void onPropertiesChanged(DeviceConfig.Properties properties) {
- if (properties.getKeyset().contains(PIP_STASHING)) {
- mEnableStash = properties.getBoolean(
- PIP_STASHING, /* defaultValue = */ false);
- }
+ properties -> {
+ if (properties.getKeyset().contains(PIP_STASHING)) {
+ mEnableStash = properties.getBoolean(
+ PIP_STASHING, /* defaultValue = */ false);
}
});
}
@@ -438,6 +422,15 @@ public class PipTouchHandler {
mShelfHeight = shelfHeight;
}
+ /**
+ * Called when SysUI state changed.
+ *
+ * @param isSysUiStateValid Is SysUI valid or not.
+ */
+ public void onSystemUiStateChanged(boolean isSysUiStateValid) {
+ mPipResizeGestureHandler.onSystemUiStateChanged(isSysUiStateValid);
+ }
+
public void adjustBoundsForRotation(Rect outBounds, Rect curBounds, Rect insetBounds) {
final Rect toMovementBounds = new Rect();
mPipBoundsHandler.getSnapAlgorithm().getMovementBounds(outBounds, insetBounds,
@@ -648,7 +641,10 @@ public class PipTouchHandler {
}
}
- private void onRegistrationChanged(boolean isRegistered) {
+ /**
+ * TODO Add appropriate description
+ */
+ public void onRegistrationChanged(boolean isRegistered) {
mAccessibilityManager.setPictureInPictureActionReplacingConnection(isRegistered
? mConnection : null);
if (!isRegistered && mTouchState.isUserInteracting()) {
@@ -664,7 +660,10 @@ public class PipTouchHandler {
shouldShowResizeHandle());
}
- private boolean handleTouchEvent(InputEvent inputEvent) {
+ /**
+ * TODO Add appropriate description
+ */
+ public boolean handleTouchEvent(InputEvent inputEvent) {
// Skip any non motion events
if (!(inputEvent instanceof MotionEvent)) {
return true;
diff --git a/packages/SystemUI/src/com/android/systemui/pip/phone/PipUtils.java b/packages/SystemUI/src/com/android/systemui/pip/phone/PipUtils.java
index baa8f118f362..1bf6dd7bb9ef 100644
--- a/packages/SystemUI/src/com/android/systemui/pip/phone/PipUtils.java
+++ b/packages/SystemUI/src/com/android/systemui/pip/phone/PipUtils.java
@@ -18,6 +18,7 @@ package com.android.systemui.pip.phone;
import static android.app.WindowConfiguration.ACTIVITY_TYPE_UNDEFINED;
import static android.app.WindowConfiguration.WINDOWING_MODE_PINNED;
+import static android.content.pm.PackageManager.FEATURE_PICTURE_IN_PICTURE;
import android.app.ActivityTaskManager;
import android.app.ActivityTaskManager.RootTaskInfo;
@@ -57,4 +58,14 @@ public class PipUtils {
}
return new Pair<>(null, 0);
}
+
+ /**
+ * The util to check if device has PIP feature
+ *
+ * @param context application context
+ * @return true if device has PIP feature, false otherwise.
+ */
+ public static boolean hasSystemFeature(Context context) {
+ return context.getPackageManager().hasSystemFeature(FEATURE_PICTURE_IN_PICTURE);
+ }
}
diff --git a/packages/SystemUI/src/com/android/systemui/pip/tv/PipController.java b/packages/SystemUI/src/com/android/systemui/pip/tv/PipController.java
index 12a545aa4b02..6ac4e4cbd80d 100644
--- a/packages/SystemUI/src/com/android/systemui/pip/tv/PipController.java
+++ b/packages/SystemUI/src/com/android/systemui/pip/tv/PipController.java
@@ -20,7 +20,7 @@ import static android.app.ActivityTaskManager.INVALID_STACK_ID;
import static android.app.WindowConfiguration.ACTIVITY_TYPE_UNDEFINED;
import static android.app.WindowConfiguration.WINDOWING_MODE_PINNED;
-import android.app.ActivityManager.RunningTaskInfo;
+import android.app.ActivityManager;
import android.app.ActivityTaskManager;
import android.app.ActivityTaskManager.RootTaskInfo;
import android.app.IActivityTaskManager;
@@ -45,23 +45,16 @@ import android.text.TextUtils;
import android.util.Log;
import android.view.DisplayInfo;
-import com.android.systemui.Dependency;
import com.android.systemui.R;
-import com.android.systemui.UiOffloadThread;
-import com.android.systemui.broadcast.BroadcastDispatcher;
import com.android.systemui.dagger.SysUISingleton;
import com.android.systemui.pip.Pip;
import com.android.systemui.pip.PipBoundsHandler;
-import com.android.systemui.pip.PipSurfaceTransactionHelper;
import com.android.systemui.pip.PipTaskOrganizer;
-import com.android.systemui.shared.system.ActivityManagerWrapper;
-import com.android.systemui.shared.system.PinnedStackListenerForwarder.PinnedStackListener;
-import com.android.systemui.shared.system.TaskStackChangeListener;
-import com.android.systemui.shared.system.WindowManagerWrapper;
+import com.android.systemui.shared.system.PinnedStackListenerForwarder;
+import com.android.systemui.wmshell.WindowManagerShellWrapper;
import java.util.ArrayList;
import java.util.List;
-import java.util.Optional;
/**
* Manages the picture-in-picture (PIP) UI and states.
@@ -113,7 +106,6 @@ public class PipController implements Pip, PipTaskOrganizer.PipTransitionCallbac
private Context mContext;
private PipBoundsHandler mPipBoundsHandler;
private PipTaskOrganizer mPipTaskOrganizer;
- private PipSurfaceTransactionHelper mPipSurfaceTransactionHelper;
private IActivityTaskManager mActivityTaskManager;
private MediaSessionManager mMediaSessionManager;
private int mState = STATE_NO_PIP;
@@ -133,6 +125,7 @@ public class PipController implements Pip, PipTaskOrganizer.PipTransitionCallbac
private String[] mLastPackagesResourceGranted;
private PipNotification mPipNotification;
private ParceledListSlice<RemoteAction> mCustomActions;
+ private WindowManagerShellWrapper mWindowManagerShellWrapper;
private int mResizeAnimationDuration;
// Used to calculate the movement bounds
@@ -143,21 +136,9 @@ public class PipController implements Pip, PipTaskOrganizer.PipTransitionCallbac
private boolean mImeVisible;
private int mImeHeightAdjustment;
- private final PinnedStackListener mPinnedStackListener = new PipControllerPinnedStackListener();
-
- private final Runnable mResizePinnedStackRunnable = new Runnable() {
- @Override
- public void run() {
- resizePinnedStack(mResumeResizePinnedStackRunnableState);
- }
- };
- private final Runnable mClosePipRunnable = new Runnable() {
- @Override
- public void run() {
- closePip();
- }
- };
-
+ private final Runnable mResizePinnedStackRunnable =
+ () -> resizePinnedStack(mResumeResizePinnedStackRunnableState);
+ private final Runnable mClosePipRunnable = () -> closePip();
private final BroadcastReceiver mBroadcastReceiver = new BroadcastReceiver() {
@Override
public void onReceive(Context context, Intent intent) {
@@ -175,17 +156,23 @@ public class PipController implements Pip, PipTaskOrganizer.PipTransitionCallbac
}
};
private final MediaSessionManager.OnActiveSessionsChangedListener mActiveMediaSessionListener =
- new MediaSessionManager.OnActiveSessionsChangedListener() {
- @Override
- public void onActiveSessionsChanged(List<MediaController> controllers) {
- updateMediaController(controllers);
- }
- };
+ controllers -> updateMediaController(controllers);
+ private final PinnedStackListenerForwarder.PinnedStackListener mPinnedStackListener =
+ new PipControllerPinnedStackListener();
+
+ @Override
+ public void registerSessionListenerForCurrentUser() {
+ // TODO Need confirm if TV have to re-registers when switch user
+ mMediaSessionManager.removeOnActiveSessionsChangedListener(mActiveMediaSessionListener);
+ mMediaSessionManager.addOnActiveSessionsChangedListener(mActiveMediaSessionListener, null,
+ UserHandle.USER_CURRENT, null);
+ }
/**
* Handler for messages from the PIP controller.
*/
- private class PipControllerPinnedStackListener extends PinnedStackListener {
+ private class PipControllerPinnedStackListener extends
+ PinnedStackListenerForwarder.PinnedStackListener {
@Override
public void onImeVisibilityChanged(boolean imeVisible, int imeHeight) {
mHandler.post(() -> {
@@ -227,18 +214,18 @@ public class PipController implements Pip, PipTaskOrganizer.PipTransitionCallbac
}
}
- public PipController(Context context, BroadcastDispatcher broadcastDispatcher,
+ public PipController(Context context,
PipBoundsHandler pipBoundsHandler,
- PipSurfaceTransactionHelper pipSurfaceTransactionHelper,
- PipTaskOrganizer pipTaskOrganizer) {
+ PipTaskOrganizer pipTaskOrganizer,
+ WindowManagerShellWrapper windowManagerShellWrapper
+ ) {
if (mInitialized) {
return;
}
mInitialized = true;
mContext = context;
- mPipNotification = new PipNotification(context, broadcastDispatcher,
- Optional.of(this).get());
+ mPipNotification = new PipNotification(context, this);
mPipBoundsHandler = pipBoundsHandler;
// Ensure that we have the display info in case we get calls to update the bounds before the
// listener calls back
@@ -248,15 +235,12 @@ public class PipController implements Pip, PipTaskOrganizer.PipTransitionCallbac
mResizeAnimationDuration = context.getResources()
.getInteger(R.integer.config_pipResizeAnimationDuration);
- mPipSurfaceTransactionHelper = pipSurfaceTransactionHelper;
mPipTaskOrganizer = pipTaskOrganizer;
mPipTaskOrganizer.registerPipTransitionCallback(this);
mActivityTaskManager = ActivityTaskManager.getService();
- ActivityManagerWrapper.getInstance().registerTaskStackListener(mTaskStackListener);
IntentFilter intentFilter = new IntentFilter();
intentFilter.addAction(Intent.ACTION_MEDIA_RESOURCE_GRANTED);
- broadcastDispatcher.registerReceiver(mBroadcastReceiver, intentFilter,
- null /* handler */, UserHandle.ALL);
+ mContext.registerReceiver(mBroadcastReceiver, intentFilter, UserHandle.USER_ALL);
// Initialize the last orientation and apply the current configuration
Configuration initialConfig = mContext.getResources().getConfiguration();
@@ -265,10 +249,10 @@ public class PipController implements Pip, PipTaskOrganizer.PipTransitionCallbac
mMediaSessionManager =
(MediaSessionManager) mContext.getSystemService(Context.MEDIA_SESSION_SERVICE);
-
+ mWindowManagerShellWrapper = windowManagerShellWrapper;
try {
- WindowManagerWrapper.getInstance().addPinnedStackListener(mPinnedStackListener);
- } catch (RemoteException | UnsupportedOperationException e) {
+ mWindowManagerShellWrapper.addPinnedStackListener(mPinnedStackListener);
+ } catch (RemoteException e) {
Log.e(TAG, "Failed to register pinned stack listener", e);
}
}
@@ -344,7 +328,6 @@ public class PipController implements Pip, PipTaskOrganizer.PipTransitionCallbac
mListeners.get(i).onPipActivityClosed();
}
mHandler.removeCallbacks(mClosePipRunnable);
- updatePipVisibility(false);
}
/**
@@ -358,7 +341,77 @@ public class PipController implements Pip, PipTaskOrganizer.PipTransitionCallbac
mListeners.get(i).onMoveToFullscreen();
}
resizePinnedStack(STATE_NO_PIP);
- updatePipVisibility(false);
+ }
+
+ @Override
+ public void onActivityPinned(String packageName) {
+ if (DEBUG) Log.d(TAG, "onActivityPinned()");
+
+ RootTaskInfo taskInfo = getPinnedTaskInfo();
+ if (taskInfo == null) {
+ Log.w(TAG, "Cannot find pinned stack");
+ return;
+ }
+ if (DEBUG) Log.d(TAG, "PINNED_STACK:" + taskInfo);
+ mPinnedStackId = taskInfo.taskId;
+ mPipTaskId = taskInfo.childTaskIds[taskInfo.childTaskIds.length - 1];
+ mPipComponentName = ComponentName.unflattenFromString(
+ taskInfo.childTaskNames[taskInfo.childTaskNames.length - 1]);
+ // Set state to STATE_PIP so we show it when the pinned stack animation ends.
+ mState = STATE_PIP;
+ mMediaSessionManager.addOnActiveSessionsChangedListener(
+ mActiveMediaSessionListener, null);
+ updateMediaController(mMediaSessionManager.getActiveSessions(null));
+ for (int i = mListeners.size() - 1; i >= 0; i--) {
+ mListeners.get(i).onPipEntered(packageName);
+ }
+ }
+
+ @Override
+ public void onActivityRestartAttempt(ActivityManager.RunningTaskInfo task,
+ boolean clearedTask) {
+ if (task.configuration.windowConfiguration.getWindowingMode()
+ != WINDOWING_MODE_PINNED) {
+ return;
+ }
+ if (DEBUG) Log.d(TAG, "onPinnedActivityRestartAttempt()");
+
+ // If PIPed activity is launched again by Launcher or intent, make it fullscreen.
+ movePipToFullscreen();
+ }
+
+ @Override
+ public void onTaskStackChanged() {
+ if (DEBUG) Log.d(TAG, "onTaskStackChanged()");
+
+ if (getState() != STATE_NO_PIP) {
+ boolean hasPip = false;
+
+ RootTaskInfo taskInfo = getPinnedTaskInfo();
+ if (taskInfo == null || taskInfo.childTaskIds == null) {
+ Log.w(TAG, "There is nothing in pinned stack");
+ closePipInternal(false);
+ return;
+ }
+ for (int i = taskInfo.childTaskIds.length - 1; i >= 0; --i) {
+ if (taskInfo.childTaskIds[i] == mPipTaskId) {
+ // PIP task is still alive.
+ hasPip = true;
+ break;
+ }
+ }
+ if (!hasPip) {
+ // PIP task doesn't exist anymore in PINNED_STACK.
+ closePipInternal(true);
+ return;
+ }
+ }
+ if (getState() == STATE_PIP) {
+ if (mPipBounds != mDefaultPipBounds) {
+ mPipBounds = mDefaultPipBounds;
+ resizePinnedStack(STATE_PIP);
+ }
+ }
}
/**
@@ -603,80 +656,6 @@ public class PipController implements Pip, PipTaskOrganizer.PipTransitionCallbac
return PLAYBACK_STATE_UNAVAILABLE;
}
- private TaskStackChangeListener mTaskStackListener = new TaskStackChangeListener() {
- @Override
- public void onTaskStackChanged() {
- if (DEBUG) Log.d(TAG, "onTaskStackChanged()");
-
- if (getState() != STATE_NO_PIP) {
- boolean hasPip = false;
-
- RootTaskInfo taskInfo = getPinnedTaskInfo();
- if (taskInfo == null || taskInfo.childTaskIds == null) {
- Log.w(TAG, "There is nothing in pinned stack");
- closePipInternal(false);
- return;
- }
- for (int i = taskInfo.childTaskIds.length - 1; i >= 0; --i) {
- if (taskInfo.childTaskIds[i] == mPipTaskId) {
- // PIP task is still alive.
- hasPip = true;
- break;
- }
- }
- if (!hasPip) {
- // PIP task doesn't exist anymore in PINNED_STACK.
- closePipInternal(true);
- return;
- }
- }
- if (getState() == STATE_PIP) {
- if (mPipBounds != mDefaultPipBounds) {
- mPipBounds = mDefaultPipBounds;
- resizePinnedStack(STATE_PIP);
- }
- }
- }
-
- @Override
- public void onActivityPinned(String packageName, int userId, int taskId, int stackId) {
- if (DEBUG) Log.d(TAG, "onActivityPinned()");
-
- RootTaskInfo taskInfo = getPinnedTaskInfo();
- if (taskInfo == null) {
- Log.w(TAG, "Cannot find pinned stack");
- return;
- }
- if (DEBUG) Log.d(TAG, "PINNED_STACK:" + taskInfo);
- mPinnedStackId = taskInfo.taskId;
- mPipTaskId = taskInfo.childTaskIds[taskInfo.childTaskIds.length - 1];
- mPipComponentName = ComponentName.unflattenFromString(
- taskInfo.childTaskNames[taskInfo.childTaskNames.length - 1]);
- // Set state to STATE_PIP so we show it when the pinned stack animation ends.
- mState = STATE_PIP;
- mMediaSessionManager.addOnActiveSessionsChangedListener(
- mActiveMediaSessionListener, null);
- updateMediaController(mMediaSessionManager.getActiveSessions(null));
- for (int i = mListeners.size() - 1; i >= 0; i--) {
- mListeners.get(i).onPipEntered(packageName);
- }
- updatePipVisibility(true);
- }
-
- @Override
- public void onActivityRestartAttempt(RunningTaskInfo task, boolean homeTaskVisible,
- boolean clearedTask, boolean wasVisible) {
- if (task.configuration.windowConfiguration.getWindowingMode()
- != WINDOWING_MODE_PINNED) {
- return;
- }
- if (DEBUG) Log.d(TAG, "onPinnedActivityRestartAttempt()");
-
- // If PIPed activity is launched again by Launcher or intent, make it fullscreen.
- movePipToFullscreen();
- }
- };
-
@Override
public void onPipTransitionStarted(ComponentName activity, int direction, Rect pipBounds) {
}
@@ -730,12 +709,6 @@ public class PipController implements Pip, PipTaskOrganizer.PipTransitionCallbac
void onMediaControllerChanged();
}
- private void updatePipVisibility(final boolean visible) {
- Dependency.get(UiOffloadThread.class).execute(() -> {
- WindowManagerWrapper.getInstance().setPipVisibility(visible);
- });
- }
-
private String getStateDescription() {
if (mSuspendPipResizingReason == 0) {
return stateToName(mState);
diff --git a/packages/SystemUI/src/com/android/systemui/pip/tv/PipControlsViewController.java b/packages/SystemUI/src/com/android/systemui/pip/tv/PipControlsViewController.java
index 4ecd52f8adf5..8c04a52987c4 100644
--- a/packages/SystemUI/src/com/android/systemui/pip/tv/PipControlsViewController.java
+++ b/packages/SystemUI/src/com/android/systemui/pip/tv/PipControlsViewController.java
@@ -36,6 +36,8 @@ import java.util.Optional;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.concurrent.atomic.AtomicReference;
+import javax.inject.Inject;
+
/**
* Controller for {@link PipControlsView}.
*/
@@ -108,7 +110,7 @@ public class PipControlsViewController {
}
};
-
+ @Inject
public PipControlsViewController(PipControlsView view, Optional<Pip> pipOptional,
LayoutInflater layoutInflater, @Main Handler handler) {
super();
@@ -145,9 +147,10 @@ public class PipControlsViewController {
return;
}
mPipOptional.ifPresent(pip -> {
- if (pip.getPlaybackState() == PipController.PLAYBACK_STATE_PAUSED) {
+ final int playbackState = pip.getPlaybackState();
+ if (playbackState == PipController.PLAYBACK_STATE_PAUSED) {
mMediaController.getTransportControls().play();
- } else if (pip.getPlaybackState() == PipController.PLAYBACK_STATE_PLAYING) {
+ } else if (playbackState == PipController.PLAYBACK_STATE_PLAYING) {
mMediaController.getTransportControls().pause();
}
});
diff --git a/packages/SystemUI/src/com/android/systemui/pip/tv/PipNotification.java b/packages/SystemUI/src/com/android/systemui/pip/tv/PipNotification.java
index 78569edf009d..0666811db3da 100644
--- a/packages/SystemUI/src/com/android/systemui/pip/tv/PipNotification.java
+++ b/packages/SystemUI/src/com/android/systemui/pip/tv/PipNotification.java
@@ -32,11 +32,11 @@ import android.graphics.Bitmap;
import android.media.MediaMetadata;
import android.media.session.MediaController;
import android.media.session.PlaybackState;
+import android.os.UserHandle;
import android.text.TextUtils;
import android.util.Log;
import com.android.internal.messages.nano.SystemMessageProto.SystemMessage;
-import com.android.systemui.broadcast.BroadcastDispatcher;
import com.android.systemui.util.NotificationChannels;
import com.android.wm.shell.R;
@@ -163,8 +163,7 @@ public class PipNotification {
}
};
- public PipNotification(Context context, BroadcastDispatcher broadcastDispatcher,
- PipController pipController) {
+ public PipNotification(Context context, PipController pipController) {
mPackageManager = context.getPackageManager();
mNotificationManager = (NotificationManager) context.getSystemService(
@@ -185,7 +184,7 @@ public class PipNotification {
IntentFilter intentFilter = new IntentFilter();
intentFilter.addAction(ACTION_MENU);
intentFilter.addAction(ACTION_CLOSE);
- broadcastDispatcher.registerReceiver(mEventReceiver, intentFilter);
+ context.registerReceiver(mEventReceiver, intentFilter, UserHandle.USER_ALL);
onConfigurationChanged(context);
}
diff --git a/packages/SystemUI/src/com/android/systemui/pip/tv/dagger/TvPipModule.java b/packages/SystemUI/src/com/android/systemui/pip/tv/dagger/TvPipModule.java
new file mode 100644
index 000000000000..f094854308bc
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/pip/tv/dagger/TvPipModule.java
@@ -0,0 +1,90 @@
+/*
+ * 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.pip.tv.dagger;
+
+import android.app.Activity;
+import android.content.Context;
+
+import com.android.systemui.dagger.SysUISingleton;
+import com.android.systemui.pip.Pip;
+import com.android.systemui.pip.PipBoundsHandler;
+import com.android.systemui.pip.PipSurfaceTransactionHelper;
+import com.android.systemui.pip.PipTaskOrganizer;
+import com.android.systemui.pip.PipUiEventLogger;
+import com.android.systemui.pip.tv.PipController;
+import com.android.systemui.pip.tv.PipMenuActivity;
+import com.android.systemui.pip.tv.PipNotification;
+import com.android.systemui.wmshell.WindowManagerShellWrapper;
+import com.android.wm.shell.ShellTaskOrganizer;
+import com.android.wm.shell.common.DisplayController;
+import com.android.wm.shell.splitscreen.SplitScreen;
+
+import java.util.Optional;
+
+import dagger.Binds;
+import dagger.Module;
+import dagger.Provides;
+import dagger.multibindings.ClassKey;
+import dagger.multibindings.IntoMap;
+
+/**
+ * Dagger module for TV Pip.
+ */
+@Module(subcomponents = {TvPipComponent.class})
+public abstract class TvPipModule {
+
+ /** Inject into PipMenuActivity. */
+ @Binds
+ @IntoMap
+ @ClassKey(PipMenuActivity.class)
+ public abstract Activity providePipMenuActivity(PipMenuActivity activity);
+
+ @SysUISingleton
+ @Provides
+ static Pip providePipController(Context context,
+ PipBoundsHandler pipBoundsHandler,
+ PipTaskOrganizer pipTaskOrganizer,
+ WindowManagerShellWrapper windowManagerShellWrapper) {
+ return new PipController(context, pipBoundsHandler, pipTaskOrganizer,
+ windowManagerShellWrapper);
+ }
+
+ @SysUISingleton
+ @Provides
+ static PipNotification providePipNotification(Context context,
+ PipController pipController) {
+ return new PipNotification(context, pipController);
+ }
+
+ @SysUISingleton
+ @Provides
+ static PipBoundsHandler providesPipBoundsHandler(Context context) {
+ return new PipBoundsHandler(context);
+ }
+
+ @SysUISingleton
+ @Provides
+ static PipTaskOrganizer providesPipTaskOrganizer(Context context,
+ PipBoundsHandler pipBoundsHandler,
+ PipSurfaceTransactionHelper pipSurfaceTransactionHelper,
+ Optional<SplitScreen> splitScreenOptional, DisplayController displayController,
+ PipUiEventLogger pipUiEventLogger, ShellTaskOrganizer shellTaskOrganizer) {
+ return new PipTaskOrganizer(context, pipBoundsHandler,
+ pipSurfaceTransactionHelper, splitScreenOptional, displayController,
+ pipUiEventLogger, shellTaskOrganizer);
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/customize/TileQueryHelper.java b/packages/SystemUI/src/com/android/systemui/qs/customize/TileQueryHelper.java
index db77e08c204b..73c6504b9983 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/customize/TileQueryHelper.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/customize/TileQueryHelper.java
@@ -80,8 +80,6 @@ public class TileQueryHelper {
mFinished = false;
// Enqueue jobs to fetch every system tile and then ever package tile.
addCurrentAndStockTiles(host);
-
- addPackageTiles(host);
}
public boolean isFinished() {
@@ -122,23 +120,86 @@ public class TileQueryHelper {
tile.destroy();
continue;
}
- tile.setListening(this, true);
- tile.refreshState();
- tile.setListening(this, false);
tile.setTileSpec(spec);
tilesToAdd.add(tile);
}
- mBgExecutor.execute(() -> {
- for (QSTile tile : tilesToAdd) {
- final QSTile.State state = tile.getState().copy();
- // Ignore the current state and get the generic label instead.
- state.label = tile.getTileLabel();
- tile.destroy();
- addTile(tile.getTileSpec(), null, state, true);
+ new TileCollector(tilesToAdd, host).startListening();
+ }
+
+ private static class TilePair {
+ QSTile mTile;
+ boolean mReady = false;
+ }
+
+ private class TileCollector implements QSTile.Callback {
+
+ private final List<TilePair> mQSTileList = new ArrayList<>();
+ private final QSTileHost mQSTileHost;
+
+ TileCollector(List<QSTile> tilesToAdd, QSTileHost host) {
+ for (QSTile tile: tilesToAdd) {
+ TilePair pair = new TilePair();
+ pair.mTile = tile;
+ mQSTileList.add(pair);
}
+ mQSTileHost = host;
+ if (tilesToAdd.isEmpty()) {
+ mBgExecutor.execute(this::finished);
+ }
+ }
+
+ private void finished() {
notifyTilesChanged(false);
- });
+ addPackageTiles(mQSTileHost);
+ }
+
+ private void startListening() {
+ for (TilePair pair: mQSTileList) {
+ pair.mTile.addCallback(this);
+ pair.mTile.setListening(this, true);
+ // Make sure that at least one refresh state happens
+ pair.mTile.refreshState();
+ }
+ }
+
+ // This is called in the Bg thread
+ @Override
+ public void onStateChanged(State s) {
+ boolean allReady = true;
+ for (TilePair pair: mQSTileList) {
+ if (!pair.mReady && pair.mTile.isTileReady()) {
+ pair.mTile.removeCallback(this);
+ pair.mTile.setListening(this, false);
+ pair.mReady = true;
+ } else if (!pair.mReady) {
+ allReady = false;
+ }
+ }
+ if (allReady) {
+ for (TilePair pair : mQSTileList) {
+ QSTile tile = pair.mTile;
+ final QSTile.State state = tile.getState().copy();
+ // Ignore the current state and get the generic label instead.
+ state.label = tile.getTileLabel();
+ tile.destroy();
+ addTile(tile.getTileSpec(), null, state, true);
+ }
+ finished();
+ }
+ }
+
+ @Override
+ public void onShowDetail(boolean show) {}
+
+ @Override
+ public void onToggleStateChanged(boolean state) {}
+
+ @Override
+ public void onScanStateChanged(boolean state) {}
+
+ @Override
+ public void onAnnouncementRequested(CharSequence announcement) {}
}
private void addPackageTiles(final QSTileHost host) {
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 255513a31c75..dfd7e2c8fdb7 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSTileImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSTileImpl.java
@@ -90,6 +90,10 @@ public abstract class QSTileImpl<TState extends State> implements QSTile, Lifecy
private static final long DEFAULT_STALE_TIMEOUT = 10 * DateUtils.MINUTE_IN_MILLIS;
protected static final Object ARG_SHOW_TRANSIENT_ENABLING = new Object();
+ private static final int READY_STATE_NOT_READY = 0;
+ private static final int READY_STATE_READYING = 1;
+ private static final int READY_STATE_READY = 2;
+
protected final QSHost mHost;
protected final Context mContext;
// @NonFinalForTesting
@@ -101,6 +105,7 @@ public abstract class QSTileImpl<TState extends State> implements QSTile, Lifecy
protected final ActivityStarter mActivityStarter;
private final UiEventLogger mUiEventLogger;
private final QSLogger mQSLogger;
+ private volatile int mReadyState;
private final ArrayList<Callback> mCallbacks = new ArrayList<>();
private final Object mStaleListener = new Object();
@@ -386,7 +391,11 @@ public abstract class QSTileImpl<TState extends State> implements QSTile, Lifecy
protected void handleRefreshState(Object arg) {
handleUpdateState(mTmpState, arg);
- final boolean changed = mTmpState.copyTo(mState);
+ boolean changed = mTmpState.copyTo(mState);
+ if (mReadyState == READY_STATE_READYING) {
+ mReadyState = READY_STATE_READY;
+ changed = true;
+ }
if (changed) {
mQSLogger.logTileUpdated(mTileSpec, mState);
handleStateChanged();
@@ -459,6 +468,9 @@ public abstract class QSTileImpl<TState extends State> implements QSTile, Lifecy
// should not refresh it anymore.
if (mLifecycle.getCurrentState().equals(DESTROYED)) return;
mLifecycle.setCurrentState(RESUMED);
+ if (mReadyState == READY_STATE_NOT_READY) {
+ mReadyState = READY_STATE_READYING;
+ }
refreshState(); // Ensure we get at least one refresh after listening.
});
}
@@ -531,6 +543,15 @@ public abstract class QSTileImpl<TState extends State> implements QSTile, Lifecy
*/
public abstract CharSequence getTileLabel();
+ /**
+ * @return {@code true} if the tile has refreshed state at least once after having set its
+ * lifecycle to {@link Lifecycle.State#RESUMED}.
+ */
+ @Override
+ public boolean isTileReady() {
+ return mReadyState == READY_STATE_READY;
+ }
+
public static int getColorForState(Context context, int state) {
switch (state) {
case Tile.STATE_UNAVAILABLE:
diff --git a/packages/SystemUI/src/com/android/systemui/recents/OverviewProxyService.java b/packages/SystemUI/src/com/android/systemui/recents/OverviewProxyService.java
index 2a976f546ba4..aa435165d3f3 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/OverviewProxyService.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/OverviewProxyService.java
@@ -16,7 +16,6 @@
package com.android.systemui.recents;
-import static android.content.pm.PackageManager.FEATURE_PICTURE_IN_PICTURE;
import static android.content.pm.PackageManager.MATCH_SYSTEM_ONLY;
import static android.view.MotionEvent.ACTION_CANCEL;
import static android.view.MotionEvent.ACTION_DOWN;
@@ -65,6 +64,7 @@ import android.view.accessibility.AccessibilityManager;
import androidx.annotation.NonNull;
import com.android.internal.accessibility.dialog.AccessibilityButtonChooserActivity;
+import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.policy.ScreenDecorationsUtils;
import com.android.internal.util.ScreenshotHelper;
import com.android.systemui.Dumpable;
@@ -77,6 +77,7 @@ import com.android.systemui.navigationbar.NavigationBarView;
import com.android.systemui.navigationbar.NavigationModeController;
import com.android.systemui.pip.Pip;
import com.android.systemui.pip.PipAnimationController;
+import com.android.systemui.pip.phone.PipUtils;
import com.android.systemui.recents.OverviewProxyService.OverviewProxyListener;
import com.android.systemui.settings.CurrentUserTracker;
import com.android.systemui.shared.recents.IOverviewProxy;
@@ -101,6 +102,7 @@ import java.util.ArrayList;
import java.util.List;
import java.util.Optional;
import java.util.function.BiConsumer;
+import java.util.function.Consumer;
import javax.inject.Inject;
@@ -141,6 +143,7 @@ public class OverviewProxyService extends CurrentUserTracker implements
private Region mActiveNavBarRegion;
+ private IPinnedStackAnimationListener mIPinnedStackAnimationListener;
private IOverviewProxy mOverviewProxy;
private int mConnectionBackoffAttempts;
private boolean mBound;
@@ -155,8 +158,8 @@ public class OverviewProxyService extends CurrentUserTracker implements
private boolean mSupportsRoundedCornersOnWindows;
private int mNavBarMode = NAV_BAR_MODE_3BUTTON;
- private ISystemUiProxy mSysUiProxy = new ISystemUiProxy.Stub() {
-
+ @VisibleForTesting
+ public ISystemUiProxy mSysUiProxy = new ISystemUiProxy.Stub() {
@Override
public void startScreenPinning(int taskId) {
if (!verifyCaller("startScreenPinning")) {
@@ -436,10 +439,11 @@ public class OverviewProxyService extends CurrentUserTracker implements
+ mHasPipFeature);
return;
}
+ mIPinnedStackAnimationListener = listener;
long token = Binder.clearCallingIdentity();
try {
mPipOptional.ifPresent(
- pip -> pip.setPinnedStackAnimationListener(listener));
+ pip -> pip.setPinnedStackAnimationListener(mPinnedStackAnimationCallback));
} finally {
Binder.restoreCallingIdentity(token);
}
@@ -605,6 +609,8 @@ public class OverviewProxyService extends CurrentUserTracker implements
private final StatusBarWindowCallback mStatusBarWindowCallback = this::onStatusBarStateChanged;
private final BiConsumer<Rect, Rect> mSplitScreenBoundsChangeListener =
this::notifySplitScreenBoundsChanged;
+ private final Consumer<Boolean> mPinnedStackAnimationCallback =
+ this::notifyPinnedStackAnimationStarted;
// This is the death handler for the binder from the launcher service
private final IBinder.DeathRecipient mOverviewServiceDeathRcpt
@@ -624,7 +630,7 @@ public class OverviewProxyService extends CurrentUserTracker implements
super(broadcastDispatcher);
mContext = context;
mPipOptional = pipOptional;
- mHasPipFeature = mContext.getPackageManager().hasSystemFeature(FEATURE_PICTURE_IN_PICTURE);
+ mHasPipFeature = PipUtils.hasSystemFeature(mContext);
mStatusBarOptionalLazy = statusBarOptionalLazy;
mHandler = new Handler();
mNavBarControllerLazy = navBarControllerLazy;
@@ -734,6 +740,17 @@ public class OverviewProxyService extends CurrentUserTracker implements
}
}
+ private void notifyPinnedStackAnimationStarted(Boolean isAnimationStarted) {
+ if (mIPinnedStackAnimationListener == null) {
+ return;
+ }
+ try {
+ mIPinnedStackAnimationListener.onPinnedStackAnimationStarted();
+ } catch (RemoteException e) {
+ Log.e(TAG_OPS, "Failed to call onPinnedStackAnimationStarted()", e);
+ }
+ }
+
private void onStatusBarStateChanged(boolean keyguardShowing, boolean keyguardOccluded,
boolean bouncerShowing) {
mSysUiState.setFlag(SYSUI_STATE_STATUS_BAR_KEYGUARD_SHOWING,
@@ -764,7 +781,7 @@ public class OverviewProxyService extends CurrentUserTracker implements
public void cleanupAfterDeath() {
if (mInputFocusTransferStarted) {
- mHandler.post(()-> {
+ mHandler.post(() -> {
mStatusBarOptionalLazy.ifPresent(statusBarLazy -> {
mInputFocusTransferStarted = false;
statusBarLazy.get().onInputFocusTransfer(false, true /* cancel */,
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/dagger/NotificationsModule.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/dagger/NotificationsModule.java
index 23270630c7da..01333f0a47d5 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/dagger/NotificationsModule.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/dagger/NotificationsModule.java
@@ -23,7 +23,6 @@ import android.content.pm.ShortcutManager;
import android.os.Handler;
import android.view.accessibility.AccessibilityManager;
-import com.android.internal.logging.MetricsLogger;
import com.android.internal.logging.UiEventLogger;
import com.android.internal.statusbar.IStatusBarService;
import com.android.systemui.R;
@@ -68,7 +67,6 @@ import com.android.systemui.statusbar.notification.logging.NotificationLogger;
import com.android.systemui.statusbar.notification.logging.NotificationPanelLogger;
import com.android.systemui.statusbar.notification.logging.NotificationPanelLoggerImpl;
import com.android.systemui.statusbar.notification.row.ChannelEditorDialogController;
-import com.android.systemui.statusbar.notification.row.NotificationBlockingHelperManager;
import com.android.systemui.statusbar.notification.row.NotificationGutsManager;
import com.android.systemui.statusbar.notification.row.OnUserInteractionCallback;
import com.android.systemui.statusbar.notification.row.PriorityOnboardingDialogController;
@@ -198,20 +196,6 @@ public interface NotificationsModule {
return new NotificationPanelLoggerImpl();
}
- /** Provides an instance of {@link NotificationBlockingHelperManager} */
- @SysUISingleton
- @Provides
- static NotificationBlockingHelperManager provideNotificationBlockingHelperManager(
- Context context,
- NotificationGutsManager notificationGutsManager,
- NotificationEntryManager notificationEntryManager,
- MetricsLogger metricsLogger,
- GroupMembershipManager groupMembershipManager) {
- return new NotificationBlockingHelperManager(
- context, notificationGutsManager, notificationEntryManager, metricsLogger,
- groupMembershipManager);
- }
-
/** Provides an instance of {@link GroupMembershipManager} */
@SysUISingleton
@Provides
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 adda049951ac..811a72de093c 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
@@ -500,10 +500,6 @@ public class ExpandableNotificationRow extends ActivatableNotificationView
* or is in a whitelist).
*/
public boolean getIsNonblockable() {
- boolean isNonblockable = Dependency.get(NotificationBlockingHelperManager.class)
- .isNonblockable(mEntry.getSbn().getPackageName(),
- mEntry.getChannel().getId());
-
// If the SystemNotifAsyncTask hasn't finished running or retrieved a value, we'll try once
// again, but in-place on the main thread this time. This should rarely ever get called.
if (mEntry != null && mEntry.mIsSystemNotification == null) {
@@ -514,13 +510,12 @@ public class ExpandableNotificationRow extends ActivatableNotificationView
mEntry.mIsSystemNotification = isSystemNotification(mContext, mEntry.getSbn());
}
- isNonblockable |= mEntry.getChannel().isImportanceLockedByOEM();
- isNonblockable |= mEntry.getChannel().isImportanceLockedByCriticalDeviceFunction();
+ boolean isNonblockable = mEntry.getChannel().isImportanceLockedByOEM()
+ || mEntry.getChannel().isImportanceLockedByCriticalDeviceFunction();
if (!isNonblockable && mEntry != null && mEntry.mIsSystemNotification != null) {
if (mEntry.mIsSystemNotification) {
- if (mEntry.getChannel() != null
- && !mEntry.getChannel().isBlockable()) {
+ if (mEntry.getChannel() != null && !mEntry.getChannel().isBlockable()) {
isNonblockable = true;
}
}
@@ -1398,26 +1393,12 @@ public class ExpandableNotificationRow extends ActivatableNotificationView
}
/**
- * Dismisses the notification with the option of showing the blocking helper in-place if we have
- * a negative user sentiment.
+ * Dismisses the notification.
*
* @param fromAccessibility whether this dismiss is coming from an accessibility action
- * @return whether a blocking helper is shown in this row
*/
- public boolean performDismissWithBlockingHelper(boolean fromAccessibility) {
- NotificationBlockingHelperManager manager =
- Dependency.get(NotificationBlockingHelperManager.class);
- boolean isBlockingHelperShown = manager.perhapsShowBlockingHelper(this, mMenuRow);
-
- Dependency.get(MetricsLogger.class).count(NotificationCounters.NOTIFICATION_DISMISSED, 1);
-
- // Continue with dismiss since we don't want the blocking helper to be directly associated
- // with a certain notification.
- performDismiss(fromAccessibility);
- return isBlockingHelperShown;
- }
-
public void performDismiss(boolean fromAccessibility) {
+ Dependency.get(MetricsLogger.class).count(NotificationCounters.NOTIFICATION_DISMISSED, 1);
dismiss(fromAccessibility);
if (mEntry.isClearable()) {
if (mOnUserInteractionCallback != null) {
@@ -2983,7 +2964,7 @@ public class ExpandableNotificationRow extends ActivatableNotificationView
}
switch (action) {
case AccessibilityNodeInfo.ACTION_DISMISS:
- performDismissWithBlockingHelper(true /* fromAccessibility */);
+ performDismiss(true /* fromAccessibility */);
return true;
case AccessibilityNodeInfo.ACTION_COLLAPSE:
case AccessibilityNodeInfo.ACTION_EXPAND:
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationGuts.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationGuts.java
index eeac46a60ac8..7071b73c2ebf 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationGuts.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationGuts.java
@@ -295,10 +295,6 @@ public class NotificationGuts extends FrameLayout {
*/
private void closeControls(int x, int y, boolean save, boolean force) {
// First try to dismiss any blocking helper.
- boolean wasBlockingHelperDismissed =
- Dependency.get(NotificationBlockingHelperManager.class)
- .dismissCurrentBlockingHelper();
-
if (getWindowToken() == null) {
if (mClosedListener != null) {
mClosedListener.onGutsClosed(this);
@@ -307,10 +303,9 @@ public class NotificationGuts extends FrameLayout {
}
if (mGutsContent == null
- || !mGutsContent.handleCloseControls(save, force)
- || wasBlockingHelperDismissed) {
+ || !mGutsContent.handleCloseControls(save, force)) {
// We only want to do a circular reveal if we're not showing the blocking helper.
- animateClose(x, y, !wasBlockingHelperDismissed /* shouldDoCircularReveal */);
+ animateClose(x, y, true /* shouldDoCircularReveal */);
setExposed(false, mNeedsFalsingProtection);
if (mClosedListener != null) {
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 7a12464bd4ff..500de2d29d03 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
@@ -106,7 +106,6 @@ 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.StackScrollerDecorView;
import com.android.systemui.statusbar.phone.HeadsUpAppearanceController;
import com.android.systemui.statusbar.phone.HeadsUpTouchHelper;
@@ -449,12 +448,11 @@ public class NotificationStackScrollLayout extends ViewGroup implements Dumpable
private DismissListener mDismissListener;
private DismissAllAnimationListener mDismissAllAnimationListener;
private NotificationRemoteInputManager mRemoteInputManager;
+ private ShadeController mShadeController;
private final DisplayMetrics mDisplayMetrics = Dependency.get(DisplayMetrics.class);
private final LockscreenGestureLogger mLockscreenGestureLogger =
Dependency.get(LockscreenGestureLogger.class);
- private final VisualStabilityManager mVisualStabilityManager =
- Dependency.get(VisualStabilityManager.class);
protected boolean mClearAllEnabled;
private Interpolator mHideXInterpolator = Interpolators.FAST_OUT_SLOW_IN;
@@ -553,13 +551,6 @@ public class NotificationStackScrollLayout extends ViewGroup implements Dumpable
res.getBoolean(R.bool.config_drawNotificationBackground);
setOutlineProvider(mOutlineProvider);
- // Blocking helper manager wants to know the expanded state, update as well.
- NotificationBlockingHelperManager blockingHelperManager =
- Dependency.get(NotificationBlockingHelperManager.class);
- addOnExpandedHeightChangedListener((height, unused) -> {
- blockingHelperManager.setNotificationShadeExpanded(height);
- });
-
boolean willDraw = mShouldDrawNotificationBackground || DEBUG;
setWillNotDraw(!willDraw);
mBackgroundPaint.setAntiAlias(true);
@@ -580,9 +571,7 @@ public class NotificationStackScrollLayout extends ViewGroup implements Dumpable
if (mFgsSectionView != null) {
return;
}
-
mFgsSectionView = fgsSectionView;
-
addView(mFgsSectionView, -1);
}
@@ -603,7 +592,6 @@ public class NotificationStackScrollLayout extends ViewGroup implements Dumpable
inflateEmptyShadeView();
inflateFooterView();
- mVisualStabilityManager.setVisibilityLocationProvider(this::isInVisibleLocation);
}
/**
@@ -1021,23 +1009,6 @@ public class NotificationStackScrollLayout extends ViewGroup implements Dumpable
}
@ShadeViewRefactor(RefactorComponent.LAYOUT_ALGORITHM)
- public boolean isInVisibleLocation(NotificationEntry entry) {
- ExpandableNotificationRow row = entry.getRow();
- ExpandableViewState childViewState = row.getViewState();
-
- if (childViewState == null) {
- return false;
- }
- if ((childViewState.location & ExpandableViewState.VISIBLE_LOCATIONS) == 0) {
- return false;
- }
- if (row.getVisibility() != View.VISIBLE) {
- return false;
- }
- return true;
- }
-
- @ShadeViewRefactor(RefactorComponent.LAYOUT_ALGORITHM)
private void setMaxLayoutHeight(int maxLayoutHeight) {
mMaxLayoutHeight = maxLayoutHeight;
mShelf.setMaxLayoutHeight(maxLayoutHeight);
@@ -5371,9 +5342,8 @@ public class NotificationStackScrollLayout extends ViewGroup implements Dumpable
}
if (viewsToRemove.isEmpty()) {
- if (closeShade) {
- Dependency.get(ShadeController.class).animateCollapsePanels(
- CommandQueue.FLAG_EXCLUDE_NONE);
+ if (closeShade && mShadeController != null) {
+ mShadeController.animateCollapsePanels(CommandQueue.FLAG_EXCLUDE_NONE);
}
return;
}
@@ -5409,11 +5379,11 @@ public class NotificationStackScrollLayout extends ViewGroup implements Dumpable
final Runnable onSlideAwayAnimationComplete = () -> {
if (closeShade) {
- Dependency.get(ShadeController.class).addPostCollapseAction(() -> {
+ mShadeController.addPostCollapseAction(() -> {
setDismissAllInProgress(false);
onAnimationComplete.run();
});
- Dependency.get(ShadeController.class).animateCollapsePanels(
+ mShadeController.animateCollapsePanels(
CommandQueue.FLAG_EXCLUDE_NONE);
} else {
setDismissAllInProgress(false);
@@ -5697,6 +5667,10 @@ public class NotificationStackScrollLayout extends ViewGroup implements Dumpable
mRemoteInputManager = remoteInputManager;
}
+ void setShadeController(ShadeController shadeController) {
+ mShadeController = shadeController;
+ }
+
/**
* A listener that is notified when the empty space below the notifications is clicked on
*/
@@ -6096,7 +6070,7 @@ public class NotificationStackScrollLayout extends ViewGroup implements Dumpable
if (!mAmbientState.isDozing() || startingChild != null) {
// We have notifications, go to locked shade.
- Dependency.get(ShadeController.class).goToLockedShade(startingChild);
+ mShadeController.goToLockedShade(startingChild);
if (startingChild instanceof ExpandableNotificationRow) {
ExpandableNotificationRow row = (ExpandableNotificationRow) startingChild;
row.onExpandedByGesture(true /* drag down is always an open */);
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutController.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutController.java
index 231ff2cec8f5..703c214ed3ac 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutController.java
@@ -21,11 +21,11 @@ import static android.service.notification.NotificationStats.DISMISS_SENTIMENT_N
import static com.android.systemui.Dependency.ALLOW_NOTIFICATION_LONG_PRESS_NAME;
import static com.android.systemui.statusbar.StatusBarState.KEYGUARD;
-import static com.android.systemui.statusbar.notification.stack.NotificationStackScrollLayout.ROWS_GENTLE;
-import static com.android.systemui.statusbar.notification.stack.NotificationStackScrollLayout.ROWS_HIGH_PRIORITY;
import static com.android.systemui.statusbar.notification.stack.NotificationStackScrollLayout.OnEmptySpaceClickListener;
import static com.android.systemui.statusbar.notification.stack.NotificationStackScrollLayout.OnOverscrollTopChangedListener;
import static com.android.systemui.statusbar.notification.stack.NotificationStackScrollLayout.ROWS_ALL;
+import static com.android.systemui.statusbar.notification.stack.NotificationStackScrollLayout.ROWS_GENTLE;
+import static com.android.systemui.statusbar.notification.stack.NotificationStackScrollLayout.ROWS_HIGH_PRIORITY;
import static com.android.systemui.statusbar.notification.stack.NotificationStackScrollLayout.SelectedRows;
import static com.android.systemui.statusbar.notification.stack.NotificationStackScrollLayout.canChildBeDismissed;
import static com.android.systemui.statusbar.phone.NotificationIconAreaController.HIGH_PRIORITY;
@@ -85,6 +85,7 @@ import com.android.systemui.statusbar.notification.collection.NotificationEntry;
import com.android.systemui.statusbar.notification.collection.legacy.NotificationGroupManagerLegacy;
import com.android.systemui.statusbar.notification.collection.legacy.NotificationGroupManagerLegacy.NotificationGroup;
import com.android.systemui.statusbar.notification.collection.legacy.NotificationGroupManagerLegacy.OnGroupChangeListener;
+import com.android.systemui.statusbar.notification.collection.legacy.VisualStabilityManager;
import com.android.systemui.statusbar.notification.collection.notifcollection.DismissedByUserStats;
import com.android.systemui.statusbar.notification.collection.notifcollection.NotifCollectionListener;
import com.android.systemui.statusbar.notification.collection.render.GroupExpansionManager;
@@ -104,6 +105,7 @@ import com.android.systemui.statusbar.phone.HeadsUpTouchHelper;
import com.android.systemui.statusbar.phone.KeyguardBypassController;
import com.android.systemui.statusbar.phone.NotificationPanelViewController;
import com.android.systemui.statusbar.phone.ScrimController;
+import com.android.systemui.statusbar.phone.ShadeController;
import com.android.systemui.statusbar.phone.StatusBar;
import com.android.systemui.statusbar.phone.dagger.StatusBarComponent;
import com.android.systemui.statusbar.policy.ConfigurationController;
@@ -139,7 +141,6 @@ public class NotificationStackScrollLayoutController {
private final ZenModeController mZenModeController;
private final MetricsLogger mMetricsLogger;
private final FalsingManager mFalsingManager;
- private final NotificationSectionsManager mNotificationSectionsManager;
private final Resources mResources;
private final NotificationSwipeHelper.Builder mNotificationSwipeHelperBuilder;
private final ScrimController mScrimController;
@@ -153,6 +154,8 @@ public class NotificationStackScrollLayoutController {
private final ForegroundServiceSectionController mFgServicesSectionController;
private final LayoutInflater mLayoutInflater;
private final NotificationRemoteInputManager mRemoteInputManager;
+ private final VisualStabilityManager mVisualStabilityManager;
+ private final ShadeController mShadeController;
private final KeyguardMediaController mKeyguardMediaController;
private final SysuiStatusBarStateController mStatusBarStateController;
private final KeyguardBypassController mKeyguardBypassController;
@@ -389,8 +392,6 @@ public class NotificationStackScrollLayoutController {
return;
}
- boolean isBlockingHelperShown = false;
-
mView.removeDraggedView(view);
mView.updateContinuousShadowDrawing();
@@ -400,13 +401,10 @@ public class NotificationStackScrollLayoutController {
mHeadsUpManager.addSwipedOutNotification(
row.getEntry().getSbn().getKey());
}
- isBlockingHelperShown =
- row.performDismissWithBlockingHelper(false /* fromAccessibility */);
+ row.performDismiss(false /* fromAccessibility */);
}
- if (!isBlockingHelperShown) {
- mView.addSwipedOutView(view);
- }
+ mView.addSwipedOutView(view);
mFalsingManager.onNotificationDismissed();
if (mFalsingManager.shouldEnforceBouncer()) {
mStatusBar.executeRunnableDismissingKeyguard(
@@ -559,7 +557,6 @@ public class NotificationStackScrollLayoutController {
NotificationLockscreenUserManager lockscreenUserManager,
MetricsLogger metricsLogger,
FalsingManager falsingManager,
- NotificationSectionsManager notificationSectionsManager,
@Main Resources resources,
NotificationSwipeHelper.Builder notificationSwipeHelperBuilder,
StatusBar statusBar,
@@ -576,7 +573,9 @@ public class NotificationStackScrollLayoutController {
ForegroundServiceDismissalFeatureController fgFeatureController,
ForegroundServiceSectionController fgServicesSectionController,
LayoutInflater layoutInflater,
- NotificationRemoteInputManager remoteInputManager) {
+ NotificationRemoteInputManager remoteInputManager,
+ VisualStabilityManager visualStabilityManager,
+ ShadeController shadeController) {
mAllowLongPress = allowLongPress;
mNotificationGutsManager = notificationGutsManager;
mHeadsUpManager = headsUpManager;
@@ -592,14 +591,12 @@ public class NotificationStackScrollLayoutController {
mLockscreenUserManager = lockscreenUserManager;
mMetricsLogger = metricsLogger;
mFalsingManager = falsingManager;
- mNotificationSectionsManager = notificationSectionsManager;
mResources = resources;
mNotificationSwipeHelperBuilder = notificationSwipeHelperBuilder;
mStatusBar = statusBar;
mScrimController = scrimController;
- groupManager.registerGroupExpansionChangeListener((changedRow, expanded) -> {
- mView.onGroupExpandChanged(changedRow, expanded);
- });
+ groupManager.registerGroupExpansionChangeListener(
+ (changedRow, expanded) -> mView.onGroupExpandChanged(changedRow, expanded));
legacyGroupManager.registerGroupChangeListener(new OnGroupChangeListener() {
@Override
public void onGroupCreatedFromChildren(NotificationGroup group) {
@@ -622,6 +619,8 @@ public class NotificationStackScrollLayoutController {
mFgServicesSectionController = fgServicesSectionController;
mLayoutInflater = layoutInflater;
mRemoteInputManager = remoteInputManager;
+ mVisualStabilityManager = visualStabilityManager;
+ mShadeController = shadeController;
}
public void attach(NotificationStackScrollLayout view) {
@@ -635,6 +634,7 @@ public class NotificationStackScrollLayoutController {
mView.setFooterDismissListener(() ->
mMetricsLogger.action(MetricsEvent.ACTION_DISMISS_ALL_NOTES));
mView.setRemoteInputManager(mRemoteInputManager);
+ mView.setShadeController(mShadeController);
if (mFgFeatureController.isForegroundServiceDismissalEnabled()) {
mView.initializeForegroundServiceSection(
@@ -681,6 +681,8 @@ public class NotificationStackScrollLayoutController {
mNotificationRoundnessManager.setOnRoundingChangedCallback(mView::invalidate);
mView.addOnExpandedHeightChangedListener(mNotificationRoundnessManager::setExpanded);
+ mVisualStabilityManager.setVisibilityLocationProvider(this::isInVisibleLocation);
+
mTunerService.addTunable(
(key, newValue) -> {
switch (key) {
@@ -723,6 +725,22 @@ public class NotificationStackScrollLayoutController {
mSilentHeaderController.setOnClearAllClickListener(v -> clearSilentNotifications());
}
+ private boolean isInVisibleLocation(NotificationEntry entry) {
+ ExpandableNotificationRow row = entry.getRow();
+ ExpandableViewState childViewState = row.getViewState();
+
+ if (childViewState == null) {
+ return false;
+ }
+ if ((childViewState.location & ExpandableViewState.VISIBLE_LOCATIONS) == 0) {
+ return false;
+ }
+ if (row.getVisibility() != View.VISIBLE) {
+ return false;
+ }
+ return true;
+ }
+
public void addOnExpandedHeightChangedListener(BiConsumer<Float, Float> listener) {
mView.addOnExpandedHeightChangedListener(listener);
}
@@ -1459,7 +1477,7 @@ public class NotificationStackScrollLayoutController {
@Override
public boolean isInVisibleLocation(NotificationEntry entry) {
- return mView.isInVisibleLocation(entry);
+ return NotificationStackScrollLayoutController.this.isInVisibleLocation(entry);
}
@Override
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardBouncer.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardBouncer.java
index 3665c39bd9ea..af6ac223ada1 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardBouncer.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardBouncer.java
@@ -40,12 +40,15 @@ import com.android.keyguard.ViewMediatorCallback;
import com.android.keyguard.dagger.KeyguardBouncerComponent;
import com.android.keyguard.dagger.RootView;
import com.android.systemui.DejankUtils;
+import com.android.systemui.biometrics.AuthController;
import com.android.systemui.keyguard.DismissCallbackRegistry;
import com.android.systemui.plugins.FalsingManager;
import com.android.systemui.shared.system.SysUiStatsLog;
import com.android.systemui.statusbar.policy.KeyguardStateController;
import java.io.PrintWriter;
+import java.util.ArrayList;
+import java.util.List;
import javax.inject.Inject;
@@ -66,7 +69,8 @@ public class KeyguardBouncer {
private final FalsingManager mFalsingManager;
private final DismissCallbackRegistry mDismissCallbackRegistry;
private final Handler mHandler;
- private final BouncerExpansionCallback mExpansionCallback;
+ private final List<BouncerExpansionCallback> mExpansionCallbacks = new ArrayList<>();
+ private final AuthController mAuthController;
private final KeyguardUpdateMonitor mKeyguardUpdateMonitor;
private final KeyguardStateController mKeyguardStateController;
private final KeyguardSecurityModel mKeyguardSecurityModel;
@@ -100,6 +104,7 @@ public class KeyguardBouncer {
ViewGroup container,
DismissCallbackRegistry dismissCallbackRegistry, FalsingManager falsingManager,
BouncerExpansionCallback expansionCallback,
+ AuthController authController,
KeyguardStateController keyguardStateController,
KeyguardUpdateMonitor keyguardUpdateMonitor,
KeyguardBypassController keyguardBypassController, Handler handler,
@@ -111,13 +116,15 @@ public class KeyguardBouncer {
mKeyguardUpdateMonitor = keyguardUpdateMonitor;
mFalsingManager = falsingManager;
mDismissCallbackRegistry = dismissCallbackRegistry;
- mExpansionCallback = expansionCallback;
mHandler = handler;
mKeyguardStateController = keyguardStateController;
mKeyguardSecurityModel = keyguardSecurityModel;
mKeyguardBouncerComponentFactory = keyguardBouncerComponentFactory;
mKeyguardUpdateMonitor.registerCallback(mUpdateMonitorCallback);
mKeyguardBypassController = keyguardBypassController;
+ mExpansionCallbacks.add(expansionCallback);
+ mExpansionCallbacks.add(authController);
+ mAuthController = authController;
}
public void show(boolean resetSecuritySelection) {
@@ -188,7 +195,7 @@ public class KeyguardBouncer {
}
mCallback.onBouncerVisiblityChanged(true /* shown */);
- mExpansionCallback.onStartingToShow();
+ dispatchStartingToShow();
}
public boolean isScrimmed() {
@@ -290,6 +297,11 @@ public class KeyguardBouncer {
mIsScrimmed = false;
mFalsingManager.onBouncerHidden();
mCallback.onBouncerVisiblityChanged(false /* shown */);
+ // TODO(b/165257355): `mAuthController.onFullyHidden` should be `dispatchFullyHidden()`
+ // But, it is causing the UDFPS icon to disappear after SystemUI restarts. I guess the
+ // ExpansionCallback from StatusBarKeyguardViewManager can't handle the call to
+ // onFullyHidden after a restart.
+ mAuthController.onFullyHidden();
cancelShowRunnable();
if (mKeyguardViewController != null) {
mKeyguardViewController.cancelDismissAction();
@@ -382,12 +394,12 @@ public class KeyguardBouncer {
if (fraction == EXPANSION_VISIBLE && oldExpansion != EXPANSION_VISIBLE) {
onFullyShown();
- mExpansionCallback.onFullyShown();
+ dispatchFullyShown();
} else if (fraction == EXPANSION_HIDDEN && oldExpansion != EXPANSION_HIDDEN) {
onFullyHidden();
- mExpansionCallback.onFullyHidden();
+ dispatchFullyHidden();
} else if (fraction != EXPANSION_VISIBLE && oldExpansion == EXPANSION_VISIBLE) {
- mExpansionCallback.onStartingToHide();
+ dispatchStartingToHide();
if (mKeyguardViewController != null) {
mKeyguardViewController.onStartingToHide();
}
@@ -492,6 +504,30 @@ public class KeyguardBouncer {
mKeyguardViewController.finish(strongAuth, KeyguardUpdateMonitor.getCurrentUser());
}
+ private void dispatchFullyShown() {
+ for (BouncerExpansionCallback callback : mExpansionCallbacks) {
+ callback.onFullyShown();
+ }
+ }
+
+ private void dispatchStartingToHide() {
+ for (BouncerExpansionCallback callback : mExpansionCallbacks) {
+ callback.onStartingToHide();
+ }
+ }
+
+ private void dispatchStartingToShow() {
+ for (BouncerExpansionCallback callback : mExpansionCallbacks) {
+ callback.onStartingToShow();
+ }
+ }
+
+ private void dispatchFullyHidden() {
+ for (BouncerExpansionCallback callback : mExpansionCallbacks) {
+ callback.onFullyHidden();
+ }
+ }
+
public void dump(PrintWriter pw) {
pw.println("KeyguardBouncer");
pw.println(" isShowing(): " + isShowing());
@@ -516,6 +552,7 @@ public class KeyguardBouncer {
private final ViewMediatorCallback mCallback;
private final DismissCallbackRegistry mDismissCallbackRegistry;
private final FalsingManager mFalsingManager;
+ private final AuthController mAuthController;
private final KeyguardStateController mKeyguardStateController;
private final KeyguardUpdateMonitor mKeyguardUpdateMonitor;
private final KeyguardBypassController mKeyguardBypassController;
@@ -526,6 +563,7 @@ public class KeyguardBouncer {
@Inject
public Factory(Context context, ViewMediatorCallback callback,
DismissCallbackRegistry dismissCallbackRegistry, FalsingManager falsingManager,
+ AuthController authController,
KeyguardStateController keyguardStateController,
KeyguardUpdateMonitor keyguardUpdateMonitor,
KeyguardBypassController keyguardBypassController, Handler handler,
@@ -535,6 +573,7 @@ public class KeyguardBouncer {
mCallback = callback;
mDismissCallbackRegistry = dismissCallbackRegistry;
mFalsingManager = falsingManager;
+ mAuthController = authController;
mKeyguardStateController = keyguardStateController;
mKeyguardUpdateMonitor = keyguardUpdateMonitor;
mKeyguardBypassController = keyguardBypassController;
@@ -547,8 +586,9 @@ public class KeyguardBouncer {
BouncerExpansionCallback expansionCallback) {
return new KeyguardBouncer(mContext, mCallback, container,
mDismissCallbackRegistry, mFalsingManager, expansionCallback,
- mKeyguardStateController, mKeyguardUpdateMonitor, mKeyguardBypassController,
- mHandler, mKeyguardSecurityModel, mKeyguardBouncerComponentFactory);
+ mAuthController, mKeyguardStateController, mKeyguardUpdateMonitor,
+ mKeyguardBypassController, mHandler, mKeyguardSecurityModel,
+ mKeyguardBouncerComponentFactory);
}
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManager.java
index 51209d166eee..9e2e57e7047e 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManager.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManager.java
@@ -16,8 +16,6 @@
package com.android.systemui.statusbar.phone;
-import static android.view.ViewRootImpl.NEW_INSETS_MODE_FULL;
-import static android.view.ViewRootImpl.sNewInsetsMode;
import static android.view.WindowInsets.Type.navigationBars;
import static com.android.systemui.plugins.ActivityStarter.OnDismissAction;
@@ -803,12 +801,8 @@ public class StatusBarKeyguardViewManager implements RemoteInputController.Callb
private Runnable mMakeNavigationBarVisibleRunnable = new Runnable() {
@Override
public void run() {
- if (ViewRootImpl.sNewInsetsMode == NEW_INSETS_MODE_FULL) {
- mStatusBar.getNotificationShadeWindowView().getWindowInsetsController()
- .show(navigationBars());
- } else {
- mStatusBar.getNavigationBarView().getRootView().setVisibility(View.VISIBLE);
- }
+ mStatusBar.getNotificationShadeWindowView().getWindowInsetsController()
+ .show(navigationBars());
}
};
@@ -876,12 +870,8 @@ public class StatusBarKeyguardViewManager implements RemoteInputController.Callb
}
} else {
mContainer.removeCallbacks(mMakeNavigationBarVisibleRunnable);
- if (sNewInsetsMode == NEW_INSETS_MODE_FULL) {
- mStatusBar.getNotificationShadeWindowView().getWindowInsetsController()
- .hide(navigationBars());
- } else {
- mStatusBar.getNavigationBarView().getRootView().setVisibility(View.GONE);
- }
+ mStatusBar.getNotificationShadeWindowView().getWindowInsetsController()
+ .hide(navigationBars());
}
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/tv/micdisclosure/AudioRecordingDisclosureBar.java b/packages/SystemUI/src/com/android/systemui/statusbar/tv/micdisclosure/AudioRecordingDisclosureBar.java
index a29db4d98329..7aeca64ba9e8 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/tv/micdisclosure/AudioRecordingDisclosureBar.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/tv/micdisclosure/AudioRecordingDisclosureBar.java
@@ -21,7 +21,6 @@ import static android.view.ViewGroup.LayoutParams.WRAP_CONTENT;
import android.animation.Animator;
import android.animation.AnimatorListenerAdapter;
-import android.animation.AnimatorSet;
import android.animation.ObjectAnimator;
import android.annotation.IntDef;
import android.annotation.UiThread;
@@ -36,7 +35,6 @@ import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewTreeObserver;
import android.view.WindowManager;
-import android.widget.TextView;
import com.android.systemui.R;
import com.android.systemui.statusbar.tv.TvStatusBar;
@@ -83,19 +81,14 @@ public class AudioRecordingDisclosureBar implements
private static final int STATE_SHOWN = 2;
private static final int STATE_DISAPPEARING = 3;
- private static final int ANIMATION_DURATION = 600;
+ private static final int ANIMATION_DURATION_MS = 200;
private final Context mContext;
private boolean mIsEnabled;
private View mIndicatorView;
- private View mIconTextsContainer;
- private View mIconContainerBg;
- private View mIcon;
- private View mBgEnd;
- private View mTextsContainers;
- private TextView mTextView;
- private boolean mIsLtr;
+ private boolean mViewAndWindowAdded;
+ private ObjectAnimator mAnimator;
@State private int mState = STATE_STOPPED;
@@ -190,7 +183,7 @@ public class AudioRecordingDisclosureBar implements
}
if (active) {
- showIfNotShown();
+ showIfNeeded();
} else {
hideIndicatorIfNeeded();
}
@@ -198,153 +191,132 @@ public class AudioRecordingDisclosureBar implements
@UiThread
private void hideIndicatorIfNeeded() {
- // If not STATE_APPEARING, will check whether the indicator should be hidden when the
- // indicator comes to the STATE_SHOWN.
- // If STATE_DISAPPEARING or STATE_SHOWN - nothing else for us to do here.
- if (mState != STATE_SHOWN) return;
-
- // If is in the STATE_SHOWN and there are no active recorders - hide.
- if (!hasActiveRecorders()) {
- hide();
+ // If STOPPED, NOT_SHOWN or DISAPPEARING - nothing else for us to do here.
+ if (mState != STATE_SHOWN && mState != STATE_APPEARING) return;
+
+ if (hasActiveRecorders()) {
+ return;
+ }
+
+ if (mViewAndWindowAdded) {
+ mState = STATE_DISAPPEARING;
+ animateDisappearance();
+ } else {
+ // Appearing animation has not started yet, as we were still waiting for the View to be
+ // laid out.
+ mState = STATE_NOT_SHOWN;
+ removeIndicatorView();
}
}
@UiThread
- private void showIfNotShown() {
- if (mState != STATE_NOT_SHOWN) return;
+ private void showIfNeeded() {
+ // If STOPPED, SHOWN or APPEARING - nothing else for us to do here.
+ if (mState != STATE_NOT_SHOWN && mState != STATE_DISAPPEARING) return;
+
if (DEBUG) Log.d(TAG, "Showing indicator");
- mIsLtr = mContext.getResources().getConfiguration().getLayoutDirection()
- == View.LAYOUT_DIRECTION_LTR;
+ final int prevState = mState;
+ mState = STATE_APPEARING;
+
+ if (prevState == STATE_DISAPPEARING) {
+ animateAppearance();
+ return;
+ }
// Inflate the indicator view
mIndicatorView = LayoutInflater.from(mContext).inflate(
- R.layout.tv_audio_recording_indicator,
- null);
- mIconTextsContainer = mIndicatorView.findViewById(R.id.icon_texts_container);
- mIconContainerBg = mIconTextsContainer.findViewById(R.id.icon_container_bg);
- mIcon = mIconTextsContainer.findViewById(R.id.icon_mic);
- mTextsContainers = mIconTextsContainer.findViewById(R.id.texts_container);
- mTextView = mTextsContainers.findViewById(R.id.text);
- mBgEnd = mIndicatorView.findViewById(R.id.bg_end);
-
- mTextsContainers.setVisibility(View.GONE);
- mIconContainerBg.setVisibility(View.GONE);
- mTextView.setVisibility(View.GONE);
- mBgEnd.setVisibility(View.GONE);
- mTextsContainers = null;
- mIconContainerBg = null;
- mTextView = null;
- mBgEnd = null;
-
- // Initially change the visibility to INVISIBLE, wait until and receives the size and
- // then animate it moving from "off" the screen correctly
- mIndicatorView.setVisibility(View.INVISIBLE);
+ R.layout.tv_audio_recording_indicator, null);
+
+ // 1. Set alpha to 0.
+ // 2. Wait until the window is shown and the view is laid out.
+ // 3. Start a "fade in" (alpha) animation.
+ mIndicatorView.setAlpha(0f);
mIndicatorView
.getViewTreeObserver()
.addOnGlobalLayoutListener(
new ViewTreeObserver.OnGlobalLayoutListener() {
@Override
public void onGlobalLayout() {
- if (mState == STATE_STOPPED) {
- return;
- }
+ // State could have changed to NOT_SHOWN (if all the recorders are
+ // already gone) to STOPPED (if the indicator was disabled)
+ if (mState != STATE_APPEARING) return;
+ mViewAndWindowAdded = true;
// Remove the observer
mIndicatorView.getViewTreeObserver().removeOnGlobalLayoutListener(
this);
- // Now that the width of the indicator has been assigned, we can
- // move it in from off the screen.
- final int initialOffset =
- (mIsLtr ? 1 : -1) * mIndicatorView.getWidth();
- final AnimatorSet set = new AnimatorSet();
- set.setDuration(ANIMATION_DURATION);
- set.playTogether(
- ObjectAnimator.ofFloat(mIndicatorView,
- View.TRANSLATION_X, initialOffset, 0),
- ObjectAnimator.ofFloat(mIndicatorView, View.ALPHA, 0f,
- 1f));
- set.addListener(
- new AnimatorListenerAdapter() {
- @Override
- public void onAnimationStart(Animator animation,
- boolean isReverse) {
- if (mState == STATE_STOPPED) return;
-
- // Indicator is INVISIBLE at the moment, change it.
- mIndicatorView.setVisibility(View.VISIBLE);
- }
-
- @Override
- public void onAnimationEnd(Animator animation) {
- onAppeared();
- }
- });
- set.start();
+ animateAppearance();
}
});
+ final boolean isLtr = mContext.getResources().getConfiguration().getLayoutDirection()
+ == View.LAYOUT_DIRECTION_LTR;
final WindowManager.LayoutParams layoutParams = new WindowManager.LayoutParams(
WRAP_CONTENT,
WRAP_CONTENT,
WindowManager.LayoutParams.TYPE_SYSTEM_OVERLAY,
WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE,
PixelFormat.TRANSLUCENT);
- layoutParams.gravity = Gravity.TOP | (mIsLtr ? Gravity.RIGHT : Gravity.LEFT);
+ layoutParams.gravity = Gravity.TOP | (isLtr ? Gravity.RIGHT : Gravity.LEFT);
layoutParams.setTitle(LAYOUT_PARAMS_TITLE);
layoutParams.packageName = mContext.getPackageName();
final WindowManager windowManager = (WindowManager) mContext.getSystemService(
Context.WINDOW_SERVICE);
windowManager.addView(mIndicatorView, layoutParams);
-
- mState = STATE_APPEARING;
}
- @UiThread
- private void hide() {
- if (DEBUG) Log.d(TAG, "Hide indicator");
-
- final int targetOffset = (mIsLtr ? 1 : -1) * (mIndicatorView.getWidth()
- - (int) mIconTextsContainer.getTranslationX());
- final AnimatorSet set = new AnimatorSet();
- set.playTogether(
- ObjectAnimator.ofFloat(mIndicatorView, View.TRANSLATION_X, targetOffset),
- ObjectAnimator.ofFloat(mIcon, View.ALPHA, 0f));
- set.setDuration(ANIMATION_DURATION);
- set.addListener(
- new AnimatorListenerAdapter() {
- @Override
- public void onAnimationEnd(Animator animation) {
- onHidden();
- }
- });
- set.start();
- mState = STATE_DISAPPEARING;
+ private void animateAppearance() {
+ animateAlphaTo(1f);
}
+ private void animateDisappearance() {
+ animateAlphaTo(0f);
+ }
- @UiThread
- private void onAppeared() {
- if (mState == STATE_STOPPED) return;
+ private void animateAlphaTo(final float endValue) {
+ if (mAnimator == null) {
+ if (DEBUG) Log.d(TAG, "set up animator");
- mState = STATE_SHOWN;
+ mAnimator = new ObjectAnimator();
+ mAnimator.setTarget(mIndicatorView);
+ mAnimator.setProperty(View.ALPHA);
+ mAnimator.addListener(new AnimatorListenerAdapter() {
+ @Override
+ public void onAnimationStart(Animator animation, boolean isReverse) {
+ if (DEBUG) Log.d(TAG, "onAnimationStart");
+ }
- hideIndicatorIfNeeded();
- }
+ @Override
+ public void onAnimationCancel(Animator animation) {
+ if (DEBUG) Log.d(TAG, "onAnimationCancel");
+ }
- @UiThread
- private void onHidden() {
- if (mState == STATE_STOPPED) return;
+ @Override
+ public void onAnimationEnd(Animator animation) {
+ if (DEBUG) Log.d(TAG, "onAnimationEnd");
+
+ if (mState == STATE_APPEARING) {
+ mState = STATE_SHOWN;
+ } else if (mState == STATE_DISAPPEARING) {
+ removeIndicatorView();
+ mState = STATE_NOT_SHOWN;
+ }
+ }
+ });
+ } else if (mAnimator.isRunning()) {
+ if (DEBUG) Log.d(TAG, "cancel running animation");
+ mAnimator.cancel();
+ }
- removeIndicatorView();
- mState = STATE_NOT_SHOWN;
+ final float currentValue = mIndicatorView.getAlpha();
+ if (DEBUG) Log.d(TAG, "animate alpha to " + endValue + " from " + currentValue);
- if (hasActiveRecorders()) {
- // Got new recorders, show again.
- showIfNotShown();
- }
+ mAnimator.setDuration((int) (Math.abs(currentValue - endValue) * ANIMATION_DURATION_MS));
+ mAnimator.setFloatValues(endValue);
+ mAnimator.start();
}
private boolean hasActiveRecorders() {
@@ -363,12 +335,9 @@ public class AudioRecordingDisclosureBar implements
windowManager.removeView(mIndicatorView);
mIndicatorView = null;
- mIconTextsContainer = null;
- mIconContainerBg = null;
- mIcon = null;
- mTextsContainers = null;
- mTextView = null;
- mBgEnd = null;
+ mAnimator = null;
+
+ mViewAndWindowAdded = false;
}
private static List<String> splitByComma(String string) {
diff --git a/packages/SystemUI/src/com/android/systemui/tv/TvSystemUIBinder.java b/packages/SystemUI/src/com/android/systemui/tv/TvSystemUIBinder.java
index 22fa0106795a..f277b30e589d 100644
--- a/packages/SystemUI/src/com/android/systemui/tv/TvSystemUIBinder.java
+++ b/packages/SystemUI/src/com/android/systemui/tv/TvSystemUIBinder.java
@@ -17,11 +17,12 @@
package com.android.systemui.tv;
import com.android.systemui.dagger.GlobalRootComponent;
+import com.android.systemui.pip.tv.dagger.TvPipModule;
import dagger.Binds;
import dagger.Module;
-@Module()
+@Module(includes = TvPipModule.class)
interface TvSystemUIBinder {
@Binds
GlobalRootComponent bindGlobalRootComponent(TvGlobalRootComponent globalRootComponent);
diff --git a/packages/SystemUI/src/com/android/systemui/util/sensors/ThresholdSensorImpl.java b/packages/SystemUI/src/com/android/systemui/util/sensors/ThresholdSensorImpl.java
index aa50292edbf7..71b255229c8f 100644
--- a/packages/SystemUI/src/com/android/systemui/util/sensors/ThresholdSensorImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/util/sensors/ThresholdSensorImpl.java
@@ -199,7 +199,7 @@ class ThresholdSensorImpl implements ThresholdSensor {
@Override
public String toString() {
- return String.format("{registered=%s, paused=%s, threshold=%s, sensor=%s}",
+ return String.format("{isLoaded=%s, registered=%s, paused=%s, threshold=%s, sensor=%s}",
isLoaded(), mRegistered, mPaused, mThreshold, mSensor);
}
diff --git a/packages/SystemUI/src/com/android/systemui/wmshell/TvWMShellModule.java b/packages/SystemUI/src/com/android/systemui/wmshell/TvWMShellModule.java
index 0869cf739d02..d6595b2323bf 100644
--- a/packages/SystemUI/src/com/android/systemui/wmshell/TvWMShellModule.java
+++ b/packages/SystemUI/src/com/android/systemui/wmshell/TvWMShellModule.java
@@ -20,17 +20,9 @@ import android.content.Context;
import android.os.Handler;
import android.view.IWindowManager;
-import com.android.systemui.broadcast.BroadcastDispatcher;
import com.android.systemui.dagger.SysUISingleton;
import com.android.systemui.dagger.qualifiers.Main;
-import com.android.systemui.pip.Pip;
-import com.android.systemui.pip.PipBoundsHandler;
-import com.android.systemui.pip.PipSurfaceTransactionHelper;
-import com.android.systemui.pip.PipTaskOrganizer;
-import com.android.systemui.pip.PipUiEventLogger;
-import com.android.systemui.pip.tv.PipController;
-import com.android.systemui.pip.tv.PipNotification;
-import com.android.systemui.pip.tv.dagger.TvPipComponent;
+import com.android.systemui.pip.tv.dagger.TvPipModule;
import com.android.wm.shell.ShellTaskOrganizer;
import com.android.wm.shell.common.DisplayController;
import com.android.wm.shell.common.DisplayImeController;
@@ -39,8 +31,6 @@ import com.android.wm.shell.common.TransactionPool;
import com.android.wm.shell.splitscreen.SplitScreen;
import com.android.wm.shell.splitscreen.SplitScreenController;
-import java.util.Optional;
-
import dagger.Module;
import dagger.Provides;
@@ -49,7 +39,7 @@ import dagger.Provides;
* branches of SystemUI.
*/
// TODO(b/162923491): Move most of these dependencies into WMSingleton scope.
-@Module(includes = WMShellBaseModule.class, subcomponents = {TvPipComponent.class})
+@Module(includes = {WMShellBaseModule.class, TvPipModule.class})
public class TvWMShellModule {
@SysUISingleton
@Provides
@@ -59,19 +49,6 @@ public class TvWMShellModule {
return new DisplayImeController(wmService, displayController, mainHandler, transactionPool);
}
- @SysUISingleton
- @Provides
- static Pip providePipController(Context context,
- BroadcastDispatcher broadcastDispatcher,
- PipBoundsHandler pipBoundsHandler,
- PipSurfaceTransactionHelper pipSurfaceTransactionHelper,
- PipTaskOrganizer pipTaskOrganizer) {
- return new PipController(context, broadcastDispatcher, pipBoundsHandler,
- pipSurfaceTransactionHelper, pipTaskOrganizer);
- }
-
- @SysUISingleton
- @Provides
static SplitScreen provideSplitScreen(Context context,
DisplayController displayController, SystemWindows systemWindows,
DisplayImeController displayImeController, @Main Handler handler,
@@ -79,30 +56,4 @@ public class TvWMShellModule {
return new SplitScreenController(context, displayController, systemWindows,
displayImeController, handler, transactionPool, shellTaskOrganizer);
}
-
- @SysUISingleton
- @Provides
- static PipNotification providePipNotification(Context context,
- BroadcastDispatcher broadcastDispatcher,
- PipController pipController) {
- return new PipNotification(context, broadcastDispatcher, pipController);
- }
-
- @SysUISingleton
- @Provides
- static PipBoundsHandler providesPipBoundsHandler(Context context) {
- return new PipBoundsHandler(context);
- }
-
- @SysUISingleton
- @Provides
- static PipTaskOrganizer providesPipTaskOrganizer(Context context,
- PipBoundsHandler pipBoundsHandler,
- PipSurfaceTransactionHelper pipSurfaceTransactionHelper,
- Optional<SplitScreen> splitScreenOptional, DisplayController displayController,
- PipUiEventLogger pipUiEventLogger, ShellTaskOrganizer shellTaskOrganizer) {
- return new PipTaskOrganizer(context, pipBoundsHandler,
- pipSurfaceTransactionHelper, splitScreenOptional, displayController,
- pipUiEventLogger, shellTaskOrganizer);
- }
}
diff --git a/packages/SystemUI/src/com/android/systemui/wmshell/WMShell.java b/packages/SystemUI/src/com/android/systemui/wmshell/WMShell.java
index ce125f3fdce0..250c59262c4d 100644
--- a/packages/SystemUI/src/com/android/systemui/wmshell/WMShell.java
+++ b/packages/SystemUI/src/com/android/systemui/wmshell/WMShell.java
@@ -16,34 +16,54 @@
package com.android.systemui.wmshell;
+import static android.app.WindowConfiguration.ACTIVITY_TYPE_UNDEFINED;
+import static android.app.WindowConfiguration.WINDOWING_MODE_PINNED;
import static android.view.Display.DEFAULT_DISPLAY;
import static android.view.WindowManagerPolicyConstants.NAV_BAR_MODE_3BUTTON;
+import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_BOUNCER_SHOWING;
+import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_BUBBLES_EXPANDED;
+import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_GLOBAL_ACTIONS_SHOWING;
+import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_NOTIFICATION_PANEL_EXPANDED;
import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_ONE_HANDED_ACTIVE;
+import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_QUICK_SETTINGS_EXPANDED;
+import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_STATUS_BAR_KEYGUARD_SHOWING;
+import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_STATUS_BAR_KEYGUARD_SHOWING_OCCLUDED;
import static com.android.systemui.shared.system.WindowManagerWrapper.WINDOWING_MODE_SPLIT_SCREEN_PRIMARY;
import android.app.ActivityManager;
+import android.app.ActivityTaskManager;
+import android.app.ActivityTaskManager.RootTaskInfo;
import android.content.ComponentName;
import android.content.Context;
import android.graphics.Rect;
+import android.graphics.drawable.Drawable;
import android.inputmethodservice.InputMethodService;
import android.os.IBinder;
import android.os.ParcelFileDescriptor;
+import android.os.RemoteException;
+import android.util.Log;
+import android.util.Pair;
import android.view.KeyEvent;
import com.android.internal.annotations.VisibleForTesting;
import com.android.keyguard.KeyguardUpdateMonitor;
import com.android.keyguard.KeyguardUpdateMonitorCallback;
+import com.android.systemui.Dependency;
import com.android.systemui.SystemUI;
import com.android.systemui.dagger.SysUISingleton;
import com.android.systemui.keyguard.ScreenLifecycle;
import com.android.systemui.model.SysUiState;
import com.android.systemui.navigationbar.NavigationModeController;
import com.android.systemui.pip.Pip;
+import com.android.systemui.pip.phone.PipUtils;
import com.android.systemui.shared.system.ActivityManagerWrapper;
+import com.android.systemui.shared.system.InputConsumerController;
import com.android.systemui.shared.system.TaskStackChangeListener;
import com.android.systemui.shared.tracing.ProtoTraceable;
import com.android.systemui.statusbar.CommandQueue;
+import com.android.systemui.statusbar.policy.ConfigurationController;
+import com.android.systemui.statusbar.policy.UserInfoController;
import com.android.systemui.tracing.ProtoTracer;
import com.android.systemui.tracing.nano.SystemUiTraceProto;
import com.android.wm.shell.ShellTaskOrganizer;
@@ -57,7 +77,6 @@ import com.android.wm.shell.protolog.ShellProtoLogImpl;
import com.android.wm.shell.splitscreen.SplitScreen;
import java.io.FileDescriptor;
-import java.io.FileOutputStream;
import java.io.PrintWriter;
import java.util.Arrays;
import java.util.Optional;
@@ -70,8 +89,20 @@ import javax.inject.Inject;
@SysUISingleton
public final class WMShell extends SystemUI
implements CommandQueue.Callbacks, ProtoTraceable<SystemUiTraceProto> {
+ private static final String TAG = WMShell.class.getName();
+ private static final int INVALID_SYSUI_STATE_MASK =
+ SYSUI_STATE_GLOBAL_ACTIONS_SHOWING
+ | SYSUI_STATE_STATUS_BAR_KEYGUARD_SHOWING
+ | SYSUI_STATE_STATUS_BAR_KEYGUARD_SHOWING_OCCLUDED
+ | SYSUI_STATE_BOUNCER_SHOWING
+ | SYSUI_STATE_NOTIFICATION_PANEL_EXPANDED
+ | SYSUI_STATE_BUBBLES_EXPANDED
+ | SYSUI_STATE_QUICK_SETTINGS_EXPANDED;
+
private final CommandQueue mCommandQueue;
+ private final ConfigurationController mConfigurationController;
private final DisplayImeController mDisplayImeController;
+ private final InputConsumerController mInputConsumerController;
private final KeyguardUpdateMonitor mKeyguardUpdateMonitor;
private final ActivityManagerWrapper mActivityManagerWrapper;
private final NavigationModeController mNavigationModeController;
@@ -84,12 +115,15 @@ public final class WMShell extends SystemUI
// are non-optional windowing features like FULLSCREEN.
private final ShellTaskOrganizer mShellTaskOrganizer;
private final ProtoTracer mProtoTracer;
-
+ private boolean mIsSysUiStateValid;
private KeyguardUpdateMonitorCallback mSplitScreenKeyguardCallback;
+ private KeyguardUpdateMonitorCallback mPipKeyguardCallback;
private KeyguardUpdateMonitorCallback mOneHandedKeyguardCallback;
@Inject
public WMShell(Context context, CommandQueue commandQueue,
+ ConfigurationController configurationController,
+ InputConsumerController inputConsumerController,
KeyguardUpdateMonitor keyguardUpdateMonitor,
ActivityManagerWrapper activityManagerWrapper,
DisplayImeController displayImeController,
@@ -103,7 +137,8 @@ public final class WMShell extends SystemUI
ProtoTracer protoTracer) {
super(context);
mCommandQueue = commandQueue;
- mCommandQueue.addCallback(this);
+ mConfigurationController = configurationController;
+ mInputConsumerController = inputConsumerController;
mKeyguardUpdateMonitor = keyguardUpdateMonitor;
mActivityManagerWrapper = activityManagerWrapper;
mDisplayImeController = displayImeController;
@@ -120,6 +155,7 @@ public final class WMShell extends SystemUI
@Override
public void start() {
+ mCommandQueue.addCallback(this);
// This is to prevent circular init problem by separating registration step out of its
// constructor. And make sure the initialization of DisplayImeController won't depend on
// specific feature anymore.
@@ -131,12 +167,99 @@ public final class WMShell extends SystemUI
@VisibleForTesting
void initPip(Pip pip) {
+ if (!PipUtils.hasSystemFeature(mContext)) {
+ return;
+ }
mCommandQueue.addCallback(new CommandQueue.Callbacks() {
@Override
public void showPictureInPictureMenu() {
pip.showPictureInPictureMenu();
}
});
+
+ mPipKeyguardCallback = new KeyguardUpdateMonitorCallback() {
+ @Override
+ public void onKeyguardVisibilityChanged(boolean showing) {
+ if (showing) {
+ pip.hidePipMenu(null, null);
+ }
+ }
+ };
+ mKeyguardUpdateMonitor.registerCallback(mPipKeyguardCallback);
+
+ mSysUiState.addCallback(sysUiStateFlag -> {
+ mIsSysUiStateValid = (sysUiStateFlag & INVALID_SYSUI_STATE_MASK) == 0;
+ pip.onSystemUiStateChanged(mIsSysUiStateValid, sysUiStateFlag);
+ });
+
+ mConfigurationController.addCallback(new ConfigurationController.ConfigurationListener() {
+ @Override
+ public void onDensityOrFontScaleChanged() {
+ pip.onDensityOrFontScaleChanged();
+ }
+
+ @Override
+ public void onOverlayChanged() {
+ pip.onOverlayChanged();
+ }
+ });
+
+ // Handle for system task stack changes.
+ mActivityManagerWrapper.registerTaskStackListener(
+ new TaskStackChangeListener() {
+ @Override
+ public void onTaskStackChanged() {
+ pip.onTaskStackChanged();
+ }
+
+ @Override
+ public void onActivityPinned(String packageName, int userId, int taskId,
+ int stackId) {
+ pip.onActivityPinned(packageName);
+ mInputConsumerController.registerInputConsumer(true /* withSfVsync */);
+ }
+
+ @Override
+ public void onActivityUnpinned() {
+ final Pair<ComponentName, Integer> topPipActivityInfo =
+ PipUtils.getTopPipActivity(
+ mContext, ActivityManager.getService());
+ final ComponentName topActivity = topPipActivityInfo.first;
+ pip.onActivityUnpinned(topActivity);
+ mInputConsumerController.unregisterInputConsumer();
+ }
+
+ @Override
+ public void onActivityRestartAttempt(ActivityManager.RunningTaskInfo task,
+ boolean homeTaskVisible, boolean clearedTask, boolean wasVisible) {
+ pip.onActivityRestartAttempt(task, clearedTask);
+ }
+ });
+
+ try {
+ RootTaskInfo taskInfo = ActivityTaskManager.getService().getRootTaskInfo(
+ WINDOWING_MODE_PINNED, ACTIVITY_TYPE_UNDEFINED);
+ if (taskInfo != null) {
+ // If SystemUI restart, and it already existed a pinned stack,
+ // register the pip input consumer to ensure touch can send to it.
+ mInputConsumerController.registerInputConsumer(true /* withSfVsync */);
+ }
+ } catch (RemoteException | UnsupportedOperationException e) {
+ Log.e(TAG, "Failed to register pinned stack listener", e);
+ e.printStackTrace();
+ }
+
+ // Register the listener for input consumer touch events. Only for Phone
+ if (pip.getPipTouchHandler() != null) {
+ mInputConsumerController.setInputListener(pip.getPipTouchHandler()::handleTouchEvent);
+ mInputConsumerController.setRegistrationListener(
+ pip.getPipTouchHandler()::onRegistrationChanged);
+ }
+
+ // The media session listener needs to be re-registered when switching users
+ UserInfoController userInfoController = Dependency.get(UserInfoController.class);
+ userInfoController.addCallback((String name, Drawable picture, String userAccount) ->
+ pip.registerSessionListenerForCurrentUser());
}
@VisibleForTesting
diff --git a/packages/SystemUI/src/com/android/systemui/wmshell/WMShellBaseModule.java b/packages/SystemUI/src/com/android/systemui/wmshell/WMShellBaseModule.java
index 7c129ac92fe3..ae96829379e9 100644
--- a/packages/SystemUI/src/com/android/systemui/wmshell/WMShellBaseModule.java
+++ b/packages/SystemUI/src/com/android/systemui/wmshell/WMShellBaseModule.java
@@ -16,6 +16,7 @@
package com.android.systemui.wmshell;
+import android.app.IActivityManager;
import android.content.Context;
import android.content.pm.PackageManager;
import android.os.Handler;
@@ -28,7 +29,10 @@ import com.android.systemui.dagger.qualifiers.Main;
import com.android.systemui.pip.Pip;
import com.android.systemui.pip.PipSurfaceTransactionHelper;
import com.android.systemui.pip.PipUiEventLogger;
-import com.android.systemui.statusbar.policy.ConfigurationController;
+import com.android.systemui.pip.phone.PipAppOpsListener;
+import com.android.systemui.pip.phone.PipMediaController;
+import com.android.systemui.pip.phone.PipTouchHandler;
+import com.android.systemui.shared.system.InputConsumerController;
import com.android.systemui.util.DeviceConfigProxy;
import com.android.systemui.util.FloatingContentCoordinator;
import com.android.wm.shell.ShellTaskOrganizer;
@@ -71,12 +75,33 @@ public abstract class WMShellBaseModule {
@SysUISingleton
@Provides
+ static InputConsumerController provideInputConsumerController() {
+ return InputConsumerController.getPipInputConsumer();
+ }
+
+ @SysUISingleton
+ @Provides
static FloatingContentCoordinator provideFloatingContentCoordinator() {
return new FloatingContentCoordinator();
}
@SysUISingleton
@Provides
+ static PipAppOpsListener providesPipAppOpsListener(Context context,
+ IActivityManager activityManager,
+ PipTouchHandler pipTouchHandler) {
+ return new PipAppOpsListener(context, activityManager, pipTouchHandler.getMotionHelper());
+ }
+
+ @SysUISingleton
+ @Provides
+ static PipMediaController providesPipMediaController(Context context,
+ IActivityManager activityManager) {
+ return new PipMediaController(context, activityManager);
+ }
+
+ @SysUISingleton
+ @Provides
static PipUiEventLogger providePipUiEventLogger(UiEventLogger uiEventLogger,
PackageManager packageManager) {
return new PipUiEventLogger(uiEventLogger, packageManager);
@@ -84,9 +109,8 @@ public abstract class WMShellBaseModule {
@SysUISingleton
@Provides
- static PipSurfaceTransactionHelper providesPipSurfaceTransactionHelper(Context context,
- ConfigurationController configController) {
- return new PipSurfaceTransactionHelper(context, configController);
+ static PipSurfaceTransactionHelper providesPipSurfaceTransactionHelper(Context context) {
+ return new PipSurfaceTransactionHelper(context);
}
@SysUISingleton
@@ -106,6 +130,12 @@ public abstract class WMShellBaseModule {
@SysUISingleton
@Provides
+ static WindowManagerShellWrapper provideWindowManagerShellWrapper() {
+ return new WindowManagerShellWrapper();
+ }
+
+ @SysUISingleton
+ @Provides
static FlingAnimationUtils.Builder provideFlingAnimationUtilsBuilder(
DisplayMetrics displayMetrics) {
return new FlingAnimationUtils.Builder(displayMetrics);
diff --git a/packages/SystemUI/src/com/android/systemui/wmshell/WMShellModule.java b/packages/SystemUI/src/com/android/systemui/wmshell/WMShellModule.java
index 16fb2cacc950..6ed836c3b954 100644
--- a/packages/SystemUI/src/com/android/systemui/wmshell/WMShellModule.java
+++ b/packages/SystemUI/src/com/android/systemui/wmshell/WMShellModule.java
@@ -20,18 +20,18 @@ import android.content.Context;
import android.os.Handler;
import android.view.IWindowManager;
-import com.android.systemui.broadcast.BroadcastDispatcher;
import com.android.systemui.dagger.SysUISingleton;
import com.android.systemui.dagger.qualifiers.Main;
-import com.android.systemui.model.SysUiState;
import com.android.systemui.pip.Pip;
import com.android.systemui.pip.PipBoundsHandler;
import com.android.systemui.pip.PipSurfaceTransactionHelper;
import com.android.systemui.pip.PipTaskOrganizer;
import com.android.systemui.pip.PipUiEventLogger;
+import com.android.systemui.pip.phone.PipAppOpsListener;
import com.android.systemui.pip.phone.PipController;
-import com.android.systemui.statusbar.policy.ConfigurationController;
-import com.android.systemui.util.DeviceConfigProxy;
+import com.android.systemui.pip.phone.PipMediaController;
+import com.android.systemui.pip.phone.PipMenuActivityController;
+import com.android.systemui.pip.phone.PipTouchHandler;
import com.android.systemui.util.FloatingContentCoordinator;
import com.android.wm.shell.ShellTaskOrganizer;
import com.android.wm.shell.common.DisplayController;
@@ -66,21 +66,17 @@ public class WMShellModule {
@SysUISingleton
@Provides
static Pip providePipController(Context context,
- BroadcastDispatcher broadcastDispatcher,
- ConfigurationController configController,
- DeviceConfigProxy deviceConfig,
DisplayController displayController,
- FloatingContentCoordinator floatingContentCoordinator,
- SysUiState sysUiState,
+ PipAppOpsListener pipAppOpsListener,
PipBoundsHandler pipBoundsHandler,
- PipSurfaceTransactionHelper surfaceTransactionHelper,
+ PipMediaController pipMediaController,
+ PipMenuActivityController pipMenuActivityController,
PipTaskOrganizer pipTaskOrganizer,
- PipUiEventLogger pipUiEventLogger) {
- return new PipController(context, broadcastDispatcher, configController, deviceConfig,
- displayController, floatingContentCoordinator, sysUiState, pipBoundsHandler,
- surfaceTransactionHelper,
- pipTaskOrganizer,
- pipUiEventLogger);
+ PipTouchHandler pipTouchHandler,
+ WindowManagerShellWrapper windowManagerShellWrapper) {
+ return new PipController(context, displayController,
+ pipAppOpsListener, pipBoundsHandler, pipMediaController, pipMenuActivityController,
+ pipTaskOrganizer, pipTouchHandler, windowManagerShellWrapper);
}
@SysUISingleton
@@ -101,6 +97,24 @@ public class WMShellModule {
@SysUISingleton
@Provides
+ static PipMenuActivityController providesPipMenuActivityController(Context context,
+ PipMediaController pipMediaController, PipTaskOrganizer pipTaskOrganizer) {
+ return new PipMenuActivityController(context, pipMediaController, pipTaskOrganizer);
+ }
+
+ @SysUISingleton
+ @Provides
+ static PipTouchHandler providesPipTouchHandler(Context context,
+ PipMenuActivityController menuActivityController, PipBoundsHandler pipBoundsHandler,
+ PipTaskOrganizer pipTaskOrganizer,
+ FloatingContentCoordinator floatingContentCoordinator,
+ PipUiEventLogger pipUiEventLogger) {
+ return new PipTouchHandler(context, menuActivityController, pipBoundsHandler,
+ pipTaskOrganizer, floatingContentCoordinator, pipUiEventLogger);
+ }
+
+ @SysUISingleton
+ @Provides
static PipTaskOrganizer providesPipTaskOrganizer(Context context,
PipBoundsHandler pipBoundsHandler,
PipSurfaceTransactionHelper pipSurfaceTransactionHelper,
diff --git a/packages/SystemUI/src/com/android/systemui/wmshell/WindowManagerShellWrapper.java b/packages/SystemUI/src/com/android/systemui/wmshell/WindowManagerShellWrapper.java
new file mode 100644
index 000000000000..178d472cfbee
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/wmshell/WindowManagerShellWrapper.java
@@ -0,0 +1,63 @@
+/*
+ * 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.wmshell;
+
+import static android.view.Display.DEFAULT_DISPLAY;
+
+import android.app.WindowConfiguration;
+import android.os.RemoteException;
+import android.view.WindowManagerGlobal;
+
+import com.android.systemui.shared.system.PinnedStackListenerForwarder;
+
+/**
+ * The singleton wrapper to communicate between WindowManagerService and WMShell features
+ * (e.g: PIP, SplitScreen, Bubble, OneHandedMode...etc)
+ */
+public class WindowManagerShellWrapper {
+ private static final String TAG = WindowManagerShellWrapper.class.getSimpleName();
+
+ public static final int WINDOWING_MODE_PINNED = WindowConfiguration.WINDOWING_MODE_PINNED;
+
+ /**
+ * Forwarder to which we can add multiple pinned stack listeners. Each listener will receive
+ * updates from the window manager service.
+ */
+ private PinnedStackListenerForwarder mPinnedStackListenerForwarder =
+ new PinnedStackListenerForwarder();
+
+ /**
+ * Adds a pinned stack listener, which will receive updates from the window manager service
+ * along with any other pinned stack listeners that were added via this method.
+ */
+ public void addPinnedStackListener(PinnedStackListenerForwarder.PinnedStackListener listener)
+ throws
+ RemoteException {
+ mPinnedStackListenerForwarder.addListener(listener);
+ WindowManagerGlobal.getWindowManagerService().registerPinnedStackListener(
+ DEFAULT_DISPLAY, mPinnedStackListenerForwarder);
+ }
+
+ /**
+ * Removes a pinned stack listener.
+ */
+ public void removePinnedStackListener(
+ PinnedStackListenerForwarder.PinnedStackListener listener) {
+ mPinnedStackListenerForwarder.removeListener(listener);
+ }
+
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/SysuiTestCase.java b/packages/SystemUI/tests/src/com/android/systemui/SysuiTestCase.java
index 0fe381746449..86b1e61d76b1 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/SysuiTestCase.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/SysuiTestCase.java
@@ -41,7 +41,6 @@ import com.android.systemui.dump.DumpManager;
import com.android.systemui.plugins.FalsingManager;
import com.android.systemui.settings.UserTracker;
import com.android.systemui.statusbar.SmartReplyController;
-import com.android.systemui.statusbar.notification.row.NotificationBlockingHelperManager;
import org.junit.After;
import org.junit.Before;
@@ -110,7 +109,6 @@ public abstract class SysuiTestCase {
// KeyguardUpdateMonitor to be created (injected).
// TODO(b/1531701009) Clean up NotificationContentView creation to prevent this
mDependency.injectMockDependency(SmartReplyController.class);
- mDependency.injectMockDependency(NotificationBlockingHelperManager.class);
}
@After
diff --git a/packages/SystemUI/tests/src/com/android/systemui/accessibility/WindowMagnificationControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/accessibility/WindowMagnificationControllerTest.java
index f1f394e70689..539b321991ad 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/accessibility/WindowMagnificationControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/accessibility/WindowMagnificationControllerTest.java
@@ -29,11 +29,14 @@ import static org.mockito.Mockito.when;
import android.app.Instrumentation;
import android.content.Context;
import android.content.pm.ActivityInfo;
+import android.content.res.Resources;
import android.os.Handler;
import android.testing.AndroidTestingRunner;
import android.view.Display;
import android.view.Surface;
import android.view.SurfaceControl;
+import android.view.View;
+import android.view.WindowManager;
import androidx.test.InstrumentationRegistry;
import androidx.test.filters.SmallTest;
@@ -63,15 +66,28 @@ public class WindowMagnificationControllerTest extends SysuiTestCase {
WindowMagnifierCallback mWindowMagnifierCallback;
@Mock
SurfaceControl.Transaction mTransaction;
- private Context mContext;
+ @Mock
+ private WindowManager mWindowManager;
+ private Resources mResources;
private WindowMagnificationController mWindowMagnificationController;
private Instrumentation mInstrumentation;
@Before
public void setUp() {
MockitoAnnotations.initMocks(this);
- mContext = Mockito.spy(getContext());
+ mContext = Mockito.spy(getContext());
mInstrumentation = InstrumentationRegistry.getInstrumentation();
+ WindowManager wm = mContext.getSystemService(WindowManager.class);
+ doAnswer(invocation ->
+ wm.getMaximumWindowMetrics()
+ ).when(mWindowManager).getMaximumWindowMetrics();
+ mContext.addMockSystemService(Context.WINDOW_SERVICE, mWindowManager);
+ doAnswer(invocation -> {
+ View view = invocation.getArgument(0);
+ WindowManager.LayoutParams lp = invocation.getArgument(1);
+ view.setLayoutParams(lp);
+ return null;
+ }).when(mWindowManager).addView(any(View.class), any(WindowManager.LayoutParams.class));
doAnswer(invocation -> {
FrameCallback callback = invocation.getArgument(0);
callback.doFrame(0);
@@ -81,6 +97,8 @@ public class WindowMagnificationControllerTest extends SysuiTestCase {
when(mTransaction.remove(any())).thenReturn(mTransaction);
when(mTransaction.setGeometry(any(), any(), any(),
anyInt())).thenReturn(mTransaction);
+ mResources = Mockito.spy(mContext.getResources());
+ when(mContext.getResources()).thenReturn(mResources);
mWindowMagnificationController = new WindowMagnificationController(mContext,
mHandler, mSfVsyncFrameProvider,
mMirrorWindowControl, mTransaction, mWindowMagnifierCallback);
@@ -150,4 +168,63 @@ public class WindowMagnificationControllerTest extends SysuiTestCase {
mWindowMagnificationController.onConfigurationChanged(ActivityInfo.CONFIG_ORIENTATION);
});
}
+
+ @Test
+ public void onOrientationChanged_enabled_updateDisplayRotationAndLayout() {
+ final Display display = Mockito.spy(mContext.getDisplay());
+ when(display.getRotation()).thenReturn(Surface.ROTATION_90);
+ when(mContext.getDisplay()).thenReturn(display);
+ mInstrumentation.runOnMainSync(() -> {
+ mWindowMagnificationController.enableWindowMagnification(Float.NaN, Float.NaN,
+ Float.NaN);
+ Mockito.reset(mWindowManager);
+ });
+
+ mInstrumentation.runOnMainSync(() -> {
+ mWindowMagnificationController.onConfigurationChanged(ActivityInfo.CONFIG_ORIENTATION);
+ });
+
+ assertEquals(Surface.ROTATION_90, mWindowMagnificationController.mRotation);
+ verify(mWindowManager).updateViewLayout(any(), any());
+ }
+
+ @Test
+ public void onOrientationChanged_disabled_updateDisplayRotation() {
+ final Display display = Mockito.spy(mContext.getDisplay());
+ when(display.getRotation()).thenReturn(Surface.ROTATION_90);
+ when(mContext.getDisplay()).thenReturn(display);
+
+ mInstrumentation.runOnMainSync(() -> {
+ mWindowMagnificationController.onConfigurationChanged(ActivityInfo.CONFIG_ORIENTATION);
+ });
+
+ assertEquals(Surface.ROTATION_90, mWindowMagnificationController.mRotation);
+ }
+
+
+ @Test
+ public void onDensityChanged_enabled_updateDimensionsAndLayout() {
+ mInstrumentation.runOnMainSync(() -> {
+ mWindowMagnificationController.enableWindowMagnification(Float.NaN, Float.NaN,
+ Float.NaN);
+ Mockito.reset(mWindowManager);
+ });
+
+ mInstrumentation.runOnMainSync(() -> {
+ mWindowMagnificationController.onConfigurationChanged(ActivityInfo.CONFIG_DENSITY);
+ });
+
+ verify(mResources, atLeastOnce()).getDimensionPixelSize(anyInt());
+ verify(mWindowManager).removeView(any());
+ verify(mWindowManager).addView(any(), any());
+ }
+
+ @Test
+ public void onDensityChanged_disabled_updateDimensions() {
+ mInstrumentation.runOnMainSync(() -> {
+ mWindowMagnificationController.onConfigurationChanged(ActivityInfo.CONFIG_DENSITY);
+ });
+
+ verify(mResources, atLeastOnce()).getDimensionPixelSize(anyInt());
+ }
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/appops/AppOpsControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/appops/AppOpsControllerTest.java
index 8f082c15df36..ade329011b7d 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/appops/AppOpsControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/appops/AppOpsControllerTest.java
@@ -47,6 +47,7 @@ import android.testing.TestableLooper;
import androidx.test.filters.SmallTest;
import com.android.systemui.SysuiTestCase;
+import com.android.systemui.broadcast.BroadcastDispatcher;
import com.android.systemui.dump.DumpManager;
import org.junit.Before;
@@ -82,6 +83,8 @@ public class AppOpsControllerTest extends SysuiTestCase {
private PackageManager mPackageManager;
@Mock(stubOnly = true)
private AudioManager mAudioManager;
+ @Mock()
+ private BroadcastDispatcher mDispatcher;
@Mock(stubOnly = true)
private AudioManager.AudioRecordingCallback mRecordingCallback;
@Mock(stubOnly = true)
@@ -120,7 +123,8 @@ public class AppOpsControllerTest extends SysuiTestCase {
mTestableLooper.getLooper(),
mDumpManager,
mFlagsCache,
- mAudioManager
+ mAudioManager,
+ mDispatcher
);
}
@@ -128,12 +132,14 @@ public class AppOpsControllerTest extends SysuiTestCase {
public void testOnlyListenForFewOps() {
mController.setListening(true);
verify(mAppOpsManager, times(1)).startWatchingActive(AppOpsControllerImpl.OPS, mController);
+ verify(mDispatcher, times(1)).registerReceiverWithHandler(eq(mController), any(), any());
}
@Test
public void testStopListening() {
mController.setListening(false);
verify(mAppOpsManager, times(1)).stopWatchingActive(mController);
+ verify(mDispatcher, times(1)).unregisterReceiver(mController);
}
@Test
diff --git a/packages/SystemUI/tests/src/com/android/systemui/biometrics/AuthControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/biometrics/AuthControllerTest.java
index 88608ebde5b6..f6b39c2ca38b 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/biometrics/AuthControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/biometrics/AuthControllerTest.java
@@ -22,6 +22,7 @@ import static junit.framework.Assert.assertEquals;
import static junit.framework.Assert.assertNull;
import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.anyBoolean;
import static org.mockito.ArgumentMatchers.anyInt;
import static org.mockito.ArgumentMatchers.anyString;
import static org.mockito.ArgumentMatchers.eq;
@@ -44,6 +45,7 @@ import android.hardware.biometrics.BiometricConstants;
import android.hardware.biometrics.BiometricPrompt;
import android.hardware.biometrics.IBiometricSysuiReceiver;
import android.hardware.biometrics.PromptInfo;
+import android.hardware.biometrics.SensorProperties;
import android.hardware.face.FaceManager;
import android.hardware.fingerprint.FingerprintManager;
import android.hardware.fingerprint.FingerprintSensorProperties;
@@ -118,8 +120,11 @@ public class AuthControllerTest extends SysuiTestCase {
when(mDialog2.isAllowDeviceCredentials()).thenReturn(false);
when(mFingerprintManager.isHardwareDetected()).thenReturn(true);
- FingerprintSensorProperties prop = new FingerprintSensorProperties(
- 1, FingerprintSensorProperties.TYPE_UDFPS, true, 1);
+ FingerprintSensorProperties prop = new FingerprintSensorProperties(1 /* sensorId */,
+ SensorProperties.STRENGTH_STRONG,
+ 1 /* maxEnrollmentsPerUser */,
+ FingerprintSensorProperties.TYPE_UDFPS_OPTICAL,
+ true /* resetLockoutRequireHardwareAuthToken */);
List<FingerprintSensorProperties> props = new ArrayList<>();
props.add(prop);
when(mFingerprintManager.getSensorProperties()).thenReturn(props);
@@ -505,6 +510,30 @@ public class AuthControllerTest extends SysuiTestCase {
verify(mUdfpsController).onCancelAodInterrupt();
}
+ @Test
+ public void testOnFullyShown_DelegatesToUdfpsController() {
+ mAuthController.onFullyShown();
+ verify(mUdfpsController).setBouncerVisibility(eq(true));
+ }
+
+ @Test
+ public void testOnFullyHidden_DelegatesToUdfpsController() {
+ mAuthController.onFullyHidden();
+ verify(mUdfpsController).setBouncerVisibility(eq(false));
+ }
+
+ @Test
+ public void testOnStartingToShow_NeverDelegatesToUdfpsController() {
+ mAuthController.onStartingToShow();
+ verify(mUdfpsController).setBouncerVisibility(eq(true));
+ }
+
+ @Test
+ public void testOnStartingToHide_NeverDelegatesToUdfpsController() {
+ mAuthController.onStartingToHide();
+ verify(mUdfpsController, never()).setBouncerVisibility(anyBoolean());
+ }
+
// Helpers
private void showDialog(int authenticators, int biometricModality) {
diff --git a/packages/SystemUI/tests/src/com/android/systemui/biometrics/UdfpsControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/biometrics/UdfpsControllerTest.java
new file mode 100644
index 000000000000..9b9f840e5383
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/biometrics/UdfpsControllerTest.java
@@ -0,0 +1,246 @@
+/*
+ * 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.biometrics;
+
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.anyFloat;
+import static org.mockito.ArgumentMatchers.eq;
+import static org.mockito.Mockito.never;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+
+import android.content.res.Resources;
+import android.content.res.TypedArray;
+import android.hardware.fingerprint.FingerprintManager;
+import android.hardware.fingerprint.IUdfpsOverlayController;
+import android.os.PowerManager;
+import android.os.RemoteException;
+import android.testing.AndroidTestingRunner;
+import android.testing.TestableLooper.RunWithLooper;
+import android.view.LayoutInflater;
+import android.view.MotionEvent;
+import android.view.WindowManager;
+
+import androidx.test.filters.SmallTest;
+
+import com.android.systemui.R;
+import com.android.systemui.SysuiTestCase;
+import com.android.systemui.plugins.statusbar.StatusBarStateController;
+import com.android.systemui.util.concurrency.FakeExecutor;
+import com.android.systemui.util.settings.FakeSettings;
+import com.android.systemui.util.time.FakeSystemClock;
+
+import org.junit.Before;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.ArgumentCaptor;
+import org.mockito.Captor;
+import org.mockito.Mock;
+import org.mockito.junit.MockitoJUnit;
+import org.mockito.junit.MockitoRule;
+
+@SmallTest
+@RunWith(AndroidTestingRunner.class)
+@RunWithLooper
+public class UdfpsControllerTest extends SysuiTestCase {
+
+ @Rule
+ public MockitoRule rule = MockitoJUnit.rule();
+
+ // Unit under test
+ private UdfpsController mUdfpsController;
+
+ // Dependencies
+ @Mock
+ private Resources mResources;
+ @Mock
+ private LayoutInflater mLayoutInflater;
+ @Mock
+ private FingerprintManager mFingerprintManager;
+ @Mock
+ private PowerManager mPowerManager;
+ @Mock
+ private WindowManager mWindowManager;
+ @Mock
+ private StatusBarStateController mStatusBarStateController;
+ private FakeSettings mSystemSettings;
+ private FakeExecutor mFgExecutor;
+
+ // Stuff for configuring mocks
+ @Mock
+ private UdfpsView mUdfpsView;
+ @Mock
+ private TypedArray mBrightnessValues;
+ @Mock
+ private TypedArray mBrightnessBacklight;
+
+ // Capture listeners so that they can be used to send events
+ @Captor private ArgumentCaptor<IUdfpsOverlayController> mOverlayCaptor;
+ private IUdfpsOverlayController mOverlayController;
+ @Captor private ArgumentCaptor<UdfpsView.OnTouchListener> mTouchListenerCaptor;
+
+ @Before
+ public void setUp() {
+ setUpResources();
+ when(mLayoutInflater.inflate(R.layout.udfps_view, null, false)).thenReturn(mUdfpsView);
+ mSystemSettings = new FakeSettings();
+ mFgExecutor = new FakeExecutor(new FakeSystemClock());
+ mUdfpsController = new UdfpsController(
+ mContext,
+ mResources,
+ mLayoutInflater,
+ mFingerprintManager,
+ mPowerManager,
+ mWindowManager,
+ mSystemSettings,
+ mStatusBarStateController,
+ mFgExecutor);
+ verify(mFingerprintManager).setUdfpsOverlayController(mOverlayCaptor.capture());
+ mOverlayController = mOverlayCaptor.getValue();
+ }
+
+ private void setUpResources() {
+ when(mBrightnessValues.length()).thenReturn(2);
+ when(mBrightnessValues.getFloat(0, PowerManager.BRIGHTNESS_OFF_FLOAT)).thenReturn(1f);
+ when(mBrightnessValues.getFloat(1, PowerManager.BRIGHTNESS_OFF_FLOAT)).thenReturn(2f);
+ when(mResources.obtainTypedArray(com.android.internal.R.array.config_screenBrightnessNits))
+ .thenReturn(mBrightnessValues);
+ when(mBrightnessBacklight.length()).thenReturn(2);
+ when(mBrightnessBacklight.getFloat(0, PowerManager.BRIGHTNESS_OFF_FLOAT)).thenReturn(1f);
+ when(mBrightnessBacklight.getFloat(1, PowerManager.BRIGHTNESS_OFF_FLOAT)).thenReturn(2f);
+ when(mResources.obtainTypedArray(
+ com.android.internal.R.array.config_autoBrightnessDisplayValuesNits))
+ .thenReturn(mBrightnessBacklight);
+ when(mResources.getIntArray(com.android.internal.R.array.config_screenBrightnessBacklight))
+ .thenReturn(new int[]{1, 2});
+ }
+
+ @Test
+ public void dozeTimeTick() {
+ mUdfpsController.dozeTimeTick();
+ verify(mUdfpsView).dozeTimeTick();
+ }
+
+ @Test
+ public void showUdfpsOverlay_addsViewToWindow() throws RemoteException {
+ mOverlayController.showUdfpsOverlay();
+ mFgExecutor.runAllReady();
+ verify(mWindowManager).addView(eq(mUdfpsView), any());
+ }
+
+ @Test
+ public void hideUdfpsOverlay_removesViewFromWindow() throws RemoteException {
+ mOverlayController.showUdfpsOverlay();
+ mOverlayController.hideUdfpsOverlay();
+ mFgExecutor.runAllReady();
+ verify(mWindowManager).removeView(eq(mUdfpsView));
+ }
+
+ @Test
+ public void showUdfpsOverlay_bouncerShowing() throws RemoteException {
+ // GIVEN that the bouncer is showing
+ mUdfpsController.setBouncerVisibility(/* isShowing */ true);
+ // WHEN a request to show the overlay is received
+ mOverlayController.showUdfpsOverlay();
+ mFgExecutor.runAllReady();
+ // THEN the overlay is not attached
+ verify(mWindowManager, never()).addView(eq(mUdfpsView), any());
+ }
+
+ @Test
+ public void setBouncerVisibility_overlayDetached() throws RemoteException {
+ // GIVEN that the overlay has been requested
+ mOverlayController.showUdfpsOverlay();
+ // WHEN the bouncer becomes visible
+ mUdfpsController.setBouncerVisibility(/* isShowing */ true);
+ mFgExecutor.runAllReady();
+ // THEN the overlay is detached
+ verify(mWindowManager).removeView(eq(mUdfpsView));
+ }
+
+ @Test
+ public void setBouncerVisibility_overlayAttached() throws RemoteException {
+ // GIVEN that the bouncer is visible
+ mUdfpsController.setBouncerVisibility(/* isShowing */ true);
+ // AND the overlay has been requested
+ mOverlayController.showUdfpsOverlay();
+ // WHEN the bouncer is closed
+ mUdfpsController.setBouncerVisibility(/* isShowing */ false);
+ mFgExecutor.runAllReady();
+ // THEN the overlay is attached
+ verify(mWindowManager).addView(eq(mUdfpsView), any());
+ }
+
+ @Test
+ public void fingerDown() throws RemoteException {
+ // Configure UdfpsView to accept the ACTION_DOWN event
+ when(mUdfpsView.isScrimShowing()).thenReturn(false);
+ when(mUdfpsView.isValidTouch(anyFloat(), anyFloat(), anyFloat())).thenReturn(true);
+
+ // GIVEN that the overlay is showing
+ mOverlayController.showUdfpsOverlay();
+ mFgExecutor.runAllReady();
+ // WHEN ACTION_DOWN is received
+ verify(mUdfpsView).setOnTouchListener(mTouchListenerCaptor.capture());
+ MotionEvent event = MotionEvent.obtain(0, 0, MotionEvent.ACTION_DOWN, 0, 0, 0);
+ mTouchListenerCaptor.getValue().onTouch(mUdfpsView, event);
+ event.recycle();
+ // THEN the event is passed to the FingerprintManager
+ verify(mFingerprintManager).onFingerDown(eq(0), eq(0), eq(0f), eq(0f));
+ // AND the scrim and dot is shown
+ verify(mUdfpsView).showScrimAndDot();
+ }
+
+ @Test
+ public void aodInterrupt() throws RemoteException {
+ // GIVEN that the overlay is showing
+ mOverlayController.showUdfpsOverlay();
+ mFgExecutor.runAllReady();
+ // WHEN fingerprint is requested because of AOD interrupt
+ mUdfpsController.onAodInterrupt(0, 0);
+ // THEN the event is passed to the FingerprintManager
+ verify(mFingerprintManager).onFingerDown(eq(0), eq(0), anyFloat(), anyFloat());
+ // AND the scrim and dot is shown
+ verify(mUdfpsView).showScrimAndDot();
+ }
+
+ @Test
+ public void cancelAodInterrupt() throws RemoteException {
+ // GIVEN AOD interrupt
+ mOverlayController.showUdfpsOverlay();
+ mFgExecutor.runAllReady();
+ mUdfpsController.onAodInterrupt(0, 0);
+ // WHEN it is cancelled
+ mUdfpsController.onCancelAodInterrupt();
+ // THEN the scrim and dot is hidden
+ verify(mUdfpsView).hideScrimAndDot();
+ }
+
+ @Test
+ public void aodInterruptTimeout() throws RemoteException {
+ // GIVEN AOD interrupt
+ mOverlayController.showUdfpsOverlay();
+ mFgExecutor.runAllReady();
+ mUdfpsController.onAodInterrupt(0, 0);
+ // WHEN it times out
+ mFgExecutor.advanceClockToNext();
+ mFgExecutor.runAllReady();
+ // THEN the scrim and dot is hidden
+ verify(mUdfpsView).hideScrimAndDot();
+ }
+}
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 f65c2c91c50a..3b8f1bb54d79 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/bubbles/BubbleControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/bubbles/BubbleControllerTest.java
@@ -92,6 +92,7 @@ import com.android.systemui.statusbar.policy.ConfigurationController;
import com.android.systemui.statusbar.policy.HeadsUpManager;
import com.android.systemui.statusbar.policy.ZenModeController;
import com.android.systemui.util.FloatingContentCoordinator;
+import com.android.systemui.wmshell.WindowManagerShellWrapper;
import com.google.common.collect.ImmutableList;
@@ -191,6 +192,8 @@ public class BubbleControllerTest extends SysuiTestCase {
private LauncherApps mLauncherApps;
@Mock private LockscreenLockIconController mLockIconController;
+ @Mock private WindowManagerShellWrapper mWindowManagerShellWrapper;
+
private BubbleData mBubbleData;
private TestableLooper mTestableLooper;
@@ -269,6 +272,7 @@ public class BubbleControllerTest extends SysuiTestCase {
mock(INotificationManager.class),
mStatusBarService,
mWindowManager,
+ mWindowManagerShellWrapper,
mLauncherApps);
mBubbleController.setExpandListener(mBubbleExpandListener);
diff --git a/packages/SystemUI/tests/src/com/android/systemui/bubbles/NewNotifPipelineBubbleControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/bubbles/NewNotifPipelineBubbleControllerTest.java
index dd191e9cd328..cfbd398ec6a4 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/bubbles/NewNotifPipelineBubbleControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/bubbles/NewNotifPipelineBubbleControllerTest.java
@@ -92,6 +92,7 @@ import com.android.systemui.statusbar.policy.HeadsUpManager;
import com.android.systemui.statusbar.policy.ZenModeController;
import com.android.systemui.util.FloatingContentCoordinator;
import com.android.systemui.util.InjectionInflationController;
+import com.android.systemui.wmshell.WindowManagerShellWrapper;
import org.junit.Before;
import org.junit.Ignore;
@@ -185,6 +186,8 @@ public class NewNotifPipelineBubbleControllerTest extends SysuiTestCase {
private IStatusBarService mStatusBarService;
@Mock
private LauncherApps mLauncherApps;
+ @Mock
+ private WindowManagerShellWrapper mWindowManagerShellWrapper;
private BubbleData mBubbleData;
@@ -271,6 +274,7 @@ public class NewNotifPipelineBubbleControllerTest extends SysuiTestCase {
mock(INotificationManager.class),
mStatusBarService,
mWindowManager,
+ mWindowManagerShellWrapper,
mLauncherApps);
mBubbleController.addNotifCallback(mNotifCallback);
mBubbleController.setExpandListener(mBubbleExpandListener);
diff --git a/packages/SystemUI/tests/src/com/android/systemui/bubbles/TestableBubbleController.java b/packages/SystemUI/tests/src/com/android/systemui/bubbles/TestableBubbleController.java
index 58b27f24a1d4..ec9571a14997 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/bubbles/TestableBubbleController.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/bubbles/TestableBubbleController.java
@@ -36,6 +36,7 @@ import com.android.systemui.statusbar.phone.ShadeController;
import com.android.systemui.statusbar.policy.ConfigurationController;
import com.android.systemui.statusbar.policy.ZenModeController;
import com.android.systemui.util.FloatingContentCoordinator;
+import com.android.systemui.wmshell.WindowManagerShellWrapper;
/**
* Testable BubbleController subclass that immediately synchronizes surfaces.
@@ -63,6 +64,7 @@ public class TestableBubbleController extends BubbleController {
INotificationManager notificationManager,
IStatusBarService statusBarService,
WindowManager windowManager,
+ WindowManagerShellWrapper windowManagerShellWrapper,
LauncherApps launcherApps) {
super(context,
notificationShadeWindowController, statusBarStateController, shadeController,
@@ -70,7 +72,7 @@ public class TestableBubbleController extends BubbleController {
zenModeController, lockscreenUserManager, groupManager, entryManager,
notifPipeline, featureFlags, dumpManager, floatingContentCoordinator,
dataRepository, sysUiState, notificationManager, statusBarService,
- windowManager, launcherApps);
+ windowManager, windowManagerShellWrapper, launcherApps);
setInflateSynchronously(true);
}
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/doze/DozeScreenBrightnessTest.java b/packages/SystemUI/tests/src/com/android/systemui/doze/DozeScreenBrightnessTest.java
index b85b1c237f6b..be0865d1b3e9 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/doze/DozeScreenBrightnessTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/doze/DozeScreenBrightnessTest.java
@@ -115,8 +115,6 @@ public class DozeScreenBrightnessTest extends SysuiTestCase {
@Test
public void testAod_usesLightSensor() {
- mScreen.transitionTo(UNINITIALIZED, INITIALIZED);
- mScreen.transitionTo(INITIALIZED, DOZE_AOD);
mScreen.onScreenState(Display.STATE_DOZE);
waitForSensorManager();
@@ -127,8 +125,6 @@ public class DozeScreenBrightnessTest extends SysuiTestCase {
@Test
public void testAod_usesDebugValue() throws Exception {
- mScreen.transitionTo(UNINITIALIZED, INITIALIZED);
- mScreen.transitionTo(INITIALIZED, DOZE_AOD);
mScreen.onScreenState(Display.STATE_DOZE);
waitForSensorManager();
@@ -181,14 +177,13 @@ public class DozeScreenBrightnessTest extends SysuiTestCase {
}
@Test
- public void testDozingAfterPulsing_pausesLightSensor() throws Exception {
+ public void testScreenOffAfterPulsing_pausesLightSensor() throws Exception {
mScreen.transitionTo(UNINITIALIZED, INITIALIZED);
mScreen.transitionTo(INITIALIZED, DOZE);
mScreen.transitionTo(DOZE, DOZE_REQUEST_PULSE);
mScreen.transitionTo(DOZE_REQUEST_PULSE, DOZE_PULSING);
mScreen.transitionTo(DOZE_PULSING, DOZE_PULSE_DONE);
mScreen.transitionTo(DOZE_PULSE_DONE, DOZE);
- mScreen.onScreenState(Display.STATE_DOZE);
waitForSensorManager();
mSensor.sendSensorEvent(1);
@@ -197,6 +192,18 @@ public class DozeScreenBrightnessTest extends SysuiTestCase {
}
@Test
+ public void testOnScreenStateSetBeforeTransition_stillRegistersSensor() {
+ mScreen.transitionTo(UNINITIALIZED, INITIALIZED);
+ mScreen.onScreenState(Display.STATE_DOZE);
+ mScreen.transitionTo(INITIALIZED, DOZE_AOD);
+ waitForSensorManager();
+
+ mSensor.sendSensorEvent(1);
+
+ assertEquals(1, mServiceFake.screenBrightness);
+ }
+
+ @Test
public void testNullSensor() throws Exception {
mScreen = new DozeScreenBrightness(mContext, mServiceFake, mSensorManager,
Optional.empty() /* sensor */, mDozeHost, null /* handler */,
@@ -206,12 +213,15 @@ public class DozeScreenBrightnessTest extends SysuiTestCase {
mScreen.transitionTo(INITIALIZED, DOZE_AOD);
mScreen.transitionTo(DOZE_AOD, DOZE_AOD_PAUSING);
mScreen.transitionTo(DOZE_AOD_PAUSING, DOZE_AOD_PAUSED);
+ mScreen.onScreenState(Display.STATE_DOZE);
+ mScreen.onScreenState(Display.STATE_OFF);
}
@Test
public void testNoBrightnessDeliveredAfterFinish() throws Exception {
mScreen.transitionTo(UNINITIALIZED, INITIALIZED);
mScreen.transitionTo(INITIALIZED, DOZE_AOD);
+ mScreen.onScreenState(Display.STATE_DOZE);
mScreen.transitionTo(DOZE_AOD, FINISH);
waitForSensorManager();
diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/WakefulnessLifecycleTest.java b/packages/SystemUI/tests/src/com/android/systemui/keyguard/WakefulnessLifecycleTest.java
index 1dc415048f74..2e8e3ed664b3 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/WakefulnessLifecycleTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/WakefulnessLifecycleTest.java
@@ -122,4 +122,8 @@ public class WakefulnessLifecycleTest extends SysuiTestCase {
mWakefulness.dump(null, new PrintWriter(new ByteArrayOutputStream()), new String[0]);
}
-} \ No newline at end of file
+ @Test(expected = NullPointerException.class)
+ public void throwNPEOnNullObserver() {
+ mWakefulness.addObserver(null);
+ }
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/media/MediaDeviceManagerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/media/MediaDeviceManagerTest.kt
index fdb432cc097c..ab3b20898b23 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/media/MediaDeviceManagerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/media/MediaDeviceManagerTest.kt
@@ -16,13 +16,12 @@
package com.android.systemui.media
-import android.app.Notification
import android.graphics.drawable.Drawable
-import android.media.MediaMetadata
import android.media.MediaRouter2Manager
import android.media.RoutingSessionInfo
+import android.media.session.MediaController
+import android.media.session.MediaController.PlaybackInfo
import android.media.session.MediaSession
-import android.media.session.PlaybackState
import android.testing.AndroidTestingRunner
import android.testing.TestableLooper
import androidx.test.filters.SmallTest
@@ -55,7 +54,6 @@ private const val KEY = "TEST_KEY"
private const val KEY_OLD = "TEST_KEY_OLD"
private const val PACKAGE = "PKG"
private const val SESSION_KEY = "SESSION_KEY"
-private const val SESSION_ARTIST = "SESSION_ARTIST"
private const val SESSION_TITLE = "SESSION_TITLE"
private const val DEVICE_NAME = "DEVICE_NAME"
private const val USER_ID = 0
@@ -68,6 +66,7 @@ private fun <T> eq(value: T): T = Mockito.eq(value) ?: value
public class MediaDeviceManagerTest : SysuiTestCase() {
private lateinit var manager: MediaDeviceManager
+ @Mock private lateinit var controllerFactory: MediaControllerFactory
@Mock private lateinit var lmmFactory: LocalMediaManagerFactory
@Mock private lateinit var lmm: LocalMediaManager
@Mock private lateinit var mr2: MediaRouter2Manager
@@ -78,10 +77,9 @@ public class MediaDeviceManagerTest : SysuiTestCase() {
@Mock private lateinit var device: MediaDevice
@Mock private lateinit var icon: Drawable
@Mock private lateinit var route: RoutingSessionInfo
+ @Mock private lateinit var controller: MediaController
+ @Mock private lateinit var playbackInfo: PlaybackInfo
private lateinit var session: MediaSession
- private lateinit var metadataBuilder: MediaMetadata.Builder
- private lateinit var playbackBuilder: PlaybackState.Builder
- private lateinit var notifBuilder: Notification.Builder
private lateinit var mediaData: MediaData
@JvmField @Rule val mockito = MockitoJUnit.rule()
@@ -89,8 +87,8 @@ public class MediaDeviceManagerTest : SysuiTestCase() {
fun setUp() {
fakeFgExecutor = FakeExecutor(FakeSystemClock())
fakeBgExecutor = FakeExecutor(FakeSystemClock())
- manager = MediaDeviceManager(context, lmmFactory, mr2, fakeFgExecutor, fakeBgExecutor,
- dumpster)
+ manager = MediaDeviceManager(controllerFactory, lmmFactory, mr2, fakeFgExecutor,
+ fakeBgExecutor, dumpster)
manager.addListener(listener)
// Configure mocks.
@@ -101,28 +99,13 @@ public class MediaDeviceManagerTest : SysuiTestCase() {
whenever(mr2.getRoutingSessionForMediaController(any())).thenReturn(route)
// Create a media sesssion and notification for testing.
- metadataBuilder = MediaMetadata.Builder().apply {
- putString(MediaMetadata.METADATA_KEY_ARTIST, SESSION_ARTIST)
- putString(MediaMetadata.METADATA_KEY_TITLE, SESSION_TITLE)
- }
- playbackBuilder = PlaybackState.Builder().apply {
- setState(PlaybackState.STATE_PAUSED, 6000L, 1f)
- setActions(PlaybackState.ACTION_PLAY)
- }
- session = MediaSession(context, SESSION_KEY).apply {
- setMetadata(metadataBuilder.build())
- setPlaybackState(playbackBuilder.build())
- }
- session.setActive(true)
- notifBuilder = Notification.Builder(context, "NONE").apply {
- setContentTitle(SESSION_TITLE)
- setContentText(SESSION_ARTIST)
- setSmallIcon(android.R.drawable.ic_media_pause)
- setStyle(Notification.MediaStyle().setMediaSession(session.getSessionToken()))
- }
+ session = MediaSession(context, SESSION_KEY)
+
mediaData = MediaData(USER_ID, true, 0, PACKAGE, null, null, SESSION_TITLE, null,
emptyList(), emptyList(), PACKAGE, session.sessionToken, clickIntent = null,
device = null, active = true, resumeAction = null)
+ whenever(controllerFactory.create(session.sessionToken))
+ .thenReturn(controller)
}
@After
@@ -336,6 +319,41 @@ public class MediaDeviceManagerTest : SysuiTestCase() {
assertThat(data.icon).isNull()
}
+ @Test
+ fun audioInfoChanged() {
+ whenever(playbackInfo.getPlaybackType()).thenReturn(PlaybackInfo.PLAYBACK_TYPE_LOCAL)
+ whenever(controller.getPlaybackInfo()).thenReturn(playbackInfo)
+ // GIVEN a controller with local playback type
+ manager.onMediaDataLoaded(KEY, null, mediaData)
+ fakeBgExecutor.runAllReady()
+ fakeFgExecutor.runAllReady()
+ reset(mr2)
+ // WHEN onAudioInfoChanged fires with remote playback type
+ whenever(playbackInfo.getPlaybackType()).thenReturn(PlaybackInfo.PLAYBACK_TYPE_REMOTE)
+ val captor = ArgumentCaptor.forClass(MediaController.Callback::class.java)
+ verify(controller).registerCallback(captor.capture())
+ captor.value.onAudioInfoChanged(playbackInfo)
+ // THEN the route is checked
+ verify(mr2).getRoutingSessionForMediaController(eq(controller))
+ }
+
+ @Test
+ fun audioInfoHasntChanged() {
+ whenever(playbackInfo.getPlaybackType()).thenReturn(PlaybackInfo.PLAYBACK_TYPE_REMOTE)
+ whenever(controller.getPlaybackInfo()).thenReturn(playbackInfo)
+ // GIVEN a controller with remote playback type
+ manager.onMediaDataLoaded(KEY, null, mediaData)
+ fakeBgExecutor.runAllReady()
+ fakeFgExecutor.runAllReady()
+ reset(mr2)
+ // WHEN onAudioInfoChanged fires with remote playback type
+ val captor = ArgumentCaptor.forClass(MediaController.Callback::class.java)
+ verify(controller).registerCallback(captor.capture())
+ captor.value.onAudioInfoChanged(playbackInfo)
+ // THEN the route is not checked
+ verify(mr2, never()).getRoutingSessionForMediaController(eq(controller))
+ }
+
fun captureCallback(): LocalMediaManager.DeviceCallback {
val captor = ArgumentCaptor.forClass(LocalMediaManager.DeviceCallback::class.java)
verify(lmm).registerCallback(captor.capture())
diff --git a/packages/SystemUI/tests/src/com/android/systemui/media/MediaTimeoutListenerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/media/MediaTimeoutListenerTest.kt
index f38524369b46..f3979592c894 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/media/MediaTimeoutListenerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/media/MediaTimeoutListenerTest.kt
@@ -23,8 +23,9 @@ import android.media.session.PlaybackState
import android.testing.AndroidTestingRunner
import androidx.test.filters.SmallTest
import com.android.systemui.SysuiTestCase
-import com.android.systemui.util.concurrency.DelayableExecutor
+import com.android.systemui.util.concurrency.FakeExecutor
import com.android.systemui.util.mockito.capture
+import com.android.systemui.util.time.FakeSystemClock
import com.google.common.truth.Truth.assertThat
import org.junit.Before
import org.junit.Rule
@@ -33,7 +34,6 @@ import org.junit.runner.RunWith
import org.mockito.ArgumentCaptor
import org.mockito.ArgumentMatchers.any
import org.mockito.ArgumentMatchers.anyBoolean
-import org.mockito.ArgumentMatchers.anyLong
import org.mockito.ArgumentMatchers.anyString
import org.mockito.Captor
import org.mockito.Mock
@@ -63,10 +63,8 @@ class MediaTimeoutListenerTest : SysuiTestCase() {
@Mock private lateinit var mediaControllerFactory: MediaControllerFactory
@Mock private lateinit var mediaController: MediaController
- @Mock private lateinit var executor: DelayableExecutor
+ private lateinit var executor: FakeExecutor
@Mock private lateinit var timeoutCallback: (String, Boolean) -> Unit
- @Mock private lateinit var cancellationRunnable: Runnable
- @Captor private lateinit var timeoutCaptor: ArgumentCaptor<Runnable>
@Captor private lateinit var mediaCallbackCaptor: ArgumentCaptor<MediaController.Callback>
@JvmField @Rule val mockito = MockitoJUnit.rule()
private lateinit var metadataBuilder: MediaMetadata.Builder
@@ -78,7 +76,7 @@ class MediaTimeoutListenerTest : SysuiTestCase() {
@Before
fun setup() {
`when`(mediaControllerFactory.create(any())).thenReturn(mediaController)
- `when`(executor.executeDelayed(any(), anyLong())).thenReturn(cancellationRunnable)
+ executor = FakeExecutor(FakeSystemClock())
mediaTimeoutListener = MediaTimeoutListener(mediaControllerFactory, executor)
mediaTimeoutListener.timeoutCallback = timeoutCallback
@@ -120,7 +118,7 @@ class MediaTimeoutListenerTest : SysuiTestCase() {
fun testOnMediaDataLoaded_registersTimeout_whenPaused() {
mediaTimeoutListener.onMediaDataLoaded(KEY, null, mediaData)
verify(mediaController).registerCallback(capture(mediaCallbackCaptor))
- verify(executor).executeDelayed(capture(timeoutCaptor), anyLong())
+ assertThat(executor.numPending()).isEqualTo(1)
verify(timeoutCallback, never()).invoke(anyString(), anyBoolean())
}
@@ -137,6 +135,17 @@ class MediaTimeoutListenerTest : SysuiTestCase() {
}
@Test
+ fun testOnMediaDataRemoved_clearsTimeout() {
+ // GIVEN media that is paused
+ mediaTimeoutListener.onMediaDataLoaded(KEY, null, mediaData)
+ assertThat(executor.numPending()).isEqualTo(1)
+ // WHEN the media is removed
+ mediaTimeoutListener.onMediaDataRemoved(KEY)
+ // THEN the timeout runnable is cancelled
+ assertThat(executor.numPending()).isEqualTo(0)
+ }
+
+ @Test
fun testOnMediaDataLoaded_migratesKeys() {
// From not playing
mediaTimeoutListener.onMediaDataLoaded(KEY, null, mediaData)
@@ -151,7 +160,7 @@ class MediaTimeoutListenerTest : SysuiTestCase() {
verify(mediaController).registerCallback(anyObject())
// Enqueues callback
- verify(executor).execute(anyObject())
+ assertThat(executor.numPending()).isEqualTo(1)
}
@Test
@@ -166,8 +175,9 @@ class MediaTimeoutListenerTest : SysuiTestCase() {
`when`(mediaController.playbackState).thenReturn(playingState)
mediaTimeoutListener.onMediaDataLoaded("NEWKEY", KEY, mediaData)
- // Never cancels callback, or schedule another one
- verify(cancellationRunnable, never()).run()
+ // The number of queued timeout tasks remains the same. The timeout task isn't cancelled nor
+ // is another scheduled
+ assertThat(executor.numPending()).isEqualTo(1)
}
@Test
@@ -177,7 +187,7 @@ class MediaTimeoutListenerTest : SysuiTestCase() {
mediaCallbackCaptor.value.onPlaybackStateChanged(PlaybackState.Builder()
.setState(PlaybackState.STATE_PAUSED, 0L, 0f).build())
- verify(executor).executeDelayed(capture(timeoutCaptor), anyLong())
+ assertThat(executor.numPending()).isEqualTo(1)
}
@Test
@@ -187,7 +197,7 @@ class MediaTimeoutListenerTest : SysuiTestCase() {
mediaCallbackCaptor.value.onPlaybackStateChanged(PlaybackState.Builder()
.setState(PlaybackState.STATE_PLAYING, 0L, 0f).build())
- verify(cancellationRunnable).run()
+ assertThat(executor.numPending()).isEqualTo(0)
}
@Test
@@ -195,10 +205,9 @@ class MediaTimeoutListenerTest : SysuiTestCase() {
// Assuming we have a pending timeout
testOnPlaybackStateChanged_schedulesTimeout_whenPaused()
- clearInvocations(cancellationRunnable)
mediaCallbackCaptor.value.onPlaybackStateChanged(PlaybackState.Builder()
.setState(PlaybackState.STATE_STOPPED, 0L, 0f).build())
- verify(cancellationRunnable, never()).run()
+ assertThat(executor.numPending()).isEqualTo(1)
}
@Test
@@ -206,7 +215,10 @@ class MediaTimeoutListenerTest : SysuiTestCase() {
// Assuming we're have a pending timeout
testOnPlaybackStateChanged_schedulesTimeout_whenPaused()
- timeoutCaptor.value.run()
+ with(executor) {
+ advanceClockToNext()
+ runAllReady()
+ }
verify(timeoutCallback).invoke(eq(KEY), eq(true))
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/media/dialog/MediaOutputBaseDialogTest.java b/packages/SystemUI/tests/src/com/android/systemui/media/dialog/MediaOutputBaseDialogTest.java
index 42b21c61510a..27b5b7fda684 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/media/dialog/MediaOutputBaseDialogTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/media/dialog/MediaOutputBaseDialogTest.java
@@ -36,7 +36,6 @@ import androidx.core.graphics.drawable.IconCompat;
import androidx.test.filters.SmallTest;
import com.android.settingslib.bluetooth.LocalBluetoothManager;
-import com.android.settingslib.media.MediaDevice;
import com.android.systemui.R;
import com.android.systemui.SysuiTestCase;
import com.android.systemui.plugins.ActivityStarter;
@@ -55,7 +54,6 @@ public class MediaOutputBaseDialogTest extends SysuiTestCase {
// Mock
private MediaOutputBaseAdapter mMediaOutputBaseAdapter = mock(MediaOutputBaseAdapter.class);
-
private MediaSessionManager mMediaSessionManager = mock(MediaSessionManager.class);
private LocalBluetoothManager mLocalBluetoothManager = mock(LocalBluetoothManager.class);
private ShadeController mShadeController = mock(ShadeController.class);
@@ -157,30 +155,6 @@ public class MediaOutputBaseDialogTest extends SysuiTestCase {
verify(mMediaOutputBaseAdapter).notifyDataSetChanged();
}
- @Test
- public void refresh_with6Devices_checkBottomPaddingVisibility() {
- for (int i = 0; i < 6; i++) {
- mMediaOutputController.mMediaDevices.add(mock(MediaDevice.class));
- }
- mMediaOutputBaseDialogImpl.refresh();
- final View view = mMediaOutputBaseDialogImpl.mDialogView.requireViewById(
- R.id.list_bottom_padding);
-
- assertThat(view.getVisibility()).isEqualTo(View.GONE);
- }
-
- @Test
- public void refresh_with5Devices_checkBottomPaddingVisibility() {
- for (int i = 0; i < 5; i++) {
- mMediaOutputController.mMediaDevices.add(mock(MediaDevice.class));
- }
- mMediaOutputBaseDialogImpl.refresh();
- final View view = mMediaOutputBaseDialogImpl.mDialogView.requireViewById(
- R.id.list_bottom_padding);
-
- assertThat(view.getVisibility()).isEqualTo(View.VISIBLE);
- }
-
class MediaOutputBaseDialogImpl extends MediaOutputBaseDialog {
MediaOutputBaseDialogImpl(Context context, MediaOutputController mediaOutputController) {
@@ -189,24 +163,34 @@ public class MediaOutputBaseDialogTest extends SysuiTestCase {
mAdapter = mMediaOutputBaseAdapter;
}
+ @Override
int getHeaderIconRes() {
return mHeaderIconRes;
}
+ @Override
IconCompat getHeaderIcon() {
return mIconCompat;
}
+ @Override
int getHeaderIconSize() {
return 10;
}
+ @Override
CharSequence getHeaderText() {
return mHeaderTitle;
}
+ @Override
CharSequence getHeaderSubtitle() {
return mHeaderSubtitle;
}
+
+ @Override
+ int getStopButtonVisibility() {
+ return 0;
+ }
}
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/media/dialog/MediaOutputDialogTest.java b/packages/SystemUI/tests/src/com/android/systemui/media/dialog/MediaOutputDialogTest.java
new file mode 100644
index 000000000000..ca328fbe44fb
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/media/dialog/MediaOutputDialogTest.java
@@ -0,0 +1,105 @@
+/*
+ * 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.media.dialog;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.when;
+
+import android.media.MediaRoute2Info;
+import android.media.session.MediaSessionManager;
+import android.testing.AndroidTestingRunner;
+import android.testing.TestableLooper;
+import android.view.View;
+
+import androidx.test.filters.SmallTest;
+
+import com.android.settingslib.bluetooth.LocalBluetoothManager;
+import com.android.settingslib.media.LocalMediaManager;
+import com.android.settingslib.media.MediaDevice;
+import com.android.systemui.SysuiTestCase;
+import com.android.systemui.plugins.ActivityStarter;
+import com.android.systemui.statusbar.phone.ShadeController;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+import java.util.ArrayList;
+import java.util.List;
+
+@SmallTest
+@RunWith(AndroidTestingRunner.class)
+@TestableLooper.RunWithLooper
+public class MediaOutputDialogTest extends SysuiTestCase {
+
+ private static final String TEST_PACKAGE = "test_package";
+
+ // Mock
+ private MediaSessionManager mMediaSessionManager = mock(MediaSessionManager.class);
+ private LocalBluetoothManager mLocalBluetoothManager = mock(LocalBluetoothManager.class);
+ private ShadeController mShadeController = mock(ShadeController.class);
+ private ActivityStarter mStarter = mock(ActivityStarter.class);
+ private LocalMediaManager mLocalMediaManager = mock(LocalMediaManager.class);
+ private MediaDevice mMediaDevice = mock(MediaDevice.class);
+
+ private MediaOutputDialog mMediaOutputDialog;
+ private MediaOutputController mMediaOutputController;
+ private List<String> mFeatures = new ArrayList<>();
+
+ @Before
+ public void setUp() {
+ mMediaOutputController = new MediaOutputController(mContext, TEST_PACKAGE,
+ mMediaSessionManager, mLocalBluetoothManager, mShadeController, mStarter);
+ mMediaOutputController.mLocalMediaManager = mLocalMediaManager;
+ mMediaOutputDialog = new MediaOutputDialog(mContext, false, mMediaOutputController);
+
+ when(mLocalMediaManager.getCurrentConnectedDevice()).thenReturn(mMediaDevice);
+ when(mMediaDevice.getFeatures()).thenReturn(mFeatures);
+ }
+
+ @Test
+ public void getStopButtonVisibility_remoteDevice_returnVisible() {
+ mFeatures.add(MediaRoute2Info.FEATURE_REMOTE_PLAYBACK);
+
+ assertThat(mMediaOutputDialog.getStopButtonVisibility()).isEqualTo(View.VISIBLE);
+
+ mFeatures.clear();
+ mFeatures.add(MediaRoute2Info.FEATURE_REMOTE_AUDIO_PLAYBACK);
+
+ assertThat(mMediaOutputDialog.getStopButtonVisibility()).isEqualTo(View.VISIBLE);
+
+ mFeatures.clear();
+ mFeatures.add(MediaRoute2Info.FEATURE_REMOTE_VIDEO_PLAYBACK);
+
+ assertThat(mMediaOutputDialog.getStopButtonVisibility()).isEqualTo(View.VISIBLE);
+
+ mFeatures.clear();
+ mFeatures.add(MediaRoute2Info.FEATURE_REMOTE_GROUP_PLAYBACK);
+
+ assertThat(mMediaOutputDialog.getStopButtonVisibility()).isEqualTo(View.VISIBLE);
+ }
+
+ @Test
+ public void getStopButtonVisibility_localDevice_returnGone() {
+ mFeatures.add(MediaRoute2Info.FEATURE_LOCAL_PLAYBACK);
+
+ assertThat(mMediaOutputDialog.getStopButtonVisibility()).isEqualTo(View.GONE);
+ }
+
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/pip/PipAnimationControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/pip/PipAnimationControllerTest.java
index f0066ba4f66a..89ca32c1f1a5 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/pip/PipAnimationControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/pip/PipAnimationControllerTest.java
@@ -22,7 +22,6 @@ import static com.android.systemui.pip.PipAnimationController.TRANSITION_DIRECTI
import static org.junit.Assert.assertEquals;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.eq;
-import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.verify;
import android.graphics.Matrix;
@@ -34,7 +33,6 @@ import android.view.SurfaceControl;
import androidx.test.filters.SmallTest;
import com.android.systemui.SysuiTestCase;
-import com.android.systemui.statusbar.policy.ConfigurationController;
import org.junit.Before;
import org.junit.Test;
@@ -61,7 +59,7 @@ public class PipAnimationControllerTest extends SysuiTestCase {
@Before
public void setUp() throws Exception {
mPipAnimationController = new PipAnimationController(
- new PipSurfaceTransactionHelper(mContext, mock(ConfigurationController.class)));
+ new PipSurfaceTransactionHelper(mContext));
mLeash = new SurfaceControl.Builder()
.setContainerLayer()
.setName("FakeLeash")
@@ -81,7 +79,7 @@ public class PipAnimationControllerTest extends SysuiTestCase {
@Test
public void getAnimator_withBounds_returnBoundsAnimator() {
final PipAnimationController.PipTransitionAnimator animator = mPipAnimationController
- .getAnimator(mLeash, new Rect(), new Rect(), null);
+ .getAnimator(mLeash, new Rect(), new Rect(), null, TRANSITION_DIRECTION_TO_PIP);
assertEquals("Expect ANIM_TYPE_BOUNDS animation",
animator.getAnimationType(), PipAnimationController.ANIM_TYPE_BOUNDS);
@@ -93,12 +91,12 @@ public class PipAnimationControllerTest extends SysuiTestCase {
final Rect endValue1 = new Rect(100, 100, 200, 200);
final Rect endValue2 = new Rect(200, 200, 300, 300);
final PipAnimationController.PipTransitionAnimator oldAnimator = mPipAnimationController
- .getAnimator(mLeash, startValue, endValue1, null);
+ .getAnimator(mLeash, startValue, endValue1, null, TRANSITION_DIRECTION_TO_PIP);
oldAnimator.setSurfaceControlTransactionFactory(DummySurfaceControlTx::new);
oldAnimator.start();
final PipAnimationController.PipTransitionAnimator newAnimator = mPipAnimationController
- .getAnimator(mLeash, startValue, endValue2, null);
+ .getAnimator(mLeash, startValue, endValue2, null, TRANSITION_DIRECTION_TO_PIP);
assertEquals("getAnimator with same type returns same animator",
oldAnimator, newAnimator);
@@ -128,7 +126,7 @@ public class PipAnimationControllerTest extends SysuiTestCase {
final Rect endValue1 = new Rect(100, 100, 200, 200);
final Rect endValue2 = new Rect(200, 200, 300, 300);
final PipAnimationController.PipTransitionAnimator animator = mPipAnimationController
- .getAnimator(mLeash, startValue, endValue1, null);
+ .getAnimator(mLeash, startValue, endValue1, null, TRANSITION_DIRECTION_TO_PIP);
animator.updateEndValue(endValue2);
@@ -140,7 +138,7 @@ public class PipAnimationControllerTest extends SysuiTestCase {
final Rect startValue = new Rect(0, 0, 100, 100);
final Rect endValue = new Rect(100, 100, 200, 200);
final PipAnimationController.PipTransitionAnimator animator = mPipAnimationController
- .getAnimator(mLeash, startValue, endValue, null);
+ .getAnimator(mLeash, startValue, endValue, null, TRANSITION_DIRECTION_TO_PIP);
animator.setSurfaceControlTransactionFactory(DummySurfaceControlTx::new);
animator.setPipAnimationCallback(mPipAnimationCallback);
diff --git a/packages/SystemUI/tests/src/com/android/systemui/pip/phone/PipControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/pip/phone/PipControllerTest.java
new file mode 100644
index 000000000000..1274621623ea
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/pip/phone/PipControllerTest.java
@@ -0,0 +1,109 @@
+/*
+ * 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.pip.phone;
+
+import static android.content.pm.PackageManager.FEATURE_PICTURE_IN_PICTURE;
+
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.Mockito.never;
+import static org.mockito.Mockito.spy;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+
+import android.content.pm.PackageManager;
+import android.os.RemoteException;
+import android.test.suitebuilder.annotation.SmallTest;
+import android.testing.AndroidTestingRunner;
+import android.testing.TestableContext;
+import android.testing.TestableLooper;
+
+import com.android.systemui.SysuiTestCase;
+import com.android.systemui.pip.PipBoundsHandler;
+import com.android.systemui.pip.PipTaskOrganizer;
+import com.android.systemui.shared.system.ActivityManagerWrapper;
+import com.android.systemui.statusbar.policy.ConfigurationController;
+import com.android.systemui.wmshell.WindowManagerShellWrapper;
+import com.android.wm.shell.common.DisplayController;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+
+/**
+ * Unit tests for {@link PipController}
+ */
+@SmallTest
+@RunWith(AndroidTestingRunner.class)
+@TestableLooper.RunWithLooper
+public class PipControllerTest extends SysuiTestCase {
+ private PipController mPipController;
+ private TestableContext mSpiedContext;
+
+ @Mock private ActivityManagerWrapper mMockActivityManagerWrapper;
+ @Mock private ConfigurationController mMockConfigurationController;
+ @Mock private DisplayController mMockdDisplayController;
+ @Mock private PackageManager mPackageManager;
+ @Mock private PipMenuActivityController mMockPipMenuActivityController;
+ @Mock private PipAppOpsListener mMockPipAppOpsListener;
+ @Mock private PipBoundsHandler mMockPipBoundsHandler;
+ @Mock private PipMediaController mMockPipMediaController;
+ @Mock private PipTaskOrganizer mMockPipTaskOrganizer;
+ @Mock private PipTouchHandler mMockPipTouchHandler;
+ @Mock private WindowManagerShellWrapper mMockWindowManagerShellWrapper;
+
+ @Before
+ public void setUp() throws RemoteException {
+ MockitoAnnotations.initMocks(this);
+
+ mSpiedContext = spy(mContext);
+
+ when(mPackageManager.hasSystemFeature(FEATURE_PICTURE_IN_PICTURE)).thenReturn(false);
+ when(mSpiedContext.getPackageManager()).thenReturn(mPackageManager);
+
+ mPipController = new PipController(mSpiedContext, mMockdDisplayController,
+ mMockPipAppOpsListener,
+ mMockPipBoundsHandler, mMockPipMediaController, mMockPipMenuActivityController,
+ mMockPipTaskOrganizer, mMockPipTouchHandler, mMockWindowManagerShellWrapper);
+ }
+
+ @Test
+ public void testNonPipDevice_shouldNotRegisterTaskStackListener() {
+ verify(mMockActivityManagerWrapper, never()).registerTaskStackListener(any());
+ }
+
+ @Test
+ public void testNonPipDevice_shouldNotRegisterPipTransitionCallback() {
+ verify(mMockPipTaskOrganizer, never()).registerPipTransitionCallback(any());
+ }
+
+ @Test
+ public void testNonPipDevice_shouldNotAddDisplayChangingController() {
+ verify(mMockdDisplayController, never()).addDisplayChangingController(any());
+ }
+
+ @Test
+ public void testNonPipDevice_shouldNotAddDisplayWindowListener() {
+ verify(mMockdDisplayController, never()).addDisplayWindowListener(any());
+ }
+
+ @Test
+ public void testNonPipDevice_shouldNotAddCallback() {
+ verify(mMockConfigurationController, never()).addCallback(any());
+ }
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/pip/phone/PipTaskOrganizerTest.java b/packages/SystemUI/tests/src/com/android/systemui/pip/phone/PipTaskOrganizerTest.java
new file mode 100644
index 000000000000..b1a7df83fa9f
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/pip/phone/PipTaskOrganizerTest.java
@@ -0,0 +1,93 @@
+/*
+ * 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.pip.phone;
+
+import static android.content.pm.PackageManager.FEATURE_PICTURE_IN_PICTURE;
+
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.anyInt;
+import static org.mockito.Mockito.never;
+import static org.mockito.Mockito.spy;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+
+import android.content.pm.PackageManager;
+import android.os.RemoteException;
+import android.test.suitebuilder.annotation.SmallTest;
+import android.testing.AndroidTestingRunner;
+import android.testing.TestableContext;
+import android.testing.TestableLooper;
+
+import com.android.systemui.SysuiTestCase;
+import com.android.systemui.pip.PipBoundsHandler;
+import com.android.systemui.pip.PipSurfaceTransactionHelper;
+import com.android.systemui.pip.PipTaskOrganizer;
+import com.android.systemui.pip.PipUiEventLogger;
+import com.android.wm.shell.ShellTaskOrganizer;
+import com.android.wm.shell.common.DisplayController;
+import com.android.wm.shell.splitscreen.SplitScreen;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+
+import java.util.Optional;
+
+/**
+ * Unit tests for {@link PipTaskOrganizer}
+ */
+@SmallTest
+@RunWith(AndroidTestingRunner.class)
+@TestableLooper.RunWithLooper
+public class PipTaskOrganizerTest extends SysuiTestCase {
+ private PipTaskOrganizer mSpiedPipTaskOrganizer;
+ private TestableContext mSpiedContext;
+
+ @Mock private DisplayController mMockdDisplayController;
+ @Mock private PackageManager mPackageManager;
+ @Mock private PipBoundsHandler mMockPipBoundsHandler;
+ @Mock private PipSurfaceTransactionHelper mMockPipSurfaceTransactionHelper;
+ @Mock private PipUiEventLogger mMockPipUiEventLogger;
+ @Mock private Optional<SplitScreen> mMockOptionalSplitScreen;
+ @Mock private ShellTaskOrganizer mMockShellTaskOrganizer;
+
+ @Before
+ public void setUp() throws RemoteException {
+ MockitoAnnotations.initMocks(this);
+
+ mSpiedContext = spy(mContext);
+
+ when(mPackageManager.hasSystemFeature(FEATURE_PICTURE_IN_PICTURE)).thenReturn(false);
+ when(mSpiedContext.getPackageManager()).thenReturn(mPackageManager);
+
+ mSpiedPipTaskOrganizer = spy(new PipTaskOrganizer(mSpiedContext, mMockPipBoundsHandler,
+ mMockPipSurfaceTransactionHelper, mMockOptionalSplitScreen, mMockdDisplayController,
+ mMockPipUiEventLogger, mMockShellTaskOrganizer));
+ }
+
+ @Test
+ public void testNonPipDevice_shellTaskOrganizer_shouldNotAddListener() {
+ verify(mMockShellTaskOrganizer, never()).addListener(any(), anyInt());
+ }
+
+ @Test
+ public void testNonPipDevice_displayController_shouldNotAddDisplayWindowListener() {
+ verify(mMockdDisplayController, never()).addDisplayWindowListener(any());
+ }
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/pip/phone/PipTouchHandlerTest.java b/packages/SystemUI/tests/src/com/android/systemui/pip/phone/PipTouchHandlerTest.java
index c8d4aca90519..ad83ebbc76fd 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/pip/phone/PipTouchHandlerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/pip/phone/PipTouchHandlerTest.java
@@ -22,7 +22,6 @@ import static org.mockito.ArgumentMatchers.anyInt;
import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
-import android.app.IActivityManager;
import android.graphics.Point;
import android.graphics.Rect;
import android.testing.AndroidTestingRunner;
@@ -33,13 +32,10 @@ import androidx.test.filters.SmallTest;
import com.android.systemui.R;
import com.android.systemui.SysuiTestCase;
-import com.android.systemui.model.SysUiState;
import com.android.systemui.pip.PipBoundsHandler;
import com.android.systemui.pip.PipSnapAlgorithm;
import com.android.systemui.pip.PipTaskOrganizer;
import com.android.systemui.pip.PipUiEventLogger;
-import com.android.systemui.shared.system.InputConsumerController;
-import com.android.systemui.util.DeviceConfigProxy;
import com.android.systemui.util.FloatingContentCoordinator;
import org.junit.Before;
@@ -63,27 +59,15 @@ public class PipTouchHandlerTest extends SysuiTestCase {
private PipTouchHandler mPipTouchHandler;
@Mock
- private IActivityManager mActivityManager;
-
- @Mock
private PipMenuActivityController mPipMenuActivityController;
@Mock
- private InputConsumerController mInputConsumerController;
-
- @Mock
private PipTaskOrganizer mPipTaskOrganizer;
@Mock
private FloatingContentCoordinator mFloatingContentCoordinator;
@Mock
- private DeviceConfigProxy mDeviceConfigProxy;
-
- @Mock
- private SysUiState mSysUiState;
-
- @Mock
private PipUiEventLogger mPipUiEventLogger;
private PipBoundsHandler mPipBoundsHandler;
@@ -105,9 +89,8 @@ public class PipTouchHandlerTest extends SysuiTestCase {
mPipBoundsHandler = new PipBoundsHandler(mContext);
mPipSnapAlgorithm = mPipBoundsHandler.getSnapAlgorithm();
mPipSnapAlgorithm = new PipSnapAlgorithm(mContext);
- mPipTouchHandler = new PipTouchHandler(mContext, mActivityManager,
- mPipMenuActivityController, mInputConsumerController, mPipBoundsHandler,
- mPipTaskOrganizer, mFloatingContentCoordinator, mDeviceConfigProxy, mSysUiState,
+ mPipTouchHandler = new PipTouchHandler(mContext, mPipMenuActivityController,
+ mPipBoundsHandler, mPipTaskOrganizer, mFloatingContentCoordinator,
mPipUiEventLogger);
mMotionHelper = Mockito.spy(mPipTouchHandler.getMotionHelper());
mPipResizeGestureHandler = Mockito.spy(mPipTouchHandler.getPipResizeGestureHandler());
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/customize/TileQueryHelperTest.java b/packages/SystemUI/tests/src/com/android/systemui/qs/customize/TileQueryHelperTest.java
index 53ef86660867..2ef7c65acb0b 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/qs/customize/TileQueryHelperTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/qs/customize/TileQueryHelperTest.java
@@ -34,6 +34,7 @@ import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
import android.Manifest;
+import android.content.Context;
import android.content.pm.ApplicationInfo;
import android.content.pm.PackageManager;
import android.content.pm.ResolveInfo;
@@ -47,8 +48,11 @@ import android.util.ArraySet;
import androidx.test.filters.SmallTest;
+import com.android.internal.logging.InstanceId;
import com.android.systemui.R;
import com.android.systemui.SysuiTestCase;
+import com.android.systemui.plugins.qs.DetailAdapter;
+import com.android.systemui.plugins.qs.QSIconView;
import com.android.systemui.plugins.qs.QSTile;
import com.android.systemui.qs.QSTileHost;
import com.android.systemui.util.concurrency.FakeExecutor;
@@ -68,6 +72,7 @@ import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.Set;
+import java.util.concurrent.Executor;
@SmallTest
@RunWith(AndroidTestingRunner.class)
@@ -116,11 +121,9 @@ public class TileQueryHelperTest extends SysuiTestCase {
doAnswer(invocation -> {
String spec = (String) invocation.getArguments()[0];
if (FACTORY_TILES.contains(spec)) {
- QSTile m = mock(QSTile.class);
- when(m.isAvailable()).thenReturn(true);
- when(m.getTileSpec()).thenReturn(spec);
- when(m.getState()).thenReturn(mState);
- return m;
+ FakeQSTile tile = new FakeQSTile(mBgExecutor, mMainExecutor);
+ tile.setState(mState);
+ return tile;
} else {
return null;
}
@@ -292,4 +295,132 @@ public class TileQueryHelperTest extends SysuiTestCase {
verifier.verify(t).setTileSpec("hotspot");
verifier.verify(t).destroy();
}
+
+ private static class FakeQSTile implements QSTile {
+
+ private String mSpec = "";
+ private List<Callback> mCallbacks = new ArrayList<>();
+ private boolean mRefreshed;
+ private boolean mListening;
+ private State mState = new State();
+ private final Executor mBgExecutor;
+ private final Executor mMainExecutor;
+
+ FakeQSTile(Executor bgExecutor, Executor mainExecutor) {
+ mBgExecutor = bgExecutor;
+ mMainExecutor = mainExecutor;
+ }
+
+ @Override
+ public String getTileSpec() {
+ return mSpec;
+ }
+
+ @Override
+ public boolean isAvailable() {
+ return true;
+ }
+
+ @Override
+ public void setTileSpec(String tileSpec) {
+ mSpec = tileSpec;
+ }
+
+ public void setState(State state) {
+ mState = state;
+ notifyChangedState(mState);
+ }
+
+ @Override
+ public void refreshState() {
+ mBgExecutor.execute(() -> {
+ mRefreshed = true;
+ notifyChangedState(mState);
+ });
+ }
+
+ private void notifyChangedState(State state) {
+ List<Callback> callbacks = new ArrayList<>(mCallbacks);
+ callbacks.forEach(callback -> callback.onStateChanged(state));
+ }
+
+ @Override
+ public void addCallback(Callback callback) {
+ mCallbacks.add(callback);
+ }
+
+ @Override
+ public void removeCallback(Callback callback) {
+ mCallbacks.remove(callback);
+ }
+
+ @Override
+ public void removeCallbacks() {
+ mCallbacks.clear();
+ }
+
+ @Override
+ public void setListening(Object client, boolean listening) {
+ if (listening) {
+ mMainExecutor.execute(() -> {
+ mListening = true;
+ refreshState();
+ });
+ }
+ }
+
+ @Override
+ public CharSequence getTileLabel() {
+ return mSpec;
+ }
+
+ @Override
+ public State getState() {
+ return mState;
+ }
+
+ @Override
+ public boolean isTileReady() {
+ return mListening && mRefreshed;
+ }
+
+ @Override
+ public QSIconView createTileView(Context context) {
+ return null;
+ }
+
+ @Override
+ public void click() {}
+
+ @Override
+ public void secondaryClick() {}
+
+ @Override
+ public void longClick() {}
+
+ @Override
+ public void userSwitch(int currentUser) {}
+
+ @Override
+ public int getMetricsCategory() {
+ return 0;
+ }
+
+ @Override
+ public InstanceId getInstanceId() {
+ return null;
+ }
+
+ @Override
+ public void setDetailListening(boolean show) {}
+
+ @Override
+ public void destroy() {}
+
+
+ @Override
+ public DetailAdapter getDetailAdapter() {
+ return null;
+ }
+ }
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/recents/OverviewProxyServiceTest.java b/packages/SystemUI/tests/src/com/android/systemui/recents/OverviewProxyServiceTest.java
new file mode 100644
index 000000000000..727f91c589df
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/recents/OverviewProxyServiceTest.java
@@ -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.recents;
+
+import static android.content.pm.PackageManager.FEATURE_PICTURE_IN_PICTURE;
+
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.Mockito.never;
+import static org.mockito.Mockito.spy;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+
+import android.content.pm.PackageManager;
+import android.os.RemoteException;
+import android.test.suitebuilder.annotation.SmallTest;
+import android.testing.AndroidTestingRunner;
+import android.testing.TestableContext;
+import android.testing.TestableLooper;
+
+import com.android.systemui.SysuiTestCase;
+import com.android.systemui.broadcast.BroadcastDispatcher;
+import com.android.systemui.model.SysUiState;
+import com.android.systemui.navigationbar.NavigationBarController;
+import com.android.systemui.navigationbar.NavigationModeController;
+import com.android.systemui.pip.Pip;
+import com.android.systemui.shared.recents.IPinnedStackAnimationListener;
+import com.android.systemui.statusbar.CommandQueue;
+import com.android.systemui.statusbar.NotificationShadeWindowController;
+import com.android.systemui.statusbar.phone.StatusBar;
+import com.android.wm.shell.splitscreen.SplitScreen;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+
+import java.util.Optional;
+
+import dagger.Lazy;
+
+/**
+ * Unit tests for {@link com.android.systemui.recents.OverviewProxyService}
+ */
+@SmallTest
+@RunWith(AndroidTestingRunner.class)
+@TestableLooper.RunWithLooper
+public class OverviewProxyServiceTest extends SysuiTestCase {
+ private OverviewProxyService mSpiedOverviewProxyService;
+ private TestableContext mSpiedContext;
+
+ @Mock private BroadcastDispatcher mMockBroadcastDispatcher;
+ @Mock private CommandQueue mMockCommandQueue;
+ @Mock private Lazy<NavigationBarController> mMockNavBarControllerLazy;
+ @Mock private IPinnedStackAnimationListener mMockPinnedStackAnimationListener;
+ @Mock private NavigationModeController mMockNavModeController;
+ @Mock private NotificationShadeWindowController mMockStatusBarWinController;
+ @Mock private Optional<Pip> mMockPipOptional;
+ @Mock private Optional<SplitScreen> mMockSplitScreenOptional;
+ @Mock private Optional<Lazy<StatusBar>> mMockStatusBarOptionalLazy;
+ @Mock private Optional<com.android.wm.shell.onehanded.OneHanded> mMockOneHandedOptional;
+ @Mock private PackageManager mPackageManager;
+ @Mock private SysUiState mMockSysUiState;
+
+ @Before
+ public void setUp() throws RemoteException {
+ MockitoAnnotations.initMocks(this);
+
+ mSpiedContext = spy(mContext);
+
+ when(mPackageManager.hasSystemFeature(FEATURE_PICTURE_IN_PICTURE)).thenReturn(false);
+ when(mSpiedContext.getPackageManager()).thenReturn(mPackageManager);
+
+ mSpiedOverviewProxyService = spy(new OverviewProxyService(mSpiedContext, mMockCommandQueue,
+ mMockNavBarControllerLazy, mMockNavModeController, mMockStatusBarWinController,
+ mMockSysUiState, mMockPipOptional, mMockSplitScreenOptional,
+ mMockStatusBarOptionalLazy, mMockOneHandedOptional,
+ mMockBroadcastDispatcher));
+ }
+
+ @Test
+ public void testNonPipDevice_shouldNotNotifySwipeToHomeFinished() throws RemoteException {
+ mSpiedOverviewProxyService.mSysUiProxy.notifySwipeToHomeFinished();
+
+ verify(mMockPipOptional, never()).ifPresent(any());
+ }
+
+ @Test
+ public void testNonPipDevice_shouldNotSetPinnedStackAnimationListener() throws RemoteException {
+ mSpiedOverviewProxyService.mSysUiProxy.setPinnedStackAnimationListener(
+ mMockPinnedStackAnimationListener);
+
+ verify(mMockPipOptional, never()).ifPresent(any());
+ }
+
+ @Test
+ public void testNonPipDevice_shouldNotSetShelfHeight() throws RemoteException {
+ mSpiedOverviewProxyService.mSysUiProxy.setShelfHeight(true /* visible */,
+ 100 /* shelfHeight */);
+
+ verify(mMockPipOptional, never()).ifPresent(any());
+ }
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRowTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRowTest.java
index f29b46c73e4d..8f1d71c80e2d 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRowTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRowTest.java
@@ -54,7 +54,6 @@ import org.junit.Before;
import org.junit.Rule;
import org.junit.Test;
import org.junit.runner.RunWith;
-import org.mockito.Mock;
import org.mockito.junit.MockitoJUnit;
import org.mockito.junit.MockitoRule;
@@ -71,7 +70,6 @@ public class ExpandableNotificationRowTest extends SysuiTestCase {
boolean mHeadsUpAnimatingAway = false;
@Rule public MockitoRule mockito = MockitoJUnit.rule();
- @Mock private NotificationBlockingHelperManager mBlockingHelperManager;
@Before
public void setUp() throws Exception {
@@ -83,9 +81,6 @@ public class ExpandableNotificationRowTest extends SysuiTestCase {
mGroupRow = mNotificationTestHelper.createGroup();
mGroupRow.setHeadsUpAnimatingAwayListener(
animatingAway -> mHeadsUpAnimatingAway = animatingAway);
- mDependency.injectTestDependency(
- NotificationBlockingHelperManager.class,
- mBlockingHelperManager);
}
@Test
@@ -257,30 +252,6 @@ public class ExpandableNotificationRowTest extends SysuiTestCase {
}
@Test
- public void testPerformDismissWithBlockingHelper_falseWhenBlockingHelperIsntShown() {
- when(mBlockingHelperManager.perhapsShowBlockingHelper(
- eq(mGroupRow), any(NotificationMenuRowPlugin.class))).thenReturn(false);
-
- assertFalse(
- mGroupRow.performDismissWithBlockingHelper(false /* fromAccessibility */));
- }
-
- @Test
- public void testPerformDismissWithBlockingHelper_doesntPerformOnGroupSummary() {
- ExpandableNotificationRow childRow = mGroupRow.getChildrenContainer().getViewAtPosition(0);
- when(mBlockingHelperManager.perhapsShowBlockingHelper(eq(childRow), any(NotificationMenuRowPlugin.class)))
- .thenReturn(true);
-
- assertTrue(
- childRow.performDismissWithBlockingHelper(false /* fromAccessibility */));
-
- verify(mBlockingHelperManager, times(1))
- .perhapsShowBlockingHelper(eq(childRow), any(NotificationMenuRowPlugin.class));
- verify(mBlockingHelperManager, times(0))
- .perhapsShowBlockingHelper(eq(mGroupRow), any(NotificationMenuRowPlugin.class));
- }
-
- @Test
public void testIsBlockingHelperShowing_isCorrectlyUpdated() {
mGroupRow.setBlockingHelperShowing(true);
assertTrue(mGroupRow.isBlockingHelperShowing());
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 0d283ce03960..bb85cec5c099 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
@@ -3,7 +3,10 @@
*
* 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 *
+ * 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.
@@ -31,7 +34,6 @@ import static org.mockito.Mockito.atLeastOnce;
import static org.mockito.Mockito.clearInvocations;
import static org.mockito.Mockito.doNothing;
import static org.mockito.Mockito.mock;
-import static org.mockito.Mockito.reset;
import static org.mockito.Mockito.spy;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
@@ -56,15 +58,14 @@ import com.android.systemui.statusbar.StatusBarState;
import com.android.systemui.statusbar.SysuiStatusBarStateController;
import com.android.systemui.statusbar.notification.collection.NotificationEntry;
import com.android.systemui.statusbar.notification.collection.legacy.NotificationGroupManagerLegacy;
-import com.android.systemui.statusbar.notification.collection.legacy.VisualStabilityManager;
import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow;
import com.android.systemui.statusbar.notification.row.FooterView;
-import com.android.systemui.statusbar.notification.row.NotificationBlockingHelperManager;
import com.android.systemui.statusbar.notification.stack.NotificationStackScrollLayout.KeyguardBypassEnabledProvider;
import com.android.systemui.statusbar.phone.ShadeController;
import com.android.systemui.statusbar.phone.StatusBar;
import org.junit.After;
+import org.junit.Assert;
import org.junit.Before;
import org.junit.Rule;
import org.junit.Test;
@@ -88,7 +89,6 @@ public class NotificationStackScrollLayoutTest extends SysuiTestCase {
@Rule public MockitoRule mockito = MockitoJUnit.rule();
@Mock private StatusBar mBar;
@Mock private SysuiStatusBarStateController mBarState;
- @Mock private NotificationBlockingHelperManager mBlockingHelperManager;
@Mock private NotificationGroupManagerLegacy mGroupMembershipManger;
@Mock private NotificationGroupManagerLegacy mGroupExpansionManager;
@Mock private ExpandHelper mExpandHelper;
@@ -117,10 +117,6 @@ public class NotificationStackScrollLayoutTest extends SysuiTestCase {
1, UserHandle.USER_CURRENT);
// Inject dependencies before initializing the layout
- mDependency.injectMockDependency(VisualStabilityManager.class);
- mDependency.injectTestDependency(
- NotificationBlockingHelperManager.class,
- mBlockingHelperManager);
mDependency.injectTestDependency(SysuiStatusBarStateController.class, mBarState);
mDependency.injectMockDependency(ShadeController.class);
@@ -217,12 +213,20 @@ public class NotificationStackScrollLayoutTest extends SysuiTestCase {
@Test
@UiThreadTest
public void testSetExpandedHeight_blockingHelperManagerReceivedCallbacks() {
- mStackScroller.setExpandedHeight(0f);
- verify(mBlockingHelperManager).setNotificationShadeExpanded(0f);
- reset(mBlockingHelperManager);
+ final float expectedHeight[] = {0f};
+ final float expectedAppear[] = {0f};
+
+ mStackScroller.addOnExpandedHeightChangedListener((height, appear) -> {
+ Assert.assertEquals(expectedHeight[0], height, 0);
+ Assert.assertEquals(expectedAppear[0], appear, .1);
+ });
+ expectedHeight[0] = 1f;
+ expectedAppear[0] = 1f;
+ mStackScroller.setExpandedHeight(expectedHeight[0]);
- mStackScroller.setExpandedHeight(100f);
- verify(mBlockingHelperManager).setNotificationShadeExpanded(100f);
+ expectedHeight[0] = 100f;
+ expectedAppear[0] = 0f;
+ mStackScroller.setExpandedHeight(expectedHeight[0]);
}
@Test
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollerControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollerControllerTest.java
index d83417638cc5..01d49c269fb2 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollerControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollerControllerTest.java
@@ -60,6 +60,7 @@ import com.android.systemui.statusbar.notification.NotificationEntryManager;
import com.android.systemui.statusbar.notification.collection.NotifCollection;
import com.android.systemui.statusbar.notification.collection.NotifPipeline;
import com.android.systemui.statusbar.notification.collection.legacy.NotificationGroupManagerLegacy;
+import com.android.systemui.statusbar.notification.collection.legacy.VisualStabilityManager;
import com.android.systemui.statusbar.notification.collection.render.SectionHeaderController;
import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow;
import com.android.systemui.statusbar.notification.row.ForegroundServiceDungeonView;
@@ -68,6 +69,7 @@ import com.android.systemui.statusbar.notification.stack.NotificationStackScroll
import com.android.systemui.statusbar.phone.HeadsUpManagerPhone;
import com.android.systemui.statusbar.phone.KeyguardBypassController;
import com.android.systemui.statusbar.phone.ScrimController;
+import com.android.systemui.statusbar.phone.ShadeController;
import com.android.systemui.statusbar.phone.StatusBar;
import com.android.systemui.statusbar.policy.ConfigurationController;
import com.android.systemui.statusbar.policy.ZenModeController;
@@ -105,7 +107,6 @@ public class NotificationStackScrollerControllerTest extends SysuiTestCase {
@Mock private NotificationLockscreenUserManager mNotificationLockscreenUserManager;
@Mock private MetricsLogger mMetricsLogger;
@Mock private FalsingManager mFalsingManager;
- @Mock private NotificationSectionsManager mNotificationSectionsManager;
@Mock private Resources mResources;
@Mock(answer = Answers.RETURNS_SELF)
private NotificationSwipeHelper.Builder mNotificationSwipeHelperBuilder;
@@ -126,6 +127,8 @@ public class NotificationStackScrollerControllerTest extends SysuiTestCase {
@Mock private LayoutInflater mLayoutInflater;
@Mock private NotificationRemoteInputManager mRemoteInputManager;
@Mock private RemoteInputController mRemoteInputController;
+ @Mock private VisualStabilityManager mVisualStabilityManager;
+ @Mock private ShadeController mShadeController;
@Captor
private ArgumentCaptor<StatusBarStateController.StateListener> mStateListenerArgumentCaptor;
@@ -158,7 +161,6 @@ public class NotificationStackScrollerControllerTest extends SysuiTestCase {
mNotificationLockscreenUserManager,
mMetricsLogger,
mFalsingManager,
- mNotificationSectionsManager,
mResources,
mNotificationSwipeHelperBuilder,
mStatusBar,
@@ -175,7 +177,9 @@ public class NotificationStackScrollerControllerTest extends SysuiTestCase {
mFgFeatureController,
mFgServicesSectionController,
mLayoutInflater,
- mRemoteInputManager
+ mRemoteInputManager,
+ mVisualStabilityManager,
+ mShadeController
);
when(mNotificationStackScrollLayout.isAttachedToWindow()).thenReturn(true);
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/KeyguardBouncerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/KeyguardBouncerTest.java
index 1b05ad7f8b5b..4fb45ec18178 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/KeyguardBouncerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/KeyguardBouncerTest.java
@@ -50,6 +50,7 @@ import com.android.keyguard.ViewMediatorCallback;
import com.android.keyguard.dagger.KeyguardBouncerComponent;
import com.android.systemui.DejankUtils;
import com.android.systemui.SysuiTestCase;
+import com.android.systemui.biometrics.AuthController;
import com.android.systemui.keyguard.DismissCallbackRegistry;
import com.android.systemui.plugins.ActivityStarter.OnDismissAction;
import com.android.systemui.plugins.FalsingManager;
@@ -78,6 +79,8 @@ public class KeyguardBouncerTest extends SysuiTestCase {
@Mock
private DismissCallbackRegistry mDismissCallbackRegistry;
@Mock
+ private AuthController mAuthController;
+ @Mock
private KeyguardHostViewController mKeyguardHostViewController;
@Mock
private KeyguardBouncer.BouncerExpansionCallback mExpansionCallback;
@@ -128,7 +131,7 @@ public class KeyguardBouncerTest extends SysuiTestCase {
final ViewGroup container = new FrameLayout(getContext());
mBouncer = new KeyguardBouncer.Factory(getContext(), mViewMediatorCallback,
- mDismissCallbackRegistry, mFalsingManager,
+ mDismissCallbackRegistry, mFalsingManager, mAuthController,
mKeyguardStateController, mKeyguardUpdateMonitor,
mKeyguardBypassController, mHandler, mKeyguardSecurityModel,
mKeyguardBouncerComponentFactory)
@@ -207,6 +210,12 @@ public class KeyguardBouncerTest extends SysuiTestCase {
}
@Test
+ public void testShow_notifiesAuthControllerStartingToShow() {
+ mBouncer.show(/* resetSecuritySelection */ false);
+ verify(mAuthController).onStartingToShow();
+ }
+
+ @Test
public void testSetExpansion_notifiesFalsingManager() {
mBouncer.ensureView();
mBouncer.setExpansion(0.5f);
@@ -237,6 +246,38 @@ public class KeyguardBouncerTest extends SysuiTestCase {
}
@Test
+ public void testSetExpansion_notifiesAuthControllerFullyShown() {
+ mBouncer.ensureView();
+ mBouncer.setExpansion(0.1f);
+ mBouncer.setExpansion(0f);
+ verify(mAuthController).onFullyShown();
+ }
+
+ @Test
+ public void testSetExpansion_notifiesAuthControllerStartingToHide() {
+ mBouncer.ensureView();
+ mBouncer.setExpansion(0f);
+ mBouncer.setExpansion(0.1f);
+ verify(mAuthController).onStartingToHide();
+ }
+
+ @Test
+ public void testSetExpansion_notifiesAuthControllerFullyHidden() {
+ mBouncer.ensureView();
+ mBouncer.setExpansion(0.9f);
+ mBouncer.setExpansion(1f);
+ verify(mAuthController).onFullyHidden();
+ }
+
+ @Test
+ public void testSetExpansion_negativeAuthControllerStartingToShow() {
+ mBouncer.ensureView();
+ mBouncer.setExpansion(1f);
+ mBouncer.setExpansion(0.9f);
+ verify(mAuthController, never()).onStartingToShow();
+ }
+
+ @Test
public void testHide_notifiesFalsingManager() {
mBouncer.hide(false);
verify(mFalsingManager).onBouncerHidden();
diff --git a/packages/SystemUI/tests/src/com/android/systemui/wmshell/WMShellTest.java b/packages/SystemUI/tests/src/com/android/systemui/wmshell/WMShellTest.java
index ef4d1078b8b9..0b7595000e3b 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/wmshell/WMShellTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/wmshell/WMShellTest.java
@@ -16,12 +16,17 @@
package com.android.systemui.wmshell;
+import static android.content.pm.PackageManager.FEATURE_PICTURE_IN_PICTURE;
+
import static org.mockito.ArgumentMatchers.any;
-import static org.mockito.Mockito.times;
+import static org.mockito.Mockito.never;
+import static org.mockito.Mockito.spy;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
+import android.content.pm.PackageManager;
import android.test.suitebuilder.annotation.SmallTest;
+import android.testing.TestableContext;
import androidx.test.runner.AndroidJUnit4;
@@ -32,9 +37,12 @@ import com.android.systemui.keyguard.ScreenLifecycle;
import com.android.systemui.model.SysUiState;
import com.android.systemui.navigationbar.NavigationModeController;
import com.android.systemui.pip.Pip;
+import com.android.systemui.pip.phone.PipTouchHandler;
import com.android.systemui.shared.system.ActivityManagerWrapper;
+import com.android.systemui.shared.system.InputConsumerController;
import com.android.systemui.shared.system.TaskStackChangeListener;
import com.android.systemui.statusbar.CommandQueue;
+import com.android.systemui.statusbar.policy.ConfigurationController;
import com.android.systemui.tracing.ProtoTracer;
import com.android.wm.shell.ShellTaskOrganizer;
import com.android.wm.shell.common.DisplayImeController;
@@ -54,9 +62,11 @@ import java.util.Optional;
@SmallTest
@RunWith(AndroidJUnit4.class)
public class WMShellTest extends SysuiTestCase {
-
+ InputConsumerController mInputConsumerController;
WMShell mWMShell;
+
@Mock CommandQueue mCommandQueue;
+ @Mock ConfigurationController mConfigurationController;
@Mock KeyguardUpdateMonitor mKeyguardUpdateMonitor;
@Mock ActivityManagerWrapper mActivityManagerWrapper;
@Mock DisplayImeController mDisplayImeController;
@@ -64,18 +74,26 @@ public class WMShellTest extends SysuiTestCase {
@Mock ScreenLifecycle mScreenLifecycle;
@Mock SysUiState mSysUiState;
@Mock Pip mPip;
+ @Mock PipTouchHandler mPipTouchHandler;
@Mock SplitScreen mSplitScreen;
@Mock OneHanded mOneHanded;
@Mock ShellTaskOrganizer mTaskOrganizer;
@Mock ProtoTracer mProtoTracer;
+ @Mock PackageManager mMockPackageManager;
@Before
public void setUp() {
MockitoAnnotations.initMocks(this);
- mWMShell = new WMShell(mContext, mCommandQueue, mKeyguardUpdateMonitor,
- mActivityManagerWrapper, mDisplayImeController, mNavigationModeController,
- mScreenLifecycle, mSysUiState, Optional.of(mPip), Optional.of(mSplitScreen),
- Optional.of(mOneHanded), mTaskOrganizer, mProtoTracer);
+ mInputConsumerController = InputConsumerController.getPipInputConsumer();
+
+ mWMShell = new WMShell(mContext, mCommandQueue, mConfigurationController,
+ mInputConsumerController, mKeyguardUpdateMonitor, mActivityManagerWrapper,
+ mDisplayImeController, mNavigationModeController, mScreenLifecycle, mSysUiState,
+ Optional.of(mPip), Optional.of(mSplitScreen), Optional.of(mOneHanded),
+ mTaskOrganizer, mProtoTracer);
+
+ when(mPip.getPipTouchHandler()).thenReturn(mPipTouchHandler);
+
}
@Test
@@ -89,8 +107,22 @@ public class WMShellTest extends SysuiTestCase {
public void initPip_registersCommandQueueCallback() {
mWMShell.initPip(mPip);
- // Once for the shell, once for pip
- verify(mCommandQueue, times(2)).addCallback(any(CommandQueue.Callbacks.class));
+ verify(mCommandQueue).addCallback(any(CommandQueue.Callbacks.class));
+ }
+
+ @Test
+ public void nonPipDevice_shouldNotInitPip() {
+ TestableContext spiedContext = spy(mContext);
+ when(mMockPackageManager.hasSystemFeature(FEATURE_PICTURE_IN_PICTURE)).thenReturn(false);
+ when(spiedContext.getPackageManager()).thenReturn(mMockPackageManager);
+ final WMShell nonPipWMShell = new WMShell(spiedContext, mCommandQueue,
+ mConfigurationController, mInputConsumerController, mKeyguardUpdateMonitor,
+ mActivityManagerWrapper, mDisplayImeController, mNavigationModeController,
+ mScreenLifecycle, mSysUiState, Optional.of(mPip), Optional.of(mSplitScreen),
+ Optional.of(mOneHanded), mTaskOrganizer, mProtoTracer);
+ nonPipWMShell.initPip(mPip);
+
+ verify(mCommandQueue, never()).addCallback(any());
}
@Test
@@ -108,8 +140,7 @@ public class WMShellTest extends SysuiTestCase {
mWMShell.initOneHanded(mOneHanded);
verify(mKeyguardUpdateMonitor).registerCallback(any(KeyguardUpdateMonitorCallback.class));
- // Once for the shell, once for the one handed mode
- verify(mCommandQueue, times(2)).addCallback(any(CommandQueue.Callbacks.class));
+ verify(mCommandQueue).addCallback(any(CommandQueue.Callbacks.class));
verify(mScreenLifecycle).addObserver(any(ScreenLifecycle.Observer.class));
verify(mNavigationModeController).addListener(
any(NavigationModeController.ModeChangedListener.class));
diff --git a/packages/Tethering/apex/Android.bp b/packages/Tethering/apex/Android.bp
index 67097a79e5c0..05243749f765 100644
--- a/packages/Tethering/apex/Android.bp
+++ b/packages/Tethering/apex/Android.bp
@@ -19,6 +19,7 @@ apex {
updatable: true,
min_sdk_version: "current",
java_libs: ["framework-tethering"],
+ bpfs: ["offload.o"],
apps: ["Tethering"],
manifest: "manifest.json",
key: "com.android.tethering.key",
diff --git a/packages/Tethering/bpf_progs/Android.bp b/packages/Tethering/bpf_progs/Android.bp
new file mode 100644
index 000000000000..d54f86148665
--- /dev/null
+++ b/packages/Tethering/bpf_progs/Android.bp
@@ -0,0 +1,33 @@
+//
+// 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.
+//
+
+//
+// bpf kernel programs
+//
+bpf {
+ name: "offload.o",
+ srcs: ["offload.c"],
+ cflags: [
+ "-Wall",
+ "-Werror",
+ ],
+ include_dirs: [
+ // TODO: get rid of system/netd.
+ "system/netd/bpf_progs", // for bpf_net_helpers.h
+ "system/netd/libnetdbpf/include", // for bpf_shared.h
+ "system/netd/libnetdutils/include", // for UidConstants.h
+ ],
+}
diff --git a/packages/Tethering/bpf_progs/offload.c b/packages/Tethering/bpf_progs/offload.c
new file mode 100644
index 000000000000..cc5af3127b02
--- /dev/null
+++ b/packages/Tethering/bpf_progs/offload.c
@@ -0,0 +1,207 @@
+/*
+ * 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.
+ */
+
+#include <linux/if.h>
+#include <linux/ip.h>
+#include <linux/ipv6.h>
+#include <linux/pkt_cls.h>
+#include <linux/tcp.h>
+
+#include "bpf_helpers.h"
+#include "bpf_net_helpers.h"
+#include "netdbpf/bpf_shared.h"
+
+DEFINE_BPF_MAP_GRW(tether_ingress_map, HASH, TetherIngressKey, TetherIngressValue, 64,
+ AID_NETWORK_STACK)
+
+// Tethering stats, indexed by upstream interface.
+DEFINE_BPF_MAP_GRW(tether_stats_map, HASH, uint32_t, TetherStatsValue, 16, AID_NETWORK_STACK)
+
+// Tethering data limit, indexed by upstream interface.
+// (tethering allowed when stats[iif].rxBytes + stats[iif].txBytes < limit[iif])
+DEFINE_BPF_MAP_GRW(tether_limit_map, HASH, uint32_t, uint64_t, 16, AID_NETWORK_STACK)
+
+static inline __always_inline int do_forward(struct __sk_buff* skb, bool is_ethernet) {
+ int l2_header_size = is_ethernet ? sizeof(struct ethhdr) : 0;
+ void* data = (void*)(long)skb->data;
+ const void* data_end = (void*)(long)skb->data_end;
+ struct ethhdr* eth = is_ethernet ? data : NULL; // used iff is_ethernet
+ struct ipv6hdr* ip6 = is_ethernet ? (void*)(eth + 1) : data;
+
+ // Must be meta-ethernet IPv6 frame
+ if (skb->protocol != htons(ETH_P_IPV6)) return TC_ACT_OK;
+
+ // Must have (ethernet and) ipv6 header
+ if (data + l2_header_size + sizeof(*ip6) > data_end) return TC_ACT_OK;
+
+ // Ethertype - if present - must be IPv6
+ if (is_ethernet && (eth->h_proto != htons(ETH_P_IPV6))) return TC_ACT_OK;
+
+ // IP version must be 6
+ if (ip6->version != 6) return TC_ACT_OK;
+
+ // Cannot decrement during forward if already zero or would be zero,
+ // Let the kernel's stack handle these cases and generate appropriate ICMP errors.
+ if (ip6->hop_limit <= 1) return TC_ACT_OK;
+
+ // Protect against forwarding packets sourced from ::1 or fe80::/64 or other weirdness.
+ __be32 src32 = ip6->saddr.s6_addr32[0];
+ if (src32 != htonl(0x0064ff9b) && // 64:ff9b:/32 incl. XLAT464 WKP
+ (src32 & htonl(0xe0000000)) != htonl(0x20000000)) // 2000::/3 Global Unicast
+ return TC_ACT_OK;
+
+ TetherIngressKey k = {
+ .iif = skb->ifindex,
+ .neigh6 = ip6->daddr,
+ };
+
+ TetherIngressValue* v = bpf_tether_ingress_map_lookup_elem(&k);
+
+ // If we don't find any offload information then simply let the core stack handle it...
+ if (!v) return TC_ACT_OK;
+
+ uint32_t stat_and_limit_k = skb->ifindex;
+
+ TetherStatsValue* stat_v = bpf_tether_stats_map_lookup_elem(&stat_and_limit_k);
+
+ // If we don't have anywhere to put stats, then abort...
+ if (!stat_v) return TC_ACT_OK;
+
+ uint64_t* limit_v = bpf_tether_limit_map_lookup_elem(&stat_and_limit_k);
+
+ // If we don't have a limit, then abort...
+ if (!limit_v) return TC_ACT_OK;
+
+ // Required IPv6 minimum mtu is 1280, below that not clear what we should do, abort...
+ const int pmtu = v->pmtu;
+ if (pmtu < IPV6_MIN_MTU) return TC_ACT_OK;
+
+ // Approximate handling of TCP/IPv6 overhead for incoming LRO/GRO packets: default
+ // outbound path mtu of 1500 is not necessarily correct, but worst case we simply
+ // undercount, which is still better then not accounting for this overhead at all.
+ // Note: this really shouldn't be device/path mtu at all, but rather should be
+ // derived from this particular connection's mss (ie. from gro segment size).
+ // This would require a much newer kernel with newer ebpf accessors.
+ // (This is also blindly assuming 12 bytes of tcp timestamp option in tcp header)
+ uint64_t packets = 1;
+ uint64_t bytes = skb->len;
+ if (bytes > pmtu) {
+ const int tcp_overhead = sizeof(struct ipv6hdr) + sizeof(struct tcphdr) + 12;
+ const int mss = pmtu - tcp_overhead;
+ const uint64_t payload = bytes - tcp_overhead;
+ packets = (payload + mss - 1) / mss;
+ bytes = tcp_overhead * packets + payload;
+ }
+
+ // Are we past the limit? If so, then abort...
+ // Note: will not overflow since u64 is 936 years even at 5Gbps.
+ // Do not drop here. Offload is just that, whenever we fail to handle
+ // a packet we let the core stack deal with things.
+ // (The core stack needs to handle limits correctly anyway,
+ // since we don't offload all traffic in both directions)
+ if (stat_v->rxBytes + stat_v->txBytes + bytes > *limit_v) return TC_ACT_OK;
+
+ if (!is_ethernet) {
+ is_ethernet = true;
+ l2_header_size = sizeof(struct ethhdr);
+ // Try to inject an ethernet header, and simply return if we fail
+ if (bpf_skb_change_head(skb, l2_header_size, /*flags*/ 0)) {
+ __sync_fetch_and_add(&stat_v->rxErrors, 1);
+ return TC_ACT_OK;
+ }
+
+ // bpf_skb_change_head() invalidates all pointers - reload them
+ data = (void*)(long)skb->data;
+ data_end = (void*)(long)skb->data_end;
+ eth = data;
+ ip6 = (void*)(eth + 1);
+
+ // I do not believe this can ever happen, but keep the verifier happy...
+ if (data + l2_header_size + sizeof(*ip6) > data_end) {
+ __sync_fetch_and_add(&stat_v->rxErrors, 1);
+ return TC_ACT_SHOT;
+ }
+ };
+
+ // CHECKSUM_COMPLETE is a 16-bit one's complement sum,
+ // thus corrections for it need to be done in 16-byte chunks at even offsets.
+ // IPv6 nexthdr is at offset 6, while hop limit is at offset 7
+ uint8_t old_hl = ip6->hop_limit;
+ --ip6->hop_limit;
+ uint8_t new_hl = ip6->hop_limit;
+
+ // bpf_csum_update() always succeeds if the skb is CHECKSUM_COMPLETE and returns an error
+ // (-ENOTSUPP) if it isn't.
+ bpf_csum_update(skb, 0xFFFF - ntohs(old_hl) + ntohs(new_hl));
+
+ __sync_fetch_and_add(&stat_v->rxPackets, packets);
+ __sync_fetch_and_add(&stat_v->rxBytes, bytes);
+
+ // Overwrite any mac header with the new one
+ *eth = v->macHeader;
+
+ // Redirect to forwarded interface.
+ //
+ // Note that bpf_redirect() cannot fail unless you pass invalid flags.
+ // The redirect actually happens after the ebpf program has already terminated,
+ // and can fail for example for mtu reasons at that point in time, but there's nothing
+ // we can do about it here.
+ return bpf_redirect(v->oif, 0 /* this is effectively BPF_F_EGRESS */);
+}
+
+SEC("schedcls/ingress/tether_ether")
+int sched_cls_ingress_tether_ether(struct __sk_buff* skb) {
+ return do_forward(skb, true);
+}
+
+// Note: section names must be unique to prevent programs from appending to each other,
+// so instead the bpf loader will strip everything past the final $ symbol when actually
+// pinning the program into the filesystem.
+//
+// bpf_skb_change_head() is only present on 4.14+ and 2 trivial kernel patches are needed:
+// ANDROID: net: bpf: Allow TC programs to call BPF_FUNC_skb_change_head
+// ANDROID: net: bpf: permit redirect from ingress L3 to egress L2 devices at near max mtu
+// (the first of those has already been upstreamed)
+//
+// 5.4 kernel support was only added to Android Common Kernel in R,
+// and thus a 5.4 kernel always supports this.
+//
+// Hence, this mandatory (must load successfully) implementation for 5.4+ kernels:
+DEFINE_BPF_PROG_KVER("schedcls/ingress/tether_rawip$5_4", AID_ROOT, AID_ROOT,
+ sched_cls_ingress_tether_rawip_5_4, KVER(5, 4, 0))
+(struct __sk_buff* skb) {
+ return do_forward(skb, false);
+}
+
+// and this identical optional (may fail to load) implementation for [4.14..5.4) patched kernels:
+DEFINE_OPTIONAL_BPF_PROG_KVER_RANGE("schedcls/ingress/tether_rawip$4_14", AID_ROOT, AID_ROOT,
+ sched_cls_ingress_tether_rawip_4_14, KVER(4, 14, 0),
+ KVER(5, 4, 0))
+(struct __sk_buff* skb) {
+ return do_forward(skb, false);
+}
+
+// and define a no-op stub for [4.9,4.14) and unpatched [4.14,5.4) kernels.
+// (if the above real 4.14+ program loaded successfully, then bpfloader will have already pinned
+// it at the same location this one would be pinned at and will thus skip loading this stub)
+DEFINE_BPF_PROG_KVER_RANGE("schedcls/ingress/tether_rawip$stub", AID_ROOT, AID_ROOT,
+ sched_cls_ingress_tether_rawip_stub, KVER_NONE, KVER(5, 4, 0))
+(struct __sk_buff* skb) {
+ return TC_ACT_OK;
+}
+
+LICENSE("Apache 2.0");
+CRITICAL("netd");
diff --git a/packages/Tethering/jni/android_net_util_TetheringUtils.cpp b/packages/Tethering/jni/android_net_util_TetheringUtils.cpp
index f6eb40a842d5..94c871d8a366 100644
--- a/packages/Tethering/jni/android_net_util_TetheringUtils.cpp
+++ b/packages/Tethering/jni/android_net_util_TetheringUtils.cpp
@@ -17,18 +17,63 @@
#include <errno.h>
#include <error.h>
#include <jni.h>
+#include <linux/filter.h>
#include <nativehelper/JNIHelp.h>
#include <nativehelper/JNIHelpCompat.h>
#include <nativehelper/ScopedUtfChars.h>
#include <net/if.h>
+#include <netinet/ether.h>
+#include <netinet/ip6.h>
#include <netinet/icmp6.h>
#include <sys/socket.h>
+#include <stdio.h>
#define LOG_TAG "TetheringUtils"
#include <android/log.h>
namespace android {
+static const uint32_t kIPv6NextHeaderOffset = offsetof(ip6_hdr, ip6_nxt);
+static const uint32_t kIPv6PayloadStart = sizeof(ip6_hdr);
+static const uint32_t kICMPv6TypeOffset = kIPv6PayloadStart + offsetof(icmp6_hdr, icmp6_type);
+
+static void android_net_util_setupIcmpFilter(JNIEnv *env, jobject javaFd, uint32_t type) {
+ sock_filter filter_code[] = {
+ // Check header is ICMPv6.
+ BPF_STMT(BPF_LD | BPF_B | BPF_ABS, kIPv6NextHeaderOffset),
+ BPF_JUMP(BPF_JMP | BPF_JEQ | BPF_K, IPPROTO_ICMPV6, 0, 3),
+
+ // Check ICMPv6 type.
+ BPF_STMT(BPF_LD | BPF_B | BPF_ABS, kICMPv6TypeOffset),
+ BPF_JUMP(BPF_JMP | BPF_JEQ | BPF_K, type, 0, 1),
+
+ // Accept or reject.
+ BPF_STMT(BPF_RET | BPF_K, 0xffff),
+ BPF_STMT(BPF_RET | BPF_K, 0)
+ };
+
+ const sock_fprog filter = {
+ sizeof(filter_code) / sizeof(filter_code[0]),
+ filter_code,
+ };
+
+ int fd = jniGetFDFromFileDescriptor(env, javaFd);
+ if (setsockopt(fd, SOL_SOCKET, SO_ATTACH_FILTER, &filter, sizeof(filter)) != 0) {
+ jniThrowExceptionFmt(env, "java/net/SocketException",
+ "setsockopt(SO_ATTACH_FILTER): %s", strerror(errno));
+ }
+}
+
+static void android_net_util_setupNaSocket(JNIEnv *env, jobject clazz, jobject javaFd)
+{
+ android_net_util_setupIcmpFilter(env, javaFd, ND_NEIGHBOR_ADVERT);
+}
+
+static void android_net_util_setupNsSocket(JNIEnv *env, jobject clazz, jobject javaFd)
+{
+ android_net_util_setupIcmpFilter(env, javaFd, ND_NEIGHBOR_SOLICIT);
+}
+
static void android_net_util_setupRaSocket(JNIEnv *env, jobject clazz, jobject javaFd,
jint ifIndex)
{
@@ -125,7 +170,12 @@ static void android_net_util_setupRaSocket(JNIEnv *env, jobject clazz, jobject j
*/
static const JNINativeMethod gMethods[] = {
/* name, signature, funcPtr */
- { "setupRaSocket", "(Ljava/io/FileDescriptor;I)V", (void*) android_net_util_setupRaSocket },
+ { "setupNaSocket", "(Ljava/io/FileDescriptor;)V",
+ (void*) android_net_util_setupNaSocket },
+ { "setupNsSocket", "(Ljava/io/FileDescriptor;)V",
+ (void*) android_net_util_setupNsSocket },
+ { "setupRaSocket", "(Ljava/io/FileDescriptor;I)V",
+ (void*) android_net_util_setupRaSocket },
};
int register_android_net_util_TetheringUtils(JNIEnv* env) {
diff --git a/packages/Tethering/src/android/net/ip/DadProxy.java b/packages/Tethering/src/android/net/ip/DadProxy.java
new file mode 100644
index 000000000000..e2976b78908c
--- /dev/null
+++ b/packages/Tethering/src/android/net/ip/DadProxy.java
@@ -0,0 +1,54 @@
+/*
+ * 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.net.ip;
+
+import android.net.util.InterfaceParams;
+import android.os.Handler;
+
+import androidx.annotation.VisibleForTesting;
+
+/**
+ * Basic Duplicate address detection proxy.
+ *
+ * @hide
+ */
+public class DadProxy {
+ private static final String TAG = DadProxy.class.getSimpleName();
+
+ @VisibleForTesting
+ public static NeighborPacketForwarder naForwarder;
+ public static NeighborPacketForwarder nsForwarder;
+
+ public DadProxy(Handler h, InterfaceParams tetheredIface) {
+ naForwarder = new NeighborPacketForwarder(h, tetheredIface,
+ NeighborPacketForwarder.ICMPV6_NEIGHBOR_ADVERTISEMENT);
+ nsForwarder = new NeighborPacketForwarder(h, tetheredIface,
+ NeighborPacketForwarder.ICMPV6_NEIGHBOR_SOLICITATION);
+ }
+
+ /** Stop NS/NA Forwarders. */
+ public void stop() {
+ naForwarder.stop();
+ nsForwarder.stop();
+ }
+
+ /** Set upstream iface on both forwarders. */
+ public void setUpstreamIface(InterfaceParams upstreamIface) {
+ naForwarder.setUpstreamIface(upstreamIface);
+ nsForwarder.setUpstreamIface(upstreamIface);
+ }
+}
diff --git a/packages/Tethering/src/android/net/ip/IpServer.java b/packages/Tethering/src/android/net/ip/IpServer.java
index 673cbf09d259..336124dc1b90 100644
--- a/packages/Tethering/src/android/net/ip/IpServer.java
+++ b/packages/Tethering/src/android/net/ip/IpServer.java
@@ -51,6 +51,7 @@ import android.net.util.InterfaceParams;
import android.net.util.InterfaceSet;
import android.net.util.PrefixUtils;
import android.net.util.SharedLog;
+import android.os.Build;
import android.os.Handler;
import android.os.Looper;
import android.os.Message;
@@ -160,6 +161,15 @@ public class IpServer extends StateMachine {
/** Capture IpServer dependencies, for injection. */
public abstract static class Dependencies {
+ /**
+ * Create a DadProxy instance to be used by IpServer.
+ * To support multiple tethered interfaces concurrently DAD Proxy
+ * needs to be supported per IpServer instead of per upstream.
+ */
+ public DadProxy getDadProxy(Handler handler, InterfaceParams ifParams) {
+ return new DadProxy(handler, ifParams);
+ }
+
/** Create an IpNeighborMonitor to be used by this IpServer */
public IpNeighborMonitor getIpNeighborMonitor(Handler handler, SharedLog log,
IpNeighborMonitor.NeighborEventConsumer consumer) {
@@ -256,6 +266,7 @@ public class IpServer extends StateMachine {
// Advertisements (otherwise, we do not add them to mLinkProperties at all).
private LinkProperties mLastIPv6LinkProperties;
private RouterAdvertisementDaemon mRaDaemon;
+ private DadProxy mDadProxy;
// To be accessed only on the handler thread
private int mDhcpServerStartIndex = 0;
@@ -674,6 +685,13 @@ public class IpServer extends StateMachine {
return false;
}
+ // TODO: use ShimUtils instead of explicitly checking the version here.
+ if (Build.VERSION.SDK_INT > Build.VERSION_CODES.R || "S".equals(Build.VERSION.CODENAME)
+ || "T".equals(Build.VERSION.CODENAME)) {
+ // DAD Proxy starts forwarding packets after IPv6 upstream is present.
+ mDadProxy = mDeps.getDadProxy(getHandler(), mInterfaceParams);
+ }
+
return true;
}
@@ -685,6 +703,11 @@ public class IpServer extends StateMachine {
mRaDaemon.stop();
mRaDaemon = null;
}
+
+ if (mDadProxy != null) {
+ mDadProxy.stop();
+ mDadProxy = null;
+ }
}
// IPv6TetheringCoordinator sends updates with carefully curated IPv6-only
@@ -702,11 +725,16 @@ public class IpServer extends StateMachine {
}
RaParams params = null;
- int upstreamIfindex = 0;
+ String upstreamIface = null;
+ InterfaceParams upstreamIfaceParams = null;
+ int upstreamIfIndex = 0;
if (v6only != null) {
- final String upstreamIface = v6only.getInterfaceName();
-
+ upstreamIface = v6only.getInterfaceName();
+ upstreamIfaceParams = mDeps.getInterfaceParams(upstreamIface);
+ if (upstreamIfaceParams != null) {
+ upstreamIfIndex = upstreamIfaceParams.index;
+ }
params = new RaParams();
params.mtu = v6only.getMtu();
params.hasDefaultRoute = v6only.hasIpv6DefaultRoute();
@@ -726,15 +754,13 @@ public class IpServer extends StateMachine {
}
}
- upstreamIfindex = mDeps.getIfindex(upstreamIface);
-
// Add upstream index to name mapping for the tether stats usage in the coordinator.
// Although this mapping could be added by both class Tethering and IpServer, adding
// mapping from IpServer guarantees that the mapping is added before the adding
// forwarding rules. That is because there are different state machines in both
// classes. It is hard to guarantee the link property update order between multiple
// state machines.
- mBpfCoordinator.addUpstreamNameToLookupTable(upstreamIfindex, upstreamIface);
+ mBpfCoordinator.addUpstreamNameToLookupTable(upstreamIfIndex, upstreamIface);
}
// If v6only is null, we pass in null to setRaParams(), which handles
@@ -743,8 +769,11 @@ public class IpServer extends StateMachine {
setRaParams(params);
mLastIPv6LinkProperties = v6only;
- updateIpv6ForwardingRules(mLastIPv6UpstreamIfindex, upstreamIfindex, null);
- mLastIPv6UpstreamIfindex = upstreamIfindex;
+ updateIpv6ForwardingRules(mLastIPv6UpstreamIfindex, upstreamIfIndex, null);
+ mLastIPv6UpstreamIfindex = upstreamIfIndex;
+ if (mDadProxy != null) {
+ mDadProxy.setUpstreamIface(upstreamIfaceParams);
+ }
}
private void removeRoutesFromLocalNetwork(@NonNull final List<RouteInfo> toBeRemoved) {
diff --git a/packages/Tethering/src/android/net/ip/NeighborPacketForwarder.java b/packages/Tethering/src/android/net/ip/NeighborPacketForwarder.java
new file mode 100644
index 000000000000..73fc833fabf5
--- /dev/null
+++ b/packages/Tethering/src/android/net/ip/NeighborPacketForwarder.java
@@ -0,0 +1,180 @@
+/*
+ * 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.net.ip;
+
+import static android.system.OsConstants.AF_INET6;
+import static android.system.OsConstants.AF_PACKET;
+import static android.system.OsConstants.ETH_P_IPV6;
+import static android.system.OsConstants.IPPROTO_RAW;
+import static android.system.OsConstants.SOCK_DGRAM;
+import static android.system.OsConstants.SOCK_NONBLOCK;
+import static android.system.OsConstants.SOCK_RAW;
+
+import android.net.util.InterfaceParams;
+import android.net.util.PacketReader;
+import android.net.util.SocketUtils;
+import android.net.util.TetheringUtils;
+import android.os.Handler;
+import android.system.ErrnoException;
+import android.system.Os;
+import android.util.Log;
+
+import java.io.FileDescriptor;
+import java.io.IOException;
+import java.net.Inet6Address;
+import java.net.InetSocketAddress;
+import java.net.SocketAddress;
+import java.net.SocketException;
+import java.net.UnknownHostException;
+import java.util.Arrays;
+
+/**
+ * Basic IPv6 Neighbor Advertisement Forwarder.
+ *
+ * Forward NA packets from upstream iface to tethered iface
+ * and NS packets from tethered iface to upstream iface.
+ *
+ * @hide
+ */
+public class NeighborPacketForwarder extends PacketReader {
+ private final String mTag;
+
+ private FileDescriptor mFd;
+
+ // TODO: get these from NetworkStackConstants.
+ private static final int IPV6_ADDR_LEN = 16;
+ private static final int IPV6_DST_ADDR_OFFSET = 24;
+ private static final int IPV6_HEADER_LEN = 40;
+ private static final int ETH_HEADER_LEN = 14;
+
+ private InterfaceParams mListenIfaceParams, mSendIfaceParams;
+
+ private final int mType;
+ public static final int ICMPV6_NEIGHBOR_ADVERTISEMENT = 136;
+ public static final int ICMPV6_NEIGHBOR_SOLICITATION = 135;
+
+ public NeighborPacketForwarder(Handler h, InterfaceParams tetheredInterface, int type) {
+ super(h);
+ mTag = NeighborPacketForwarder.class.getSimpleName() + "-"
+ + tetheredInterface.name + "-" + type;
+ mType = type;
+
+ if (mType == ICMPV6_NEIGHBOR_ADVERTISEMENT) {
+ mSendIfaceParams = tetheredInterface;
+ } else {
+ mListenIfaceParams = tetheredInterface;
+ }
+ }
+
+ /** Set new upstream iface and start/stop based on new params. */
+ public void setUpstreamIface(InterfaceParams upstreamParams) {
+ final InterfaceParams oldUpstreamParams;
+
+ if (mType == ICMPV6_NEIGHBOR_ADVERTISEMENT) {
+ oldUpstreamParams = mListenIfaceParams;
+ mListenIfaceParams = upstreamParams;
+ } else {
+ oldUpstreamParams = mSendIfaceParams;
+ mSendIfaceParams = upstreamParams;
+ }
+
+ if (oldUpstreamParams == null && upstreamParams != null) {
+ start();
+ } else if (oldUpstreamParams != null && upstreamParams == null) {
+ stop();
+ } else if (oldUpstreamParams != null && upstreamParams != null
+ && oldUpstreamParams.index != upstreamParams.index) {
+ stop();
+ start();
+ }
+ }
+
+ // TODO: move NetworkStackUtils.closeSocketQuietly to
+ // frameworks/libs/net/common/device/com/android/net/module/util/[someclass].
+ private void closeSocketQuietly(FileDescriptor fd) {
+ try {
+ SocketUtils.closeSocket(fd);
+ } catch (IOException ignored) {
+ }
+ }
+
+ @Override
+ protected FileDescriptor createFd() {
+ try {
+ // ICMPv6 packets from modem do not have eth header, so RAW socket cannot be used.
+ // To keep uniformity in both directions PACKET socket can be used.
+ mFd = Os.socket(AF_PACKET, SOCK_DGRAM | SOCK_NONBLOCK, 0);
+
+ // TODO: convert setup*Socket to setupICMPv6BpfFilter with filter type?
+ if (mType == ICMPV6_NEIGHBOR_ADVERTISEMENT) {
+ TetheringUtils.setupNaSocket(mFd);
+ } else if (mType == ICMPV6_NEIGHBOR_SOLICITATION) {
+ TetheringUtils.setupNsSocket(mFd);
+ }
+
+ SocketAddress bindAddress = SocketUtils.makePacketSocketAddress(
+ ETH_P_IPV6, mListenIfaceParams.index);
+ Os.bind(mFd, bindAddress);
+ } catch (ErrnoException | SocketException e) {
+ Log.wtf(mTag, "Failed to create socket", e);
+ closeSocketQuietly(mFd);
+ return null;
+ }
+
+ return mFd;
+ }
+
+ private Inet6Address getIpv6DestinationAddress(byte[] recvbuf) {
+ Inet6Address dstAddr;
+ try {
+ dstAddr = (Inet6Address) Inet6Address.getByAddress(Arrays.copyOfRange(recvbuf,
+ IPV6_DST_ADDR_OFFSET, IPV6_DST_ADDR_OFFSET + IPV6_ADDR_LEN));
+ } catch (UnknownHostException | ClassCastException impossible) {
+ throw new AssertionError("16-byte array not valid IPv6 address?");
+ }
+ return dstAddr;
+ }
+
+ @Override
+ protected void handlePacket(byte[] recvbuf, int length) {
+ if (mSendIfaceParams == null) {
+ return;
+ }
+
+ // The BPF filter should already have checked the length of the packet, but...
+ if (length < IPV6_HEADER_LEN) {
+ return;
+ }
+ Inet6Address destv6 = getIpv6DestinationAddress(recvbuf);
+ if (!destv6.isMulticastAddress()) {
+ return;
+ }
+ InetSocketAddress dest = new InetSocketAddress(destv6, 0);
+
+ FileDescriptor fd = null;
+ try {
+ fd = Os.socket(AF_INET6, SOCK_RAW | SOCK_NONBLOCK, IPPROTO_RAW);
+ SocketUtils.bindSocketToInterface(fd, mSendIfaceParams.name);
+
+ int ret = Os.sendto(fd, recvbuf, 0, length, 0, dest);
+ } catch (ErrnoException | SocketException e) {
+ Log.e(mTag, "handlePacket error: " + e);
+ } finally {
+ closeSocketQuietly(fd);
+ }
+ }
+}
diff --git a/packages/Tethering/src/android/net/ip/RouterAdvertisementDaemon.java b/packages/Tethering/src/android/net/ip/RouterAdvertisementDaemon.java
index 6f017dcb623f..7c0b7cc7515c 100644
--- a/packages/Tethering/src/android/net/ip/RouterAdvertisementDaemon.java
+++ b/packages/Tethering/src/android/net/ip/RouterAdvertisementDaemon.java
@@ -18,6 +18,7 @@ package android.net.ip;
import static android.net.util.NetworkConstants.IPV6_MIN_MTU;
import static android.net.util.NetworkConstants.RFC7421_PREFIX_LENGTH;
+import static android.net.util.TetheringUtils.getAllNodesForScopeId;
import static android.system.OsConstants.AF_INET6;
import static android.system.OsConstants.IPPROTO_ICMPV6;
import static android.system.OsConstants.SOCK_RAW;
@@ -44,7 +45,6 @@ import java.net.Inet6Address;
import java.net.InetAddress;
import java.net.InetSocketAddress;
import java.net.SocketException;
-import java.net.UnknownHostException;
import java.nio.BufferOverflowException;
import java.nio.ByteBuffer;
import java.nio.ByteOrder;
@@ -92,10 +92,6 @@ public class RouterAdvertisementDaemon {
private static final int DAY_IN_SECONDS = 86_400;
- private static final byte[] ALL_NODES = new byte[] {
- (byte) 0xff, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1
- };
-
private final InterfaceParams mInterface;
private final InetSocketAddress mAllNodes;
@@ -240,7 +236,6 @@ public class RouterAdvertisementDaemon {
}
}
-
public RouterAdvertisementDaemon(InterfaceParams ifParams) {
mInterface = ifParams;
mAllNodes = new InetSocketAddress(getAllNodesForScopeId(mInterface.index), 0);
@@ -363,15 +358,6 @@ public class RouterAdvertisementDaemon {
}
}
- private static Inet6Address getAllNodesForScopeId(int scopeId) {
- try {
- return Inet6Address.getByAddress("ff02::1", ALL_NODES, scopeId);
- } catch (UnknownHostException uhe) {
- Log.wtf(TAG, "Failed to construct ff02::1 InetAddress: " + uhe);
- return null;
- }
- }
-
private static byte asByte(int value) {
return (byte) value;
}
diff --git a/packages/Tethering/src/android/net/util/TetheringUtils.java b/packages/Tethering/src/android/net/util/TetheringUtils.java
index b17b4ba77cfb..53b54f7de05d 100644
--- a/packages/Tethering/src/android/net/util/TetheringUtils.java
+++ b/packages/Tethering/src/android/net/util/TetheringUtils.java
@@ -17,11 +17,15 @@ package android.net.util;
import android.net.TetherStatsParcel;
import android.net.TetheringRequestParcel;
+import android.util.Log;
import androidx.annotation.NonNull;
import java.io.FileDescriptor;
+import java.net.Inet6Address;
import java.net.SocketException;
+import java.net.UnknownHostException;
+import java.util.Arrays;
import java.util.Objects;
/**
@@ -30,6 +34,24 @@ import java.util.Objects;
* {@hide}
*/
public class TetheringUtils {
+ public static final byte[] ALL_NODES = new byte[] {
+ (byte) 0xff, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1
+ };
+
+ /**
+ * Configures a socket for receiving and sending ICMPv6 neighbor advertisments.
+ * @param fd the socket's {@link FileDescriptor}.
+ */
+ public static native void setupNaSocket(FileDescriptor fd)
+ throws SocketException;
+
+ /**
+ * Configures a socket for receiving and sending ICMPv6 neighbor solicitations.
+ * @param fd the socket's {@link FileDescriptor}.
+ */
+ public static native void setupNsSocket(FileDescriptor fd)
+ throws SocketException;
+
/**
* The object which records offload Tx/Rx forwarded bytes/packets.
* TODO: Replace the inner class ForwardedStats of class OffloadHardwareInterface with
@@ -129,4 +151,15 @@ public class TetheringUtils {
&& request.exemptFromEntitlementCheck == otherRequest.exemptFromEntitlementCheck
&& request.showProvisioningUi == otherRequest.showProvisioningUi;
}
+
+ /** Get inet6 address for all nodes given scope ID. */
+ public static Inet6Address getAllNodesForScopeId(int scopeId) {
+ try {
+ return Inet6Address.getByAddress("ff02::1", ALL_NODES, scopeId);
+ } catch (UnknownHostException uhe) {
+ Log.wtf("TetheringUtils", "Failed to construct Inet6Address from "
+ + Arrays.toString(ALL_NODES) + " and scopedId " + scopeId);
+ return null;
+ }
+ }
}
diff --git a/packages/Tethering/tests/integration/Android.bp b/packages/Tethering/tests/integration/Android.bp
index ed69b7d63cb4..02bab9ba353e 100644
--- a/packages/Tethering/tests/integration/Android.bp
+++ b/packages/Tethering/tests/integration/Android.bp
@@ -22,7 +22,6 @@ java_defaults {
static_libs: [
"NetworkStackApiStableLib",
"androidx.test.rules",
- "frameworks-base-testutils",
"mockito-target-extended-minus-junit4",
"net-tests-utils",
"testables",
diff --git a/packages/Tethering/tests/privileged/Android.bp b/packages/Tethering/tests/privileged/Android.bp
index a0fb24603a93..9217345dc2f5 100644
--- a/packages/Tethering/tests/privileged/Android.bp
+++ b/packages/Tethering/tests/privileged/Android.bp
@@ -14,8 +14,22 @@
// limitations under the License.
//
+java_defaults {
+ name: "TetheringPrivilegedTestsJniDefaults",
+ jni_libs: [
+ "libdexmakerjvmtiagent",
+ "libstaticjvmtiagent",
+ "libtetherutilsjni",
+ ],
+ jni_uses_sdk_apis: true,
+ visibility: ["//visibility:private"],
+}
+
android_test {
name: "TetheringPrivilegedTests",
+ defaults: [
+ "TetheringPrivilegedTestsJniDefaults",
+ ],
srcs: [
"src/**/*.java",
"src/**/*.kt",
@@ -23,8 +37,13 @@ android_test {
certificate: "networkstack",
platform_apis: true,
test_suites: [
- "general-tests",
+ "device-tests",
"mts",
],
+ static_libs: [
+ "androidx.test.rules",
+ "net-tests-utils",
+ "TetheringApiCurrentLib",
+ ],
compile_multilib: "both",
}
diff --git a/packages/Tethering/tests/privileged/src/android/net/ip/DadProxyTest.java b/packages/Tethering/tests/privileged/src/android/net/ip/DadProxyTest.java
new file mode 100644
index 000000000000..747d3e803026
--- /dev/null
+++ b/packages/Tethering/tests/privileged/src/android/net/ip/DadProxyTest.java
@@ -0,0 +1,338 @@
+/*
+ * 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.net.ip;
+
+import static android.system.OsConstants.IPPROTO_ICMPV6;
+import static android.system.OsConstants.IPPROTO_TCP;
+
+import static com.android.internal.util.BitUtils.uint16;
+
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertTrue;
+
+import android.app.Instrumentation;
+import android.content.Context;
+import android.net.INetd;
+import android.net.InetAddresses;
+import android.net.MacAddress;
+import android.net.TestNetworkInterface;
+import android.net.TestNetworkManager;
+import android.net.util.InterfaceParams;
+import android.net.util.IpUtils;
+import android.net.util.TetheringUtils;
+import android.os.Handler;
+import android.os.HandlerThread;
+import android.os.IBinder;
+import android.os.Looper;
+import android.system.ErrnoException;
+import android.system.Os;
+
+import androidx.test.InstrumentationRegistry;
+import androidx.test.filters.SmallTest;
+import androidx.test.runner.AndroidJUnit4;
+
+import com.android.testutils.TapPacketReader;
+
+import org.junit.After;
+import org.junit.Before;
+import org.junit.BeforeClass;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.MockitoAnnotations;
+
+import java.io.FileDescriptor;
+import java.nio.ByteBuffer;
+import java.util.concurrent.atomic.AtomicReference;
+
+@RunWith(AndroidJUnit4.class)
+@SmallTest
+public class DadProxyTest {
+ private static final int DATA_BUFFER_LEN = 4096;
+ private static final int PACKET_TIMEOUT_MS = 5_000;
+
+ // TODO: make NetworkStackConstants accessible to this test and use the constant from there.
+ private static final int ETHER_SRC_ADDR_OFFSET = 6;
+
+ private DadProxy mProxy;
+ TestNetworkInterface mUpstreamTestIface, mTetheredTestIface;
+ private InterfaceParams mUpstreamParams, mTetheredParams;
+ private HandlerThread mHandlerThread;
+ private Handler mHandler;
+ private TapPacketReader mUpstreamPacketReader, mTetheredPacketReader;
+ private FileDescriptor mUpstreamTapFd, mTetheredTapFd;
+
+ private static INetd sNetd;
+
+ @BeforeClass
+ public static void setupOnce() {
+ System.loadLibrary("tetherutilsjni");
+
+ final Instrumentation inst = InstrumentationRegistry.getInstrumentation();
+ final IBinder netdIBinder =
+ (IBinder) inst.getContext().getSystemService(Context.NETD_SERVICE);
+ sNetd = INetd.Stub.asInterface(netdIBinder);
+ }
+
+ @Before
+ public void setUp() throws Exception {
+ MockitoAnnotations.initMocks(this);
+
+ mHandlerThread = new HandlerThread(getClass().getSimpleName());
+ mHandlerThread.start();
+ mHandler = new Handler(mHandlerThread.getLooper());
+
+ setupTapInterfaces();
+
+ // Looper must be prepared here since AndroidJUnitRunner runs tests on separate threads.
+ if (Looper.myLooper() == null) Looper.prepare();
+
+ DadProxy mProxy = setupProxy();
+ }
+
+ @After
+ public void tearDown() throws Exception {
+ if (mHandlerThread != null) {
+ mHandler.post(mUpstreamPacketReader::stop); // Also closes the socket
+ mHandler.post(mTetheredPacketReader::stop); // Also closes the socket
+ mUpstreamTapFd = null;
+ mTetheredTapFd = null;
+ mHandlerThread.quitSafely();
+ }
+
+ if (mTetheredParams != null) {
+ sNetd.networkRemoveInterface(INetd.LOCAL_NET_ID, mTetheredParams.name);
+ }
+ if (mUpstreamParams != null) {
+ sNetd.networkRemoveInterface(INetd.LOCAL_NET_ID, mUpstreamParams.name);
+ }
+
+ if (mUpstreamTestIface != null) {
+ try {
+ Os.close(mUpstreamTestIface.getFileDescriptor().getFileDescriptor());
+ } catch (ErrnoException e) { }
+ }
+
+ if (mTetheredTestIface != null) {
+ try {
+ Os.close(mTetheredTestIface.getFileDescriptor().getFileDescriptor());
+ } catch (ErrnoException e) { }
+ }
+ }
+
+ private TestNetworkInterface setupTapInterface() {
+ final Instrumentation inst = InstrumentationRegistry.getInstrumentation();
+ AtomicReference<TestNetworkInterface> iface = new AtomicReference<>();
+
+ inst.getUiAutomation().adoptShellPermissionIdentity();
+ try {
+ final TestNetworkManager tnm = (TestNetworkManager) inst.getContext().getSystemService(
+ Context.TEST_NETWORK_SERVICE);
+ iface.set(tnm.createTapInterface());
+ } finally {
+ inst.getUiAutomation().dropShellPermissionIdentity();
+ }
+
+ return iface.get();
+ }
+
+ private void setupTapInterfaces() {
+ // Create upstream test iface.
+ mUpstreamTestIface = setupTapInterface();
+ mUpstreamParams = InterfaceParams.getByName(mUpstreamTestIface.getInterfaceName());
+ assertNotNull(mUpstreamParams);
+ mUpstreamTapFd = mUpstreamTestIface.getFileDescriptor().getFileDescriptor();
+ mUpstreamPacketReader = new TapPacketReader(mHandler, mUpstreamTapFd,
+ DATA_BUFFER_LEN);
+ mHandler.post(mUpstreamPacketReader::start);
+
+ // Create tethered test iface.
+ mTetheredTestIface = setupTapInterface();
+ mTetheredParams = InterfaceParams.getByName(mTetheredTestIface.getInterfaceName());
+ assertNotNull(mTetheredParams);
+ mTetheredTapFd = mTetheredTestIface.getFileDescriptor().getFileDescriptor();
+ mTetheredPacketReader = new TapPacketReader(mHandler, mTetheredTapFd,
+ DATA_BUFFER_LEN);
+ mHandler.post(mTetheredPacketReader::start);
+ }
+
+ private static final int IPV6_HEADER_LEN = 40;
+ private static final int ETH_HEADER_LEN = 14;
+ private static final int ICMPV6_NA_NS_LEN = 24;
+ private static final int LL_TARGET_OPTION_LEN = 8;
+ private static final int ICMPV6_CHECKSUM_OFFSET = 2;
+ private static final int ETHER_TYPE_IPV6 = 0x86dd;
+
+ // TODO: move the IpUtils code to frameworks/lib/net and link it statically.
+ private static int checksumFold(int sum) {
+ while (sum > 0xffff) {
+ sum = (sum >> 16) + (sum & 0xffff);
+ }
+ return sum;
+ }
+
+ // TODO: move the IpUtils code to frameworks/lib/net and link it statically.
+ private static short checksumAdjust(short checksum, short oldWord, short newWord) {
+ checksum = (short) ~checksum;
+ int tempSum = checksumFold(uint16(checksum) + uint16(newWord) + 0xffff - uint16(oldWord));
+ return (short) ~tempSum;
+ }
+
+ // TODO: move the IpUtils code to frameworks/lib/net and link it statically.
+ private static short icmpv6Checksum(ByteBuffer buf, int ipOffset, int transportOffset,
+ int transportLen) {
+ // The ICMPv6 checksum is the same as the TCP checksum, except the pseudo-header uses
+ // 58 (ICMPv6) instead of 6 (TCP). Calculate the TCP checksum, and then do an incremental
+ // checksum adjustment for the change in the next header byte.
+ short checksum = IpUtils.tcpChecksum(buf, ipOffset, transportOffset, transportLen);
+ return checksumAdjust(checksum, (short) IPPROTO_TCP, (short) IPPROTO_ICMPV6);
+ }
+
+ private static ByteBuffer createDadPacket(int type) {
+ // Refer to buildArpPacket()
+ int icmpLen = ICMPV6_NA_NS_LEN
+ + (type == NeighborPacketForwarder.ICMPV6_NEIGHBOR_ADVERTISEMENT
+ ? LL_TARGET_OPTION_LEN : 0);
+ final ByteBuffer buf = ByteBuffer.allocate(icmpLen + IPV6_HEADER_LEN + ETH_HEADER_LEN);
+
+ // Ethernet header.
+ final MacAddress srcMac = MacAddress.fromString("33:33:ff:66:77:88");
+ buf.put(srcMac.toByteArray());
+ final MacAddress dstMac = MacAddress.fromString("01:02:03:04:05:06");
+ buf.put(dstMac.toByteArray());
+ buf.putShort((short) ETHER_TYPE_IPV6);
+
+ // IPv6 header
+ byte[] version = {(byte) 0x60, 0x00, 0x00, 0x00};
+ buf.put(version); // Version
+ buf.putShort((byte) icmpLen); // Length
+ buf.put((byte) IPPROTO_ICMPV6); // Next header
+ buf.put((byte) 0xff); // Hop limit
+
+ final byte[] target =
+ InetAddresses.parseNumericAddress("fe80::1122:3344:5566:7788").getAddress();
+ final byte[] src;
+ final byte[] dst;
+ if (type == NeighborPacketForwarder.ICMPV6_NEIGHBOR_SOLICITATION) {
+ src = InetAddresses.parseNumericAddress("::").getAddress();
+ dst = InetAddresses.parseNumericAddress("ff02::1:ff66:7788").getAddress();
+ } else {
+ src = target;
+ dst = TetheringUtils.ALL_NODES;
+ }
+ buf.put(src);
+ buf.put(dst);
+
+ // ICMPv6 Header
+ buf.put((byte) type); // Type
+ buf.put((byte) 0x00); // Code
+ buf.putShort((short) 0); // Checksum
+ buf.putInt(0); // Reserved
+ buf.put(target);
+
+ if (type == NeighborPacketForwarder.ICMPV6_NEIGHBOR_ADVERTISEMENT) {
+ //NA packet has LL target address
+ //ICMPv6 Option
+ buf.put((byte) 0x02); // Type
+ buf.put((byte) 0x01); // Length
+ byte[] ll_target = MacAddress.fromString("01:02:03:04:05:06").toByteArray();
+ buf.put(ll_target);
+ }
+
+ // Populate checksum field
+ final int transportOffset = ETH_HEADER_LEN + IPV6_HEADER_LEN;
+ final short checksum = icmpv6Checksum(buf, ETH_HEADER_LEN, transportOffset, icmpLen);
+ buf.putShort(transportOffset + ICMPV6_CHECKSUM_OFFSET, checksum);
+
+ buf.flip();
+ return buf;
+ }
+
+ private DadProxy setupProxy() throws Exception {
+ DadProxy proxy = new DadProxy(mHandler, mTetheredParams);
+ mHandler.post(() -> proxy.setUpstreamIface(mUpstreamParams));
+
+ // Upstream iface is added to local network to simplify test case.
+ // Otherwise the test needs to create and destroy a network for the upstream iface.
+ sNetd.networkAddInterface(INetd.LOCAL_NET_ID, mUpstreamParams.name);
+ sNetd.networkAddInterface(INetd.LOCAL_NET_ID, mTetheredParams.name);
+
+ return proxy;
+ }
+
+ // TODO: change to assert.
+ private boolean waitForPacket(ByteBuffer packet, TapPacketReader reader) {
+ byte[] p;
+
+ while ((p = reader.popPacket(PACKET_TIMEOUT_MS)) != null) {
+ final ByteBuffer buffer = ByteBuffer.wrap(p);
+
+ if (buffer.compareTo(packet) == 0) return true;
+ }
+ return false;
+ }
+
+ private void updateDstMac(ByteBuffer buf, MacAddress mac) {
+ buf.put(mac.toByteArray());
+ buf.rewind();
+ }
+ private void updateSrcMac(ByteBuffer buf, InterfaceParams ifaceParams) {
+ buf.position(ETHER_SRC_ADDR_OFFSET);
+ buf.put(ifaceParams.macAddr.toByteArray());
+ buf.rewind();
+ }
+
+ @Test
+ public void testNaForwardingFromUpstreamToTether() throws Exception {
+ ByteBuffer na = createDadPacket(NeighborPacketForwarder.ICMPV6_NEIGHBOR_ADVERTISEMENT);
+
+ mUpstreamPacketReader.sendResponse(na);
+ updateDstMac(na, MacAddress.fromString("33:33:00:00:00:01"));
+ updateSrcMac(na, mTetheredParams);
+ assertTrue(waitForPacket(na, mTetheredPacketReader));
+ }
+
+ @Test
+ // TODO: remove test once DAD works in both directions.
+ public void testNaForwardingFromTetherToUpstream() throws Exception {
+ ByteBuffer na = createDadPacket(NeighborPacketForwarder.ICMPV6_NEIGHBOR_ADVERTISEMENT);
+
+ mTetheredPacketReader.sendResponse(na);
+ updateDstMac(na, MacAddress.fromString("33:33:00:00:00:01"));
+ updateSrcMac(na, mTetheredParams);
+ assertFalse(waitForPacket(na, mUpstreamPacketReader));
+ }
+
+ @Test
+ public void testNsForwardingFromTetherToUpstream() throws Exception {
+ ByteBuffer ns = createDadPacket(NeighborPacketForwarder.ICMPV6_NEIGHBOR_SOLICITATION);
+
+ mTetheredPacketReader.sendResponse(ns);
+ updateSrcMac(ns, mUpstreamParams);
+ assertTrue(waitForPacket(ns, mUpstreamPacketReader));
+ }
+
+ @Test
+ // TODO: remove test once DAD works in both directions.
+ public void testNsForwardingFromUpstreamToTether() throws Exception {
+ ByteBuffer ns = createDadPacket(NeighborPacketForwarder.ICMPV6_NEIGHBOR_SOLICITATION);
+
+ mUpstreamPacketReader.sendResponse(ns);
+ updateSrcMac(ns, mUpstreamParams);
+ assertFalse(waitForPacket(ns, mTetheredPacketReader));
+ }
+}
diff --git a/packages/Tethering/tests/unit/jarjar-rules.txt b/packages/Tethering/tests/unit/jarjar-rules.txt
index ec2d2b02004e..7ed89632a861 100644
--- a/packages/Tethering/tests/unit/jarjar-rules.txt
+++ b/packages/Tethering/tests/unit/jarjar-rules.txt
@@ -9,3 +9,8 @@ rule com.android.internal.util.StateMachine* com.android.networkstack.tethering.
rule com.android.internal.util.TrafficStatsConstants* com.android.networkstack.tethering.util.TrafficStatsConstants@1
rule android.util.LocalLog* com.android.networkstack.tethering.util.LocalLog@1
+
+# TODO: either stop using frameworks-base-testutils or remove the unit test classes it contains.
+# TestableLooper from "testables" can be used instead of TestLooper from frameworks-base-testutils.
+zap android.os.test.TestLooperTest*
+zap com.android.test.filters.SelectTestTests* \ No newline at end of file
diff --git a/packages/Tethering/tests/unit/src/android/net/ip/IpServerTest.java b/packages/Tethering/tests/unit/src/android/net/ip/IpServerTest.java
index 3b72b5b47191..1a976adc60e0 100644
--- a/packages/Tethering/tests/unit/src/android/net/ip/IpServerTest.java
+++ b/packages/Tethering/tests/unit/src/android/net/ip/IpServerTest.java
@@ -86,6 +86,7 @@ import android.net.util.InterfaceParams;
import android.net.util.InterfaceSet;
import android.net.util.PrefixUtils;
import android.net.util.SharedLog;
+import android.os.Build;
import android.os.Handler;
import android.os.RemoteException;
import android.os.test.TestLooper;
@@ -100,8 +101,12 @@ import com.android.networkstack.tethering.BpfCoordinator;
import com.android.networkstack.tethering.BpfCoordinator.Ipv6ForwardingRule;
import com.android.networkstack.tethering.PrivateAddressCoordinator;
import com.android.networkstack.tethering.TetheringConfiguration;
+import com.android.testutils.DevSdkIgnoreRule;
+import com.android.testutils.DevSdkIgnoreRule.IgnoreAfter;
+import com.android.testutils.DevSdkIgnoreRule.IgnoreUpTo;
import org.junit.Before;
+import org.junit.Rule;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.ArgumentCaptor;
@@ -120,6 +125,9 @@ import java.util.List;
@RunWith(AndroidJUnit4.class)
@SmallTest
public class IpServerTest {
+ @Rule
+ public final DevSdkIgnoreRule mIgnoreRule = new DevSdkIgnoreRule();
+
private static final String IFACE_NAME = "testnet1";
private static final String UPSTREAM_IFACE = "upstream0";
private static final String UPSTREAM_IFACE2 = "upstream1";
@@ -132,6 +140,11 @@ public class IpServerTest {
private static final InterfaceParams TEST_IFACE_PARAMS = new InterfaceParams(
IFACE_NAME, 42 /* index */, MacAddress.ALL_ZEROS_ADDRESS, 1500 /* defaultMtu */);
+ private static final InterfaceParams UPSTREAM_IFACE_PARAMS = new InterfaceParams(
+ UPSTREAM_IFACE, UPSTREAM_IFINDEX, MacAddress.ALL_ZEROS_ADDRESS, 1500 /* defaultMtu */);
+ private static final InterfaceParams UPSTREAM_IFACE_PARAMS2 = new InterfaceParams(
+ UPSTREAM_IFACE2, UPSTREAM_IFINDEX2, MacAddress.ALL_ZEROS_ADDRESS,
+ 1500 /* defaultMtu */);
private static final int MAKE_DHCPSERVER_TIMEOUT_MS = 1000;
@@ -142,6 +155,7 @@ public class IpServerTest {
@Mock private IpServer.Callback mCallback;
@Mock private SharedLog mSharedLog;
@Mock private IDhcpServer mDhcpServer;
+ @Mock private DadProxy mDadProxy;
@Mock private RouterAdvertisementDaemon mRaDaemon;
@Mock private IpNeighborMonitor mIpNeighborMonitor;
@Mock private IpServer.Dependencies mDependencies;
@@ -165,8 +179,11 @@ public class IpServerTest {
private void initStateMachine(int interfaceType, boolean usingLegacyDhcp,
boolean usingBpfOffload) throws Exception {
+ when(mDependencies.getDadProxy(any(), any())).thenReturn(mDadProxy);
when(mDependencies.getRouterAdvertisementDaemon(any())).thenReturn(mRaDaemon);
when(mDependencies.getInterfaceParams(IFACE_NAME)).thenReturn(TEST_IFACE_PARAMS);
+ when(mDependencies.getInterfaceParams(UPSTREAM_IFACE)).thenReturn(UPSTREAM_IFACE_PARAMS);
+ when(mDependencies.getInterfaceParams(UPSTREAM_IFACE2)).thenReturn(UPSTREAM_IFACE_PARAMS2);
when(mDependencies.getIfindex(eq(UPSTREAM_IFACE))).thenReturn(UPSTREAM_IFINDEX);
when(mDependencies.getIfindex(eq(UPSTREAM_IFACE2))).thenReturn(UPSTREAM_IFINDEX2);
@@ -1103,4 +1120,78 @@ public class IpServerTest {
}
return true;
}
+
+ @Test @IgnoreUpTo(Build.VERSION_CODES.R)
+ public void dadProxyUpdates() throws Exception {
+ InOrder inOrder = inOrder(mDadProxy);
+ initTetheredStateMachine(TETHERING_WIFI, UPSTREAM_IFACE);
+ inOrder.verify(mDadProxy).setUpstreamIface(UPSTREAM_IFACE_PARAMS);
+
+ // Add an upstream without IPv6.
+ dispatchTetherConnectionChanged(UPSTREAM_IFACE, null, 0);
+ inOrder.verify(mDadProxy).setUpstreamIface(null);
+
+ // Add IPv6 to the upstream.
+ LinkProperties lp = new LinkProperties();
+ lp.setInterfaceName(UPSTREAM_IFACE);
+ dispatchTetherConnectionChanged(UPSTREAM_IFACE, lp, 0);
+ inOrder.verify(mDadProxy).setUpstreamIface(UPSTREAM_IFACE_PARAMS);
+
+ // Change upstream.
+ // New linkproperties is needed, otherwise changing the iface has no impact.
+ LinkProperties lp2 = new LinkProperties();
+ lp2.setInterfaceName(UPSTREAM_IFACE2);
+ dispatchTetherConnectionChanged(UPSTREAM_IFACE2, lp2, 0);
+ inOrder.verify(mDadProxy).setUpstreamIface(UPSTREAM_IFACE_PARAMS2);
+
+ // Lose IPv6 on the upstream...
+ dispatchTetherConnectionChanged(UPSTREAM_IFACE2, null, 0);
+ inOrder.verify(mDadProxy).setUpstreamIface(null);
+
+ // ... and regain it on a different upstream.
+ dispatchTetherConnectionChanged(UPSTREAM_IFACE, lp, 0);
+ inOrder.verify(mDadProxy).setUpstreamIface(UPSTREAM_IFACE_PARAMS);
+
+ // Lose upstream.
+ dispatchTetherConnectionChanged(null, null, 0);
+ inOrder.verify(mDadProxy).setUpstreamIface(null);
+
+ // Regain upstream.
+ dispatchTetherConnectionChanged(UPSTREAM_IFACE, lp, 0);
+ inOrder.verify(mDadProxy).setUpstreamIface(UPSTREAM_IFACE_PARAMS);
+
+ // Stop tethering.
+ mIpServer.stop();
+ mLooper.dispatchAll();
+ }
+
+ private void checkDadProxyEnabled(boolean expectEnabled) throws Exception {
+ initTetheredStateMachine(TETHERING_WIFI, UPSTREAM_IFACE);
+ InOrder inOrder = inOrder(mDadProxy);
+ // Add IPv6 to the upstream.
+ LinkProperties lp = new LinkProperties();
+ lp.setInterfaceName(UPSTREAM_IFACE);
+ if (expectEnabled) {
+ inOrder.verify(mDadProxy).setUpstreamIface(UPSTREAM_IFACE_PARAMS);
+ } else {
+ inOrder.verifyNoMoreInteractions();
+ }
+ // Stop tethering.
+ mIpServer.stop();
+ mLooper.dispatchAll();
+ if (expectEnabled) {
+ inOrder.verify(mDadProxy).stop();
+ }
+ else {
+ verify(mDependencies, never()).getDadProxy(any(), any());
+ }
+ }
+ @Test @IgnoreAfter(Build.VERSION_CODES.R)
+ public void testDadProxyUpdates_DisabledUpToR() throws Exception {
+ checkDadProxyEnabled(false);
+ }
+ @Test @IgnoreUpTo(Build.VERSION_CODES.R)
+ public void testDadProxyUpdates_EnabledAfterR() throws Exception {
+ checkDadProxyEnabled(true);
+ }
}
diff --git a/packages/Tethering/tests/unit/src/com/android/networkstack/tethering/TetheringTest.java b/packages/Tethering/tests/unit/src/com/android/networkstack/tethering/TetheringTest.java
index 1fe3840b51a8..df570206e389 100644
--- a/packages/Tethering/tests/unit/src/com/android/networkstack/tethering/TetheringTest.java
+++ b/packages/Tethering/tests/unit/src/com/android/networkstack/tethering/TetheringTest.java
@@ -110,6 +110,7 @@ import android.net.TetheringRequestParcel;
import android.net.dhcp.DhcpServerCallbacks;
import android.net.dhcp.DhcpServingParamsParcel;
import android.net.dhcp.IDhcpServer;
+import android.net.ip.DadProxy;
import android.net.ip.IpNeighborMonitor;
import android.net.ip.IpServer;
import android.net.ip.RouterAdvertisementDaemon;
@@ -196,6 +197,7 @@ public class TetheringTest {
@Mock private CarrierConfigManager mCarrierConfigManager;
@Mock private UpstreamNetworkMonitor mUpstreamNetworkMonitor;
@Mock private IPv6TetheringCoordinator mIPv6TetheringCoordinator;
+ @Mock private DadProxy mDadProxy;
@Mock private RouterAdvertisementDaemon mRouterAdvertisementDaemon;
@Mock private IpNeighborMonitor mIpNeighborMonitor;
@Mock private IDhcpServer mDhcpServer;
@@ -280,6 +282,12 @@ public class TetheringTest {
public class MockIpServerDependencies extends IpServer.Dependencies {
@Override
+ public DadProxy getDadProxy(
+ Handler handler, InterfaceParams ifParams) {
+ return mDadProxy;
+ }
+
+ @Override
public RouterAdvertisementDaemon getRouterAdvertisementDaemon(
InterfaceParams ifParams) {
return mRouterAdvertisementDaemon;
@@ -832,6 +840,7 @@ public class TetheringTest {
verify(mNetd, times(1)).ipfwdAddInterfaceForward(TEST_USB_IFNAME, TEST_MOBILE_IFNAME);
sendIPv6TetherUpdates(upstreamState);
+ verify(mDadProxy, never()).setUpstreamIface(notNull());
verify(mRouterAdvertisementDaemon, never()).buildNewRa(any(), notNull());
verify(mDhcpServer, timeout(DHCPSERVER_START_TIMEOUT_MS).times(1)).startWithCallbacks(
any(), any());
@@ -858,6 +867,8 @@ public class TetheringTest {
verify(mNetd, times(1)).ipfwdAddInterfaceForward(TEST_USB_IFNAME, TEST_MOBILE_IFNAME);
sendIPv6TetherUpdates(upstreamState);
+ // TODO: add interfaceParams to compare in verify.
+ verify(mDadProxy, times(1)).setUpstreamIface(notNull());
verify(mRouterAdvertisementDaemon, times(1)).buildNewRa(any(), notNull());
verify(mNetd, times(1)).tetherApplyDnsInterfaces();
}
@@ -874,6 +885,7 @@ public class TetheringTest {
any(), any());
sendIPv6TetherUpdates(upstreamState);
+ verify(mDadProxy, times(1)).setUpstreamIface(notNull());
verify(mRouterAdvertisementDaemon, times(1)).buildNewRa(any(), notNull());
verify(mNetd, times(1)).tetherApplyDnsInterfaces();
}
@@ -891,6 +903,7 @@ public class TetheringTest {
verify(mNetd, times(1)).ipfwdAddInterfaceForward(TEST_USB_IFNAME, TEST_MOBILE_IFNAME);
sendIPv6TetherUpdates(upstreamState);
+ verify(mDadProxy, times(1)).setUpstreamIface(notNull());
verify(mRouterAdvertisementDaemon, times(1)).buildNewRa(any(), notNull());
verify(mNetd, times(1)).tetherApplyDnsInterfaces();
}
diff --git a/services/accessibility/java/com/android/server/accessibility/AccessibilityWindowManager.java b/services/accessibility/java/com/android/server/accessibility/AccessibilityWindowManager.java
index ee03b150b519..ff794691d2b4 100644
--- a/services/accessibility/java/com/android/server/accessibility/AccessibilityWindowManager.java
+++ b/services/accessibility/java/com/android/server/accessibility/AccessibilityWindowManager.java
@@ -707,12 +707,12 @@ public class AccessibilityWindowManager {
case WindowManager.LayoutParams.TYPE_PHONE:
case WindowManager.LayoutParams.TYPE_PRIORITY_PHONE:
case WindowManager.LayoutParams.TYPE_TOAST:
- case WindowManager.LayoutParams.TYPE_APPLICATION_ATTACHED_DIALOG: {
+ case WindowManager.LayoutParams.TYPE_APPLICATION_ATTACHED_DIALOG:
+ case WindowManager.LayoutParams.TYPE_INPUT_METHOD_DIALOG: {
return AccessibilityWindowInfo.TYPE_APPLICATION;
}
- case WindowManager.LayoutParams.TYPE_INPUT_METHOD:
- case WindowManager.LayoutParams.TYPE_INPUT_METHOD_DIALOG: {
+ case WindowManager.LayoutParams.TYPE_INPUT_METHOD: {
return AccessibilityWindowInfo.TYPE_INPUT_METHOD;
}
@@ -730,7 +730,8 @@ public class AccessibilityWindowManager {
case WindowManager.LayoutParams.TYPE_SYSTEM_ERROR:
case WindowManager.LayoutParams.TYPE_SYSTEM_OVERLAY:
case WindowManager.LayoutParams.TYPE_APPLICATION_OVERLAY:
- case WindowManager.LayoutParams.TYPE_SCREENSHOT: {
+ case WindowManager.LayoutParams.TYPE_SCREENSHOT:
+ case WindowManager.LayoutParams.TYPE_ACCESSIBILITY_MAGNIFICATION_OVERLAY: {
return AccessibilityWindowInfo.TYPE_SYSTEM;
}
diff --git a/services/autofill/java/com/android/server/autofill/RemoteAugmentedAutofillService.java b/services/autofill/java/com/android/server/autofill/RemoteAugmentedAutofillService.java
index 533bbe68e274..92b8608f4f6c 100644
--- a/services/autofill/java/com/android/server/autofill/RemoteAugmentedAutofillService.java
+++ b/services/autofill/java/com/android/server/autofill/RemoteAugmentedAutofillService.java
@@ -261,9 +261,13 @@ final class RemoteAugmentedAutofillService
focusedValue != null && focusedValue.isText()
? focusedValue.getTextValue().toString() : null;
+ final InlineFillUi.InlineFillUiInfo inlineFillUiInfo =
+ new InlineFillUi.InlineFillUiInfo(request, focusedId, filterText,
+ remoteRenderService, userId, sessionId);
+
final InlineFillUi inlineFillUi =
InlineFillUi.forAugmentedAutofill(
- request, inlineSuggestionsData, focusedId, filterText,
+ inlineFillUiInfo, inlineSuggestionsData,
new InlineFillUi.InlineSuggestionUiCallback() {
@Override
public void autofill(Dataset dataset, int datasetIndex) {
@@ -305,15 +309,24 @@ final class RemoteAugmentedAutofillService
}
@Override
- public void startIntentSender(IntentSender intentSender,
- Intent intent) {
+ public void authenticate(int requestId, int datasetIndex) {
+ Slog.e(TAG, "authenticate not implemented for augmented autofill");
+ }
+
+ @Override
+ public void startIntentSender(IntentSender intentSender) {
try {
- client.startIntentSender(intentSender, intent);
+ client.startIntentSender(intentSender, new Intent());
} catch (RemoteException e) {
Slog.w(TAG, "RemoteException starting intent sender");
}
}
- }, onErrorCallback, remoteRenderService, userId, sessionId);
+
+ @Override
+ public void onError() {
+ onErrorCallback.run();
+ }
+ });
if (inlineSuggestionsCallback.apply(inlineFillUi)) {
mCallbacks.logAugmentedAutofillShown(sessionId, clientState);
diff --git a/services/autofill/java/com/android/server/autofill/Session.java b/services/autofill/java/com/android/server/autofill/Session.java
index 1970b5774bbb..4d2e4f6ee486 100644
--- a/services/autofill/java/com/android/server/autofill/Session.java
+++ b/services/autofill/java/com/android/server/autofill/Session.java
@@ -3008,14 +3008,36 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState
return false;
}
- InlineFillUi inlineFillUi = InlineFillUi.forAutofill(
- inlineSuggestionsRequest.get(), response, focusedId, filterText,
- /*uiCallback*/this, /*onErrorCallback*/ () -> {
- synchronized (mLock) {
- mInlineSessionController.setInlineFillUiLocked(
- InlineFillUi.emptyUi(focusedId));
+ final InlineFillUi.InlineFillUiInfo inlineFillUiInfo =
+ new InlineFillUi.InlineFillUiInfo(inlineSuggestionsRequest.get(), focusedId,
+ filterText, remoteRenderService, userId, id);
+ InlineFillUi inlineFillUi = InlineFillUi.forAutofill(inlineFillUiInfo, response,
+ new InlineFillUi.InlineSuggestionUiCallback() {
+ @Override
+ public void autofill(@NonNull Dataset dataset, int datasetIndex) {
+ fill(response.getRequestId(), datasetIndex, dataset);
+ }
+
+ @Override
+ public void authenticate(int requestId, int datasetIndex) {
+ Session.this.authenticate(response.getRequestId(), datasetIndex,
+ response.getAuthentication(), response.getClientState(),
+ /* authenticateInline= */ true);
+ }
+
+ @Override
+ public void startIntentSender(@NonNull IntentSender intentSender) {
+ Session.this.startIntentSender(intentSender, new Intent());
}
- }, remoteRenderService, userId, id);
+
+ @Override
+ public void onError() {
+ synchronized (mLock) {
+ mInlineSessionController.setInlineFillUiLocked(
+ InlineFillUi.emptyUi(focusedId));
+ }
+ }
+ });
return mInlineSessionController.setInlineFillUiLocked(inlineFillUi);
}
diff --git a/services/autofill/java/com/android/server/autofill/ui/InlineFillUi.java b/services/autofill/java/com/android/server/autofill/ui/InlineFillUi.java
index 25e9d5c90764..ff175904fb61 100644
--- a/services/autofill/java/com/android/server/autofill/ui/InlineFillUi.java
+++ b/services/autofill/java/com/android/server/autofill/ui/InlineFillUi.java
@@ -20,7 +20,7 @@ import static com.android.server.autofill.Helper.sVerbose;
import android.annotation.NonNull;
import android.annotation.Nullable;
-import android.content.Intent;
+import android.annotation.UserIdInt;
import android.content.IntentSender;
import android.service.autofill.Dataset;
import android.service.autofill.FillResponse;
@@ -32,6 +32,7 @@ import android.util.SparseArray;
import android.view.autofill.AutofillId;
import android.view.autofill.AutofillValue;
import android.view.inputmethod.InlineSuggestion;
+import android.view.inputmethod.InlineSuggestionInfo;
import android.view.inputmethod.InlineSuggestionsRequest;
import android.view.inputmethod.InlineSuggestionsResponse;
@@ -93,59 +94,72 @@ public final class InlineFillUi {
*/
@NonNull
public static InlineFillUi emptyUi(@NonNull AutofillId autofillId) {
- return new InlineFillUi(autofillId, new SparseArray<>(), null);
+ return new InlineFillUi(autofillId);
+ }
+
+ /**
+ * Encapsulates various arguments used by {@link #forAutofill} and {@link #forAugmentedAutofill}
+ */
+ public static class InlineFillUiInfo {
+
+ public int mUserId;
+ public int mSessionId;
+ public InlineSuggestionsRequest mInlineRequest;
+ public AutofillId mFocusedId;
+ public String mFilterText;
+ public RemoteInlineSuggestionRenderService mRemoteRenderService;
+
+ public InlineFillUiInfo(@NonNull InlineSuggestionsRequest inlineRequest,
+ @NonNull AutofillId focusedId, @NonNull String filterText,
+ @NonNull RemoteInlineSuggestionRenderService remoteRenderService,
+ @UserIdInt int userId, int sessionId) {
+ mUserId = userId;
+ mSessionId = sessionId;
+ mInlineRequest = inlineRequest;
+ mFocusedId = focusedId;
+ mFilterText = filterText;
+ mRemoteRenderService = remoteRenderService;
+ }
}
/**
* Returns an inline autofill UI for a field based on an Autofilll response.
*/
@NonNull
- public static InlineFillUi forAutofill(@NonNull InlineSuggestionsRequest request,
+ public static InlineFillUi forAutofill(@NonNull InlineFillUiInfo inlineFillUiInfo,
@NonNull FillResponse response,
- @NonNull AutofillId focusedViewId, @Nullable String filterText,
- @NonNull AutoFillUI.AutoFillUiCallback uiCallback,
- @NonNull Runnable onErrorCallback,
- @Nullable RemoteInlineSuggestionRenderService remoteRenderService,
- int userId, int sessionId) {
-
- if (InlineSuggestionFactory.responseNeedAuthentication(response)) {
+ @NonNull InlineSuggestionUiCallback uiCallback) {
+ if (response.getAuthentication() != null && response.getInlinePresentation() != null) {
InlineSuggestion inlineAuthentication =
- InlineSuggestionFactory.createInlineAuthentication(request, response,
- uiCallback, onErrorCallback, remoteRenderService, userId, sessionId);
- return new InlineFillUi(focusedViewId, inlineAuthentication, filterText);
+ InlineSuggestionFactory.createInlineAuthentication(inlineFillUiInfo, response,
+ uiCallback);
+ return new InlineFillUi(inlineFillUiInfo, inlineAuthentication);
} else if (response.getDatasets() != null) {
SparseArray<Pair<Dataset, InlineSuggestion>> inlineSuggestions =
- InlineSuggestionFactory.createAutofillInlineSuggestions(request,
- response.getRequestId(),
- response.getDatasets(), focusedViewId, uiCallback, onErrorCallback,
- remoteRenderService, userId, sessionId);
- return new InlineFillUi(focusedViewId, inlineSuggestions, filterText);
+ InlineSuggestionFactory.createInlineSuggestions(inlineFillUiInfo,
+ InlineSuggestionInfo.SOURCE_AUTOFILL, response.getDatasets(),
+ uiCallback);
+ return new InlineFillUi(inlineFillUiInfo, inlineSuggestions);
}
- return new InlineFillUi(focusedViewId, new SparseArray<>(), filterText);
+ return new InlineFillUi(inlineFillUiInfo, new SparseArray<>());
}
/**
* Returns an inline autofill UI for a field based on an Autofilll response.
*/
@NonNull
- public static InlineFillUi forAugmentedAutofill(@NonNull InlineSuggestionsRequest request,
+ public static InlineFillUi forAugmentedAutofill(@NonNull InlineFillUiInfo inlineFillUiInfo,
@NonNull List<Dataset> datasets,
- @NonNull AutofillId focusedViewId, @Nullable String filterText,
- @NonNull InlineSuggestionUiCallback uiCallback,
- @NonNull Runnable onErrorCallback,
- @Nullable RemoteInlineSuggestionRenderService remoteRenderService,
- int userId, int sessionId) {
+ @NonNull InlineSuggestionUiCallback uiCallback) {
SparseArray<Pair<Dataset, InlineSuggestion>> inlineSuggestions =
- InlineSuggestionFactory.createAugmentedAutofillInlineSuggestions(request, datasets,
- focusedViewId,
- uiCallback, onErrorCallback, remoteRenderService, userId, sessionId);
- return new InlineFillUi(focusedViewId, inlineSuggestions, filterText);
+ InlineSuggestionFactory.createInlineSuggestions(inlineFillUiInfo,
+ InlineSuggestionInfo.SOURCE_PLATFORM, datasets, uiCallback);
+ return new InlineFillUi(inlineFillUiInfo, inlineSuggestions);
}
- InlineFillUi(@NonNull AutofillId autofillId,
- @NonNull SparseArray<Pair<Dataset, InlineSuggestion>> inlineSuggestions,
- @Nullable String filterText) {
- mAutofillId = autofillId;
+ private InlineFillUi(@Nullable InlineFillUiInfo inlineFillUiInfo,
+ @NonNull SparseArray<Pair<Dataset, InlineSuggestion>> inlineSuggestions) {
+ mAutofillId = inlineFillUiInfo.mFocusedId;
int size = inlineSuggestions.size();
mDatasets = new ArrayList<>(size);
mInlineSuggestions = new ArrayList<>(size);
@@ -154,16 +168,26 @@ public final class InlineFillUi {
mDatasets.add(value.first);
mInlineSuggestions.add(value.second);
}
- mFilterText = filterText;
+ mFilterText = inlineFillUiInfo.mFilterText;
}
- InlineFillUi(@NonNull AutofillId autofillId, InlineSuggestion inlineSuggestion,
- @Nullable String filterText) {
- mAutofillId = autofillId;
+ private InlineFillUi(@NonNull InlineFillUiInfo inlineFillUiInfo,
+ @NonNull InlineSuggestion inlineSuggestion) {
+ mAutofillId = inlineFillUiInfo.mFocusedId;
mDatasets = null;
mInlineSuggestions = new ArrayList<>();
mInlineSuggestions.add(inlineSuggestion);
- mFilterText = filterText;
+ mFilterText = inlineFillUiInfo.mFilterText;
+ }
+
+ /**
+ * Only used for constructing an empty InlineFillUi with {@link #emptyUi}
+ */
+ private InlineFillUi(@NonNull AutofillId focusedId) {
+ mAutofillId = focusedId;
+ mDatasets = new ArrayList<>(0);
+ mInlineSuggestions = new ArrayList<>(0);
+ mFilterText = null;
}
@NonNull
@@ -295,9 +319,21 @@ public final class InlineFillUi {
void autofill(@NonNull Dataset dataset, int datasetIndex);
/**
+ * Callback to authenticate a dataset.
+ *
+ * <p>Only implemented by regular autofill for now.</p>
+ */
+ void authenticate(int requestId, int datasetIndex);
+
+ /**
* Callback to start Intent in client app.
*/
- void startIntentSender(@NonNull IntentSender intentSender, @NonNull Intent intent);
+ void startIntentSender(@NonNull IntentSender intentSender);
+
+ /**
+ * Callback on errors.
+ */
+ void onError();
}
/**
diff --git a/services/autofill/java/com/android/server/autofill/ui/InlineSuggestionFactory.java b/services/autofill/java/com/android/server/autofill/ui/InlineSuggestionFactory.java
index 8fcb8aa9393c..b15d07b0f1a9 100644
--- a/services/autofill/java/com/android/server/autofill/ui/InlineSuggestionFactory.java
+++ b/services/autofill/java/com/android/server/autofill/ui/InlineSuggestionFactory.java
@@ -20,184 +20,98 @@ import static com.android.server.autofill.Helper.sDebug;
import android.annotation.NonNull;
import android.annotation.Nullable;
-import android.content.Intent;
-import android.content.IntentSender;
-import android.os.IBinder;
import android.service.autofill.Dataset;
import android.service.autofill.FillResponse;
import android.service.autofill.InlinePresentation;
import android.util.Pair;
import android.util.Slog;
import android.util.SparseArray;
-import android.view.autofill.AutofillId;
import android.view.autofill.AutofillManager;
import android.view.inputmethod.InlineSuggestion;
import android.view.inputmethod.InlineSuggestionInfo;
import android.view.inputmethod.InlineSuggestionsRequest;
-import android.view.inputmethod.InlineSuggestionsResponse;
import android.widget.inline.InlinePresentationSpec;
import com.android.internal.view.inline.IInlineContentProvider;
-import com.android.server.autofill.RemoteInlineSuggestionRenderService;
import java.util.List;
-import java.util.function.BiConsumer;
-import java.util.function.Consumer;
final class InlineSuggestionFactory {
private static final String TAG = "InlineSuggestionFactory";
- public static boolean responseNeedAuthentication(@NonNull FillResponse response) {
- return response.getAuthentication() != null && response.getInlinePresentation() != null;
- }
-
public static InlineSuggestion createInlineAuthentication(
- @NonNull InlineSuggestionsRequest request, @NonNull FillResponse response,
- @NonNull AutoFillUI.AutoFillUiCallback client, @NonNull Runnable onErrorCallback,
- @Nullable RemoteInlineSuggestionRenderService remoteRenderService, int userId,
- int sessionId) {
- final BiConsumer<Dataset, Integer> onClickFactory = (dataset, datasetIndex) -> {
- client.authenticate(response.getRequestId(),
- datasetIndex, response.getAuthentication(), response.getClientState(),
- /* authenticateInline= */ true);
- };
- final Consumer<IntentSender> intentSenderConsumer = (intentSender) ->
- client.startIntentSender(intentSender, new Intent());
+ @NonNull InlineFillUi.InlineFillUiInfo inlineFillUiInfo, @NonNull FillResponse response,
+ @NonNull InlineFillUi.InlineSuggestionUiCallback uiCallback) {
InlinePresentation inlineAuthentication = response.getInlinePresentation();
- return createInlineAuthSuggestion(
- mergedInlinePresentation(request, 0, inlineAuthentication),
- remoteRenderService, userId, sessionId,
- onClickFactory, onErrorCallback, intentSenderConsumer,
- request.getHostInputToken(), request.getHostDisplayId());
- }
+ final int requestId = response.getRequestId();
- /**
- * Creates an {@link InlineSuggestionsResponse} with the {@code datasets} provided by the
- * autofill service, potentially filtering the datasets.
- */
- @Nullable
- public static SparseArray<Pair<Dataset, InlineSuggestion>> createAutofillInlineSuggestions(
- @NonNull InlineSuggestionsRequest request, int requestId,
- @NonNull List<Dataset> datasets,
- @NonNull AutofillId autofillId,
- @NonNull AutoFillUI.AutoFillUiCallback client, @NonNull Runnable onErrorCallback,
- @Nullable RemoteInlineSuggestionRenderService remoteRenderService,
- int userId, int sessionId) {
- if (sDebug) Slog.d(TAG, "createInlineSuggestionsResponse called");
- final Consumer<IntentSender> intentSenderConsumer = (intentSender) ->
- client.startIntentSender(intentSender, new Intent());
- final BiConsumer<Dataset, Integer> onClickFactory = (dataset, datasetIndex) -> {
- client.fill(requestId, datasetIndex, dataset);
- };
-
- return createInlineSuggestionsInternal(/* isAugmented= */ false, request,
- datasets, autofillId,
- onErrorCallback, onClickFactory, intentSenderConsumer, remoteRenderService, userId,
- sessionId);
+ return createInlineSuggestion(inlineFillUiInfo, InlineSuggestionInfo.SOURCE_AUTOFILL,
+ InlineSuggestionInfo.TYPE_ACTION, () -> uiCallback.authenticate(requestId,
+ AutofillManager.AUTHENTICATION_ID_DATASET_ID_UNDEFINED),
+ mergedInlinePresentation(inlineFillUiInfo.mInlineRequest, 0, inlineAuthentication),
+ uiCallback);
}
/**
- * Creates an {@link InlineSuggestionsResponse} with the {@code datasets} provided by augmented
- * autofill service.
+ * Creates an array of {@link InlineSuggestion}s with the {@code datasets} provided by either
+ * regular/augmented autofill services.
*/
@Nullable
- public static SparseArray<Pair<Dataset, InlineSuggestion>>
- createAugmentedAutofillInlineSuggestions(
- @NonNull InlineSuggestionsRequest request, @NonNull List<Dataset> datasets,
- @NonNull AutofillId autofillId,
- @NonNull InlineFillUi.InlineSuggestionUiCallback inlineSuggestionUiCallback,
- @NonNull Runnable onErrorCallback,
- @Nullable RemoteInlineSuggestionRenderService remoteRenderService,
- int userId, int sessionId) {
- if (sDebug) Slog.d(TAG, "createAugmentedInlineSuggestionsResponse called");
- return createInlineSuggestionsInternal(/* isAugmented= */ true, request,
- datasets, autofillId, onErrorCallback,
- (dataset, datasetIndex) ->
- inlineSuggestionUiCallback.autofill(dataset, datasetIndex),
- (intentSender) ->
- inlineSuggestionUiCallback.startIntentSender(intentSender, new Intent()),
- remoteRenderService, userId, sessionId);
- }
+ public static SparseArray<Pair<Dataset, InlineSuggestion>> createInlineSuggestions(
+ @NonNull InlineFillUi.InlineFillUiInfo inlineFillUiInfo,
+ @NonNull @InlineSuggestionInfo.Source String suggestionSource,
+ @NonNull List<Dataset> datasets,
+ @NonNull InlineFillUi.InlineSuggestionUiCallback uiCallback) {
+ if (sDebug) Slog.d(TAG, "createInlineSuggestions(source=" + suggestionSource + ") called");
- @Nullable
- private static SparseArray<Pair<Dataset, InlineSuggestion>> createInlineSuggestionsInternal(
- boolean isAugmented, @NonNull InlineSuggestionsRequest request,
- @NonNull List<Dataset> datasets, @NonNull AutofillId autofillId,
- @NonNull Runnable onErrorCallback, @NonNull BiConsumer<Dataset, Integer> onClickFactory,
- @NonNull Consumer<IntentSender> intentSenderConsumer,
- @Nullable RemoteInlineSuggestionRenderService remoteRenderService,
- int userId, int sessionId) {
+ final InlineSuggestionsRequest request = inlineFillUiInfo.mInlineRequest;
SparseArray<Pair<Dataset, InlineSuggestion>> response = new SparseArray<>(datasets.size());
for (int datasetIndex = 0; datasetIndex < datasets.size(); datasetIndex++) {
final Dataset dataset = datasets.get(datasetIndex);
- final int fieldIndex = dataset.getFieldIds().indexOf(autofillId);
+ final int fieldIndex = dataset.getFieldIds().indexOf(inlineFillUiInfo.mFocusedId);
if (fieldIndex < 0) {
- Slog.w(TAG, "AutofillId=" + autofillId + " not found in dataset");
+ Slog.w(TAG, "AutofillId=" + inlineFillUiInfo.mFocusedId + " not found in dataset");
continue;
}
- final InlinePresentation inlinePresentation = dataset.getFieldInlinePresentation(
- fieldIndex);
+
+ final InlinePresentation inlinePresentation =
+ dataset.getFieldInlinePresentation(fieldIndex);
if (inlinePresentation == null) {
Slog.w(TAG, "InlinePresentation not found in dataset");
continue;
}
- InlineSuggestion inlineSuggestion = createInlineSuggestion(isAugmented, dataset,
- datasetIndex,
+
+ final String suggestionType =
+ dataset.getAuthentication() == null ? InlineSuggestionInfo.TYPE_SUGGESTION
+ : InlineSuggestionInfo.TYPE_ACTION;
+ final int index = datasetIndex;
+
+ InlineSuggestion inlineSuggestion = createInlineSuggestion(
+ inlineFillUiInfo, suggestionSource, suggestionType,
+ () -> uiCallback.autofill(dataset, index),
mergedInlinePresentation(request, datasetIndex, inlinePresentation),
- onClickFactory, remoteRenderService, userId, sessionId,
- onErrorCallback, intentSenderConsumer,
- request.getHostInputToken(), request.getHostDisplayId());
+ uiCallback);
response.append(datasetIndex, Pair.create(dataset, inlineSuggestion));
}
+
return response;
}
- private static InlineSuggestion createInlineSuggestion(boolean isAugmented,
- @NonNull Dataset dataset, int datasetIndex,
+ private static InlineSuggestion createInlineSuggestion(
+ @NonNull InlineFillUi.InlineFillUiInfo inlineFillUiInfo,
+ @NonNull @InlineSuggestionInfo.Source String suggestionSource,
+ @NonNull @InlineSuggestionInfo.Type String suggestionType,
+ @NonNull Runnable onClickAction,
@NonNull InlinePresentation inlinePresentation,
- @NonNull BiConsumer<Dataset, Integer> onClickFactory,
- @NonNull RemoteInlineSuggestionRenderService remoteRenderService,
- int userId, int sessionId,
- @NonNull Runnable onErrorCallback, @NonNull Consumer<IntentSender> intentSenderConsumer,
- @Nullable IBinder hostInputToken,
- int displayId) {
- final String suggestionSource = isAugmented ? InlineSuggestionInfo.SOURCE_PLATFORM
- : InlineSuggestionInfo.SOURCE_AUTOFILL;
- final String suggestionType =
- dataset.getAuthentication() == null ? InlineSuggestionInfo.TYPE_SUGGESTION
- : InlineSuggestionInfo.TYPE_ACTION;
+ @NonNull InlineFillUi.InlineSuggestionUiCallback uiCallback) {
final InlineSuggestionInfo inlineSuggestionInfo = new InlineSuggestionInfo(
inlinePresentation.getInlinePresentationSpec(), suggestionSource,
inlinePresentation.getAutofillHints(), suggestionType,
inlinePresentation.isPinned());
- final InlineSuggestion inlineSuggestion = new InlineSuggestion(inlineSuggestionInfo,
- createInlineContentProvider(inlinePresentation,
- () -> onClickFactory.accept(dataset, datasetIndex), onErrorCallback,
- intentSenderConsumer, remoteRenderService, userId, sessionId,
- hostInputToken, displayId));
-
- return inlineSuggestion;
- }
-
- private static InlineSuggestion createInlineAuthSuggestion(
- @NonNull InlinePresentation inlinePresentation,
- @NonNull RemoteInlineSuggestionRenderService remoteRenderService,
- int userId, int sessionId,
- @NonNull BiConsumer<Dataset, Integer> onClickFactory, @NonNull Runnable onErrorCallback,
- @NonNull Consumer<IntentSender> intentSenderConsumer,
- @Nullable IBinder hostInputToken, int displayId) {
- final InlineSuggestionInfo inlineSuggestionInfo = new InlineSuggestionInfo(
- inlinePresentation.getInlinePresentationSpec(),
- InlineSuggestionInfo.SOURCE_AUTOFILL, inlinePresentation.getAutofillHints(),
- InlineSuggestionInfo.TYPE_ACTION, inlinePresentation.isPinned());
-
return new InlineSuggestion(inlineSuggestionInfo,
- createInlineContentProvider(inlinePresentation,
- () -> onClickFactory.accept(null,
- AutofillManager.AUTHENTICATION_ID_DATASET_ID_UNDEFINED),
- onErrorCallback, intentSenderConsumer, remoteRenderService, userId,
- sessionId, hostInputToken, displayId));
+ createInlineContentProvider(inlineFillUiInfo, inlinePresentation,
+ onClickAction, uiCallback));
}
/**
@@ -216,25 +130,20 @@ final class InlineSuggestionFactory {
inlinePresentation.getInlinePresentationSpec().getMinSize(),
inlinePresentation.getInlinePresentationSpec().getMaxSize()).setStyle(
specFromHost.getStyle()).build();
+
return new InlinePresentation(inlinePresentation.getSlice(), mergedInlinePresentation,
inlinePresentation.isPinned());
}
private static IInlineContentProvider createInlineContentProvider(
+ @NonNull InlineFillUi.InlineFillUiInfo inlineFillUiInfo,
@NonNull InlinePresentation inlinePresentation, @Nullable Runnable onClickAction,
- @NonNull Runnable onErrorCallback,
- @NonNull Consumer<IntentSender> intentSenderConsumer,
- @Nullable RemoteInlineSuggestionRenderService remoteRenderService,
- int userId, int sessionId,
- @Nullable IBinder hostInputToken,
- int displayId) {
- RemoteInlineSuggestionViewConnector
- remoteInlineSuggestionViewConnector = new RemoteInlineSuggestionViewConnector(
- remoteRenderService, userId, sessionId, inlinePresentation, hostInputToken,
- displayId, onClickAction, onErrorCallback, intentSenderConsumer);
- InlineContentProviderImpl inlineContentProvider = new InlineContentProviderImpl(
- remoteInlineSuggestionViewConnector, null);
- return inlineContentProvider;
+ @NonNull InlineFillUi.InlineSuggestionUiCallback uiCallback) {
+ RemoteInlineSuggestionViewConnector remoteInlineSuggestionViewConnector =
+ new RemoteInlineSuggestionViewConnector(inlineFillUiInfo, inlinePresentation,
+ onClickAction, uiCallback);
+
+ return new InlineContentProviderImpl(remoteInlineSuggestionViewConnector, null);
}
private InlineSuggestionFactory() {
diff --git a/services/autofill/java/com/android/server/autofill/ui/RemoteInlineSuggestionViewConnector.java b/services/autofill/java/com/android/server/autofill/ui/RemoteInlineSuggestionViewConnector.java
index 7257255d1ee4..46d435d8811d 100644
--- a/services/autofill/java/com/android/server/autofill/ui/RemoteInlineSuggestionViewConnector.java
+++ b/services/autofill/java/com/android/server/autofill/ui/RemoteInlineSuggestionViewConnector.java
@@ -57,24 +57,20 @@ final class RemoteInlineSuggestionViewConnector {
private final Consumer<IntentSender> mStartIntentSenderFromClientApp;
RemoteInlineSuggestionViewConnector(
- @Nullable RemoteInlineSuggestionRenderService remoteRenderService,
- int userId, int sessionId,
+ @NonNull InlineFillUi.InlineFillUiInfo inlineFillUiInfo,
@NonNull InlinePresentation inlinePresentation,
- @Nullable IBinder hostInputToken,
- int displayId,
@NonNull Runnable onAutofillCallback,
- @NonNull Runnable onErrorCallback,
- @NonNull Consumer<IntentSender> startIntentSenderFromClientApp) {
- mRemoteRenderService = remoteRenderService;
+ @NonNull InlineFillUi.InlineSuggestionUiCallback uiCallback) {
+ mRemoteRenderService = inlineFillUiInfo.mRemoteRenderService;
mInlinePresentation = inlinePresentation;
- mHostInputToken = hostInputToken;
- mDisplayId = displayId;
- mUserId = userId;
- mSessionId = sessionId;
+ mHostInputToken = inlineFillUiInfo.mInlineRequest.getHostInputToken();
+ mDisplayId = inlineFillUiInfo.mInlineRequest.getHostDisplayId();
+ mUserId = inlineFillUiInfo.mUserId;
+ mSessionId = inlineFillUiInfo.mSessionId;
mOnAutofillCallback = onAutofillCallback;
- mOnErrorCallback = onErrorCallback;
- mStartIntentSenderFromClientApp = startIntentSenderFromClientApp;
+ mOnErrorCallback = uiCallback::onError;
+ mStartIntentSenderFromClientApp = uiCallback::startIntentSender;
}
/**
diff --git a/services/companion/java/com/android/server/companion/CompanionDeviceManagerService.java b/services/companion/java/com/android/server/companion/CompanionDeviceManagerService.java
index c9e5d011f9f5..6ca99d1a244b 100644
--- a/services/companion/java/com/android/server/companion/CompanionDeviceManagerService.java
+++ b/services/companion/java/com/android/server/companion/CompanionDeviceManagerService.java
@@ -83,6 +83,7 @@ import com.android.internal.notification.NotificationAccessConfirmationActivityC
import com.android.internal.os.BackgroundThread;
import com.android.internal.util.ArrayUtils;
import com.android.internal.util.CollectionUtils;
+import com.android.internal.util.DumpUtils;
import com.android.internal.util.function.pooled.PooledLambda;
import com.android.server.FgThread;
import com.android.server.LocalServices;
@@ -484,6 +485,10 @@ public class CompanionDeviceManagerService extends SystemService implements Bind
public void dump(@NonNull FileDescriptor fd,
@NonNull PrintWriter fout,
@Nullable String[] args) {
+ if (!DumpUtils.checkDumpAndUsageStatsPermission(getContext(), LOG_TAG, fout)) {
+ return;
+ }
+
fout.append("Companion Device Associations:").append('\n');
synchronized (mLock) {
forEach(mCachedAssociations, a -> {
diff --git a/services/core/java/android/os/BatteryStatsInternal.java b/services/core/java/android/os/BatteryStatsInternal.java
index b7fed87d570d..958c15c8d432 100644
--- a/services/core/java/android/os/BatteryStatsInternal.java
+++ b/services/core/java/android/os/BatteryStatsInternal.java
@@ -50,5 +50,10 @@ public abstract class BatteryStatsInternal {
* Informs battery stats of binder stats for the given work source UID.
*/
public abstract void noteBinderCallStats(int workSourceUid, long incrementalBinderCallCount,
- Collection<BinderCallsStats.CallStat> callStats, int[] binderThreadNativeTids);
+ Collection<BinderCallsStats.CallStat> callStats);
+
+ /**
+ * Informs battery stats of native thread IDs of threads taking incoming binder calls.
+ */
+ public abstract void noteBinderThreadNativeIds(int[] binderThreadNativeTids);
}
diff --git a/services/core/java/com/android/server/BinderCallsStatsService.java b/services/core/java/com/android/server/BinderCallsStatsService.java
index c9513592ea79..66ac889ff2ca 100644
--- a/services/core/java/com/android/server/BinderCallsStatsService.java
+++ b/services/core/java/com/android/server/BinderCallsStatsService.java
@@ -49,6 +49,7 @@ import com.android.internal.util.DumpUtils;
import java.io.FileDescriptor;
import java.io.PrintWriter;
import java.util.ArrayList;
+import java.util.Collection;
import java.util.List;
public class BinderCallsStatsService extends Binder {
@@ -273,7 +274,19 @@ public class BinderCallsStatsService extends Binder {
BatteryStatsInternal batteryStatsInternal = getLocalService(
BatteryStatsInternal.class);
- mBinderCallsStats.setCallStatsObserver(batteryStatsInternal::noteBinderCallStats);
+ mBinderCallsStats.setCallStatsObserver(new BinderInternal.CallStatsObserver() {
+ @Override
+ public void noteCallStats(int workSourceUid, long incrementalCallCount,
+ Collection<BinderCallsStats.CallStat> callStats) {
+ batteryStatsInternal.noteBinderCallStats(workSourceUid,
+ incrementalCallCount, callStats);
+ }
+
+ @Override
+ public void noteBinderThreadNativeIds(int[] binderThreadNativeTids) {
+ batteryStatsInternal.noteBinderThreadNativeIds(binderThreadNativeTids);
+ }
+ });
// It needs to be called before mService.systemReady to make sure the observer is
// initialized before installing it.
diff --git a/services/core/java/com/android/server/BluetoothManagerService.java b/services/core/java/com/android/server/BluetoothManagerService.java
index 0d79240a4b59..4e405cc7f7ea 100644
--- a/services/core/java/com/android/server/BluetoothManagerService.java
+++ b/services/core/java/com/android/server/BluetoothManagerService.java
@@ -418,7 +418,8 @@ class BluetoothManagerService extends IBluetoothManager.Stub {
if (BluetoothAdapter.ACTION_LOCAL_NAME_CHANGED.equals(action)) {
String newName = intent.getStringExtra(BluetoothAdapter.EXTRA_LOCAL_NAME);
if (DBG) {
- Slog.d(TAG, "Bluetooth Adapter name changed to " + newName);
+ Slog.d(TAG, "Bluetooth Adapter name changed to " + newName + " by "
+ + mContext.getPackageName());
}
if (newName != null) {
storeNameAndAddress(newName, null);
diff --git a/services/core/java/com/android/server/StorageManagerService.java b/services/core/java/com/android/server/StorageManagerService.java
index e342c1fcf114..6d774869b391 100644
--- a/services/core/java/com/android/server/StorageManagerService.java
+++ b/services/core/java/com/android/server/StorageManagerService.java
@@ -4641,6 +4641,11 @@ class StorageManagerService extends IStorageManager.Stub
// make sure the OBB dir for the application is setup correctly, if it exists.
File[] packageObbDirs = userEnv.buildExternalStorageAppObbDirs(packageName);
for (File packageObbDir : packageObbDirs) {
+ if (packageObbDir.getPath().startsWith(
+ Environment.getDataPreloadsMediaDirectory().getPath())) {
+ Slog.i(TAG, "Skipping app data preparation for " + packageObbDir);
+ continue;
+ }
try {
mVold.fixupAppDir(packageObbDir.getCanonicalPath() + "/", uid);
} catch (IOException e) {
diff --git a/services/core/java/com/android/server/TelephonyRegistry.java b/services/core/java/com/android/server/TelephonyRegistry.java
index 2522e937025b..ba2e147a2acf 100644
--- a/services/core/java/com/android/server/TelephonyRegistry.java
+++ b/services/core/java/com/android/server/TelephonyRegistry.java
@@ -2145,11 +2145,11 @@ public class TelephonyRegistry extends ITelephonyRegistry.Stub {
if (validatePhoneId(phoneId)) {
mOutgoingSmsEmergencyNumber[phoneId] = emergencyNumber;
for (Record r : mRecords) {
+ // Send to all listeners regardless of subscription
if (r.matchPhoneStateListenerEvent(
- PhoneStateListener.LISTEN_OUTGOING_EMERGENCY_SMS)
- && idMatch(r.subId, subId, phoneId)) {
+ PhoneStateListener.LISTEN_OUTGOING_EMERGENCY_SMS)) {
try {
- r.callback.onOutgoingEmergencySms(emergencyNumber);
+ r.callback.onOutgoingEmergencySms(emergencyNumber, subId);
} catch (RemoteException ex) {
mRemoveList.add(r.binder);
}
diff --git a/services/core/java/com/android/server/UiModeManagerService.java b/services/core/java/com/android/server/UiModeManagerService.java
index bcb7bfacfaf3..0135b9d2f8ab 100644
--- a/services/core/java/com/android/server/UiModeManagerService.java
+++ b/services/core/java/com/android/server/UiModeManagerService.java
@@ -72,7 +72,6 @@ import com.android.internal.app.DisableCarModeActivity;
import com.android.internal.messages.nano.SystemMessageProto.SystemMessage;
import com.android.internal.notification.SystemNotificationChannels;
import com.android.internal.util.DumpUtils;
-import com.android.server.SystemService.TargetUser;
import com.android.server.twilight.TwilightListener;
import com.android.server.twilight.TwilightManager;
import com.android.server.twilight.TwilightState;
@@ -327,8 +326,16 @@ final class UiModeManagerService extends SystemService {
@Override
public void onUserSwitching(@Nullable TargetUser from, @NonNull TargetUser to) {
mCurrentUser = to.getUserIdentifier();
+ if (mNightMode == MODE_NIGHT_AUTO) persistComputedNightMode(from.getUserIdentifier());
getContext().getContentResolver().unregisterContentObserver(mSetupWizardObserver);
verifySetupWizardCompleted();
+ synchronized (mLock) {
+ // only update if the value is actually changed
+ if (updateNightModeFromSettingsLocked(getContext(), getContext().getResources(),
+ to.getUserIdentifier())) {
+ updateLocked(0, 0);
+ }
+ }
}
@Override
@@ -356,11 +363,10 @@ final class UiModeManagerService extends SystemService {
new IntentFilter(Intent.ACTION_DOCK_EVENT));
IntentFilter batteryFilter = new IntentFilter(Intent.ACTION_BATTERY_CHANGED);
context.registerReceiver(mBatteryReceiver, batteryFilter);
- IntentFilter filter = new IntentFilter();
- filter.addAction(Intent.ACTION_USER_SWITCHED);
context.registerReceiver(mSettingsRestored,
new IntentFilter(Intent.ACTION_SETTING_RESTORED));
- context.registerReceiver(new UserSwitchedReceiver(), filter, null, mHandler);
+ context.registerReceiver(mOnShutdown,
+ new IntentFilter(Intent.ACTION_SHUTDOWN));
updateConfigurationLocked();
applyConfigurationExternallyLocked();
}
@@ -407,6 +413,21 @@ final class UiModeManagerService extends SystemService {
publishLocalService(UiModeManagerInternal.class, mLocalService);
}
+ private final BroadcastReceiver mOnShutdown = new BroadcastReceiver() {
+ @Override
+ public void onReceive(Context context, Intent intent) {
+ if (mNightMode == MODE_NIGHT_AUTO) {
+ persistComputedNightMode(mCurrentUser);
+ }
+ }
+ };
+
+ private void persistComputedNightMode(int userId) {
+ Secure.putIntForUser(getContext().getContentResolver(),
+ Secure.UI_NIGHT_MODE_LAST_COMPUTED, mComputedNightMode ? 1 : 0,
+ userId);
+ }
+
private final BroadcastReceiver mSettingsRestored = new BroadcastReceiver() {
@Override
public void onReceive(Context context, Intent intent) {
@@ -508,6 +529,10 @@ final class UiModeManagerService extends SystemService {
Secure.getLongForUser(context.getContentResolver(),
Secure.DARK_THEME_CUSTOM_END_TIME,
DEFAULT_CUSTOM_NIGHT_END_TIME.toNanoOfDay() / 1000L, userId) * 1000);
+ if (mNightMode == MODE_NIGHT_AUTO) {
+ mComputedNightMode = Secure.getIntForUser(context.getContentResolver(),
+ Secure.UI_NIGHT_MODE_LAST_COMPUTED, 0, userId) != 0;
+ }
}
return oldNightMode != mNightMode;
@@ -1630,18 +1655,4 @@ final class UiModeManagerService extends SystemService {
}
}
}
-
- private final class UserSwitchedReceiver extends BroadcastReceiver {
- @Override
- public void onReceive(Context context, Intent intent) {
- synchronized (mLock) {
- final int currentId = intent.getIntExtra(
- Intent.EXTRA_USER_HANDLE, USER_SYSTEM);
- // only update if the value is actually changed
- if (updateNightModeFromSettingsLocked(context, context.getResources(), currentId)) {
- updateLocked(0, 0);
- }
- }
- }
- }
}
diff --git a/services/core/java/com/android/server/am/ActiveServices.java b/services/core/java/com/android/server/am/ActiveServices.java
index 20f3231de200..c0f6011a45cd 100644
--- a/services/core/java/com/android/server/am/ActiveServices.java
+++ b/services/core/java/com/android/server/am/ActiveServices.java
@@ -482,13 +482,14 @@ public final class ActiveServices {
String callingPackage, @Nullable String callingFeatureId, final int userId)
throws TransactionTooLargeException {
return startServiceLocked(caller, service, resolvedType, callingPid, callingUid, fgRequired,
- hideFgNotification, callingPackage, callingFeatureId, userId, false);
+ hideFgNotification, callingPackage, callingFeatureId, userId, false, null);
}
ComponentName startServiceLocked(IApplicationThread caller, Intent service, String resolvedType,
int callingPid, int callingUid, boolean fgRequired, boolean hideFgNotification,
String callingPackage, @Nullable String callingFeatureId, final int userId,
- boolean allowBackgroundActivityStarts) throws TransactionTooLargeException {
+ boolean allowBackgroundActivityStarts, @Nullable IBinder backgroundActivityStartsToken)
+ throws TransactionTooLargeException {
if (DEBUG_DELAYED_STARTS) Slog.v(TAG_SERVICE, "startService: " + service
+ " type=" + resolvedType + " args=" + service.getExtras());
@@ -733,7 +734,7 @@ public final class ActiveServices {
}
}
if (allowBackgroundActivityStarts) {
- r.allowBgActivityStartsOnServiceStart();
+ r.allowBgActivityStartsOnServiceStart(backgroundActivityStartsToken);
}
ComponentName cmp = startServiceInnerLocked(smap, service, r, callerFg, addToStarting);
return cmp;
diff --git a/services/core/java/com/android/server/am/ActivityManagerService.java b/services/core/java/com/android/server/am/ActivityManagerService.java
index b1b4018036c0..ef75264b4c00 100644
--- a/services/core/java/com/android/server/am/ActivityManagerService.java
+++ b/services/core/java/com/android/server/am/ActivityManagerService.java
@@ -5753,11 +5753,6 @@ public class ActivityManagerService extends IActivityManager.Stub
}
@Override
- public void positionTaskInStack(int taskId, int stackId, int position) {
- mActivityTaskManager.positionTaskInStack(taskId, stackId, position);
- }
-
- @Override
public List<RootTaskInfo> getAllRootTaskInfos() {
return mActivityTaskManager.getAllRootTaskInfos();
}
@@ -11238,10 +11233,10 @@ public class ActivityManagerService extends IActivityManager.Stub
}
long kernelUsed = memInfo.getKernelUsedSizeKb();
final long ionHeap = Debug.getIonHeapsSizeKb();
- if (ionHeap > 0) {
+ final long ionPool = Debug.getIonPoolsSizeKb();
+ if (ionHeap >= 0 && ionPool >= 0) {
final long ionMapped = Debug.getIonMappedSizeKb();
final long ionUnmapped = ionHeap - ionMapped;
- final long ionPool = Debug.getIonPoolsSizeKb();
pw.print(" ION: ");
pw.print(stringifyKBSize(ionHeap + ionPool));
pw.print(" (");
@@ -12038,10 +12033,10 @@ public class ActivityManagerService extends IActivityManager.Stub
memInfoBuilder.append("\n");
long kernelUsed = memInfo.getKernelUsedSizeKb();
final long ionHeap = Debug.getIonHeapsSizeKb();
- if (ionHeap > 0) {
+ final long ionPool = Debug.getIonPoolsSizeKb();
+ if (ionHeap >= 0 && ionPool >= 0) {
final long ionMapped = Debug.getIonMappedSizeKb();
final long ionUnmapped = ionHeap - ionMapped;
- final long ionPool = Debug.getIonPoolsSizeKb();
memInfoBuilder.append(" ION: ");
memInfoBuilder.append(stringifyKBSize(ionHeap + ionPool));
memInfoBuilder.append("\n");
@@ -16311,7 +16306,8 @@ public class ActivityManagerService extends IActivityManager.Stub
try {
res = mServices.startServiceLocked(null, service,
resolvedType, -1, uid, fgRequired, false, callingPackage,
- callingFeatureId, userId, allowBackgroundActivityStarts);
+ callingFeatureId, userId, allowBackgroundActivityStarts,
+ backgroundActivityStartsToken);
} finally {
Binder.restoreCallingIdentity(origId);
}
diff --git a/services/core/java/com/android/server/am/ActivityManagerShellCommand.java b/services/core/java/com/android/server/am/ActivityManagerShellCommand.java
index 9ce8b11a7351..11197287114c 100644
--- a/services/core/java/com/android/server/am/ActivityManagerShellCommand.java
+++ b/services/core/java/com/android/server/am/ActivityManagerShellCommand.java
@@ -2582,8 +2582,6 @@ final class ActivityManagerShellCommand extends ShellCommand {
switch (op) {
case "move-task":
return runStackMoveTask(pw);
- case "positiontask":
- return runStackPositionTask(pw);
case "list":
return runStackList(pw);
case "info":
@@ -2656,18 +2654,6 @@ final class ActivityManagerShellCommand extends ShellCommand {
return 0;
}
- int runStackPositionTask(PrintWriter pw) throws RemoteException {
- String taskIdStr = getNextArgRequired();
- int taskId = Integer.parseInt(taskIdStr);
- String stackIdStr = getNextArgRequired();
- int stackId = Integer.parseInt(stackIdStr);
- String positionStr = getNextArgRequired();
- int position = Integer.parseInt(positionStr);
-
- mTaskInterface.positionTaskInStack(taskId, stackId, position);
- return 0;
- }
-
int runStackList(PrintWriter pw) throws RemoteException {
List<RootTaskInfo> tasks = mTaskInterface.getAllRootTaskInfos();
for (RootTaskInfo info : tasks) {
diff --git a/services/core/java/com/android/server/am/BatteryStatsService.java b/services/core/java/com/android/server/am/BatteryStatsService.java
index f42753287e35..76125aabc978 100644
--- a/services/core/java/com/android/server/am/BatteryStatsService.java
+++ b/services/core/java/com/android/server/am/BatteryStatsService.java
@@ -240,14 +240,20 @@ public final class BatteryStatsService extends IBatteryStats.Stub
@Override
public void noteBinderCallStats(int workSourceUid, long incrementatCallCount,
- Collection<BinderCallsStats.CallStat> callStats, int[] binderThreadNativeTids) {
+ Collection<BinderCallsStats.CallStat> callStats) {
synchronized (BatteryStatsService.this.mLock) {
mHandler.sendMessage(PooledLambda.obtainMessage(
- mStats::noteBinderCallStats, workSourceUid, incrementatCallCount,
- callStats, binderThreadNativeTids,
+ mStats::noteBinderCallStats, workSourceUid, incrementatCallCount, callStats,
SystemClock.elapsedRealtime(), SystemClock.uptimeMillis()));
}
}
+
+ @Override
+ public void noteBinderThreadNativeIds(int[] binderThreadNativeTids) {
+ synchronized (BatteryStatsService.this.mLock) {
+ mStats.noteBinderThreadNativeIds(binderThreadNativeTids);
+ }
+ }
}
@Override
@@ -276,15 +282,13 @@ public final class BatteryStatsService extends IBatteryStats.Stub
}
private void awaitCompletion() {
- synchronized (mLock) {
- final CountDownLatch latch = new CountDownLatch(1);
- mHandler.post(() -> {
- latch.countDown();
- });
- try {
- latch.await();
- } catch (InterruptedException e) {
- }
+ final CountDownLatch latch = new CountDownLatch(1);
+ mHandler.post(() -> {
+ latch.countDown();
+ });
+ try {
+ latch.await();
+ } catch (InterruptedException e) {
}
}
diff --git a/services/core/java/com/android/server/am/BroadcastQueue.java b/services/core/java/com/android/server/am/BroadcastQueue.java
index 93105daa6c5d..57f811215e50 100644
--- a/services/core/java/com/android/server/am/BroadcastQueue.java
+++ b/services/core/java/com/android/server/am/BroadcastQueue.java
@@ -1686,7 +1686,7 @@ public final class BroadcastQueue {
// that request - we don't want the token to be swept from under our feet...
mHandler.removeCallbacksAndMessages(msgToken);
// ...then add the token
- proc.addAllowBackgroundActivityStartsToken(r, r.mBackgroundActivityStartsToken);
+ proc.addOrUpdateAllowBackgroundActivityStartsToken(r, r.mBackgroundActivityStartsToken);
}
final void setBroadcastTimeoutLocked(long timeoutTime) {
diff --git a/services/core/java/com/android/server/am/CachedAppOptimizer.java b/services/core/java/com/android/server/am/CachedAppOptimizer.java
index 966038986791..8112bb854b71 100644
--- a/services/core/java/com/android/server/am/CachedAppOptimizer.java
+++ b/services/core/java/com/android/server/am/CachedAppOptimizer.java
@@ -22,6 +22,7 @@ import static com.android.server.am.ActivityManagerDebugConfig.TAG_AM;
import android.app.ActivityManager;
import android.app.ActivityThread;
+import android.app.ApplicationExitInfo;
import android.os.Debug;
import android.os.Handler;
import android.os.Message;
@@ -132,11 +133,15 @@ public final class CachedAppOptimizer {
static final int REPORT_UNFREEZE_MSG = 4;
//TODO:change this static definition into a configurable flag.
- static final int FREEZE_TIMEOUT_MS = 500;
+ static final int FREEZE_TIMEOUT_MS = 10000;
static final int DO_FREEZE = 1;
static final int REPORT_UNFREEZE = 2;
+ // Bitfield values for sync/async transactions reveived by frozen processes
+ static final int SYNC_RECEIVED_WHILE_FROZEN = 1;
+ static final int ASYNC_RECEIVED_WHILE_FROZEN = 2;
+
/**
* This thread must be moved to the system background cpuset.
* If that doesn't happen, it's probably going to draw a lot of power.
@@ -494,6 +499,15 @@ public final class CachedAppOptimizer {
private static native void freezeBinder(int pid, boolean freeze);
/**
+ * Retrieves binder freeze info about a process.
+ * @param pid the pid for which binder freeze info is to be retrieved.
+ *
+ * @throws RuntimeException if the operation could not complete successfully.
+ * @return a bit field reporting the binder freeze info for the process.
+ */
+ private static native int getBinderFreezeInfo(int pid);
+
+ /**
* Determines whether the freezer is supported by this system
*/
public static boolean isFreezerSupported() {
@@ -729,6 +743,37 @@ public final class CachedAppOptimizer {
return;
}
+ boolean processKilled = false;
+
+ try {
+ int freezeInfo = getBinderFreezeInfo(app.pid);
+
+ if ((freezeInfo & SYNC_RECEIVED_WHILE_FROZEN) != 0) {
+ Slog.d(TAG_AM, "pid " + app.pid + " " + app.processName + " "
+ + " received sync transactions while frozen, killing");
+ app.kill("Sync transaction while in frozen state",
+ ApplicationExitInfo.REASON_OTHER,
+ ApplicationExitInfo.SUBREASON_INVALID_STATE, true);
+ processKilled = true;
+ }
+
+ if ((freezeInfo & ASYNC_RECEIVED_WHILE_FROZEN) != 0) {
+ Slog.d(TAG_AM, "pid " + app.pid + " " + app.processName + " "
+ + " received async transactions while frozen");
+ }
+ } catch (Exception e) {
+ Slog.d(TAG_AM, "Unable to query binder frozen info for pid " + app.pid + " "
+ + app.processName + ". Killing it. Exception: " + e);
+ app.kill("Unable to query binder frozen stats",
+ ApplicationExitInfo.REASON_OTHER,
+ ApplicationExitInfo.SUBREASON_INVALID_STATE, true);
+ processKilled = true;
+ }
+
+ if (processKilled) {
+ return;
+ }
+
long freezeTime = app.freezeUnfreezeTime;
try {
@@ -745,8 +790,12 @@ public final class CachedAppOptimizer {
try {
freezeBinder(app.pid, false);
} catch (RuntimeException e) {
- // TODO: it might be preferable to kill the target pid in this case
- Slog.e(TAG_AM, "Unable to unfreeze binder for " + app.pid + " " + app.processName);
+ Slog.e(TAG_AM, "Unable to unfreeze binder for " + app.pid + " " + app.processName
+ + ". Killing it");
+ app.kill("Unable to unfreeze",
+ ApplicationExitInfo.REASON_OTHER,
+ ApplicationExitInfo.SUBREASON_INVALID_STATE, true);
+ return;
}
if (DEBUG_FREEZER) {
diff --git a/services/core/java/com/android/server/am/ProcessRecord.java b/services/core/java/com/android/server/am/ProcessRecord.java
index f1e12c55df18..2dced8d704bb 100644
--- a/services/core/java/com/android/server/am/ProcessRecord.java
+++ b/services/core/java/com/android/server/am/ProcessRecord.java
@@ -77,6 +77,7 @@ import java.io.StringWriter;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
+import java.util.Objects;
import java.util.function.Consumer;
/**
@@ -1338,13 +1339,15 @@ class ProcessRecord implements WindowProcessListener {
* {@param originatingToken} if you have one such originating token, this is useful for tracing
* back the grant in the case of the notification token.
*/
- void addAllowBackgroundActivityStartsToken(Binder entity, @Nullable IBinder originatingToken) {
- if (entity == null) return;
- mWindowProcessController.addAllowBackgroundActivityStartsToken(entity, originatingToken);
+ void addOrUpdateAllowBackgroundActivityStartsToken(Binder entity,
+ @Nullable IBinder originatingToken) {
+ Objects.requireNonNull(entity);
+ mWindowProcessController.addOrUpdateAllowBackgroundActivityStartsToken(entity,
+ originatingToken);
}
void removeAllowBackgroundActivityStartsToken(Binder entity) {
- if (entity == null) return;
+ Objects.requireNonNull(entity);
mWindowProcessController.removeAllowBackgroundActivityStartsToken(entity);
}
diff --git a/services/core/java/com/android/server/am/ServiceRecord.java b/services/core/java/com/android/server/am/ServiceRecord.java
index 55e0f2ea5212..a7d8ca496f75 100644
--- a/services/core/java/com/android/server/am/ServiceRecord.java
+++ b/services/core/java/com/android/server/am/ServiceRecord.java
@@ -22,6 +22,7 @@ import static android.app.PendingIntent.FLAG_UPDATE_CURRENT;
import static com.android.server.am.ActivityManagerDebugConfig.TAG_AM;
import static com.android.server.am.ActivityManagerDebugConfig.TAG_WITH_CLASS_NAME;
+import android.annotation.Nullable;
import android.app.Notification;
import android.app.PendingIntent;
import android.content.ComponentName;
@@ -139,6 +140,12 @@ final class ServiceRecord extends Binder implements ComponentName.WithComponentN
// used to clean up the state of mIsAllowedBgActivityStartsByStart after a timeout
private Runnable mCleanUpAllowBgActivityStartsByStartCallback;
private ProcessRecord mAppForAllowingBgActivityStartsByStart;
+ // These are the originating tokens that currently allow bg activity starts by service start.
+ // This is used to trace back the grant when starting activities. We only pass such token to the
+ // ProcessRecord if it's the *only* cause for bg activity starts exemption, otherwise we pass
+ // null.
+ @GuardedBy("ams")
+ private List<IBinder> mBgActivityStartsByStartOriginatingTokens = new ArrayList<>();
// allow while-in-use permissions in foreground service or not.
// while-in-use permissions in FGS started from background might be restricted.
@@ -588,7 +595,8 @@ final class ServiceRecord extends Binder implements ComponentName.WithComponentN
? _proc : null;
if (mIsAllowedBgActivityStartsByStart
|| mIsAllowedBgActivityStartsByBinding) {
- _proc.addAllowBackgroundActivityStartsToken(this, null);
+ _proc.addOrUpdateAllowBackgroundActivityStartsToken(this,
+ getExclusiveOriginatingToken());
} else {
_proc.removeAllowBackgroundActivityStartsToken(this);
}
@@ -679,10 +687,8 @@ final class ServiceRecord extends Binder implements ComponentName.WithComponentN
}
void setAllowedBgActivityStartsByBinding(boolean newValue) {
- if (mIsAllowedBgActivityStartsByBinding != newValue) {
- mIsAllowedBgActivityStartsByBinding = newValue;
- updateParentProcessBgActivityStartsToken();
- }
+ mIsAllowedBgActivityStartsByBinding = newValue;
+ updateParentProcessBgActivityStartsToken();
}
/**
@@ -691,7 +697,8 @@ final class ServiceRecord extends Binder implements ComponentName.WithComponentN
* timeout. Note that the ability for starting background activities persists for the process
* even if the service is subsequently stopped.
*/
- void allowBgActivityStartsOnServiceStart() {
+ void allowBgActivityStartsOnServiceStart(@Nullable IBinder originatingToken) {
+ mBgActivityStartsByStartOriginatingTokens.add(originatingToken);
setAllowedBgActivityStartsByStart(true);
if (app != null) {
mAppForAllowingBgActivityStartsByStart = app;
@@ -701,32 +708,49 @@ final class ServiceRecord extends Binder implements ComponentName.WithComponentN
if (mCleanUpAllowBgActivityStartsByStartCallback == null) {
mCleanUpAllowBgActivityStartsByStartCallback = () -> {
synchronized (ams) {
- if (app == mAppForAllowingBgActivityStartsByStart) {
- // The process we allowed is still running the service. We remove
- // the ability by start, but it may still be allowed via bound connections.
- setAllowedBgActivityStartsByStart(false);
- } else if (mAppForAllowingBgActivityStartsByStart != null) {
- // The process we allowed is not running the service. It therefore can't be
- // bound so we can unconditionally remove the ability.
- mAppForAllowingBgActivityStartsByStart
- .removeAllowBackgroundActivityStartsToken(ServiceRecord.this);
+ mBgActivityStartsByStartOriginatingTokens.remove(0);
+ if (!mBgActivityStartsByStartOriginatingTokens.isEmpty()) {
+ // There are other callbacks in the queue, let's just update the originating
+ // token
+ if (mIsAllowedBgActivityStartsByStart) {
+ mAppForAllowingBgActivityStartsByStart
+ .addOrUpdateAllowBackgroundActivityStartsToken(
+ this, getExclusiveOriginatingToken());
+ } else {
+ Slog.wtf(TAG,
+ "Service callback to revoke bg activity starts by service "
+ + "start triggered but "
+ + "mIsAllowedBgActivityStartsByStart = false. This "
+ + "should never happen.");
+ }
+ } else {
+ // Last callback on the queue
+ if (app == mAppForAllowingBgActivityStartsByStart) {
+ // The process we allowed is still running the service. We remove
+ // the ability by start, but it may still be allowed via bound
+ // connections.
+ setAllowedBgActivityStartsByStart(false);
+ } else if (mAppForAllowingBgActivityStartsByStart != null) {
+ // The process we allowed is not running the service. It therefore can't
+ // be bound so we can unconditionally remove the ability.
+ mAppForAllowingBgActivityStartsByStart
+ .removeAllowBackgroundActivityStartsToken(ServiceRecord.this);
+ }
+ mAppForAllowingBgActivityStartsByStart = null;
}
- mAppForAllowingBgActivityStartsByStart = null;
}
};
}
- // if there's a request pending from the past, drop it before scheduling a new one
- ams.mHandler.removeCallbacks(mCleanUpAllowBgActivityStartsByStartCallback);
+ // Existing callbacks will only update the originating token, only when the last callback is
+ // executed is the grant revoked.
ams.mHandler.postDelayed(mCleanUpAllowBgActivityStartsByStartCallback,
ams.mConstants.SERVICE_BG_ACTIVITY_START_TIMEOUT);
}
private void setAllowedBgActivityStartsByStart(boolean newValue) {
- if (mIsAllowedBgActivityStartsByStart != newValue) {
- mIsAllowedBgActivityStartsByStart = newValue;
- updateParentProcessBgActivityStartsToken();
- }
+ mIsAllowedBgActivityStartsByStart = newValue;
+ updateParentProcessBgActivityStartsToken();
}
/**
@@ -736,8 +760,8 @@ final class ServiceRecord extends Binder implements ComponentName.WithComponentN
* {@code mIsAllowedBgActivityStartsByBinding}. If either is true, this ServiceRecord
* should be contributing as a token in parent ProcessRecord.
*
- * @see com.android.server.am.ProcessRecord#addAllowBackgroundActivityStartsToken(Binder,
- * IBinder)
+ * @see com.android.server.am.ProcessRecord#addOrUpdateAllowBackgroundActivityStartsToken(
+ * Binder, IBinder)
* @see com.android.server.am.ProcessRecord#removeAllowBackgroundActivityStartsToken(Binder)
*/
private void updateParentProcessBgActivityStartsToken() {
@@ -747,12 +771,37 @@ final class ServiceRecord extends Binder implements ComponentName.WithComponentN
if (mIsAllowedBgActivityStartsByStart || mIsAllowedBgActivityStartsByBinding) {
// if the token is already there it's safe to "re-add it" - we're dealing with
// a set of Binder objects
- app.addAllowBackgroundActivityStartsToken(this, null);
+ app.addOrUpdateAllowBackgroundActivityStartsToken(this, getExclusiveOriginatingToken());
} else {
app.removeAllowBackgroundActivityStartsToken(this);
}
}
+ /**
+ * Returns the originating token if that's the only reason background activity starts are
+ * allowed. In order for that to happen the service has to be allowed only due to starts, since
+ * bindings are not associated with originating tokens, and all the start tokens have to be the
+ * same and there can't be any null originating token in the queue.
+ *
+ * Originating tokens are optional, so the caller could provide null when it allows bg activity
+ * starts.
+ */
+ @Nullable
+ private IBinder getExclusiveOriginatingToken() {
+ if (mIsAllowedBgActivityStartsByBinding
+ || mBgActivityStartsByStartOriginatingTokens.isEmpty()) {
+ return null;
+ }
+ IBinder firstToken = mBgActivityStartsByStartOriginatingTokens.get(0);
+ for (int i = 1, n = mBgActivityStartsByStartOriginatingTokens.size(); i < n; i++) {
+ IBinder token = mBgActivityStartsByStartOriginatingTokens.get(i);
+ if (token != firstToken) {
+ return null;
+ }
+ }
+ return firstToken;
+ }
+
@GuardedBy("ams")
void updateKeepWarmLocked() {
mKeepWarming = ams.mConstants.KEEP_WARMING_SERVICES.contains(name)
diff --git a/services/core/java/com/android/server/appop/AppOpsService.java b/services/core/java/com/android/server/appop/AppOpsService.java
index 06f44b1bd388..668713f0fee7 100644
--- a/services/core/java/com/android/server/appop/AppOpsService.java
+++ b/services/core/java/com/android/server/appop/AppOpsService.java
@@ -1794,9 +1794,9 @@ public class AppOpsService extends IAppOpsService.Stub {
}
}
}
-
- mHistoricalRegistry.clearHistory(uid, packageName);
}
+
+ mHistoricalRegistry.clearHistory(uid, packageName);
}
public void uidRemoved(int uid) {
@@ -6247,7 +6247,7 @@ public class AppOpsService extends IAppOpsService.Stub {
int[] users;
if (userId == UserHandle.USER_ALL) {
- // TODO(b/157921703): this call is returning all users, not just live ones - we
+ // TODO(b/162888972): this call is returning all users, not just live ones - we
// need to either fix the method called, or rename the variable
List<UserInfo> liveUsers = UserManager.get(mContext).getUsers();
diff --git a/services/core/java/com/android/server/audio/AudioService.java b/services/core/java/com/android/server/audio/AudioService.java
index 6110759b5a9e..f1561cab8fb4 100644
--- a/services/core/java/com/android/server/audio/AudioService.java
+++ b/services/core/java/com/android/server/audio/AudioService.java
@@ -6735,14 +6735,17 @@ public class AudioService extends IAudioService.Stub
if (msg.obj == null) {
break;
}
- // If the app corresponding to this mode death handler object is not
- // capturing or playing audio anymore after 3 seconds, remove it
- // from the stack. Otherwise, check again in 3 seconds.
+ // If no other app is currently owning the audio mode and
+ // the app corresponding to this mode death handler object is still in the
+ // mode owner stack but not capturing or playing audio after 3 seconds,
+ // remove it from the stack.
+ // Otherwise, check again in 3 seconds.
SetModeDeathHandler h = (SetModeDeathHandler) msg.obj;
if (mSetModeDeathHandlers.indexOf(h) < 0) {
break;
}
- if (mRecordMonitor.isRecordingActiveForUid(h.getUid())
+ if (getModeOwnerUid() != h.getUid()
+ || mRecordMonitor.isRecordingActiveForUid(h.getUid())
|| mPlaybackMonitor.isPlaybackActiveForUid(h.getUid())) {
sendMsg(mAudioHandler,
MSG_CHECK_MODE_FOR_UID,
diff --git a/services/core/java/com/android/server/audio/BtHelper.java b/services/core/java/com/android/server/audio/BtHelper.java
index ded0f9a3dca7..8de31d999b53 100644
--- a/services/core/java/com/android/server/audio/BtHelper.java
+++ b/services/core/java/com/android/server/audio/BtHelper.java
@@ -450,6 +450,7 @@ public class BtHelper {
}
@GuardedBy("AudioDeviceBroker.mDeviceStateLock")
+ // @GuardedBy("BtHelper.this")
private void stopAndRemoveClient(ScoClient client, @NonNull String eventSource) {
AudioService.sDeviceLogger.log(new AudioEventLogger.StringEvent(eventSource));
client.requestScoState(BluetoothHeadset.STATE_AUDIO_DISCONNECTED,
diff --git a/services/core/java/com/android/server/biometrics/SensorConfig.java b/services/core/java/com/android/server/biometrics/SensorConfig.java
index 7743f1c91307..a83f67a5e29c 100644
--- a/services/core/java/com/android/server/biometrics/SensorConfig.java
+++ b/services/core/java/com/android/server/biometrics/SensorConfig.java
@@ -16,13 +16,15 @@
package com.android.server.biometrics;
+import android.hardware.biometrics.BiometricManager;
+
/**
* Parsed sensor config. See core/res/res/values/config.xml config_biometric_sensors
*/
public class SensorConfig {
public final int id;
final int modality;
- final int strength;
+ @BiometricManager.Authenticators.Types public final int strength;
public SensorConfig(String config) {
String[] elems = config.split(":");
diff --git a/services/core/java/com/android/server/biometrics/Utils.java b/services/core/java/com/android/server/biometrics/Utils.java
index 88fd44456ff6..c300169d0b39 100644
--- a/services/core/java/com/android/server/biometrics/Utils.java
+++ b/services/core/java/com/android/server/biometrics/Utils.java
@@ -48,6 +48,7 @@ import android.hardware.biometrics.BiometricPrompt;
import android.hardware.biometrics.BiometricPrompt.AuthenticationResultType;
import android.hardware.biometrics.IBiometricService;
import android.hardware.biometrics.PromptInfo;
+import android.hardware.biometrics.SensorProperties;
import android.os.Binder;
import android.os.Build;
import android.os.RemoteException;
@@ -444,4 +445,22 @@ public class Utils {
}
return false;
}
+
+ /**
+ * Converts from {@link BiometricManager.Authenticators} biometric strength to the internal
+ * {@link SensorProperties} strength.
+ */
+ public static @SensorProperties.Strength int authenticatorStrengthToPropertyStrength(
+ @BiometricManager.Authenticators.Types int strength) {
+ switch (strength) {
+ case BiometricManager.Authenticators.BIOMETRIC_CONVENIENCE:
+ return SensorProperties.STRENGTH_CONVENIENCE;
+ case BiometricManager.Authenticators.BIOMETRIC_WEAK:
+ return SensorProperties.STRENGTH_WEAK;
+ case BiometricManager.Authenticators.BIOMETRIC_STRONG:
+ return SensorProperties.STRENGTH_STRONG;
+ default:
+ throw new IllegalArgumentException("Unknown strength: " + strength);
+ }
+ }
}
diff --git a/services/core/java/com/android/server/biometrics/sensors/face/Face10.java b/services/core/java/com/android/server/biometrics/sensors/face/Face10.java
index 1613d3bb4e8a..6e6ec747e24e 100644
--- a/services/core/java/com/android/server/biometrics/sensors/face/Face10.java
+++ b/services/core/java/com/android/server/biometrics/sensors/face/Face10.java
@@ -26,6 +26,7 @@ import android.content.Context;
import android.content.pm.UserInfo;
import android.hardware.biometrics.BiometricConstants;
import android.hardware.biometrics.BiometricFaceConstants;
+import android.hardware.biometrics.BiometricManager;
import android.hardware.biometrics.BiometricsProtoEnums;
import android.hardware.biometrics.face.V1_0.IBiometricsFace;
import android.hardware.biometrics.face.V1_0.IBiometricsFaceClientCallback;
@@ -279,6 +280,7 @@ class Face10 implements IHwBinder.DeathRecipient {
};
Face10(@NonNull Context context, int sensorId,
+ @BiometricManager.Authenticators.Types int strength,
@NonNull LockoutResetDispatcher lockoutResetDispatcher) {
final boolean supportsSelfIllumination = context.getResources()
.getBoolean(R.bool.config_faceAuthSupportsSelfIllumination);
diff --git a/services/core/java/com/android/server/biometrics/sensors/face/FaceAuthenticator.java b/services/core/java/com/android/server/biometrics/sensors/face/FaceAuthenticator.java
index bbee6cde4603..3318bcb8d593 100644
--- a/services/core/java/com/android/server/biometrics/sensors/face/FaceAuthenticator.java
+++ b/services/core/java/com/android/server/biometrics/sensors/face/FaceAuthenticator.java
@@ -34,7 +34,7 @@ public final class FaceAuthenticator extends IBiometricAuthenticator.Stub {
public FaceAuthenticator(IFaceService faceService, SensorConfig config)
throws RemoteException {
mFaceService = faceService;
- mFaceService.initializeConfiguration(config.id);
+ mFaceService.initializeConfiguration(config.id, config.strength);
}
@Override
diff --git a/services/core/java/com/android/server/biometrics/sensors/face/FaceService.java b/services/core/java/com/android/server/biometrics/sensors/face/FaceService.java
index c6664f4d96ff..82dc0d04d8a7 100644
--- a/services/core/java/com/android/server/biometrics/sensors/face/FaceService.java
+++ b/services/core/java/com/android/server/biometrics/sensors/face/FaceService.java
@@ -21,6 +21,7 @@ import static android.Manifest.permission.USE_BIOMETRIC_INTERNAL;
import android.annotation.NonNull;
import android.content.Context;
+import android.hardware.biometrics.BiometricManager;
import android.hardware.biometrics.BiometricsProtoEnums;
import android.hardware.biometrics.IBiometricSensorReceiver;
import android.hardware.biometrics.IBiometricServiceLockoutResetCallback;
@@ -308,9 +309,10 @@ public class FaceService extends SystemService {
}
@Override // Binder call
- public void initializeConfiguration(int sensorId) {
+ public void initializeConfiguration(int sensorId,
+ @BiometricManager.Authenticators.Types int strength) {
Utils.checkPermission(getContext(), USE_BIOMETRIC_INTERNAL);
- mFace10 = new Face10(getContext(), sensorId, mLockoutResetDispatcher);
+ mFace10 = new Face10(getContext(), sensorId, strength, mLockoutResetDispatcher);
}
}
diff --git a/services/core/java/com/android/server/biometrics/sensors/fingerprint/Fingerprint21.java b/services/core/java/com/android/server/biometrics/sensors/fingerprint/Fingerprint21.java
index 7e62329a168a..507b5dd3f224 100644
--- a/services/core/java/com/android/server/biometrics/sensors/fingerprint/Fingerprint21.java
+++ b/services/core/java/com/android/server/biometrics/sensors/fingerprint/Fingerprint21.java
@@ -27,6 +27,7 @@ import android.app.UserSwitchObserver;
import android.content.Context;
import android.content.pm.UserInfo;
import android.hardware.biometrics.BiometricConstants;
+import android.hardware.biometrics.BiometricManager;
import android.hardware.biometrics.BiometricsProtoEnums;
import android.hardware.biometrics.fingerprint.V2_1.IBiometricsFingerprint;
import android.hardware.biometrics.fingerprint.V2_2.IBiometricsFingerprintClientCallback;
@@ -296,6 +297,7 @@ class Fingerprint21 implements IHwBinder.DeathRecipient {
Fingerprint21(@NonNull Context context, @NonNull BiometricScheduler scheduler,
@NonNull Handler handler, int sensorId,
+ @BiometricManager.Authenticators.Types int strength,
@NonNull LockoutResetDispatcher lockoutResetDispatcher,
@NonNull HalResultController controller) {
mContext = context;
@@ -335,25 +337,27 @@ class Fingerprint21 implements IHwBinder.DeathRecipient {
}
final @FingerprintSensorProperties.SensorType int sensorType =
- isUdfps ? FingerprintSensorProperties.TYPE_UDFPS
+ isUdfps ? FingerprintSensorProperties.TYPE_UDFPS_OPTICAL
: FingerprintSensorProperties.TYPE_REAR;
// resetLockout is controlled by the framework, so hardwareAuthToken is not required
final boolean resetLockoutRequiresHardwareAuthToken = false;
- final int maxTemplatesAllowed = mContext.getResources()
+ final int maxEnrollmentsPerUser = mContext.getResources()
.getInteger(R.integer.config_fingerprintMaxTemplatesPerUser);
- mSensorProperties = new FingerprintSensorProperties(sensorId, sensorType,
- resetLockoutRequiresHardwareAuthToken, maxTemplatesAllowed);
+
+ mSensorProperties = new FingerprintSensorProperties(sensorId,
+ Utils.authenticatorStrengthToPropertyStrength(strength), maxEnrollmentsPerUser,
+ sensorType, resetLockoutRequiresHardwareAuthToken);
}
- static Fingerprint21 newInstance(@NonNull Context context, int sensorId,
+ static Fingerprint21 newInstance(@NonNull Context context, int sensorId, int strength,
@NonNull LockoutResetDispatcher lockoutResetDispatcher,
@NonNull GestureAvailabilityDispatcher gestureAvailabilityDispatcher) {
final Handler handler = new Handler(Looper.getMainLooper());
final BiometricScheduler scheduler =
new BiometricScheduler(TAG, gestureAvailabilityDispatcher);
final HalResultController controller = new HalResultController(context, handler, scheduler);
- return new Fingerprint21(context, scheduler, handler, sensorId, lockoutResetDispatcher,
- controller);
+ return new Fingerprint21(context, scheduler, handler, sensorId, strength,
+ lockoutResetDispatcher, controller);
}
@Override
diff --git a/services/core/java/com/android/server/biometrics/sensors/fingerprint/Fingerprint21UdfpsMock.java b/services/core/java/com/android/server/biometrics/sensors/fingerprint/Fingerprint21UdfpsMock.java
index 7cab2b889eaa..044dbe9664ed 100644
--- a/services/core/java/com/android/server/biometrics/sensors/fingerprint/Fingerprint21UdfpsMock.java
+++ b/services/core/java/com/android/server/biometrics/sensors/fingerprint/Fingerprint21UdfpsMock.java
@@ -21,6 +21,7 @@ import android.annotation.Nullable;
import android.app.trust.TrustManager;
import android.content.ContentResolver;
import android.content.Context;
+import android.hardware.biometrics.BiometricManager;
import android.hardware.fingerprint.FingerprintManager;
import android.hardware.fingerprint.FingerprintManager.AuthenticationCallback;
import android.hardware.fingerprint.FingerprintManager.AuthenticationResult;
@@ -36,6 +37,7 @@ import android.util.Slog;
import android.util.SparseBooleanArray;
import com.android.internal.R;
+import com.android.server.biometrics.Utils;
import com.android.server.biometrics.sensors.AuthenticationConsumer;
import com.android.server.biometrics.sensors.BiometricScheduler;
import com.android.server.biometrics.sensors.ClientMonitor;
@@ -266,6 +268,7 @@ public class Fingerprint21UdfpsMock extends Fingerprint21 implements TrustManage
}
static Fingerprint21UdfpsMock newInstance(@NonNull Context context, int sensorId,
+ @BiometricManager.Authenticators.Types int strength,
@NonNull LockoutResetDispatcher lockoutResetDispatcher,
@NonNull GestureAvailabilityDispatcher gestureAvailabilityDispatcher) {
Slog.d(TAG, "Creating Fingerprint23Mock!");
@@ -275,7 +278,7 @@ public class Fingerprint21UdfpsMock extends Fingerprint21 implements TrustManage
new TestableBiometricScheduler(TAG, gestureAvailabilityDispatcher);
final MockHalResultController controller =
new MockHalResultController(context, handler, scheduler);
- return new Fingerprint21UdfpsMock(context, scheduler, handler, sensorId,
+ return new Fingerprint21UdfpsMock(context, scheduler, handler, sensorId, strength,
lockoutResetDispatcher, controller);
}
@@ -401,9 +404,10 @@ public class Fingerprint21UdfpsMock extends Fingerprint21 implements TrustManage
private Fingerprint21UdfpsMock(@NonNull Context context,
@NonNull TestableBiometricScheduler scheduler,
@NonNull Handler handler, int sensorId,
+ @BiometricManager.Authenticators.Types int strength,
@NonNull LockoutResetDispatcher lockoutResetDispatcher,
@NonNull MockHalResultController controller) {
- super(context, scheduler, handler, sensorId, lockoutResetDispatcher, controller);
+ super(context, scheduler, handler, sensorId, strength, lockoutResetDispatcher, controller);
mScheduler = scheduler;
mScheduler.init(this);
mHandler = handler;
@@ -412,8 +416,9 @@ public class Fingerprint21UdfpsMock extends Fingerprint21 implements TrustManage
final int maxTemplatesAllowed = mContext.getResources()
.getInteger(R.integer.config_fingerprintMaxTemplatesPerUser);
mSensorProperties = new FingerprintSensorProperties(sensorId,
- FingerprintSensorProperties.TYPE_UDFPS, resetLockoutRequiresHardwareAuthToken,
- maxTemplatesAllowed);
+ Utils.authenticatorStrengthToPropertyStrength(strength), maxTemplatesAllowed,
+ FingerprintSensorProperties.TYPE_UDFPS_OPTICAL,
+ resetLockoutRequiresHardwareAuthToken);
mMockHalResultController = controller;
mUserHasTrust = new SparseBooleanArray();
mTrustManager = context.getSystemService(TrustManager.class);
diff --git a/services/core/java/com/android/server/biometrics/sensors/fingerprint/FingerprintAuthenticator.java b/services/core/java/com/android/server/biometrics/sensors/fingerprint/FingerprintAuthenticator.java
index 21a46d58a3b3..5219df4a841d 100644
--- a/services/core/java/com/android/server/biometrics/sensors/fingerprint/FingerprintAuthenticator.java
+++ b/services/core/java/com/android/server/biometrics/sensors/fingerprint/FingerprintAuthenticator.java
@@ -34,7 +34,7 @@ public final class FingerprintAuthenticator extends IBiometricAuthenticator.Stub
public FingerprintAuthenticator(IFingerprintService fingerprintService, SensorConfig config)
throws RemoteException {
mFingerprintService = fingerprintService;
- mFingerprintService.initializeConfiguration(config.id);
+ mFingerprintService.initializeConfiguration(config.id, config.strength);
}
@Override
diff --git a/services/core/java/com/android/server/biometrics/sensors/fingerprint/FingerprintService.java b/services/core/java/com/android/server/biometrics/sensors/fingerprint/FingerprintService.java
index 7c7da118df10..2f0e56447e20 100644
--- a/services/core/java/com/android/server/biometrics/sensors/fingerprint/FingerprintService.java
+++ b/services/core/java/com/android/server/biometrics/sensors/fingerprint/FingerprintService.java
@@ -384,7 +384,7 @@ public class FingerprintService extends SystemService {
}
@Override // Binder call
- public void initializeConfiguration(int sensorId) {
+ public void initializeConfiguration(int sensorId, int strength) {
Utils.checkPermission(getContext(), USE_BIOMETRIC_INTERNAL);
if ((Build.IS_USERDEBUG || Build.IS_ENG)
@@ -393,9 +393,9 @@ public class FingerprintService extends SystemService {
Fingerprint21UdfpsMock.CONFIG_ENABLE_TEST_UDFPS, 0 /* default */,
UserHandle.USER_CURRENT) != 0) {
mFingerprint21 = Fingerprint21UdfpsMock.newInstance(getContext(), sensorId,
- mLockoutResetDispatcher, mGestureAvailabilityDispatcher);
+ strength, mLockoutResetDispatcher, mGestureAvailabilityDispatcher);
} else {
- mFingerprint21 = Fingerprint21.newInstance(getContext(), sensorId,
+ mFingerprint21 = Fingerprint21.newInstance(getContext(), sensorId, strength,
mLockoutResetDispatcher, mGestureAvailabilityDispatcher);
}
}
diff --git a/services/core/java/com/android/server/connectivity/KeepaliveTracker.java b/services/core/java/com/android/server/connectivity/KeepaliveTracker.java
index 1f0066a43538..01fa9e755bd6 100644
--- a/services/core/java/com/android/server/connectivity/KeepaliveTracker.java
+++ b/services/core/java/com/android/server/connectivity/KeepaliveTracker.java
@@ -367,6 +367,13 @@ public class KeepaliveTracker {
Log.e(TAG, "Cannot stop unowned keepalive " + mSlot + " on " + mNai.network);
}
}
+ // Ignore the case when the network disconnects immediately after stop() has been
+ // called and the keepalive code is waiting for the response from the modem. This
+ // might happen when the caller listens for a lower-layer network disconnect
+ // callback and stop the keepalive at that time. But the stop() races with the
+ // stop() generated in ConnectivityService network disconnection code path.
+ if (mStartedState == STOPPING && reason == ERROR_INVALID_NETWORK) return;
+
// Store the reason of stopping, and report it after the keepalive is fully stopped.
if (mStopReason != ERROR_STOP_REASON_UNINITIALIZED) {
throw new IllegalStateException("Unexpected stop reason: " + mStopReason);
@@ -380,9 +387,6 @@ public class KeepaliveTracker {
// e.g. invalid parameter.
cleanupStoppedKeepalive(mNai, mSlot);
break;
- case STOPPING:
- // Keepalive is already in stopping state, ignore.
- return;
default:
mStartedState = STOPPING;
switch (mType) {
diff --git a/services/core/java/com/android/server/connectivity/Vpn.java b/services/core/java/com/android/server/connectivity/Vpn.java
index 7175489614ea..99dc58e20209 100644
--- a/services/core/java/com/android/server/connectivity/Vpn.java
+++ b/services/core/java/com/android/server/connectivity/Vpn.java
@@ -256,8 +256,8 @@ public class Vpn {
@GuardedBy("this")
private final Set<UidRange> mBlockedUidsAsToldToNetd = new ArraySet<>();
- // Handle of the user initiating VPN.
- private final int mUserHandle;
+ // The user id of initiating VPN.
+ private final int mUserId;
interface RetryScheduler {
void checkInterruptAndDelay(boolean sleepLonger) throws InterruptedException;
@@ -384,26 +384,26 @@ public class Vpn {
}
public Vpn(Looper looper, Context context, INetworkManagementService netService,
- @UserIdInt int userHandle, @NonNull KeyStore keyStore) {
- this(looper, context, new Dependencies(), netService, userHandle, keyStore,
+ @UserIdInt int userId, @NonNull KeyStore keyStore) {
+ this(looper, context, new Dependencies(), netService, userId, keyStore,
new SystemServices(context), new Ikev2SessionCreator());
}
@VisibleForTesting
protected Vpn(Looper looper, Context context, Dependencies deps,
INetworkManagementService netService,
- int userHandle, @NonNull KeyStore keyStore, SystemServices systemServices,
+ int userId, @NonNull KeyStore keyStore, SystemServices systemServices,
Ikev2SessionCreator ikev2SessionCreator) {
mContext = context;
mDeps = deps;
mNetd = netService;
- mUserHandle = userHandle;
+ mUserId = userId;
mLooper = looper;
mSystemServices = systemServices;
mIkev2SessionCreator = ikev2SessionCreator;
mPackage = VpnConfig.LEGACY_VPN;
- mOwnerUID = getAppUid(mPackage, mUserHandle);
+ mOwnerUID = getAppUid(mPackage, mUserId);
mIsPackageTargetingAtLeastQ = doesPackageTargetAtLeastQ(mPackage);
try {
@@ -613,7 +613,7 @@ public class Vpn {
PackageManager pm = mContext.getPackageManager();
ApplicationInfo appInfo = null;
try {
- appInfo = pm.getApplicationInfoAsUser(packageName, 0 /*flags*/, mUserHandle);
+ appInfo = pm.getApplicationInfoAsUser(packageName, 0 /*flags*/, mUserId);
} catch (NameNotFoundException unused) {
Log.w(TAG, "Can't find \"" + packageName + "\" when checking always-on support");
}
@@ -624,7 +624,7 @@ public class Vpn {
final Intent intent = new Intent(VpnConfig.SERVICE_INTERFACE);
intent.setPackage(packageName);
List<ResolveInfo> services =
- pm.queryIntentServicesAsUser(intent, PackageManager.GET_META_DATA, mUserHandle);
+ pm.queryIntentServicesAsUser(intent, PackageManager.GET_META_DATA, mUserId);
if (services == null || services.size() == 0) {
return false;
}
@@ -769,12 +769,12 @@ public class Vpn {
final long token = Binder.clearCallingIdentity();
try {
mSystemServices.settingsSecurePutStringForUser(Settings.Secure.ALWAYS_ON_VPN_APP,
- getAlwaysOnPackage(), mUserHandle);
+ getAlwaysOnPackage(), mUserId);
mSystemServices.settingsSecurePutIntForUser(Settings.Secure.ALWAYS_ON_VPN_LOCKDOWN,
- (mAlwaysOn && mLockdown ? 1 : 0), mUserHandle);
+ (mAlwaysOn && mLockdown ? 1 : 0), mUserId);
mSystemServices.settingsSecurePutStringForUser(
LOCKDOWN_ALLOWLIST_SETTING_NAME,
- String.join(",", mLockdownAllowlist), mUserHandle);
+ String.join(",", mLockdownAllowlist), mUserId);
} finally {
Binder.restoreCallingIdentity(token);
}
@@ -786,11 +786,11 @@ public class Vpn {
final long token = Binder.clearCallingIdentity();
try {
final String alwaysOnPackage = mSystemServices.settingsSecureGetStringForUser(
- Settings.Secure.ALWAYS_ON_VPN_APP, mUserHandle);
+ Settings.Secure.ALWAYS_ON_VPN_APP, mUserId);
final boolean alwaysOnLockdown = mSystemServices.settingsSecureGetIntForUser(
- Settings.Secure.ALWAYS_ON_VPN_LOCKDOWN, 0 /*default*/, mUserHandle) != 0;
+ Settings.Secure.ALWAYS_ON_VPN_LOCKDOWN, 0 /*default*/, mUserId) != 0;
final String allowlistString = mSystemServices.settingsSecureGetStringForUser(
- LOCKDOWN_ALLOWLIST_SETTING_NAME, mUserHandle);
+ LOCKDOWN_ALLOWLIST_SETTING_NAME, mUserId);
final List<String> allowedPackages = TextUtils.isEmpty(allowlistString)
? Collections.emptyList() : Arrays.asList(allowlistString.split(","));
setAlwaysOnPackageInternal(
@@ -850,13 +850,13 @@ public class Vpn {
DeviceIdleInternal idleController =
LocalServices.getService(DeviceIdleInternal.class);
idleController.addPowerSaveTempWhitelistApp(Process.myUid(), alwaysOnPackage,
- VPN_LAUNCH_IDLE_ALLOWLIST_DURATION_MS, mUserHandle, false, "vpn");
+ VPN_LAUNCH_IDLE_ALLOWLIST_DURATION_MS, mUserId, false, "vpn");
// Start the VPN service declared in the app's manifest.
Intent serviceIntent = new Intent(VpnConfig.SERVICE_INTERFACE);
serviceIntent.setPackage(alwaysOnPackage);
try {
- return mContext.startServiceAsUser(serviceIntent, UserHandle.of(mUserHandle)) != null;
+ return mContext.startServiceAsUser(serviceIntent, UserHandle.of(mUserId)) != null;
} catch (RuntimeException e) {
Log.e(TAG, "VpnService " + serviceIntent + " failed to start", e);
return false;
@@ -958,7 +958,7 @@ public class Vpn {
// We can't just check that packageName matches mPackage, because if the app was uninstalled
// and reinstalled it will no longer be prepared. Similarly if there is a shared UID, the
// calling package may not be the same as the prepared package. Check both UID and package.
- return getAppUid(packageName, mUserHandle) == mOwnerUID && mPackage.equals(packageName);
+ return getAppUid(packageName, mUserId) == mOwnerUID && mPackage.equals(packageName);
}
/** Prepare the VPN for the given package. Does not perform permission checks. */
@@ -998,7 +998,7 @@ public class Vpn {
Log.i(TAG, "Switched from " + mPackage + " to " + newPackage);
mPackage = newPackage;
- mOwnerUID = getAppUid(newPackage, mUserHandle);
+ mOwnerUID = getAppUid(newPackage, mUserId);
mIsPackageTargetingAtLeastQ = doesPackageTargetAtLeastQ(newPackage);
try {
mNetd.allowProtect(mOwnerUID);
@@ -1019,7 +1019,7 @@ public class Vpn {
// Check if the caller is authorized.
enforceControlPermissionOrInternalCaller();
- final int uid = getAppUid(packageName, mUserHandle);
+ final int uid = getAppUid(packageName, mUserId);
if (uid == -1 || VpnConfig.LEGACY_VPN.equals(packageName)) {
// Authorization for nonexistent packages (or fake ones) can't be updated.
return false;
@@ -1095,14 +1095,14 @@ public class Vpn {
|| isVpnServicePreConsented(context, packageName);
}
- private int getAppUid(final String app, final int userHandle) {
+ private int getAppUid(final String app, final int userId) {
if (VpnConfig.LEGACY_VPN.equals(app)) {
return Process.myUid();
}
PackageManager pm = mContext.getPackageManager();
return Binder.withCleanCallingIdentity(() -> {
try {
- return pm.getPackageUidAsUser(app, userHandle);
+ return pm.getPackageUidAsUser(app, userId);
} catch (NameNotFoundException e) {
return -1;
}
@@ -1116,7 +1116,7 @@ public class Vpn {
PackageManager pm = mContext.getPackageManager();
try {
ApplicationInfo appInfo =
- pm.getApplicationInfoAsUser(packageName, 0 /*flags*/, mUserHandle);
+ pm.getApplicationInfoAsUser(packageName, 0 /*flags*/, mUserId);
return appInfo.targetSdkVersion >= VERSION_CODES.Q;
} catch (NameNotFoundException unused) {
Log.w(TAG, "Can't find \"" + packageName + "\"");
@@ -1241,7 +1241,7 @@ public class Vpn {
mNetworkCapabilities.setOwnerUid(mOwnerUID);
mNetworkCapabilities.setAdministratorUids(new int[] {mOwnerUID});
- mNetworkCapabilities.setUids(createUserAndRestrictedProfilesRanges(mUserHandle,
+ mNetworkCapabilities.setUids(createUserAndRestrictedProfilesRanges(mUserId,
mConfig.allowedApplications, mConfig.disallowedApplications));
long token = Binder.clearCallingIdentity();
try {
@@ -1315,7 +1315,7 @@ public class Vpn {
enforceNotRestrictedUser();
ResolveInfo info = AppGlobals.getPackageManager().resolveService(intent,
- null, 0, mUserHandle);
+ null, 0, mUserId);
if (info == null) {
throw new SecurityException("Cannot find " + config.user);
}
@@ -1352,7 +1352,7 @@ public class Vpn {
Connection connection = new Connection();
if (!mContext.bindServiceAsUser(intent, connection,
Context.BIND_AUTO_CREATE | Context.BIND_FOREGROUND_SERVICE,
- new UserHandle(mUserHandle))) {
+ new UserHandle(mUserId))) {
throw new IllegalStateException("Cannot bind " + config.user);
}
@@ -1427,10 +1427,10 @@ public class Vpn {
}
// Note: Return type guarantees results are deduped and sorted, which callers require.
- private SortedSet<Integer> getAppsUids(List<String> packageNames, int userHandle) {
+ private SortedSet<Integer> getAppsUids(List<String> packageNames, int userId) {
SortedSet<Integer> uids = new TreeSet<>();
for (String app : packageNames) {
- int uid = getAppUid(app, userHandle);
+ int uid = getAppUid(app, userId);
if (uid != -1) uids.add(uid);
}
return uids;
@@ -1444,22 +1444,22 @@ public class Vpn {
* the UID ranges will match the app list specified there. Otherwise, all UIDs
* in each user and profile will be included.
*
- * @param userHandle The userId to create UID ranges for along with any of its restricted
+ * @param userId The userId to create UID ranges for along with any of its restricted
* profiles.
* @param allowedApplications (optional) List of applications to allow.
* @param disallowedApplications (optional) List of applications to deny.
*/
@VisibleForTesting
- Set<UidRange> createUserAndRestrictedProfilesRanges(@UserIdInt int userHandle,
+ Set<UidRange> createUserAndRestrictedProfilesRanges(@UserIdInt int userId,
@Nullable List<String> allowedApplications,
@Nullable List<String> disallowedApplications) {
final Set<UidRange> ranges = new ArraySet<>();
// Assign the top-level user to the set of ranges
- addUserToRanges(ranges, userHandle, allowedApplications, disallowedApplications);
+ addUserToRanges(ranges, userId, allowedApplications, disallowedApplications);
// If the user can have restricted profiles, assign all its restricted profiles too
- if (canHaveRestrictedProfile(userHandle)) {
+ if (canHaveRestrictedProfile(userId)) {
final long token = Binder.clearCallingIdentity();
List<UserInfo> users;
try {
@@ -1468,7 +1468,7 @@ public class Vpn {
Binder.restoreCallingIdentity(token);
}
for (UserInfo user : users) {
- if (user.isRestricted() && (user.restrictedProfileParentId == userHandle)) {
+ if (user.isRestricted() && (user.restrictedProfileParentId == userId)) {
addUserToRanges(ranges, user.id, allowedApplications, disallowedApplications);
}
}
@@ -1485,18 +1485,18 @@ public class Vpn {
* in the user will be included.
*
* @param ranges {@link Set} of {@link UidRange}s to which to add.
- * @param userHandle The userId to add to {@param ranges}.
+ * @param userId The userId to add to {@param ranges}.
* @param allowedApplications (optional) allowlist of applications to include.
* @param disallowedApplications (optional) denylist of applications to exclude.
*/
@VisibleForTesting
- void addUserToRanges(@NonNull Set<UidRange> ranges, @UserIdInt int userHandle,
+ void addUserToRanges(@NonNull Set<UidRange> ranges, @UserIdInt int userId,
@Nullable List<String> allowedApplications,
@Nullable List<String> disallowedApplications) {
if (allowedApplications != null) {
// Add ranges covering all UIDs for allowedApplications.
int start = -1, stop = -1;
- for (int uid : getAppsUids(allowedApplications, userHandle)) {
+ for (int uid : getAppsUids(allowedApplications, userId)) {
if (start == -1) {
start = uid;
} else if (uid != stop + 1) {
@@ -1508,9 +1508,9 @@ public class Vpn {
if (start != -1) ranges.add(new UidRange(start, stop));
} else if (disallowedApplications != null) {
// Add all ranges for user skipping UIDs for disallowedApplications.
- final UidRange userRange = UidRange.createForUser(userHandle);
+ final UidRange userRange = UidRange.createForUser(userId);
int start = userRange.start;
- for (int uid : getAppsUids(disallowedApplications, userHandle)) {
+ for (int uid : getAppsUids(disallowedApplications, userId)) {
if (uid == start) {
start++;
} else {
@@ -1521,16 +1521,16 @@ public class Vpn {
if (start <= userRange.stop) ranges.add(new UidRange(start, userRange.stop));
} else {
// Add all UIDs for the user.
- ranges.add(UidRange.createForUser(userHandle));
+ ranges.add(UidRange.createForUser(userId));
}
}
// Returns the subset of the full list of active UID ranges the VPN applies to (mVpnUsers) that
- // apply to userHandle.
- static private List<UidRange> uidRangesForUser(int userHandle, Set<UidRange> existingRanges) {
+ // apply to userId.
+ private static List<UidRange> uidRangesForUser(int userId, Set<UidRange> existingRanges) {
// UidRange#createForUser returns the entire range of UIDs available to a macro-user.
// This is something like 0-99999 ; {@see UserHandle#PER_USER_RANGE}
- final UidRange userRange = UidRange.createForUser(userHandle);
+ final UidRange userRange = UidRange.createForUser(userId);
final List<UidRange> ranges = new ArrayList<>();
for (UidRange range : existingRanges) {
if (userRange.containsRange(range)) {
@@ -1545,15 +1545,15 @@ public class Vpn {
*
* <p>Should be called on primary ConnectivityService thread.
*/
- public void onUserAdded(int userHandle) {
+ public void onUserAdded(int userId) {
// If the user is restricted tie them to the parent user's VPN
- UserInfo user = UserManager.get(mContext).getUserInfo(userHandle);
- if (user.isRestricted() && user.restrictedProfileParentId == mUserHandle) {
+ UserInfo user = UserManager.get(mContext).getUserInfo(userId);
+ if (user.isRestricted() && user.restrictedProfileParentId == mUserId) {
synchronized(Vpn.this) {
final Set<UidRange> existingRanges = mNetworkCapabilities.getUids();
if (existingRanges != null) {
try {
- addUserToRanges(existingRanges, userHandle, mConfig.allowedApplications,
+ addUserToRanges(existingRanges, userId, mConfig.allowedApplications,
mConfig.disallowedApplications);
// ConnectivityService will call {@link #updateCapabilities} and apply
// those for VPN network.
@@ -1572,16 +1572,16 @@ public class Vpn {
*
* <p>Should be called on primary ConnectivityService thread.
*/
- public void onUserRemoved(int userHandle) {
+ public void onUserRemoved(int userId) {
// clean up if restricted
- UserInfo user = UserManager.get(mContext).getUserInfo(userHandle);
- if (user.isRestricted() && user.restrictedProfileParentId == mUserHandle) {
+ UserInfo user = UserManager.get(mContext).getUserInfo(userId);
+ if (user.isRestricted() && user.restrictedProfileParentId == mUserId) {
synchronized(Vpn.this) {
final Set<UidRange> existingRanges = mNetworkCapabilities.getUids();
if (existingRanges != null) {
try {
final List<UidRange> removedRanges =
- uidRangesForUser(userHandle, existingRanges);
+ uidRangesForUser(userId, existingRanges);
existingRanges.removeAll(removedRanges);
// ConnectivityService will call {@link #updateCapabilities} and
// apply those for VPN network.
@@ -1639,7 +1639,7 @@ public class Vpn {
final Set<UidRange> rangesToTellNetdToAdd;
if (enforce) {
final Set<UidRange> rangesThatShouldBeBlocked =
- createUserAndRestrictedProfilesRanges(mUserHandle,
+ createUserAndRestrictedProfilesRanges(mUserId,
/* allowedApplications */ null,
/* disallowedApplications */ exemptedPackages);
@@ -1909,7 +1909,7 @@ public class Vpn {
private void updateAlwaysOnNotification(DetailedState networkState) {
final boolean visible = (mAlwaysOn && networkState != DetailedState.CONNECTED);
- final UserHandle user = UserHandle.of(mUserHandle);
+ final UserHandle user = UserHandle.of(mUserId);
final long token = Binder.clearCallingIdentity();
try {
final NotificationManager notificationManager = NotificationManager.from(mContext);
@@ -2019,7 +2019,7 @@ public class Vpn {
private void enforceNotRestrictedUser() {
Binder.withCleanCallingIdentity(() -> {
final UserManager mgr = UserManager.get(mContext);
- final UserInfo user = mgr.getUserInfo(mUserHandle);
+ final UserInfo user = mgr.getUserInfo(mUserId);
if (user.isRestricted()) {
throw new SecurityException("Restricted users cannot configure VPNs");
@@ -2054,9 +2054,9 @@ public class Vpn {
public void startLegacyVpnPrivileged(VpnProfile profile, KeyStore keyStore,
LinkProperties egress) {
UserManager mgr = UserManager.get(mContext);
- UserInfo user = mgr.getUserInfo(mUserHandle);
+ UserInfo user = mgr.getUserInfo(mUserId);
if (user.isRestricted() || mgr.hasUserRestriction(UserManager.DISALLOW_CONFIG_VPN,
- new UserHandle(mUserHandle))) {
+ new UserHandle(mUserId))) {
throw new SecurityException("Restricted users cannot establish VPNs");
}
@@ -2984,14 +2984,14 @@ public class Vpn {
}
private void verifyCallingUidAndPackage(String packageName) {
- if (getAppUid(packageName, mUserHandle) != Binder.getCallingUid()) {
+ if (getAppUid(packageName, mUserId) != Binder.getCallingUid()) {
throw new SecurityException("Mismatched package and UID");
}
}
@VisibleForTesting
String getProfileNameForPackage(String packageName) {
- return Credentials.PLATFORM_VPN + mUserHandle + "_" + packageName;
+ return Credentials.PLATFORM_VPN + mUserId + "_" + packageName;
}
@VisibleForTesting
diff --git a/services/core/java/com/android/server/display/DisplayDeviceConfig.java b/services/core/java/com/android/server/display/DisplayDeviceConfig.java
index 0d824334d873..23e5a43a359f 100644
--- a/services/core/java/com/android/server/display/DisplayDeviceConfig.java
+++ b/services/core/java/com/android/server/display/DisplayDeviceConfig.java
@@ -18,6 +18,7 @@ package com.android.server.display;
import android.os.Environment;
import android.util.Slog;
+import android.view.DisplayAddress;
import com.android.server.display.config.DisplayConfiguration;
import com.android.server.display.config.NitsMap;
@@ -31,6 +32,7 @@ import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
+import java.util.Arrays;
import java.util.List;
import javax.xml.datatype.DatatypeConfigurationException;
@@ -45,7 +47,11 @@ public class DisplayDeviceConfig {
private static final String ETC_DIR = "etc";
private static final String DISPLAY_CONFIG_DIR = "displayconfig";
- private static final String CONFIG_FILE_FORMAT = "display_%d.xml";
+ private static final String CONFIG_FILE_FORMAT = "display_%s.xml";
+ private static final String PORT_SUFFIX_FORMAT = "port_%d";
+ private static final String STABLE_ID_SUFFIX_FORMAT = "id_%d";
+ private static final String NO_SUFFIX_FORMAT = "%d";
+ private static final long STABLE_FLAG = 1L << 62;
private float[] mNits;
private float[] mBrightness;
@@ -55,17 +61,43 @@ public class DisplayDeviceConfig {
/**
* Creates an instance for the specified display.
- *
+ * Tries to find a file with identifier in the following priority order:
+ * <ol>
+ * <li>physicalDisplayId</li>
+ * <li>physicalDisplayId without a stable flag (old system)</li>
+ * <li>portId</li>
+ * </ol>
* @param physicalDisplayId The display ID for which to load the configuration.
* @return A configuration instance for the specified display.
*/
public static DisplayDeviceConfig create(long physicalDisplayId) {
- final DisplayDeviceConfig config = new DisplayDeviceConfig();
- final String filename = String.format(CONFIG_FILE_FORMAT, physicalDisplayId);
+ DisplayDeviceConfig config;
+
+ // Create config using filename from physical ID (including "stable" bit).
+ config = getConfigFromSuffix(STABLE_ID_SUFFIX_FORMAT, physicalDisplayId);
+ if (config != null) {
+ return config;
+ }
+
+ // Create config using filename from physical ID (excluding "stable" bit).
+ final long withoutStableFlag = physicalDisplayId & ~STABLE_FLAG;
+ config = getConfigFromSuffix(NO_SUFFIX_FORMAT, withoutStableFlag);
+ if (config != null) {
+ return config;
+ }
+
+ // Create config using filename from port ID.
+ final DisplayAddress.Physical physicalAddress =
+ DisplayAddress.fromPhysicalDisplayId(physicalDisplayId);
+ int port = physicalAddress.getPort();
+ config = getConfigFromSuffix(PORT_SUFFIX_FORMAT, port);
+ if (config != null) {
+ return config;
+ }
+
+ // None of these files exist.
+ return null;
- config.initFromFile(Environment.buildPath(
- Environment.getProductDirectory(), ETC_DIR, DISPLAY_CONFIG_DIR, filename));
- return config;
}
/**
@@ -86,6 +118,30 @@ public class DisplayDeviceConfig {
return mBrightness;
}
+ @Override
+ public String toString() {
+ String str = "DisplayDeviceConfig{"
+ + "mBrightness=" + Arrays.toString(mBrightness)
+ + ", mNits=" + Arrays.toString(mNits)
+ + "}";
+ return str;
+ }
+
+ private static DisplayDeviceConfig getConfigFromSuffix(String suffixFormat, long idNumber) {
+
+ final String suffix = String.format(suffixFormat, idNumber);
+ final String filename = String.format(CONFIG_FILE_FORMAT, suffix);
+ final File filePath = Environment.buildPath(
+ Environment.getProductDirectory(), ETC_DIR, DISPLAY_CONFIG_DIR, filename);
+
+ if (filePath.exists()) {
+ final DisplayDeviceConfig config = new DisplayDeviceConfig();
+ config.initFromFile(filePath);
+ return config;
+ }
+ return null;
+ }
+
private void initFromFile(File configFile) {
if (!configFile.exists()) {
// Display configuration files aren't required to exist.
@@ -121,13 +177,13 @@ public class DisplayDeviceConfig {
if (i > 0) {
if (nits[i] < nits[i - 1]) {
Slog.e(TAG, "screenBrightnessMap must be non-decreasing, ignoring rest "
- + " of configuration. Nits: " + nits[i] + " < " + nits[i - 1]);
+ + " of configuration. Nits: " + nits[i] + " < " + nits[i - 1]);
return;
}
if (backlight[i] < backlight[i - 1]) {
Slog.e(TAG, "screenBrightnessMap must be non-decreasing, ignoring rest "
- + " of configuration. Value: " + backlight[i] + " < "
+ + " of configuration. Value: " + backlight[i] + " < "
+ backlight[i - 1]);
return;
}
diff --git a/services/core/java/com/android/server/display/DisplayManagerService.java b/services/core/java/com/android/server/display/DisplayManagerService.java
index 4a12ee71adbe..97c4cf531a53 100644
--- a/services/core/java/com/android/server/display/DisplayManagerService.java
+++ b/services/core/java/com/android/server/display/DisplayManagerService.java
@@ -1179,6 +1179,15 @@ public final class DisplayManagerService extends SystemService {
return mWideColorSpace.getId();
}
+ void setShouldAlwaysRespectAppRequestedModeInternal(boolean enabled) {
+ mDisplayModeDirector.setShouldAlwaysRespectAppRequestedMode(enabled);
+ }
+
+
+ boolean shouldAlwaysRespectAppRequestedModeInternal() {
+ return mDisplayModeDirector.shouldAlwaysRespectAppRequestedMode();
+ }
+
private void setBrightnessConfigurationForUserInternal(
@Nullable BrightnessConfiguration c, @UserIdInt int userId,
@Nullable String packageName) {
@@ -2463,6 +2472,32 @@ public final class DisplayManagerService extends SystemService {
}
}
+ @Override // Binder call
+ public void setShouldAlwaysRespectAppRequestedMode(boolean enabled) {
+ mContext.enforceCallingOrSelfPermission(
+ Manifest.permission.OVERRIDE_DISPLAY_MODE_REQUESTS,
+ "Permission required to override display mode requests.");
+ final long token = Binder.clearCallingIdentity();
+ try {
+ setShouldAlwaysRespectAppRequestedModeInternal(enabled);
+ } finally {
+ Binder.restoreCallingIdentity(token);
+ }
+ }
+
+ @Override // Binder call
+ public boolean shouldAlwaysRespectAppRequestedMode() {
+ mContext.enforceCallingOrSelfPermission(
+ Manifest.permission.OVERRIDE_DISPLAY_MODE_REQUESTS,
+ "Permission required to override display mode requests.");
+ final long token = Binder.clearCallingIdentity();
+ try {
+ return shouldAlwaysRespectAppRequestedModeInternal();
+ } finally {
+ Binder.restoreCallingIdentity(token);
+ }
+ }
+
private boolean validatePackageName(int uid, String packageName) {
if (packageName != null) {
String[] packageNames = mContext.getPackageManager().getPackagesForUid(uid);
diff --git a/services/core/java/com/android/server/display/DisplayModeDirector.java b/services/core/java/com/android/server/display/DisplayModeDirector.java
index b1c91a690d7f..4f5a0faee8dd 100644
--- a/services/core/java/com/android/server/display/DisplayModeDirector.java
+++ b/services/core/java/com/android/server/display/DisplayModeDirector.java
@@ -56,8 +56,8 @@ import java.util.List;
import java.util.Objects;
/**
- * The DisplayModeDirector is responsible for determining what modes are allowed to be
- * automatically picked by the system based on system-wide and display-specific configuration.
+ * The DisplayModeDirector is responsible for determining what modes are allowed to be automatically
+ * picked by the system based on system-wide and display-specific configuration.
*/
public class DisplayModeDirector {
private static final String TAG = "DisplayModeDirector";
@@ -97,17 +97,20 @@ public class DisplayModeDirector {
private final DeviceConfigDisplaySettings mDeviceConfigDisplaySettings;
private DesiredDisplayModeSpecsListener mDesiredDisplayModeSpecsListener;
+ private boolean mAlwaysRespectAppRequest;
+
public DisplayModeDirector(@NonNull Context context, @NonNull Handler handler) {
mContext = context;
mHandler = new DisplayModeDirectorHandler(handler.getLooper());
mVotesByDisplay = new SparseArray<>();
mSupportedModesByDisplay = new SparseArray<>();
- mDefaultModeByDisplay = new SparseArray<>();
+ mDefaultModeByDisplay = new SparseArray<>();
mAppRequestObserver = new AppRequestObserver();
mSettingsObserver = new SettingsObserver(context, handler);
mDisplayObserver = new DisplayObserver(context, handler);
mBrightnessObserver = new BrightnessObserver(context, handler);
mDeviceConfigDisplaySettings = new DeviceConfigDisplaySettings();
+ mAlwaysRespectAppRequest = false;
}
/**
@@ -127,7 +130,6 @@ public class DisplayModeDirector {
// notify them to pick up our newly initialized state.
notifyDesiredDisplayModeSpecsChangedLocked();
}
-
}
@NonNull
@@ -173,9 +175,14 @@ public class DisplayModeDirector {
// VoteSummary is returned as an output param to cut down a bit on the number of temporary
// objects.
private void summarizeVotes(
- SparseArray<Vote> votes, int lowestConsideredPriority, /*out*/ VoteSummary summary) {
+ SparseArray<Vote> votes,
+ int lowestConsideredPriority,
+ int highestConsideredPriority,
+ /*out*/ VoteSummary summary) {
summary.reset();
- for (int priority = Vote.MAX_PRIORITY; priority >= lowestConsideredPriority; priority--) {
+ for (int priority = highestConsideredPriority;
+ priority >= lowestConsideredPriority;
+ priority--) {
Vote vote = votes.get(priority);
if (vote == null) {
continue;
@@ -217,8 +224,16 @@ public class DisplayModeDirector {
int[] availableModes = new int[]{defaultMode.getModeId()};
VoteSummary primarySummary = new VoteSummary();
int lowestConsideredPriority = Vote.MIN_PRIORITY;
- while (lowestConsideredPriority <= Vote.MAX_PRIORITY) {
- summarizeVotes(votes, lowestConsideredPriority, primarySummary);
+ int highestConsideredPriority = Vote.MAX_PRIORITY;
+
+ if (mAlwaysRespectAppRequest) {
+ lowestConsideredPriority = Vote.PRIORITY_APP_REQUEST_REFRESH_RATE;
+ highestConsideredPriority = Vote.PRIORITY_APP_REQUEST_SIZE;
+ }
+
+ while (lowestConsideredPriority <= highestConsideredPriority) {
+ summarizeVotes(
+ votes, lowestConsideredPriority, highestConsideredPriority, primarySummary);
// If we don't have anything specifying the width / height of the display, just use
// the default width and height. We don't want these switching out from underneath
@@ -261,7 +276,10 @@ public class DisplayModeDirector {
VoteSummary appRequestSummary = new VoteSummary();
summarizeVotes(
- votes, Vote.APP_REQUEST_REFRESH_RATE_RANGE_PRIORITY_CUTOFF, appRequestSummary);
+ votes,
+ Vote.APP_REQUEST_REFRESH_RATE_RANGE_PRIORITY_CUTOFF,
+ Vote.MAX_PRIORITY,
+ appRequestSummary);
appRequestSummary.minRefreshRate =
Math.min(appRequestSummary.minRefreshRate, primarySummary.minRefreshRate);
appRequestSummary.maxRefreshRate =
@@ -338,8 +356,7 @@ public class DisplayModeDirector {
}
/**
- * Sets the desiredDisplayModeSpecsListener for changes to display mode and refresh rate
- * ranges.
+ * Sets the desiredDisplayModeSpecsListener for changes to display mode and refresh rate ranges.
*/
public void setDesiredDisplayModeSpecsListener(
@Nullable DesiredDisplayModeSpecsListener desiredDisplayModeSpecsListener) {
@@ -349,6 +366,26 @@ public class DisplayModeDirector {
}
/**
+ * When enabled the app requested display mode is always selected and all
+ * other votes will be ignored. This is used for testing purposes.
+ */
+ public void setShouldAlwaysRespectAppRequestedMode(boolean enabled) {
+ synchronized (mLock) {
+ mAlwaysRespectAppRequest = enabled;
+ }
+ }
+
+ /**
+ * Returns whether we are running in a mode which always selects the app requested display mode
+ * and ignores user settings and policies for low brightness, low battery etc.
+ */
+ public boolean shouldAlwaysRespectAppRequestedMode() {
+ synchronized (mLock) {
+ return mAlwaysRespectAppRequest;
+ }
+ }
+
+ /**
* Print the object's state and debug information into the given stream.
*
* @param pw The stream to dump information to.
@@ -380,6 +417,7 @@ public class DisplayModeDirector {
pw.println(" " + Vote.priorityToString(p) + " -> " + vote);
}
}
+ pw.println(" mAlwaysRespectAppRequest: " + mAlwaysRespectAppRequest);
mSettingsObserver.dumpLocked(pw);
mAppRequestObserver.dumpLocked(pw);
mBrightnessObserver.dumpLocked(pw);
diff --git a/services/core/java/com/android/server/display/LocalDisplayAdapter.java b/services/core/java/com/android/server/display/LocalDisplayAdapter.java
index 0be428bdba47..507a265f0203 100644
--- a/services/core/java/com/android/server/display/LocalDisplayAdapter.java
+++ b/services/core/java/com/android/server/display/LocalDisplayAdapter.java
@@ -391,11 +391,11 @@ final class LocalDisplayAdapter extends DisplayAdapter {
Spline sysToNits = null;
// Load the mapping from nits to HAL brightness range (display-device-config.xml)
- DisplayDeviceConfig config = DisplayDeviceConfig.create(mPhysicalDisplayId);
- mDisplayDeviceConfig = config;
- if (config == null) {
+ mDisplayDeviceConfig = DisplayDeviceConfig.create(mPhysicalDisplayId);
+ if (mDisplayDeviceConfig == null) {
return;
}
+
final float[] halNits = mDisplayDeviceConfig.getNits();
final float[] halBrightness = mDisplayDeviceConfig.getBrightness();
if (halNits == null || halBrightness == null) {
@@ -942,7 +942,8 @@ final class LocalDisplayAdapter extends DisplayAdapter {
for (int i = 0; i < mSupportedModes.size(); i++) {
pw.println(" " + mSupportedModes.valueAt(i));
}
- pw.print("mSupportedColorModes=" + mSupportedColorModes.toString());
+ pw.println("mSupportedColorModes=" + mSupportedColorModes.toString());
+ pw.print("mDisplayDeviceConfig=" + mDisplayDeviceConfig);
}
private int findDisplayConfigIdLocked(int modeId, int configGroup) {
diff --git a/services/core/java/com/android/server/hdmi/HdmiCecAtomWriter.java b/services/core/java/com/android/server/hdmi/HdmiCecAtomWriter.java
new file mode 100644
index 000000000000..2878a94fb860
--- /dev/null
+++ b/services/core/java/com/android/server/hdmi/HdmiCecAtomWriter.java
@@ -0,0 +1,192 @@
+/*
+ * 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.hdmi;
+
+import android.stats.hdmi.HdmiStatsEnums;
+
+import com.android.internal.util.FrameworkStatsLog;
+
+class HdmiCecAtomWriter {
+
+ private static final int FEATURE_ABORT_OPCODE_UNKNOWN = 0x100;
+ private static final int ERROR_CODE_UNKNOWN = -1;
+
+ /**
+ * Writes a HdmiCecMessageReported atom representing an HDMI CEC message.
+ * Should only be directly used for sent messages; for received messages,
+ * use the overloaded version with the errorCode argument omitted.
+ *
+ * @param message The HDMI CEC message
+ * @param direction Whether the message is incoming, outgoing, or neither
+ * @param errorCode The error code from the final attempt to send the message
+ */
+ public void messageReported(HdmiCecMessage message, int direction, int errorCode) {
+ MessageReportedGenericArgs genericArgs = createMessageReportedGenericArgs(
+ message, direction, errorCode);
+ MessageReportedSpecialArgs specialArgs = createMessageReportedSpecialArgs(message);
+ messageReportedBase(genericArgs, specialArgs);
+ }
+
+ /**
+ * Version of messageReported for received messages, where no error code is present.
+ *
+ * @param message The HDMI CEC message
+ * @param direction Whether the message is incoming, outgoing, or neither
+ */
+ public void messageReported(HdmiCecMessage message, int direction) {
+ messageReported(message, direction, ERROR_CODE_UNKNOWN);
+ }
+
+ /**
+ * Constructs the generic arguments for logging a HDMI CEC message.
+ *
+ * @param message The HDMI CEC message
+ * @param direction Whether the message is incoming, outgoing, or neither
+ * @param errorCode The error code of the message if it's outgoing;
+ * otherwise, ERROR_CODE_UNKNOWN
+ */
+ private MessageReportedGenericArgs createMessageReportedGenericArgs(
+ HdmiCecMessage message, int direction, int errorCode) {
+ int sendMessageResult = errorCode == ERROR_CODE_UNKNOWN
+ ? HdmiStatsEnums.SEND_MESSAGE_RESULT_UNKNOWN
+ : errorCode + 10;
+ return new MessageReportedGenericArgs(direction, message.getSource(),
+ message.getDestination(), message.getOpcode(), sendMessageResult);
+ }
+
+ /**
+ * Constructs the special arguments for logging an HDMI CEC message.
+ *
+ * @param message The HDMI CEC message to log
+ * @return An object containing the special arguments for the message
+ */
+ private MessageReportedSpecialArgs createMessageReportedSpecialArgs(HdmiCecMessage message) {
+ // Special arguments depend on message opcode
+ switch (message.getOpcode()) {
+ case Constants.MESSAGE_USER_CONTROL_PRESSED:
+ return createUserControlPressedSpecialArgs(message);
+ case Constants.MESSAGE_FEATURE_ABORT:
+ return createFeatureAbortSpecialArgs(message);
+ default:
+ return new MessageReportedSpecialArgs();
+ }
+ }
+
+ /**
+ * Constructs the special arguments for a <User Control Pressed> message.
+ *
+ * @param message The HDMI CEC message to log
+ */
+ private MessageReportedSpecialArgs createUserControlPressedSpecialArgs(
+ HdmiCecMessage message) {
+ MessageReportedSpecialArgs specialArgs = new MessageReportedSpecialArgs();
+
+ int keycode = message.getParams()[0];
+ if (keycode >= 0x1E && keycode <= 0x29) {
+ specialArgs.mUserControlPressedCommand = HdmiStatsEnums.NUMBER;
+ } else {
+ specialArgs.mUserControlPressedCommand = keycode + 0x100;
+ }
+
+ return specialArgs;
+ }
+
+ /**
+ * Constructs method for constructing the special arguments for a <Feature Abort> message.
+ *
+ * @param message The HDMI CEC message to log
+ */
+ private MessageReportedSpecialArgs createFeatureAbortSpecialArgs(HdmiCecMessage message) {
+ MessageReportedSpecialArgs specialArgs = new MessageReportedSpecialArgs();
+
+ specialArgs.mFeatureAbortOpcode = message.getParams()[0] & 0xFF; // Unsigned byte
+ specialArgs.mFeatureAbortReason = message.getParams()[1] + 10;
+
+ return specialArgs;
+ }
+
+ /**
+ * Writes a HdmiCecMessageReported atom.
+ *
+ * @param genericArgs Generic arguments; shared by all HdmiCecMessageReported atoms
+ * @param specialArgs Special arguments; depends on the opcode of the message
+ */
+ private void messageReportedBase(MessageReportedGenericArgs genericArgs,
+ MessageReportedSpecialArgs specialArgs) {
+ FrameworkStatsLog.write(
+ FrameworkStatsLog.HDMI_CEC_MESSAGE_REPORTED,
+ 0, // Placeholder field
+ genericArgs.mDirection,
+ genericArgs.mInitiatorLogicalAddress,
+ genericArgs.mDestinationLogicalAddress,
+ genericArgs.mOpcode,
+ genericArgs.mSendMessageResult,
+ specialArgs.mUserControlPressedCommand,
+ specialArgs.mFeatureAbortOpcode,
+ specialArgs.mFeatureAbortReason);
+ }
+
+
+ /**
+ * Writes a HdmiCecActiveSourceChanged atom representing a change in the active source.
+ *
+ * @param logicalAddress The Logical Address of the new active source
+ * @param physicalAddress The Physical Address of the new active source
+ * @param relationshipToActiveSource The relationship between this device and the active source
+ */
+ public void activeSourceChanged(int logicalAddress, int physicalAddress,
+ @Constants.PathRelationship int relationshipToActiveSource) {
+ FrameworkStatsLog.write(
+ FrameworkStatsLog.HDMI_CEC_ACTIVE_SOURCE_CHANGED,
+ logicalAddress,
+ physicalAddress,
+ relationshipToActiveSource
+ );
+ }
+
+ /**
+ * Contains the required arguments for creating any HdmiCecMessageReported atom
+ */
+ private class MessageReportedGenericArgs {
+ final int mDirection;
+ final int mInitiatorLogicalAddress;
+ final int mDestinationLogicalAddress;
+ final int mOpcode;
+ final int mSendMessageResult;
+
+ MessageReportedGenericArgs(int direction, int initiatorLogicalAddress,
+ int destinationLogicalAddress, int opcode, int sendMessageResult) {
+ this.mDirection = direction;
+ this.mInitiatorLogicalAddress = initiatorLogicalAddress;
+ this.mDestinationLogicalAddress = destinationLogicalAddress;
+ this.mOpcode = opcode;
+ this.mSendMessageResult = sendMessageResult;
+ }
+ }
+
+ /**
+ * Contains the opcode-dependent arguments for creating a HdmiCecMessageReported atom. Each
+ * field is initialized to a null-like value by default. Therefore, a freshly constructed
+ * instance of this object represents a HDMI CEC message whose type does not require any
+ * additional arguments.
+ */
+ private class MessageReportedSpecialArgs {
+ int mUserControlPressedCommand = HdmiStatsEnums.USER_CONTROL_PRESSED_COMMAND_UNKNOWN;
+ int mFeatureAbortOpcode = FEATURE_ABORT_OPCODE_UNKNOWN;
+ int mFeatureAbortReason = HdmiStatsEnums.FEATURE_ABORT_REASON_UNKNOWN;
+ }
+}
diff --git a/services/core/java/com/android/server/hdmi/HdmiCecController.java b/services/core/java/com/android/server/hdmi/HdmiCecController.java
index 7dc4d6e8efcf..bb6f14c0f6c4 100644
--- a/services/core/java/com/android/server/hdmi/HdmiCecController.java
+++ b/services/core/java/com/android/server/hdmi/HdmiCecController.java
@@ -28,9 +28,11 @@ import android.os.Handler;
import android.os.IHwBinder;
import android.os.Looper;
import android.os.RemoteException;
+import android.stats.hdmi.HdmiStatsEnums;
import android.util.Slog;
import android.util.SparseArray;
+import com.android.internal.util.FrameworkStatsLog;
import com.android.internal.util.IndentingPrintWriter;
import com.android.server.hdmi.HdmiAnnotations.IoThreadOnly;
import com.android.server.hdmi.HdmiAnnotations.ServiceThreadOnly;
@@ -123,10 +125,14 @@ final class HdmiCecController {
private final NativeWrapper mNativeWrapperImpl;
+ private final HdmiCecAtomWriter mHdmiCecAtomWriter;
+
// Private constructor. Use HdmiCecController.create().
- private HdmiCecController(HdmiControlService service, NativeWrapper nativeWrapper) {
+ private HdmiCecController(
+ HdmiControlService service, NativeWrapper nativeWrapper, HdmiCecAtomWriter atomWriter) {
mService = service;
mNativeWrapperImpl = nativeWrapper;
+ mHdmiCecAtomWriter = atomWriter;
}
/**
@@ -134,21 +140,22 @@ final class HdmiCecController {
* inner device or has no device it will return {@code null}.
*
* <p>Declared as package-private, accessed by {@link HdmiControlService} only.
- * @param service {@link HdmiControlService} instance used to create internal handler
- * and to pass callback for incoming message or event.
+ * @param service {@link HdmiControlService} instance used to create internal handler
+ * and to pass callback for incoming message or event.
+ * @param atomWriter {@link HdmiCecAtomWriter} instance for writing atoms for metrics.
* @return {@link HdmiCecController} if device is initialized successfully. Otherwise,
* returns {@code null}.
*/
- static HdmiCecController create(HdmiControlService service) {
- return createWithNativeWrapper(service, new NativeWrapperImpl());
+ static HdmiCecController create(HdmiControlService service, HdmiCecAtomWriter atomWriter) {
+ return createWithNativeWrapper(service, new NativeWrapperImpl(), atomWriter);
}
/**
* A factory method with injection of native methods for testing.
*/
static HdmiCecController createWithNativeWrapper(
- HdmiControlService service, NativeWrapper nativeWrapper) {
- HdmiCecController controller = new HdmiCecController(service, nativeWrapper);
+ HdmiControlService service, NativeWrapper nativeWrapper, HdmiCecAtomWriter atomWriter) {
+ HdmiCecController controller = new HdmiCecController(service, nativeWrapper, atomWriter);
String nativePtr = nativeWrapper.nativeInit();
if (nativePtr == null) {
HdmiLogger.warning("Couldn't get tv.cec service.");
@@ -619,7 +626,7 @@ final class HdmiCecController {
public void run() {
HdmiLogger.debug("[S]:" + cecMessage);
byte[] body = buildBody(cecMessage.getOpcode(), cecMessage.getParams());
- int i = 0;
+ int retransmissionCount = 0;
int errorCode = SendMessageResult.SUCCESS;
do {
errorCode = mNativeWrapperImpl.nativeSendCecCommand(
@@ -627,20 +634,25 @@ final class HdmiCecController {
if (errorCode == SendMessageResult.SUCCESS) {
break;
}
- } while (i++ < HdmiConfig.RETRANSMISSION_COUNT);
+ } while (retransmissionCount++ < HdmiConfig.RETRANSMISSION_COUNT);
final int finalError = errorCode;
if (finalError != SendMessageResult.SUCCESS) {
Slog.w(TAG, "Failed to send " + cecMessage + " with errorCode=" + finalError);
}
- if (callback != null) {
- runOnServiceThread(new Runnable() {
- @Override
- public void run() {
+ runOnServiceThread(new Runnable() {
+ @Override
+ public void run() {
+ mHdmiCecAtomWriter.messageReported(
+ cecMessage,
+ FrameworkStatsLog.HDMI_CEC_MESSAGE_REPORTED__DIRECTION__OUTGOING,
+ finalError
+ );
+ if (callback != null) {
callback.onSendCompleted(finalError);
}
- });
- }
+ }
+ });
}
});
}
@@ -654,10 +666,39 @@ final class HdmiCecController {
HdmiCecMessage command = HdmiCecMessageBuilder.of(srcAddress, dstAddress, body);
HdmiLogger.debug("[R]:" + command);
addCecMessageToHistory(true /* isReceived */, command);
+
+ mHdmiCecAtomWriter.messageReported(command,
+ incomingMessageDirection(srcAddress, dstAddress));
+
onReceiveCommand(command);
}
/**
+ * Computes the direction of an incoming message, as implied by the source and
+ * destination addresses. This will usually return INCOMING; if not, it can indicate a bug.
+ */
+ private int incomingMessageDirection(int srcAddress, int dstAddress) {
+ boolean sourceIsLocal = false;
+ boolean destinationIsLocal = false;
+ for (HdmiCecLocalDevice localDevice : getLocalDeviceList()) {
+ int logicalAddress = localDevice.getDeviceInfo().getLogicalAddress();
+ if (logicalAddress == srcAddress) {
+ sourceIsLocal = true;
+ }
+ if (logicalAddress == dstAddress) {
+ destinationIsLocal = true;
+ }
+ }
+
+ if (!sourceIsLocal && destinationIsLocal) {
+ return HdmiStatsEnums.INCOMING;
+ } else if (sourceIsLocal && destinationIsLocal) {
+ return HdmiStatsEnums.TO_SELF;
+ }
+ return HdmiStatsEnums.MESSAGE_DIRECTION_OTHER;
+ }
+
+ /**
* Called when a hotplug event issues.
*/
@ServiceThreadOnly
diff --git a/services/core/java/com/android/server/hdmi/HdmiCecMessageValidator.java b/services/core/java/com/android/server/hdmi/HdmiCecMessageValidator.java
index 0b4f31d365d3..28bd97e4843c 100644
--- a/services/core/java/com/android/server/hdmi/HdmiCecMessageValidator.java
+++ b/services/core/java/com/android/server/hdmi/HdmiCecMessageValidator.java
@@ -117,14 +117,15 @@ public class HdmiCecMessageValidator {
// TODO: Validate more than length for the following messages.
// Messages for the One Touch Record.
- FixedLengthValidator oneByteValidator = new FixedLengthValidator(1);
addValidationInfo(Constants.MESSAGE_RECORD_ON,
new VariableLengthValidator(1, 8), DEST_DIRECT);
- addValidationInfo(Constants.MESSAGE_RECORD_STATUS, oneByteValidator, DEST_DIRECT);
+ addValidationInfo(Constants.MESSAGE_RECORD_STATUS,
+ new RecordStatusInfoValidator(), DEST_DIRECT);
// TODO: Handle messages for the Timer Programming.
// Messages for the System Information.
+ FixedLengthValidator oneByteValidator = new FixedLengthValidator(1);
addValidationInfo(Constants.MESSAGE_CEC_VERSION, oneByteValidator, DEST_DIRECT);
addValidationInfo(Constants.MESSAGE_SET_MENU_LANGUAGE,
new FixedLengthValidator(3), DEST_BROADCAST);
@@ -339,4 +340,23 @@ public class HdmiCecMessageValidator {
isValidPhysicalAddress(params, 0) && isValidPhysicalAddress(params, 2));
}
}
+
+ /**
+ * Check if the given record status message parameter is valid.
+ * A valid parameter should lie within the range description of Record Status Info defined in
+ * CEC 1.4 Specification : Operand Descriptions (Section 17)
+ */
+ private class RecordStatusInfoValidator implements ParameterValidator {
+ @Override
+ public int isValid(byte[] params) {
+ if (params.length < 1) {
+ return ERROR_PARAMETER_SHORT;
+ }
+ return toErrorCode(isWithinRange(params[0], 0x01, 0x07)
+ || isWithinRange(params[0], 0x09, 0x0E)
+ || isWithinRange(params[0], 0x10, 0x17)
+ || isWithinRange(params[0], 0x1A, 0x1B)
+ || params[0] == 0x1F);
+ }
+ }
}
diff --git a/services/core/java/com/android/server/hdmi/HdmiControlService.java b/services/core/java/com/android/server/hdmi/HdmiControlService.java
index 44b6a63faea1..b407234457a4 100644
--- a/services/core/java/com/android/server/hdmi/HdmiControlService.java
+++ b/services/core/java/com/android/server/hdmi/HdmiControlService.java
@@ -397,6 +397,10 @@ public class HdmiControlService extends SystemService {
// Set to true if the logical address allocation is completed.
private boolean mAddressAllocated = false;
+ // Object that handles logging statsd atoms.
+ // Use getAtomWriter() instead of accessing directly, to allow dependency injection for testing.
+ private HdmiCecAtomWriter mAtomWriter = new HdmiCecAtomWriter();
+
// Buffer for processing the incoming cec messages while allocating logical addresses.
private final class CecMessageBuffer {
private List<HdmiCecMessage> mBuffer = new ArrayList<>();
@@ -509,7 +513,7 @@ public class HdmiControlService extends SystemService {
mMhlInputChangeEnabled = readBooleanSetting(Global.MHL_INPUT_SWITCHING_ENABLED, true);
if (mCecController == null) {
- mCecController = HdmiCecController.create(this);
+ mCecController = HdmiCecController.create(this, getAtomWriter());
}
if (mCecController != null) {
if (mHdmiControlEnabled) {
@@ -3233,6 +3237,10 @@ public class HdmiControlService extends SystemService {
mActiveSource.logicalAddress = logicalAddress;
mActiveSource.physicalAddress = physicalAddress;
}
+
+ getAtomWriter().activeSourceChanged(logicalAddress, physicalAddress,
+ HdmiUtils.pathRelationship(getPhysicalAddress(), physicalAddress));
+
// If the current device is a source device, check if the current Active Source matches
// the local device info. Set mIsActiveSource of the local device accordingly.
for (HdmiCecLocalDevice device : getAllLocalDevices()) {
@@ -3363,6 +3371,11 @@ public class HdmiControlService extends SystemService {
}
}
+ @VisibleForTesting
+ HdmiCecAtomWriter getAtomWriter() {
+ return mAtomWriter;
+ }
+
boolean isMhlInputChangeEnabled() {
synchronized (mLock) {
return mMhlInputChangeEnabled;
diff --git a/services/core/java/com/android/server/input/InputManagerService.java b/services/core/java/com/android/server/input/InputManagerService.java
index 6c14b2cbed09..7c98c6c7947b 100644
--- a/services/core/java/com/android/server/input/InputManagerService.java
+++ b/services/core/java/com/android/server/input/InputManagerService.java
@@ -219,10 +219,10 @@ public class InputManagerService extends IInputManager.Stub
int deviceId, int sourceMask, int sw);
private static native boolean nativeHasKeys(long ptr,
int deviceId, int sourceMask, int[] keyCodes, boolean[] keyExists);
- private static native void nativeRegisterInputChannel(long ptr, InputChannel inputChannel);
- private static native void nativeRegisterInputMonitor(long ptr, InputChannel inputChannel,
- int displayId, boolean isGestureMonitor);
- private static native void nativeUnregisterInputChannel(long ptr, IBinder connectionToken);
+ private static native InputChannel nativeCreateInputChannel(long ptr, String name);
+ private static native InputChannel nativeCreateInputMonitor(long ptr, int displayId,
+ boolean isGestureMonitor, String name);
+ private static native void nativeRemoveInputChannel(long ptr, IBinder connectionToken);
private static native void nativePilferPointers(long ptr, IBinder token);
private static native void nativeSetInputFilterEnabled(long ptr, boolean enable);
private static native void nativeSetInTouchMode(long ptr, boolean inTouchMode);
@@ -522,10 +522,8 @@ public class InputManagerService extends IInputManager.Stub
throw new IllegalArgumentException("displayId must >= 0.");
}
- InputChannel[] inputChannels = InputChannel.openInputChannelPair(inputChannelName);
- nativeRegisterInputMonitor(mPtr, inputChannels[0], displayId, false /*isGestureMonitor*/);
- inputChannels[0].dispose(); // don't need to retain the Java object reference
- return inputChannels[1];
+ return nativeCreateInputMonitor(mPtr, displayId, false /* isGestureMonitor */,
+ inputChannelName);
}
/**
@@ -552,38 +550,32 @@ public class InputManagerService extends IInputManager.Stub
final long ident = Binder.clearCallingIdentity();
try {
- InputChannel[] inputChannels = InputChannel.openInputChannelPair(inputChannelName);
- InputMonitorHost host = new InputMonitorHost(inputChannels[0]);
- nativeRegisterInputMonitor(
- mPtr, inputChannels[0], displayId, true /*isGestureMonitor*/);
+ InputChannel inputChannel = nativeCreateInputMonitor(
+ mPtr, displayId, true /*isGestureMonitor*/, inputChannelName);
+ InputMonitorHost host = new InputMonitorHost(inputChannel.getToken());
synchronized (mGestureMonitorPidsLock) {
- mGestureMonitorPidsByToken.put(inputChannels[1].getToken(), pid);
+ mGestureMonitorPidsByToken.put(inputChannel.getToken(), pid);
}
- return new InputMonitor(inputChannels[1], host);
+ return new InputMonitor(inputChannel, host);
} finally {
Binder.restoreCallingIdentity(ident);
}
}
/**
- * Registers an input channel so that it can be used as an input event target. The channel is
- * registered with a generated token.
+ * Creates an input channel to be used as an input event target.
*
- * @param inputChannel The input channel to register.
+ * @param name The name of this input channel
*/
- public void registerInputChannel(InputChannel inputChannel) {
- if (inputChannel == null) {
- throw new IllegalArgumentException("inputChannel must not be null.");
- }
-
- nativeRegisterInputChannel(mPtr, inputChannel);
+ public InputChannel createInputChannel(String name) {
+ return nativeCreateInputChannel(mPtr, name);
}
/**
- * Unregisters an input channel.
+ * Removes an input channel.
* @param connectionToken The input channel to unregister.
*/
- public void unregisterInputChannel(IBinder connectionToken) {
+ public void removeInputChannel(IBinder connectionToken) {
if (connectionToken == null) {
throw new IllegalArgumentException("connectionToken must not be null.");
}
@@ -591,7 +583,7 @@ public class InputManagerService extends IInputManager.Stub
mGestureMonitorPidsByToken.remove(connectionToken);
}
- nativeUnregisterInputChannel(mPtr, connectionToken);
+ nativeRemoveInputChannel(mPtr, connectionToken);
}
/**
@@ -2442,21 +2434,20 @@ public class InputManagerService extends IInputManager.Stub
* Interface for the system to handle request from InputMonitors.
*/
private final class InputMonitorHost extends IInputMonitorHost.Stub {
- private final InputChannel mInputChannel;
+ private final IBinder mToken;
- InputMonitorHost(InputChannel channel) {
- mInputChannel = channel;
+ InputMonitorHost(IBinder token) {
+ mToken = token;
}
@Override
public void pilferPointers() {
- nativePilferPointers(mPtr, mInputChannel.getToken());
+ nativePilferPointers(mPtr, mToken);
}
@Override
public void dispose() {
- nativeUnregisterInputChannel(mPtr, mInputChannel.getToken());
- mInputChannel.dispose();
+ nativeRemoveInputChannel(mPtr, mToken);
}
}
diff --git a/services/core/java/com/android/server/location/AbstractLocationProvider.java b/services/core/java/com/android/server/location/AbstractLocationProvider.java
index 06e7a762d28e..56982a81f83b 100644
--- a/services/core/java/com/android/server/location/AbstractLocationProvider.java
+++ b/services/core/java/com/android/server/location/AbstractLocationProvider.java
@@ -16,8 +16,6 @@
package com.android.server.location;
-import static com.android.internal.util.function.pooled.PooledLambda.obtainRunnable;
-
import android.annotation.Nullable;
import android.location.Location;
import android.location.util.identity.CallerIdentity;
@@ -342,8 +340,7 @@ public abstract class AbstractLocationProvider {
*/
public final void setRequest(ProviderRequest request) {
// all calls into the provider must be moved onto the provider thread to prevent deadlock
- mExecutor.execute(obtainRunnable(AbstractLocationProvider::onSetRequest, this, request)
- .recycleOnUse());
+ mExecutor.execute(() -> onSetRequest(request));
}
/**
@@ -356,13 +353,7 @@ public abstract class AbstractLocationProvider {
*/
public final void sendExtraCommand(int uid, int pid, String command, Bundle extras) {
// all calls into the provider must be moved onto the provider thread to prevent deadlock
-
- // the integer boxing done here likely cancels out any gains from removing lambda
- // allocation, but since this an infrequently used api with no real performance needs, we
- // we use pooled lambdas anyways for consistency.
- mExecutor.execute(
- obtainRunnable(AbstractLocationProvider::onExtraCommand, this, uid, pid, command,
- extras).recycleOnUse());
+ mExecutor.execute(() -> onExtraCommand(uid, pid, command, extras));
}
/**
diff --git a/services/core/java/com/android/server/location/LocationManagerService.java b/services/core/java/com/android/server/location/LocationManagerService.java
index 807784dde505..72734c47873e 100644
--- a/services/core/java/com/android/server/location/LocationManagerService.java
+++ b/services/core/java/com/android/server/location/LocationManagerService.java
@@ -701,10 +701,11 @@ public class LocationManagerService extends ILocationManager.Stub {
return location;
}
+ @Nullable
@Override
- public void getCurrentLocation(String provider, LocationRequest request,
- ICancellationSignal cancellationTransport, ILocationCallback consumer,
- String packageName, String attributionTag, String listenerId) {
+ public ICancellationSignal getCurrentLocation(String provider, LocationRequest request,
+ ILocationCallback consumer, String packageName, String attributionTag,
+ String listenerId) {
CallerIdentity identity = CallerIdentity.fromBinder(mContext, packageName, attributionTag,
listenerId);
int permissionLevel = LocationPermissions.getPermissionLevel(mContext, identity.getUid(),
@@ -721,8 +722,7 @@ public class LocationManagerService extends ILocationManager.Stub {
Preconditions.checkArgument(manager != null,
"provider \"" + provider + "\" does not exist");
- manager.getCurrentLocation(request, identity, permissionLevel, cancellationTransport,
- consumer);
+ return manager.getCurrentLocation(request, identity, permissionLevel, consumer);
}
@Override
diff --git a/services/core/java/com/android/server/location/LocationProviderManager.java b/services/core/java/com/android/server/location/LocationProviderManager.java
index e224a9d85115..138301ae934d 100644
--- a/services/core/java/com/android/server/location/LocationProviderManager.java
+++ b/services/core/java/com/android/server/location/LocationProviderManager.java
@@ -29,6 +29,7 @@ import static android.os.PowerManager.LOCATION_MODE_FOREGROUND_ONLY;
import static android.os.PowerManager.LOCATION_MODE_GPS_DISABLED_WHEN_SCREEN_OFF;
import static android.os.PowerManager.LOCATION_MODE_THROTTLE_REQUESTS_WHEN_SCREEN_OFF;
+import static com.android.internal.location.ProviderRequest.EMPTY_REQUEST;
import static com.android.server.location.LocationManagerService.D;
import static com.android.server.location.LocationManagerService.TAG;
import static com.android.server.location.LocationPermissions.PERMISSION_COARSE;
@@ -77,6 +78,7 @@ import android.util.SparseBooleanArray;
import android.util.TimeUtils;
import com.android.internal.annotations.GuardedBy;
+import com.android.internal.listeners.ListenerExecutor.ListenerOperation;
import com.android.internal.location.ProviderProperties;
import com.android.internal.location.ProviderRequest;
import com.android.internal.util.Preconditions;
@@ -111,7 +113,8 @@ import java.util.List;
import java.util.Objects;
class LocationProviderManager extends
- ListenerMultiplexer<Object, LocationRequest, LocationProviderManager.LocationTransport,
+ ListenerMultiplexer<Object, LocationProviderManager.LocationTransport,
+ LocationProviderManager.LocationListenerOperation,
LocationProviderManager.Registration, ProviderRequest> implements
AbstractLocationProvider.Listener {
@@ -121,16 +124,16 @@ class LocationProviderManager extends
private static final String WAKELOCK_TAG = "*location*";
private static final long WAKELOCK_TIMEOUT_MS = 30 * 1000;
- // maximum interval to be considered "high power" request
+ // max interval to be considered "high power" request
private static final long MAX_HIGH_POWER_INTERVAL_MS = 5 * 60 * 1000;
- // maximum age of a location before it is no longer considered "current"
+ // max age of a location before it is no longer considered "current"
private static final long MAX_CURRENT_LOCATION_AGE_MS = 10 * 1000;
// max timeout allowed for getting the current location
private static final long GET_CURRENT_LOCATION_MAX_TIMEOUT_MS = 30 * 1000;
- // maximum jitter allowed for fastest interval evaluation
+ // max jitter allowed for fastest interval evaluation
private static final int MAX_FASTEST_INTERVAL_JITTER_MS = 100;
protected interface LocationTransport {
@@ -214,8 +217,15 @@ class LocationProviderManager extends
}
}
- protected abstract class Registration extends
- RemoteListenerRegistration<LocationRequest, LocationTransport> {
+ protected interface LocationListenerOperation extends ListenerOperation<LocationTransport> {
+ /**
+ * Must be implemented to return the location this operation intends to deliver.
+ */
+ Location getLocation();
+ }
+
+ protected abstract class Registration extends RemoteListenerRegistration<LocationRequest,
+ LocationTransport, LocationListenerOperation> {
@PermissionLevel protected final int mPermissionLevel;
private final WorkSource mWorkSource;
@@ -226,9 +236,11 @@ class LocationProviderManager extends
private LocationRequest mProviderLocationRequest;
private boolean mIsUsingHighPower;
+ @Nullable private Location mLastLocation = null;
+
protected Registration(LocationRequest request, CallerIdentity identity,
LocationTransport transport, @PermissionLevel int permissionLevel) {
- super(TAG, Objects.requireNonNull(request), identity, transport);
+ super(Objects.requireNonNull(request), identity, transport);
Preconditions.checkArgument(permissionLevel > PERMISSION_NONE);
mPermissionLevel = permissionLevel;
@@ -291,7 +303,11 @@ class LocationProviderManager extends
protected void onProviderListenerUnregister() {}
@Override
- protected final ListenerOperation<LocationTransport> onActive() {
+ protected final LocationListenerOperation onActive() {
+ if (Build.IS_DEBUGGABLE) {
+ Preconditions.checkState(Thread.holdsLock(mLock));
+ }
+
if (!getRequest().isHiddenFromAppOps()) {
mLocationAttributionHelper.reportLocationStart(getIdentity(), getName(), getKey());
}
@@ -300,7 +316,11 @@ class LocationProviderManager extends
}
@Override
- protected final ListenerOperation<LocationTransport> onInactive() {
+ protected final LocationListenerOperation onInactive() {
+ if (Build.IS_DEBUGGABLE) {
+ Preconditions.checkState(Thread.holdsLock(mLock));
+ }
+
onHighPowerUsageChanged();
if (!getRequest().isHiddenFromAppOps()) {
mLocationAttributionHelper.reportLocationStop(getIdentity(), getName(), getKey());
@@ -309,18 +329,14 @@ class LocationProviderManager extends
}
@Override
- public final <Listener> void onOperationFailure(ListenerOperation<Listener> operation,
- Exception e) {
- synchronized (mLock) {
- super.onOperationFailure(operation, e);
- }
- }
-
- @Override
public final LocationRequest getRequest() {
return mProviderLocationRequest;
}
+ public final Location getLastDeliveredLocation() {
+ return mLastLocation;
+ }
+
public final boolean isForeground() {
return mForeground;
}
@@ -480,8 +496,17 @@ class LocationProviderManager extends
return mLocationManagerInternal.isProvider(null, getIdentity());
}
+ @GuardedBy("mLock")
+ @Override
+ protected final LocationListenerOperation onExecuteOperation(
+ LocationListenerOperation operation) {
+ mLastLocation = operation.getLocation();
+ return super.onExecuteOperation(operation);
+ }
+
+ @GuardedBy("mLock")
@Nullable
- abstract ListenerOperation<LocationTransport> acceptLocationChange(Location fineLocation);
+ abstract LocationListenerOperation acceptLocationChange(Location fineLocation);
@Override
public String toString() {
@@ -514,7 +539,6 @@ class LocationProviderManager extends
private final PowerManager.WakeLock mWakeLock;
private volatile ProviderTransport mProviderTransport;
- @Nullable private Location mLastLocation = null;
private int mNumLocationsDelivered = 0;
private long mExpirationRealtimeMs = Long.MAX_VALUE;
@@ -606,7 +630,7 @@ class LocationProviderManager extends
@GuardedBy("mLock")
@Nullable
@Override
- ListenerOperation<LocationTransport> acceptLocationChange(Location fineLocation) {
+ LocationListenerOperation acceptLocationChange(Location fineLocation) {
if (Build.IS_DEBUGGABLE) {
Preconditions.checkState(Thread.holdsLock(mLock));
}
@@ -631,11 +655,12 @@ class LocationProviderManager extends
throw new AssertionError();
}
- if (mLastLocation != null) {
+ Location lastDeliveredLocation = getLastDeliveredLocation();
+ if (lastDeliveredLocation != null) {
// check fastest interval
long deltaMs = NANOSECONDS.toMillis(
location.getElapsedRealtimeNanos()
- - mLastLocation.getElapsedRealtimeNanos());
+ - lastDeliveredLocation.getElapsedRealtimeNanos());
if (deltaMs < getRequest().getMinUpdateIntervalMillis()
- MAX_FASTEST_INTERVAL_JITTER_MS) {
return null;
@@ -643,7 +668,7 @@ class LocationProviderManager extends
// check smallest displacement
double smallestDisplacementM = getRequest().getMinUpdateDistanceMeters();
- if (smallestDisplacementM > 0.0 && location.distanceTo(mLastLocation)
+ if (smallestDisplacementM > 0.0 && location.distanceTo(lastDeliveredLocation)
<= smallestDisplacementM) {
return null;
}
@@ -658,10 +683,12 @@ class LocationProviderManager extends
return null;
}
- // update last location
- mLastLocation = location;
+ return new LocationListenerOperation() {
+ @Override
+ public Location getLocation() {
+ return location;
+ }
- return new ListenerOperation<LocationTransport>() {
@Override
public void onPreExecute() {
// don't acquire a wakelock for mock locations to prevent abuse
@@ -720,8 +747,12 @@ class LocationProviderManager extends
// we choose not to hold a wakelock for provider enabled changed events
executeSafely(getExecutor(), () -> mProviderTransport,
- listener -> listener.deliverOnProviderEnabledChanged(mName, enabled));
+ listener -> listener.deliverOnProviderEnabledChanged(mName, enabled),
+ this::onProviderOperationFailure);
}
+
+ protected abstract void onProviderOperationFailure(
+ ListenerOperation<ProviderTransport> operation, Exception exception);
}
protected final class LocationListenerRegistration extends LocationRegistration implements
@@ -749,6 +780,28 @@ class LocationProviderManager extends
}
@Override
+ protected void onProviderOperationFailure(ListenerOperation<ProviderTransport> operation,
+ Exception exception) {
+ onTransportFailure(exception);
+ }
+
+ @Override
+ public void onOperationFailure(LocationListenerOperation operation, Exception exception) {
+ onTransportFailure(exception);
+ }
+
+ private void onTransportFailure(Exception exception) {
+ if (exception instanceof RemoteException) {
+ Log.w(TAG, "registration " + this + " removed", exception);
+ synchronized (mLock) {
+ remove();
+ }
+ } else {
+ throw new AssertionError(exception);
+ }
+ }
+
+ @Override
public void binderDied() {
try {
if (D) {
@@ -788,6 +841,28 @@ class LocationProviderManager extends
}
@Override
+ protected void onProviderOperationFailure(ListenerOperation<ProviderTransport> operation,
+ Exception exception) {
+ onTransportFailure(exception);
+ }
+
+ @Override
+ public void onOperationFailure(LocationListenerOperation operation, Exception exception) {
+ onTransportFailure(exception);
+ }
+
+ private void onTransportFailure(Exception exception) {
+ if (exception instanceof PendingIntent.CanceledException) {
+ Log.w(TAG, "registration " + this + " removed", exception);
+ synchronized (mLock) {
+ remove();
+ }
+ } else {
+ throw new AssertionError(exception);
+ }
+ }
+
+ @Override
public void onCancelled(PendingIntent intent) {
synchronized (mLock) {
remove();
@@ -838,13 +913,17 @@ class LocationProviderManager extends
0, this, FgThread.getHandler(), getWorkSource());
}
- // start listening for provider enabled/disabled events
- addEnabledListener(this);
+ // if this request is ignoring location settings, then we don't want to immediately fail
+ // it if the provider is disabled or becomes disabled.
+ if (!getRequest().isLocationSettingsIgnored()) {
+ // start listening for provider enabled/disabled events
+ addEnabledListener(this);
- // if the provider is currently disabled fail immediately
- int userId = getIdentity().getUserId();
- if (!getRequest().isLocationSettingsIgnored() && !isEnabled(userId)) {
- deliverLocation(null);
+ // if the provider is currently disabled fail immediately
+ int userId = getIdentity().getUserId();
+ if (!getRequest().isLocationSettingsIgnored() && !isEnabled(userId)) {
+ deliverLocation(null);
+ }
}
}
@@ -880,7 +959,7 @@ class LocationProviderManager extends
@GuardedBy("mLock")
@Nullable
@Override
- ListenerOperation<LocationTransport> acceptLocationChange(@Nullable Location fineLocation) {
+ LocationListenerOperation acceptLocationChange(@Nullable Location fineLocation) {
if (Build.IS_DEBUGGABLE) {
Preconditions.checkState(Thread.holdsLock(mLock));
}
@@ -909,19 +988,35 @@ class LocationProviderManager extends
}
}
- return listener -> {
- // if delivering to the same process, make a copy of the location first (since
- // location is mutable)
- Location deliveryLocation = location;
- if (getIdentity().getPid() == Process.myPid() && location != null) {
- deliveryLocation = new Location(location);
+ return new LocationListenerOperation() {
+ @Override
+ public Location getLocation() {
+ return location;
}
- // we currently don't hold a wakelock for getCurrentLocation deliveries
- listener.deliverOnLocationChanged(deliveryLocation, null);
+ @Override
+ public void operate(LocationTransport listener) {
+ // if delivering to the same process, make a copy of the location first (since
+ // location is mutable)
+ Location deliveryLocation = location;
+ if (getIdentity().getPid() == Process.myPid() && location != null) {
+ deliveryLocation = new Location(location);
+ }
- synchronized (mLock) {
- remove();
+ // we currently don't hold a wakelock for getCurrentLocation deliveries
+ try {
+ listener.deliverOnLocationChanged(deliveryLocation, null);
+ } catch (Exception exception) {
+ if (exception instanceof RemoteException) {
+ Log.w(TAG, "registration " + this + " failed", exception);
+ } else {
+ throw new AssertionError(exception);
+ }
+ }
+
+ synchronized (mLock) {
+ remove();
+ }
}
};
}
@@ -1054,6 +1149,11 @@ class LocationProviderManager extends
mProvider = new MockableLocationProvider(mLock, this);
}
+ @Override
+ public String getTag() {
+ return TAG;
+ }
+
public void startManager() {
synchronized (mLock) {
mStarted = true;
@@ -1260,8 +1360,7 @@ class LocationProviderManager extends
/**
* This function does not perform any permissions or safety checks, by calling it you are
- * committing to performing all applicable checks yourself. Prefer
- * {@link #getLastLocation(CallerIdentity, int, boolean)} where possible.
+ * committing to performing all applicable checks yourself.
*/
@Nullable
public Location getLastLocationUnsafe(int userId, @PermissionLevel int permissionLevel,
@@ -1325,9 +1424,9 @@ class LocationProviderManager extends
}
}
- public void getCurrentLocation(LocationRequest request, CallerIdentity callerIdentity,
- int permissionLevel, ICancellationSignal cancellationTransport,
- ILocationCallback callback) {
+ @Nullable
+ public ICancellationSignal getCurrentLocation(LocationRequest request,
+ CallerIdentity callerIdentity, int permissionLevel, ILocationCallback callback) {
if (request.getDurationMillis() > GET_CURRENT_LOCATION_MAX_TIMEOUT_MS) {
request = new LocationRequest.Builder(request)
.setDurationMillis(GET_CURRENT_LOCATION_MAX_TIMEOUT_MS)
@@ -1345,15 +1444,15 @@ class LocationProviderManager extends
if (mSettingsHelper.isLocationPackageBlacklisted(callerIdentity.getUserId(),
callerIdentity.getPackageName())) {
registration.deliverLocation(null);
- return;
+ return null;
}
if (!mUserInfoHelper.isCurrentUserId(callerIdentity.getUserId())) {
registration.deliverLocation(null);
- return;
+ return null;
}
if (!request.isLocationSettingsIgnored() && !isEnabled(callerIdentity.getUserId())) {
registration.deliverLocation(null);
- return;
+ return null;
}
Location lastLocation = getLastLocationUnsafe(callerIdentity.getUserId(),
@@ -1364,13 +1463,13 @@ class LocationProviderManager extends
- lastLocation.getElapsedRealtimeNanos());
if (locationAgeMs < MAX_CURRENT_LOCATION_AGE_MS) {
registration.deliverLocation(lastLocation);
- return;
+ return null;
}
if (!mAppForegroundHelper.isAppForeground(Binder.getCallingUid())
&& locationAgeMs < mSettingsHelper.getBackgroundThrottleIntervalMs()) {
registration.deliverLocation(null);
- return;
+ return null;
}
}
@@ -1383,16 +1482,15 @@ class LocationProviderManager extends
}
}
- CancellationSignal cancellationSignal = CancellationSignal.fromTransport(
- cancellationTransport);
- if (cancellationSignal != null) {
- cancellationSignal.setOnCancelListener(SingleUseCallback.wrap(
- () -> {
- synchronized (mLock) {
- removeRegistration(callback.asBinder(), registration);
- }
- }));
- }
+ ICancellationSignal cancelTransport = CancellationSignal.createTransport();
+ CancellationSignal cancellationSignal = CancellationSignal.fromTransport(cancelTransport);
+ cancellationSignal.setOnCancelListener(SingleUseCallback.wrap(
+ () -> {
+ synchronized (mLock) {
+ removeRegistration(callback.asBinder(), registration);
+ }
+ }));
+ return cancelTransport;
}
public void sendExtraCommand(int uid, int pid, String command, Bundle extras) {
@@ -1554,7 +1652,8 @@ class LocationProviderManager extends
@GuardedBy("mLock")
@Override
- protected boolean registerWithService(ProviderRequest mergedRequest) {
+ protected boolean registerWithService(ProviderRequest mergedRequest,
+ Collection<Registration> registrations) {
if (Build.IS_DEBUGGABLE) {
Preconditions.checkState(Thread.holdsLock(mLock));
}
@@ -1566,8 +1665,8 @@ class LocationProviderManager extends
@GuardedBy("mLock")
@Override
protected boolean reregisterWithService(ProviderRequest oldRequest,
- ProviderRequest newRequest) {
- return registerWithService(newRequest);
+ ProviderRequest newRequest, Collection<Registration> registrations) {
+ return registerWithService(newRequest, registrations);
}
@GuardedBy("mLock")
@@ -1576,7 +1675,8 @@ class LocationProviderManager extends
if (Build.IS_DEBUGGABLE) {
Preconditions.checkState(Thread.holdsLock(mLock));
}
- mProvider.setRequest(ProviderRequest.EMPTY_REQUEST);
+
+ mProvider.setRequest(EMPTY_REQUEST);
}
@GuardedBy("mLock")
@@ -1628,7 +1728,7 @@ class LocationProviderManager extends
@GuardedBy("mLock")
@Override
- protected ProviderRequest mergeRequests(Collection<Registration> registrations) {
+ protected ProviderRequest mergeRegistrations(Collection<Registration> registrations) {
if (Build.IS_DEBUGGABLE) {
Preconditions.checkState(Thread.holdsLock(mLock));
}
@@ -1814,9 +1914,7 @@ class LocationProviderManager extends
}
// attempt listener delivery
- deliverToListeners(registration -> {
- return registration.acceptLocationChange(location);
- });
+ deliverToListeners(registration -> registration.acceptLocationChange(location));
}
@GuardedBy("mLock")
@@ -1951,7 +2049,8 @@ class LocationProviderManager extends
public void dump(FileDescriptor fd, IndentingPrintWriter ipw, String[] args) {
synchronized (mLock) {
- ipw.print(mName + " provider");
+ ipw.print(mName);
+ ipw.print(" provider");
if (mProvider.isMock()) {
ipw.print(" [mock]");
}
@@ -1963,12 +2062,15 @@ class LocationProviderManager extends
int[] userIds = mUserInfoHelper.getRunningUserIds();
for (int userId : userIds) {
if (userIds.length != 1) {
- ipw.println("user " + userId + ":");
+ ipw.print("user ");
+ ipw.print(userId);
+ ipw.println(":");
ipw.increaseIndent();
}
- ipw.println(
- "last location=" + getLastLocationUnsafe(userId, PERMISSION_FINE, false));
- ipw.println("enabled=" + isEnabled(userId));
+ ipw.print("last location=");
+ ipw.println(getLastLocationUnsafe(userId, PERMISSION_FINE, false));
+ ipw.print("enabled=");
+ ipw.println(isEnabled(userId));
if (userIds.length != 1) {
ipw.decreaseIndent();
}
diff --git a/services/core/java/com/android/server/location/PassiveLocationProviderManager.java b/services/core/java/com/android/server/location/PassiveLocationProviderManager.java
index 5133683b0625..afeb6444c40b 100644
--- a/services/core/java/com/android/server/location/PassiveLocationProviderManager.java
+++ b/services/core/java/com/android/server/location/PassiveLocationProviderManager.java
@@ -64,7 +64,7 @@ class PassiveLocationProviderManager extends LocationProviderManager {
}
@Override
- protected ProviderRequest mergeRequests(Collection<Registration> registrations) {
+ protected ProviderRequest mergeRegistrations(Collection<Registration> registrations) {
ProviderRequest.Builder providerRequest = new ProviderRequest.Builder()
.setIntervalMillis(0);
diff --git a/services/core/java/com/android/server/location/geofence/GeofenceManager.java b/services/core/java/com/android/server/location/geofence/GeofenceManager.java
index 7f02b2a807c3..58e6d593ed2d 100644
--- a/services/core/java/com/android/server/location/geofence/GeofenceManager.java
+++ b/services/core/java/com/android/server/location/geofence/GeofenceManager.java
@@ -41,6 +41,7 @@ import android.stats.location.LocationStatsEnums;
import android.util.ArraySet;
import com.android.internal.annotations.GuardedBy;
+import com.android.internal.listeners.ListenerExecutor.ListenerOperation;
import com.android.server.PendingIntentUtils;
import com.android.server.location.LocationPermissions;
import com.android.server.location.listeners.ListenerMultiplexer;
@@ -59,7 +60,7 @@ import java.util.Objects;
* Manages all geofences.
*/
public class GeofenceManager extends
- ListenerMultiplexer<GeofenceKey, Geofence, PendingIntent,
+ ListenerMultiplexer<GeofenceKey, PendingIntent, ListenerOperation<PendingIntent>,
GeofenceManager.GeofenceRegistration, LocationRequest> implements
LocationListener {
@@ -94,7 +95,7 @@ public class GeofenceManager extends
protected GeofenceRegistration(Geofence geofence, CallerIdentity identity,
PendingIntent pendingIntent) {
- super(TAG, geofence, identity, pendingIntent);
+ super(geofence, identity, pendingIntent);
mCenter = new Location("");
mCenter.setLatitude(geofence.getLatitude());
@@ -273,6 +274,11 @@ public class GeofenceManager extends
mLocationUsageLogger = injector.getLocationUsageLogger();
}
+ @Override
+ public String getTag() {
+ return TAG;
+ }
+
private LocationManager getLocationManager() {
synchronized (mLock) {
if (mLocationManager == null) {
@@ -372,7 +378,8 @@ public class GeofenceManager extends
}
@Override
- protected boolean registerWithService(LocationRequest locationRequest) {
+ protected boolean registerWithService(LocationRequest locationRequest,
+ Collection<GeofenceRegistration> registrations) {
getLocationManager().requestLocationUpdates(FUSED_PROVIDER, locationRequest,
DIRECT_EXECUTOR, this);
return true;
@@ -387,7 +394,7 @@ public class GeofenceManager extends
}
@Override
- protected LocationRequest mergeRequests(Collection<GeofenceRegistration> registrations) {
+ protected LocationRequest mergeRegistrations(Collection<GeofenceRegistration> registrations) {
Location location = getLastLocation();
long realtimeMs = SystemClock.elapsedRealtime();
diff --git a/services/core/java/com/android/server/location/gnss/GnssAntennaInfoProvider.java b/services/core/java/com/android/server/location/gnss/GnssAntennaInfoProvider.java
index 5ddc6a1e401d..b99e26988abf 100644
--- a/services/core/java/com/android/server/location/gnss/GnssAntennaInfoProvider.java
+++ b/services/core/java/com/android/server/location/gnss/GnssAntennaInfoProvider.java
@@ -28,6 +28,7 @@ import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.util.Preconditions;
import com.android.server.location.util.Injector;
+import java.util.Collection;
import java.util.List;
/**
@@ -59,7 +60,8 @@ public class GnssAntennaInfoProvider extends
}
@Override
- protected boolean registerWithService(Void ignored) {
+ protected boolean registerWithService(Void ignored,
+ Collection<GnssListenerRegistration> registrations) {
Preconditions.checkState(mNative.isAntennaInfoSupported());
if (mNative.startAntennaInfoListening()) {
diff --git a/services/core/java/com/android/server/location/gnss/GnssListenerMultiplexer.java b/services/core/java/com/android/server/location/gnss/GnssListenerMultiplexer.java
index a9fdacca9a06..a830e2fe7973 100644
--- a/services/core/java/com/android/server/location/gnss/GnssListenerMultiplexer.java
+++ b/services/core/java/com/android/server/location/gnss/GnssListenerMultiplexer.java
@@ -26,11 +26,13 @@ import android.location.LocationManagerInternal;
import android.location.LocationManagerInternal.ProviderEnabledListener;
import android.location.util.identity.CallerIdentity;
import android.os.Binder;
+import android.os.Build;
import android.os.IBinder;
import android.os.IInterface;
import android.os.Process;
import android.util.ArraySet;
+import com.android.internal.listeners.ListenerExecutor.ListenerOperation;
import com.android.internal.util.Preconditions;
import com.android.server.LocalServices;
import com.android.server.location.listeners.BinderListenerRegistration;
@@ -43,6 +45,7 @@ import com.android.server.location.util.UserInfoHelper;
import com.android.server.location.util.UserInfoHelper.UserListener;
import java.io.PrintWriter;
+import java.util.Collection;
import java.util.Objects;
/**
@@ -52,14 +55,15 @@ import java.util.Objects;
* Listeners must be registered with the associated IBinder as the key, if the IBinder dies, the
* registration will automatically be removed.
*
- * @param <TRequest> request type
- * @param <TListener> listener type
- * @param <TMergedRequest> merged request type
+ * @param <TRequest> request type
+ * @param <TListener> listener type
+ * @param <TMergedRegistration> merged registration type
*/
public abstract class GnssListenerMultiplexer<TRequest, TListener extends IInterface,
- TMergedRequest> extends
- ListenerMultiplexer<IBinder, TRequest, TListener, GnssListenerMultiplexer<TRequest,
- TListener, TMergedRequest>.GnssListenerRegistration, TMergedRequest> {
+ TMergedRegistration> extends
+ ListenerMultiplexer<IBinder, TListener, ListenerOperation<TListener>,
+ GnssListenerMultiplexer<TRequest, TListener, TMergedRegistration>
+ .GnssListenerRegistration, TMergedRegistration> {
/**
* Registration object for GNSS listeners.
@@ -74,11 +78,11 @@ public abstract class GnssListenerMultiplexer<TRequest, TListener extends IInter
protected GnssListenerRegistration(@Nullable TRequest request,
CallerIdentity callerIdentity, TListener listener) {
- super(TAG, request, callerIdentity, listener);
+ super(request, callerIdentity, listener);
}
@Override
- protected GnssListenerMultiplexer<TRequest, TListener, TMergedRequest> getOwner() {
+ protected GnssListenerMultiplexer<TRequest, TListener, TMergedRegistration> getOwner() {
return GnssListenerMultiplexer.this;
}
@@ -199,6 +203,11 @@ public abstract class GnssListenerMultiplexer<TRequest, TListener extends IInter
LocalServices.getService(LocationManagerInternal.class));
}
+ @Override
+ public String getTag() {
+ return TAG;
+ }
+
/**
* May be overridden by subclasses to return whether the service is supported or not. This value
* should never change for the lifetime of the multiplexer. If the service is unsupported, all
@@ -271,6 +280,21 @@ public abstract class GnssListenerMultiplexer<TRequest, TListener extends IInter
return mLocationManagerInternal.isProvider(null, identity);
}
+ // this provides a default implementation for all further subclasses which assumes that there is
+ // never an associated request object, and thus nothing interesting to merge. the majority of
+ // gnss listener multiplexers do not current have associated requests, and the ones that do can
+ // override this implementation.
+ protected TMergedRegistration mergeRegistrations(
+ Collection<GnssListenerRegistration> gnssListenerRegistrations) {
+ if (Build.IS_DEBUGGABLE) {
+ for (GnssListenerRegistration registration : gnssListenerRegistrations) {
+ Preconditions.checkState(registration.getRequest() == null);
+ }
+ }
+
+ return null;
+ }
+
@Override
protected void onRegister() {
if (!isServiceSupported()) {
diff --git a/services/core/java/com/android/server/location/gnss/GnssMeasurementsProvider.java b/services/core/java/com/android/server/location/gnss/GnssMeasurementsProvider.java
index 37db02337a2f..2faa15fac42f 100644
--- a/services/core/java/com/android/server/location/gnss/GnssMeasurementsProvider.java
+++ b/services/core/java/com/android/server/location/gnss/GnssMeasurementsProvider.java
@@ -76,7 +76,8 @@ public class GnssMeasurementsProvider extends
}
@Override
- protected boolean registerWithService(Boolean fullTrackingRequest) {
+ protected boolean registerWithService(Boolean fullTrackingRequest,
+ Collection<GnssListenerRegistration> registrations) {
Preconditions.checkState(mNative.isMeasurementSupported());
if (mNative.startMeasurementCollection(fullTrackingRequest)) {
@@ -121,7 +122,7 @@ public class GnssMeasurementsProvider extends
}
@Override
- protected Boolean mergeRequests(Collection<GnssListenerRegistration> registrations) {
+ protected Boolean mergeRegistrations(Collection<GnssListenerRegistration> registrations) {
if (mSettingsHelper.isGnssMeasurementsFullTrackingEnabled()) {
return true;
}
diff --git a/services/core/java/com/android/server/location/gnss/GnssNavigationMessageProvider.java b/services/core/java/com/android/server/location/gnss/GnssNavigationMessageProvider.java
index 7dcffc664f52..861abc3ef592 100644
--- a/services/core/java/com/android/server/location/gnss/GnssNavigationMessageProvider.java
+++ b/services/core/java/com/android/server/location/gnss/GnssNavigationMessageProvider.java
@@ -30,6 +30,8 @@ import com.android.internal.util.Preconditions;
import com.android.server.location.util.AppOpsHelper;
import com.android.server.location.util.Injector;
+import java.util.Collection;
+
/**
* An base implementation for GPS navigation messages provider.
* It abstracts out the responsibility of handling listeners, while still allowing technology
@@ -61,7 +63,8 @@ public class GnssNavigationMessageProvider extends
}
@Override
- protected boolean registerWithService(Void ignored) {
+ protected boolean registerWithService(Void ignored,
+ Collection<GnssListenerRegistration> registrations) {
Preconditions.checkState(mNative.isNavigationMessageSupported());
if (mNative.startNavigationMessageCollection()) {
diff --git a/services/core/java/com/android/server/location/gnss/GnssStatusProvider.java b/services/core/java/com/android/server/location/gnss/GnssStatusProvider.java
index 68813b3e0777..248d38a940dd 100644
--- a/services/core/java/com/android/server/location/gnss/GnssStatusProvider.java
+++ b/services/core/java/com/android/server/location/gnss/GnssStatusProvider.java
@@ -31,6 +31,8 @@ import com.android.server.location.util.AppOpsHelper;
import com.android.server.location.util.Injector;
import com.android.server.location.util.LocationUsageLogger;
+import java.util.Collection;
+
/**
* Implementation of a handler for {@link IGnssStatusListener}.
*/
@@ -51,7 +53,8 @@ public class GnssStatusProvider extends GnssListenerMultiplexer<Void, IGnssStatu
}
@Override
- protected boolean registerWithService(Void ignored) {
+ protected boolean registerWithService(Void ignored,
+ Collection<GnssListenerRegistration> registrations) {
if (D) {
Log.d(TAG, "starting gnss status");
}
diff --git a/services/core/java/com/android/server/location/listeners/BinderListenerRegistration.java b/services/core/java/com/android/server/location/listeners/BinderListenerRegistration.java
index 58aabdad056f..bc675ceda970 100644
--- a/services/core/java/com/android/server/location/listeners/BinderListenerRegistration.java
+++ b/services/core/java/com/android/server/location/listeners/BinderListenerRegistration.java
@@ -23,6 +23,8 @@ import android.os.IBinder;
import android.os.RemoteException;
import android.util.Log;
+import com.android.internal.listeners.ListenerExecutor.ListenerOperation;
+
/**
* A registration that works with IBinder keys, and registers a DeathListener to automatically
* remove the registration if the binder dies. The key for this registration must either be an
@@ -32,7 +34,8 @@ import android.util.Log;
* @param <TListener> listener type
*/
public abstract class BinderListenerRegistration<TRequest, TListener> extends
- RemoteListenerRegistration<TRequest, TListener> implements Binder.DeathRecipient {
+ RemoteListenerRegistration<TRequest, TListener, ListenerOperation<TListener>> implements
+ Binder.DeathRecipient {
/**
* Interface to allow binder retrieval when keys are not themselves IBinders.
@@ -44,9 +47,9 @@ public abstract class BinderListenerRegistration<TRequest, TListener> extends
IBinder getBinder();
}
- protected BinderListenerRegistration(String tag, @Nullable TRequest request,
- CallerIdentity callerIdentity, TListener listener) {
- super(tag, request, callerIdentity, listener);
+ protected BinderListenerRegistration(@Nullable TRequest request, CallerIdentity callerIdentity,
+ TListener listener) {
+ super(request, callerIdentity, listener);
}
@Override
@@ -78,10 +81,20 @@ public abstract class BinderListenerRegistration<TRequest, TListener> extends
protected void onBinderListenerUnregister() {}
@Override
+ public void onOperationFailure(ListenerOperation<TListener> operation, Exception e) {
+ if (e instanceof RemoteException) {
+ Log.w(getOwner().getTag(), "registration " + this + " removed", e);
+ remove();
+ } else {
+ super.onOperationFailure(operation, e);
+ }
+ }
+
+ @Override
public void binderDied() {
try {
- if (Log.isLoggable(mTag, Log.DEBUG)) {
- Log.d(mTag, "binder registration " + getIdentity() + " died");
+ if (Log.isLoggable(getOwner().getTag(), Log.DEBUG)) {
+ Log.d(getOwner().getTag(), "binder registration " + getIdentity() + " died");
}
remove();
} catch (RuntimeException e) {
diff --git a/services/core/java/com/android/server/location/listeners/ListenerMultiplexer.java b/services/core/java/com/android/server/location/listeners/ListenerMultiplexer.java
index feb4fbd1de31..87d668a07d70 100644
--- a/services/core/java/com/android/server/location/listeners/ListenerMultiplexer.java
+++ b/services/core/java/com/android/server/location/listeners/ListenerMultiplexer.java
@@ -37,12 +37,20 @@ import java.util.function.Function;
import java.util.function.Predicate;
/**
- * A base class to multiplex client listener registrations within system server. Registrations are
- * divided into two categories, active registrations and inactive registrations, as defined by
- * {@link #isActive(ListenerRegistration)}. If a registration's active state changes,
+ * A base class to multiplex client listener registrations within system server. Every listener is
+ * represented by a registration object which stores all required state for a listener. Keys are
+ * used to uniquely identify every registration. Listener operations may be executed on
+ * registrations in order to invoke the represented listener.
+ *
+ * Registrations are divided into two categories, active registrations and inactive registrations,
+ * as defined by {@link #isActive(ListenerRegistration)}. If a registration's active state changes,
* {@link #updateRegistrations(Predicate)} must be invoked and return true for any registration
* whose active state may have changed. Listeners will only be invoked for active registrations.
*
+ * The set of active registrations is combined into a single merged registration, which is submitted
+ * to the backing service when necessary in order to register the service. The merged registration
+ * is updated whenever the set of active registration changes.
+ *
* Callbacks invoked for various changes will always be ordered according to this lifecycle list:
*
* <ul>
@@ -63,14 +71,16 @@ import java.util.function.Predicate;
* {@link #removeRegistration(Object, ListenerRegistration)}, not via any other removal method. This
* ensures re-entrant removal does not accidentally remove the incorrect registration.
*
- * @param <TKey> key type
- * @param <TRequest> request type
- * @param <TListener> listener type
- * @param <TRegistration> registration type
- * @param <TMergedRequest> merged request type
+ * @param <TKey> key type
+ * @param <TListener> listener type
+ * @param <TListenerOperation> listener operation type
+ * @param <TRegistration> registration type
+ * @param <TMergedRegistration> merged registration type
*/
-public abstract class ListenerMultiplexer<TKey, TRequest, TListener,
- TRegistration extends ListenerRegistration<TRequest, TListener>, TMergedRequest> {
+public abstract class ListenerMultiplexer<TKey, TListener,
+ TListenerOperation extends ListenerOperation<TListener>,
+ TRegistration extends ListenerRegistration<TListener, TListenerOperation>,
+ TMergedRegistration> {
@GuardedBy("mRegistrations")
private final ArrayMap<TKey, TRegistration> mRegistrations = new ArrayMap<>();
@@ -88,25 +98,42 @@ public abstract class ListenerMultiplexer<TKey, TRequest, TListener,
private boolean mServiceRegistered = false;
@GuardedBy("mRegistrations")
- @Nullable private TMergedRequest mCurrentRequest;
+ @Nullable private TMergedRegistration mMerged;
+
+ /**
+ * Should be implemented to return a unique identifying tag that may be used for logging, etc...
+ */
+ public abstract @NonNull String getTag();
/**
- * Should be implemented to register with the backing service with the given merged request, and
- * should return true if a matching call to {@link #unregisterWithService()} is required to
- * unregister (ie, if registration succeeds).
+ * Should be implemented to register with the backing service with the given merged
+ * registration, and should return true if a matching call to {@link #unregisterWithService()}
+ * is required to unregister (ie, if registration succeeds). The set of registrations passed in
+ * is the same set passed into {@link #mergeRegistrations(Collection)} to generate the merged
+ * registration.
*
- * @see #reregisterWithService(Object, Object)
+ * <p class="note">It may seem redundant to pass in the set of active registrations when they
+ * have already been used to generate the merged request, and indeed, for many implementations
+ * this parameter can likely simply be ignored. However, some implementations may require access
+ * to the set of registrations used to generate the merged requestion for further logic even
+ * after the merged registration has been generated.
+ *
+ * @see #mergeRegistrations(Collection)
+ * @see #reregisterWithService(Object, Object, Collection)
*/
- protected abstract boolean registerWithService(TMergedRequest newRequest);
+ protected abstract boolean registerWithService(TMergedRegistration merged,
+ @NonNull Collection<TRegistration> registrations);
/**
- * Invoked when the service already has a request, and it is being replaced with a new request.
- * The default implementation unregisters first, then registers with the new merged request, but
- * this may be overridden by subclasses in order to reregister more efficiently.
+ * Invoked when the service has already been registered with some merged registration, and is
+ * now being registered with a different merged registration. The default implementation simply
+ * invokes {@link #registerWithService(Object, Collection)}.
+ *
+ * @see #registerWithService(Object, Collection)
*/
- protected boolean reregisterWithService(TMergedRequest oldRequest, TMergedRequest newRequest) {
- unregisterWithService();
- return registerWithService(newRequest);
+ protected boolean reregisterWithService(TMergedRegistration oldMerged,
+ TMergedRegistration newMerged, @NonNull Collection<TRegistration> registrations) {
+ return registerWithService(newMerged, registrations);
}
/**
@@ -116,28 +143,23 @@ public abstract class ListenerMultiplexer<TKey, TRequest, TListener,
/**
* Defines whether a registration is currently active or not. Only active registrations will be
- * considered within {@link #mergeRequests(Collection)} to calculate the merged request, and
- * listener invocations will only be delivered to active requests. If a registration's active
- * state changes, {@link #updateRegistrations(Predicate)} must be invoked with a function that
- * returns true for any registrations that may have changed their active state.
+ * forwarded to {@link #registerWithService(Object, Collection)}, and listener invocations will
+ * only be delivered to active requests. If a registration's active state changes,
+ * {@link #updateRegistrations(Predicate)} must be invoked with a function that returns true for
+ * any registrations that may have changed their active state.
*/
protected abstract boolean isActive(@NonNull TRegistration registration);
/**
- * Called in order to generate a merged request from the given registrations. The list of
- * registrations will never be empty.
+ * Called in order to generate a merged registration from the given set of active registrations.
+ * The list of registrations will never be empty. If the resulting merged registration is equal
+ * to the currently registered merged registration, nothing further will happen. If the merged
+ * registration differs, {@link #registerWithService(Object, Collection)} or
+ * {@link #reregisterWithService(Object, Object, Collection)} will be invoked with the new
+ * merged registration so that the backing service can be updated.
*/
- protected @Nullable TMergedRequest mergeRequests(
- @NonNull Collection<TRegistration> registrations) {
- if (Build.IS_DEBUGGABLE) {
- for (TRegistration registration : registrations) {
- // if using non-null requests then implementations must override this method
- Preconditions.checkState(registration.getRequest() == null);
- }
- }
-
- return null;
- }
+ protected abstract @Nullable TMergedRegistration mergeRegistrations(
+ @NonNull Collection<TRegistration> registrations);
/**
* Invoked when the multiplexer goes from having no registrations to having some registrations.
@@ -348,41 +370,42 @@ public abstract class ListenerMultiplexer<TKey, TRequest, TListener,
}
if (actives.isEmpty()) {
- mCurrentRequest = null;
if (mServiceRegistered) {
+ mMerged = null;
mServiceRegistered = false;
- mCurrentRequest = null;
unregisterWithService();
}
return;
}
- TMergedRequest merged = mergeRequests(actives);
- if (!mServiceRegistered || !Objects.equals(merged, mCurrentRequest)) {
- if (mServiceRegistered) {
- mServiceRegistered = reregisterWithService(mCurrentRequest, merged);
- } else {
- mServiceRegistered = registerWithService(merged);
- }
+ TMergedRegistration merged = mergeRegistrations(actives);
+ if (!mServiceRegistered || !Objects.equals(merged, mMerged)) {
if (mServiceRegistered) {
- mCurrentRequest = merged;
+ mServiceRegistered = reregisterWithService(mMerged, merged, actives);
} else {
- mCurrentRequest = null;
+ mServiceRegistered = registerWithService(merged, actives);
}
+ mMerged = mServiceRegistered ? merged : null;
}
}
}
/**
- * Clears currently stored service state, and invokes {@link #updateService()} to force a new
- * call to {@link #registerWithService(Object)} if necessary. This is useful, for instance, if
- * the backing service has crashed or otherwise lost state, and needs to be re-initialized.
+ * If the service is currently registered, unregisters it and then calls
+ * {@link #updateService()} so that {@link #registerWithService(Object, Collection)} will be
+ * re-invoked. This is useful, for instance, if the backing service has crashed or otherwise
+ * lost state, and needs to be re-initialized. Because this unregisters first, this is safe to
+ * use even if there is a possibility the backing server has not crashed, or has already been
+ * reinitialized.
*/
protected final void resetService() {
synchronized (mRegistrations) {
- mServiceRegistered = false;
- mCurrentRequest = null;
- updateService();
+ if (mServiceRegistered) {
+ mMerged = null;
+ mServiceRegistered = false;
+ unregisterWithService();
+ updateService();
+ }
}
}
@@ -435,12 +458,12 @@ public abstract class ListenerMultiplexer<TKey, TRequest, TListener,
if (++mActiveRegistrationsCount == 1) {
onActive();
}
- ListenerOperation<TListener> operation = registration.onActive();
+ TListenerOperation operation = registration.onActive();
if (operation != null) {
execute(registration, operation);
}
} else {
- ListenerOperation<TListener> operation = registration.onInactive();
+ TListenerOperation operation = registration.onInactive();
if (operation != null) {
execute(registration, operation);
}
@@ -459,14 +482,14 @@ public abstract class ListenerMultiplexer<TKey, TRequest, TListener,
* change the active state of the registration.
*/
protected final void deliverToListeners(
- @NonNull Function<TRegistration, ListenerOperation<TListener>> function) {
+ @NonNull Function<TRegistration, TListenerOperation> function) {
synchronized (mRegistrations) {
try (ReentrancyGuard ignored = mReentrancyGuard.acquire()) {
final int size = mRegistrations.size();
for (int i = 0; i < size; i++) {
TRegistration registration = mRegistrations.valueAt(i);
if (registration.isActive()) {
- ListenerOperation<TListener> operation = function.apply(registration);
+ TListenerOperation operation = function.apply(registration);
if (operation != null) {
execute(registration, operation);
}
@@ -483,7 +506,7 @@ public abstract class ListenerMultiplexer<TKey, TRequest, TListener,
* deliverToListeners(registration -> operation);
* </pre>
*/
- protected final void deliverToListeners(@NonNull ListenerOperation<TListener> operation) {
+ protected final void deliverToListeners(@NonNull TListenerOperation operation) {
synchronized (mRegistrations) {
try (ReentrancyGuard ignored = mReentrancyGuard.acquire()) {
final int size = mRegistrations.size();
@@ -502,7 +525,7 @@ public abstract class ListenerMultiplexer<TKey, TRequest, TListener,
onRegistrationActiveChanged(registration);
}
- private void execute(TRegistration registration, ListenerOperation<TListener> operation) {
+ private void execute(TRegistration registration, TListenerOperation operation) {
registration.executeInternal(operation);
}
@@ -539,10 +562,11 @@ public abstract class ListenerMultiplexer<TKey, TRequest, TListener,
*/
protected void dumpServiceState(PrintWriter pw) {
if (mServiceRegistered) {
- if (mCurrentRequest == null) {
- pw.print("registered");
- } else {
- pw.print("registered with " + mCurrentRequest);
+ pw.print("registered");
+ if (mMerged != null) {
+ pw.print(" [");
+ pw.print(mMerged);
+ pw.print("]");
}
} else {
pw.print("unregistered");
diff --git a/services/core/java/com/android/server/location/listeners/ListenerRegistration.java b/services/core/java/com/android/server/location/listeners/ListenerRegistration.java
index deb9660a1c82..d7ecbcb7cfdf 100644
--- a/services/core/java/com/android/server/location/listeners/ListenerRegistration.java
+++ b/services/core/java/com/android/server/location/listeners/ListenerRegistration.java
@@ -21,6 +21,7 @@ import android.annotation.NonNull;
import android.annotation.Nullable;
import com.android.internal.listeners.ListenerExecutor;
+import com.android.internal.listeners.ListenerExecutor.ListenerOperation;
import java.util.Objects;
import java.util.concurrent.Executor;
@@ -29,22 +30,21 @@ import java.util.concurrent.Executor;
* A listener registration object which holds data associated with the listener, such as an optional
* request, and an executor responsible for listener invocations.
*
- * @param <TRequest> request type
- * @param <TListener> listener type
+ * @param <TListener> listener type
+ * @param <TListenerOperation> listener operation type
*/
-public class ListenerRegistration<TRequest, TListener> implements ListenerExecutor {
+public class ListenerRegistration<TListener,
+ TListenerOperation extends ListenerOperation<TListener>> implements
+ ListenerExecutor {
private final Executor mExecutor;
- private final @Nullable TRequest mRequest;
private boolean mActive;
private volatile @Nullable TListener mListener;
- protected ListenerRegistration(Executor executor, @Nullable TRequest request,
- TListener listener) {
+ protected ListenerRegistration(Executor executor, TListener listener) {
mExecutor = Objects.requireNonNull(executor);
- mRequest = request;
mActive = false;
mListener = Objects.requireNonNull(listener);
}
@@ -54,13 +54,6 @@ public class ListenerRegistration<TRequest, TListener> implements ListenerExecut
}
/**
- * Returns the request associated with this listener, or null if one wasn't supplied.
- */
- public @Nullable TRequest getRequest() {
- return mRequest;
- }
-
- /**
* May be overridden by subclasses. Invoked when registration occurs. Invoked while holding the
* owning multiplexer's internal lock.
*/
@@ -77,7 +70,7 @@ public class ListenerRegistration<TRequest, TListener> implements ListenerExecut
* returns a non-null operation, that operation will be invoked for the listener. Invoked
* while holding the owning multiplexer's internal lock.
*/
- protected @Nullable ListenerOperation<TListener> onActive() {
+ protected @Nullable TListenerOperation onActive() {
return null;
}
@@ -86,7 +79,7 @@ public class ListenerRegistration<TRequest, TListener> implements ListenerExecut
* a non-null operation, that operation will be invoked for the listener. Invoked while holding
* the owning multiplexer's internal lock.
*/
- protected @Nullable ListenerOperation<TListener> onInactive() {
+ protected @Nullable TListenerOperation onInactive() {
return null;
}
@@ -115,21 +108,38 @@ public class ListenerRegistration<TRequest, TListener> implements ListenerExecut
/**
* May be overridden by subclasses, however should rarely be needed. Invoked when the listener
* associated with this registration is unregistered, which may occur before the registration
- * itself is unregistered. This immediately prevents the listener from being further invoked.
+ * itself is unregistered. This immediately prevents the listener from being further invoked
+ * until the registration itself can be finalized and unregistered completely.
*/
- protected void onListenerUnregister() {};
+ protected void onListenerUnregister() {}
- final void executeInternal(@NonNull ListenerOperation<TListener> operation) {
- executeSafely(mExecutor, () -> mListener, operation);
+ /**
+ * May be overridden by subclasses, however should rarely be needed. Invoked whenever a listener
+ * operation is submitted for execution, and allows the registration a chance to replace the
+ * listener operation or perform related bookkeeping. There is no guarantee a listener operation
+ * submitted or returned here will ever be invoked. Will always be invoked on the calling
+ * thread.
+ */
+ protected TListenerOperation onExecuteOperation(@NonNull TListenerOperation operation) {
+ return operation;
+ }
+
+ /**
+ * May be overridden by subclasses to handle listener operation failures. The default behavior
+ * is to further propagate any exceptions. Will always be invoked on the executor thread.
+ */
+ protected void onOperationFailure(TListenerOperation operation, Exception exception) {
+ throw new AssertionError(exception);
+ }
+
+ final void executeInternal(@NonNull TListenerOperation operation) {
+ executeSafely(mExecutor, () -> mListener,
+ onExecuteOperation(Objects.requireNonNull(operation)), this::onOperationFailure);
}
@Override
public String toString() {
- if (mRequest == null) {
- return "[]";
- } else {
- return mRequest.toString();
- }
+ return "[]";
}
@Override
diff --git a/services/core/java/com/android/server/location/listeners/PendingIntentListenerRegistration.java b/services/core/java/com/android/server/location/listeners/PendingIntentListenerRegistration.java
index 7b6154eb0d00..e57b5322de8d 100644
--- a/services/core/java/com/android/server/location/listeners/PendingIntentListenerRegistration.java
+++ b/services/core/java/com/android/server/location/listeners/PendingIntentListenerRegistration.java
@@ -21,6 +21,8 @@ import android.app.PendingIntent;
import android.location.util.identity.CallerIdentity;
import android.util.Log;
+import com.android.internal.listeners.ListenerExecutor.ListenerOperation;
+
/**
* A registration that works with PendingIntent keys, and registers a CancelListener to
* automatically remove the registration if the PendingIntent is canceled. The key for this
@@ -30,7 +32,8 @@ import android.util.Log;
* @param <TListener> listener type
*/
public abstract class PendingIntentListenerRegistration<TRequest, TListener> extends
- RemoteListenerRegistration<TRequest, TListener> implements PendingIntent.CancelListener {
+ RemoteListenerRegistration<TRequest, TListener, ListenerOperation<TListener>> implements
+ PendingIntent.CancelListener {
/**
* Interface to allowed pending intent retrieval when keys are not themselves PendingIntents.
@@ -42,9 +45,9 @@ public abstract class PendingIntentListenerRegistration<TRequest, TListener> ext
PendingIntent getPendingIntent();
}
- protected PendingIntentListenerRegistration(String tag, @Nullable TRequest request,
+ protected PendingIntentListenerRegistration(@Nullable TRequest request,
CallerIdentity callerIdentity, TListener listener) {
- super(tag, request, callerIdentity, listener);
+ super(request, callerIdentity, listener);
}
@Override
@@ -70,9 +73,20 @@ public abstract class PendingIntentListenerRegistration<TRequest, TListener> ext
protected void onPendingIntentListenerUnregister() {}
@Override
+ public void onOperationFailure(ListenerOperation<TListener> operation, Exception e) {
+ if (e instanceof PendingIntent.CanceledException) {
+ Log.w(getOwner().getTag(), "registration " + this + " removed", e);
+ remove();
+ } else {
+ super.onOperationFailure(operation, e);
+ }
+ }
+
+ @Override
public void onCancelled(PendingIntent intent) {
- if (Log.isLoggable(mTag, Log.DEBUG)) {
- Log.d(mTag, "pending intent registration " + getIdentity() + " canceled");
+ if (Log.isLoggable(getOwner().getTag(), Log.DEBUG)) {
+ Log.d(getOwner().getTag(),
+ "pending intent registration " + getIdentity() + " canceled");
}
remove();
diff --git a/services/core/java/com/android/server/location/listeners/RemoteListenerRegistration.java b/services/core/java/com/android/server/location/listeners/RemoteListenerRegistration.java
index e4b0b190d34c..242bf323f6cd 100644
--- a/services/core/java/com/android/server/location/listeners/RemoteListenerRegistration.java
+++ b/services/core/java/com/android/server/location/listeners/RemoteListenerRegistration.java
@@ -24,6 +24,7 @@ import android.location.util.identity.CallerIdentity;
import android.os.Process;
import com.android.internal.annotations.VisibleForTesting;
+import com.android.internal.listeners.ListenerExecutor.ListenerOperation;
import com.android.server.FgThread;
import java.util.Objects;
@@ -35,11 +36,13 @@ import java.util.concurrent.Executor;
* invocation should already be asynchronous. Listeners from the same process will be run on a
* normal executor, since in-process listener invocation may be synchronous.
*
- * @param <TRequest> request type
- * @param <TListener> listener type
+ * @param <TRequest> request type
+ * @param <TListener> listener type
+ * @param <TListenerOperation> listener operation type
*/
-public abstract class RemoteListenerRegistration<TRequest, TListener> extends
- RemovableListenerRegistration<TRequest, TListener> {
+public abstract class RemoteListenerRegistration<TRequest, TListener,
+ TListenerOperation extends ListenerOperation<TListener>> extends
+ RemovableListenerRegistration<TRequest, TListener, TListenerOperation> {
@VisibleForTesting
public static final Executor IN_PROCESS_EXECUTOR = FgThread.getExecutor();
@@ -59,9 +62,9 @@ public abstract class RemoteListenerRegistration<TRequest, TListener> extends
private final CallerIdentity mIdentity;
- protected RemoteListenerRegistration(String tag, @Nullable TRequest request,
- CallerIdentity identity, TListener listener) {
- super(tag, chooseExecutor(identity), request, listener);
+ protected RemoteListenerRegistration(@Nullable TRequest request, CallerIdentity identity,
+ TListener listener) {
+ super(chooseExecutor(identity), request, listener);
mIdentity = Objects.requireNonNull(identity);
}
diff --git a/services/core/java/com/android/server/location/listeners/RemovableListenerRegistration.java b/services/core/java/com/android/server/location/listeners/RemovableListenerRegistration.java
index 2383bece4e0a..d3b5f6696167 100644
--- a/services/core/java/com/android/server/location/listeners/RemovableListenerRegistration.java
+++ b/services/core/java/com/android/server/location/listeners/RemovableListenerRegistration.java
@@ -17,7 +17,8 @@
package com.android.server.location.listeners;
import android.annotation.Nullable;
-import android.util.Log;
+
+import com.android.internal.listeners.ListenerExecutor.ListenerOperation;
import java.util.Objects;
import java.util.concurrent.Executor;
@@ -26,20 +27,19 @@ import java.util.concurrent.Executor;
* A listener registration that stores its own key, and thus can remove itself. By default it will
* remove itself if any checked exception occurs on listener execution.
*
- * @param <TRequest> request type
- * @param <TListener> listener type
+ * @param <TRequest> request type
+ * @param <TListener> listener type
+ * @param <TListenerOperation> listener operation type
*/
-public abstract class RemovableListenerRegistration<TRequest, TListener> extends
- ListenerRegistration<TRequest, TListener> {
-
- protected final String mTag;
+public abstract class RemovableListenerRegistration<TRequest, TListener,
+ TListenerOperation extends ListenerOperation<TListener>> extends
+ RequestListenerRegistration<TRequest, TListener, TListenerOperation> {
private volatile @Nullable Object mKey;
- protected RemovableListenerRegistration(String tag, Executor executor,
- @Nullable TRequest request, TListener listener) {
+ protected RemovableListenerRegistration(Executor executor, @Nullable TRequest request,
+ TListener listener) {
super(executor, request, listener);
- mTag = Objects.requireNonNull(tag);
}
/**
@@ -47,7 +47,8 @@ public abstract class RemovableListenerRegistration<TRequest, TListener> extends
* with. Often this is easiest to accomplish by defining registration subclasses as non-static
* inner classes of the multiplexer they are to be used with.
*/
- protected abstract ListenerMultiplexer<?, ? super TRequest, ? super TListener, ?, ?> getOwner();
+ protected abstract ListenerMultiplexer<?, ? super TListener, ?
+ super TListenerOperation, ?, ?> getOwner();
/**
* Returns the key associated with this registration. May not be invoked before
@@ -69,12 +70,6 @@ public abstract class RemovableListenerRegistration<TRequest, TListener> extends
}
@Override
- public <Listener> void onOperationFailure(ListenerOperation<Listener> operation, Exception e) {
- Log.w(mTag, "registration " + this + " removed due to unexpected exception", e);
- remove();
- }
-
- @Override
protected final void onRegister(Object key) {
mKey = Objects.requireNonNull(key);
onRemovableListenerRegister();
diff --git a/services/core/java/com/android/server/location/listeners/RequestListenerRegistration.java b/services/core/java/com/android/server/location/listeners/RequestListenerRegistration.java
new file mode 100644
index 000000000000..d97abae59dd3
--- /dev/null
+++ b/services/core/java/com/android/server/location/listeners/RequestListenerRegistration.java
@@ -0,0 +1,58 @@
+/*
+ * 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.location.listeners;
+
+import com.android.internal.listeners.ListenerExecutor.ListenerOperation;
+
+import java.util.concurrent.Executor;
+
+/**
+ * A listener registration object which includes an associated request.
+ *
+ * @param <TRequest> request type
+ * @param <TListener> listener type
+ * @param <TListenerOperation> listener operation type
+ */
+public class RequestListenerRegistration<TRequest, TListener,
+ TListenerOperation extends ListenerOperation<TListener>> extends
+ ListenerRegistration<TListener, TListenerOperation> {
+
+ private final TRequest mRequest;
+
+ protected RequestListenerRegistration(Executor executor, TRequest request,
+ TListener listener) {
+ super(executor, listener);
+ mRequest = request;
+ }
+
+ /**
+ * Returns the request associated with this listener, or null if one wasn't supplied.
+ */
+ public TRequest getRequest() {
+ return mRequest;
+ }
+
+ @Override
+ public String toString() {
+ if (mRequest == null) {
+ return "[]";
+ } else {
+ return mRequest.toString();
+ }
+ }
+}
+
diff --git a/services/core/java/com/android/server/location/timezone/BinderLocationTimeZoneProvider.java b/services/core/java/com/android/server/location/timezone/BinderLocationTimeZoneProvider.java
new file mode 100644
index 000000000000..92dabe337d87
--- /dev/null
+++ b/services/core/java/com/android/server/location/timezone/BinderLocationTimeZoneProvider.java
@@ -0,0 +1,214 @@
+/*
+ * 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.location.timezone;
+
+import static com.android.server.location.timezone.LocationTimeZoneManagerService.debugLog;
+import static com.android.server.location.timezone.LocationTimeZoneProvider.ProviderState.PROVIDER_STATE_DISABLED;
+import static com.android.server.location.timezone.LocationTimeZoneProvider.ProviderState.PROVIDER_STATE_ENABLED;
+import static com.android.server.location.timezone.LocationTimeZoneProvider.ProviderState.PROVIDER_STATE_PERM_FAILED;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.location.timezone.LocationTimeZoneEvent;
+import android.util.IndentingPrintWriter;
+import android.util.Slog;
+
+import com.android.internal.location.timezone.LocationTimeZoneProviderRequest;
+
+import java.util.Objects;
+
+/**
+ * The real, system-server side implementation of a binder call backed {@link
+ * LocationTimeZoneProvider}. It handles keeping track of current state, timeouts and ensuring
+ * events are passed to the {@link LocationTimeZoneProviderController} on the required thread.
+ */
+class BinderLocationTimeZoneProvider extends LocationTimeZoneProvider {
+
+ private static final String TAG = LocationTimeZoneManagerService.TAG;
+
+ @NonNull private final LocationTimeZoneProviderProxy mProxy;
+
+ BinderLocationTimeZoneProvider(
+ @NonNull ThreadingDomain threadingDomain,
+ @NonNull String providerName,
+ @NonNull LocationTimeZoneProviderProxy proxy) {
+ super(threadingDomain, providerName);
+ mProxy = Objects.requireNonNull(proxy);
+ }
+
+ @Override
+ void onInitialize() {
+ mProxy.setListener(new LocationTimeZoneProviderProxy.Listener() {
+ @Override
+ public void onReportLocationTimeZoneEvent(
+ @NonNull LocationTimeZoneEvent locationTimeZoneEvent) {
+ handleLocationTimeZoneEvent(locationTimeZoneEvent);
+ }
+
+ @Override
+ public void onProviderBound() {
+ handleOnProviderBound();
+ }
+
+ @Override
+ public void onProviderUnbound() {
+ handleProviderLost("onProviderUnbound()");
+ }
+ });
+ }
+
+ private void handleProviderLost(String reason) {
+ mThreadingDomain.assertCurrentThread();
+
+ synchronized (mSharedLock) {
+ ProviderState currentState = mCurrentState.get();
+ switch (currentState.stateEnum) {
+ case PROVIDER_STATE_ENABLED: {
+ // Losing a remote provider is treated as becoming uncertain.
+ String msg = "handleProviderLost reason=" + reason
+ + ", mProviderName=" + mProviderName
+ + ", currentState=" + currentState;
+ debugLog(msg);
+ // This is an unusual PROVIDER_STATE_ENABLED state because event == null
+ ProviderState newState = currentState.newState(
+ PROVIDER_STATE_ENABLED, null, currentState.currentUserConfiguration,
+ msg);
+ setCurrentState(newState, true);
+ break;
+ }
+ case PROVIDER_STATE_DISABLED: {
+ debugLog("handleProviderLost reason=" + reason
+ + ", mProviderName=" + mProviderName
+ + ", currentState=" + currentState
+ + ": No state change required, provider is disabled.");
+ break;
+ }
+ case PROVIDER_STATE_PERM_FAILED: {
+ debugLog("handleProviderLost reason=" + reason
+ + ", mProviderName=" + mProviderName
+ + ", currentState=" + currentState
+ + ": No state change required, provider is perm failed.");
+ break;
+ }
+ default: {
+ throw new IllegalStateException("Unknown currentState=" + currentState);
+ }
+ }
+ }
+ }
+
+ private void handleOnProviderBound() {
+ mThreadingDomain.assertCurrentThread();
+
+ synchronized (mSharedLock) {
+ ProviderState currentState = mCurrentState.get();
+ switch (currentState.stateEnum) {
+ case PROVIDER_STATE_ENABLED: {
+ debugLog("handleOnProviderBound mProviderName=" + mProviderName
+ + ", currentState=" + currentState + ": Provider is enabled.");
+ break;
+ }
+ case PROVIDER_STATE_DISABLED: {
+ debugLog("handleOnProviderBound mProviderName=" + mProviderName
+ + ", currentState=" + currentState + ": Provider is disabled.");
+ break;
+ }
+ case PROVIDER_STATE_PERM_FAILED: {
+ debugLog("handleOnProviderBound"
+ + ", mProviderName=" + mProviderName
+ + ", currentState=" + currentState
+ + ": No state change required, provider is perm failed.");
+ break;
+ }
+ default: {
+ throw new IllegalStateException("Unknown currentState=" + currentState);
+ }
+ }
+ }
+ }
+
+ @Override
+ void onEnable() {
+ // Set a request on the proxy - it will be sent immediately if the service is bound,
+ // or will be sent as soon as the service becomes bound.
+ // TODO(b/152744911): Decide whether to send a timeout so the provider knows how long
+ // it has to generate the first event before it could be bypassed.
+ LocationTimeZoneProviderRequest request =
+ new LocationTimeZoneProviderRequest.Builder()
+ .setReportLocationTimeZone(true)
+ .build();
+ mProxy.setRequest(request);
+ }
+
+ @Override
+ void onDisable() {
+ LocationTimeZoneProviderRequest request =
+ new LocationTimeZoneProviderRequest.Builder()
+ .setReportLocationTimeZone(false)
+ .build();
+ mProxy.setRequest(request);
+ }
+
+ @Override
+ void logWarn(String msg) {
+ Slog.w(TAG, msg);
+ }
+
+ @Override
+ public void dump(@NonNull IndentingPrintWriter ipw, @Nullable String[] args) {
+ synchronized (mSharedLock) {
+ ipw.println("{BinderLocationTimeZoneProvider}");
+ ipw.println("mProviderName=" + mProviderName);
+ ipw.println("mCurrentState=" + mCurrentState);
+ ipw.println("mProxy=" + mProxy);
+
+ ipw.println("State history:");
+ ipw.increaseIndent();
+ mCurrentState.dump(ipw);
+ ipw.decreaseIndent();
+
+ ipw.println("Proxy details:");
+ ipw.increaseIndent();
+ mProxy.dump(ipw, args);
+ ipw.decreaseIndent();
+ }
+ }
+
+ @Override
+ public String toString() {
+ synchronized (mSharedLock) {
+ return "BinderLocationTimeZoneProvider{"
+ + "mProviderName=" + mProviderName
+ + "mCurrentState=" + mCurrentState
+ + "mProxy=" + mProxy
+ + '}';
+ }
+ }
+
+ /**
+ * Passes the supplied simulation / testing event to the current proxy iff the proxy is a
+ * {@link SimulatedLocationTimeZoneProviderProxy}. If not, the event is logged but discarded.
+ */
+ void simulateBinderProviderEvent(SimulatedBinderProviderEvent event) {
+ if (!(mProxy instanceof SimulatedLocationTimeZoneProviderProxy)) {
+ Slog.w(TAG, mProxy + " is not a " + SimulatedLocationTimeZoneProviderProxy.class
+ + ", event=" + event);
+ return;
+ }
+ ((SimulatedLocationTimeZoneProviderProxy) mProxy).simulate(event);
+ }
+}
diff --git a/services/core/java/com/android/server/location/timezone/ControllerCallbackImpl.java b/services/core/java/com/android/server/location/timezone/ControllerCallbackImpl.java
new file mode 100644
index 000000000000..cd9aa2fd6a5b
--- /dev/null
+++ b/services/core/java/com/android/server/location/timezone/ControllerCallbackImpl.java
@@ -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.server.location.timezone;
+
+import android.annotation.NonNull;
+
+import com.android.server.LocalServices;
+import com.android.server.timezonedetector.GeolocationTimeZoneSuggestion;
+import com.android.server.timezonedetector.TimeZoneDetectorInternal;
+
+/**
+ * The real implementation of {@link LocationTimeZoneProviderController.Callback} used by
+ * {@link ControllerImpl} to interact with other server components.
+ */
+class ControllerCallbackImpl extends LocationTimeZoneProviderController.Callback {
+
+ ControllerCallbackImpl(@NonNull ThreadingDomain threadingDomain) {
+ super(threadingDomain);
+ }
+
+ @Override
+ void suggest(@NonNull GeolocationTimeZoneSuggestion suggestion) {
+ mThreadingDomain.assertCurrentThread();
+
+ TimeZoneDetectorInternal timeZoneDetector =
+ LocalServices.getService(TimeZoneDetectorInternal.class);
+ timeZoneDetector.suggestGeolocationTimeZone(suggestion);
+ }
+}
diff --git a/services/core/java/com/android/server/location/timezone/ControllerEnvironmentImpl.java b/services/core/java/com/android/server/location/timezone/ControllerEnvironmentImpl.java
new file mode 100644
index 000000000000..2e2481c30f22
--- /dev/null
+++ b/services/core/java/com/android/server/location/timezone/ControllerEnvironmentImpl.java
@@ -0,0 +1,51 @@
+/*
+ * 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.location.timezone;
+
+import android.annotation.NonNull;
+
+import com.android.server.LocalServices;
+import com.android.server.timezonedetector.ConfigurationInternal;
+import com.android.server.timezonedetector.TimeZoneDetectorInternal;
+
+import java.util.Objects;
+
+/**
+ * The real implementation of {@link LocationTimeZoneProviderController.Environment} used by
+ * {@link ControllerImpl} to interact with other server components.
+ */
+class ControllerEnvironmentImpl extends LocationTimeZoneProviderController.Environment {
+
+ @NonNull private final TimeZoneDetectorInternal mTimeZoneDetectorInternal;
+ @NonNull private final LocationTimeZoneProviderController mController;
+
+ ControllerEnvironmentImpl(@NonNull ThreadingDomain threadingDomain,
+ @NonNull LocationTimeZoneProviderController controller) {
+ super(threadingDomain);
+ mController = Objects.requireNonNull(controller);
+ mTimeZoneDetectorInternal = LocalServices.getService(TimeZoneDetectorInternal.class);
+
+ // Listen for configuration changes.
+ mTimeZoneDetectorInternal.addConfigurationListener(
+ () -> mThreadingDomain.post(mController::onConfigChanged));
+ }
+
+ @Override
+ ConfigurationInternal getCurrentUserConfigurationInternal() {
+ return mTimeZoneDetectorInternal.getCurrentUserConfigurationInternal();
+ }
+}
diff --git a/services/core/java/com/android/server/location/timezone/ControllerImpl.java b/services/core/java/com/android/server/location/timezone/ControllerImpl.java
new file mode 100644
index 000000000000..e31cfc4e8b40
--- /dev/null
+++ b/services/core/java/com/android/server/location/timezone/ControllerImpl.java
@@ -0,0 +1,420 @@
+/*
+ * 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.location.timezone;
+
+import static android.location.timezone.LocationTimeZoneEvent.EVENT_TYPE_PERMANENT_FAILURE;
+import static android.location.timezone.LocationTimeZoneEvent.EVENT_TYPE_SUCCESS;
+import static android.location.timezone.LocationTimeZoneEvent.EVENT_TYPE_UNCERTAIN;
+
+import static com.android.server.location.timezone.LocationTimeZoneManagerService.debugLog;
+import static com.android.server.location.timezone.LocationTimeZoneManagerService.warnLog;
+import static com.android.server.location.timezone.LocationTimeZoneProvider.ProviderState;
+import static com.android.server.location.timezone.LocationTimeZoneProvider.ProviderState.PROVIDER_STATE_DISABLED;
+import static com.android.server.location.timezone.LocationTimeZoneProvider.ProviderState.PROVIDER_STATE_ENABLED;
+import static com.android.server.location.timezone.LocationTimeZoneProvider.ProviderState.PROVIDER_STATE_PERM_FAILED;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.location.timezone.LocationTimeZoneEvent;
+import android.util.IndentingPrintWriter;
+
+import com.android.internal.annotations.GuardedBy;
+import com.android.internal.annotations.VisibleForTesting;
+import com.android.server.location.timezone.ThreadingDomain.SingleRunnableQueue;
+import com.android.server.timezonedetector.ConfigurationInternal;
+import com.android.server.timezonedetector.GeolocationTimeZoneSuggestion;
+
+import java.time.Duration;
+import java.util.Objects;
+
+/**
+ * A real implementation of {@link LocationTimeZoneProviderController} that supports a single
+ * {@link LocationTimeZoneProvider}.
+ *
+ * TODO(b/152744911): This implementation currently only supports a single ("primary") provider.
+ * Support for a secondary provider will be added in a later commit.
+ */
+class ControllerImpl extends LocationTimeZoneProviderController {
+
+ @VisibleForTesting
+ static final Duration UNCERTAINTY_DELAY = Duration.ofMinutes(5);
+
+ @NonNull private final LocationTimeZoneProvider mProvider;
+ @NonNull private final SingleRunnableQueue mDelayedSuggestionQueue;
+
+ @GuardedBy("mSharedLock")
+ // Non-null after initialize()
+ private ConfigurationInternal mCurrentUserConfiguration;
+
+ @GuardedBy("mSharedLock")
+ // Non-null after initialize()
+ private Environment mEnvironment;
+
+ @GuardedBy("mSharedLock")
+ // Non-null after initialize()
+ private Callback mCallback;
+
+ /**
+ * Contains any currently pending suggestion on {@link #mDelayedSuggestionQueue}, if there is
+ * one.
+ */
+ @GuardedBy("mSharedLock")
+ @Nullable
+ private GeolocationTimeZoneSuggestion mPendingSuggestion;
+
+ /** Contains the last suggestion actually made, if there is one. */
+ @GuardedBy("mSharedLock")
+ @Nullable
+ private GeolocationTimeZoneSuggestion mLastSuggestion;
+
+ ControllerImpl(@NonNull ThreadingDomain threadingDomain,
+ @NonNull LocationTimeZoneProvider provider) {
+ super(threadingDomain);
+ mDelayedSuggestionQueue = threadingDomain.createSingleRunnableQueue();
+ mProvider = Objects.requireNonNull(provider);
+ }
+
+ @Override
+ void initialize(@NonNull Environment environment, @NonNull Callback callback) {
+ mThreadingDomain.assertCurrentThread();
+
+ synchronized (mSharedLock) {
+ debugLog("initialize()");
+ mEnvironment = Objects.requireNonNull(environment);
+ mCallback = Objects.requireNonNull(callback);
+ mCurrentUserConfiguration = environment.getCurrentUserConfigurationInternal();
+
+ mProvider.initialize(ControllerImpl.this::onProviderStateChange);
+ enableOrDisableProvider(mCurrentUserConfiguration);
+ }
+ }
+
+ @Override
+ void onConfigChanged() {
+ mThreadingDomain.assertCurrentThread();
+
+ synchronized (mSharedLock) {
+ debugLog("onEnvironmentConfigChanged()");
+
+ ConfigurationInternal oldConfig = mCurrentUserConfiguration;
+ ConfigurationInternal newConfig = mEnvironment.getCurrentUserConfigurationInternal();
+ mCurrentUserConfiguration = newConfig;
+
+ if (!newConfig.equals(oldConfig)) {
+ if (newConfig.getUserId() != oldConfig.getUserId()) {
+ // If the user changed, disable the provider if needed. It may be re-enabled for
+ // the new user below if their settings allow.
+ debugLog("User changed. old=" + oldConfig.getUserId()
+ + ", new=" + newConfig.getUserId());
+ debugLog("Disabling LocationTimeZoneProviders as needed");
+ if (mProvider.getCurrentState().stateEnum == PROVIDER_STATE_ENABLED) {
+ mProvider.disable();
+ }
+ }
+
+ enableOrDisableProvider(newConfig);
+ }
+ }
+ }
+
+ @GuardedBy("mSharedLock")
+ private void enableOrDisableProvider(@NonNull ConfigurationInternal configuration) {
+ ProviderState providerState = mProvider.getCurrentState();
+ boolean geoDetectionEnabled = configuration.getGeoDetectionEnabledBehavior();
+ boolean providerWasEnabled = providerState.stateEnum == PROVIDER_STATE_ENABLED;
+ if (geoDetectionEnabled) {
+ switch (providerState.stateEnum) {
+ case PROVIDER_STATE_DISABLED: {
+ debugLog("Enabling " + mProvider);
+ mProvider.enable(configuration);
+ break;
+ }
+ case PROVIDER_STATE_ENABLED: {
+ debugLog("No need to enable " + mProvider + ": already enabled");
+ break;
+ }
+ case PROVIDER_STATE_PERM_FAILED: {
+ debugLog("Unable to enable " + mProvider + ": it is perm failed");
+ break;
+ }
+ default:
+ warnLog("Unknown provider state: " + mProvider);
+ break;
+ }
+ } else {
+ switch (providerState.stateEnum) {
+ case PROVIDER_STATE_DISABLED: {
+ debugLog("No need to disable " + mProvider + ": already enabled");
+ break;
+ }
+ case PROVIDER_STATE_ENABLED: {
+ debugLog("Disabling " + mProvider);
+ mProvider.disable();
+ break;
+ }
+ case PROVIDER_STATE_PERM_FAILED: {
+ debugLog("Unable to disable " + mProvider + ": it is perm failed");
+ break;
+ }
+ default: {
+ warnLog("Unknown provider state: " + mProvider);
+ break;
+ }
+ }
+ }
+
+ boolean isProviderEnabled =
+ mProvider.getCurrentState().stateEnum == PROVIDER_STATE_ENABLED;
+
+ if (isProviderEnabled) {
+ if (!providerWasEnabled) {
+ // When a provider has first been enabled, we allow it some time for it to
+ // initialize.
+ // This sets up an empty suggestion to trigger if no explicit "certain" or
+ // "uncertain" suggestion preempts it within UNCERTAINTY_DELAY. If, for some reason,
+ // the provider does provide any events then this scheduled suggestion will ensure
+ // the controller makes at least an uncertain suggestion.
+ suggestDelayed(createEmptySuggestion(
+ "No event received in delay=" + UNCERTAINTY_DELAY), UNCERTAINTY_DELAY);
+ }
+ } else {
+ // Clear any queued suggestions.
+ clearDelayedSuggestion();
+
+ // If the provider is now not enabled, and a previous "certain" suggestion has been
+ // made, then a new "uncertain" suggestion must be made to indicate the provider no
+ // longer has an opinion and will not be sending updates.
+ if (mLastSuggestion != null && mLastSuggestion.getZoneIds() != null) {
+ suggestImmediate(createEmptySuggestion(""));
+ }
+ }
+ }
+
+ void onProviderStateChange(@NonNull ProviderState providerState) {
+ mThreadingDomain.assertCurrentThread();
+ assertProviderKnown(providerState.provider);
+
+ synchronized (mSharedLock) {
+ switch (providerState.stateEnum) {
+ case PROVIDER_STATE_DISABLED: {
+ // This should never happen: entering disabled does not trigger an event.
+ warnLog("onProviderStateChange: Unexpected state change for disabled provider,"
+ + " providerState=" + providerState);
+ break;
+ }
+ case PROVIDER_STATE_ENABLED: {
+ // Entering enabled does not trigger an event, so this only happens if an event
+ // is received while the provider is enabled.
+ debugLog("onProviderStateChange: Received notification of an event while"
+ + " enabled, providerState=" + providerState);
+ providerEnabledProcessEvent(providerState);
+ break;
+ }
+ case PROVIDER_STATE_PERM_FAILED: {
+ debugLog("Received notification of permanent failure for"
+ + " provider=" + providerState);
+ GeolocationTimeZoneSuggestion suggestion = createEmptySuggestion(
+ "provider=" + providerState.provider
+ + " permanently failed: " + providerState);
+ suggestImmediate(suggestion);
+ break;
+ }
+ default: {
+ warnLog("onProviderStateChange: Unexpected providerState=" + providerState);
+ }
+ }
+ }
+ }
+
+ private void assertProviderKnown(LocationTimeZoneProvider provider) {
+ if (provider != mProvider) {
+ throw new IllegalArgumentException("Unknown provider: " + provider);
+ }
+ }
+
+ /**
+ * Called when a provider has changed state but just moved from a PROVIDER_STATE_ENABLED state
+ * to another PROVIDER_STATE_ENABLED state, usually as a result of a new {@link
+ * LocationTimeZoneEvent} being received. There are some cases where event can be null.
+ */
+ private void providerEnabledProcessEvent(@NonNull ProviderState providerState) {
+ LocationTimeZoneEvent event = providerState.event;
+ if (event == null) {
+ // Implicit uncertainty, i.e. where the provider is enabled, but a problem has been
+ // detected without having received an event. For example, if the process has detected
+ // the loss of a binder-based provider. This is treated like explicit uncertainty, i.e.
+ // where the provider has explicitly told this process it is uncertain.
+ scheduleUncertainSuggestionIfNeeded(null);
+ return;
+ }
+
+ // Consistency check for user. This may be possible as there are various races around
+ // current user switches.
+ if (!Objects.equals(event.getUserHandle(), mCurrentUserConfiguration.getUserHandle())) {
+ warnLog("Using event=" + event + " from a different user="
+ + mCurrentUserConfiguration);
+ }
+
+ if (!mCurrentUserConfiguration.getGeoDetectionEnabledBehavior()) {
+ // This should not happen: the provider should not be in an enabled state if the user
+ // does not have geodetection enabled.
+ warnLog("Provider=" + providerState + " is enabled, but currentUserConfiguration="
+ + mCurrentUserConfiguration + " suggests it shouldn't be.");
+ }
+
+ switch (event.getEventType()) {
+ case EVENT_TYPE_PERMANENT_FAILURE: {
+ // This shouldn't happen. Providers cannot be enabled and have this event.
+ warnLog("Provider=" + providerState
+ + " is enabled, but event suggests it shouldn't be");
+ break;
+ }
+ case EVENT_TYPE_UNCERTAIN: {
+ scheduleUncertainSuggestionIfNeeded(event);
+ break;
+ }
+ case EVENT_TYPE_SUCCESS: {
+ GeolocationTimeZoneSuggestion suggestion =
+ new GeolocationTimeZoneSuggestion(event.getTimeZoneIds());
+ suggestion.addDebugInfo("Event received provider=" + mProvider.getName()
+ + ", event=" + event);
+ // Rely on the receiver to dedupe events. It is better to over-communicate.
+ suggestImmediate(suggestion);
+ break;
+ }
+ default: {
+ warnLog("Unknown eventType=" + event.getEventType());
+ break;
+ }
+ }
+ }
+
+ /**
+ * Indicates a provider has become uncertain with the event (if any) received that indicates
+ * that.
+ *
+ * <p>Providers are expected to report their uncertainty as soon as they become uncertain, as
+ * this enables the most flexibility for the controller to enable other providers when there are
+ * multiple ones. The controller is therefore responsible for deciding when to make a
+ * "uncertain" suggestion.
+ *
+ * <p>This method schedules an "uncertain" suggestion (if one isn't already scheduled) to be
+ * made later if nothing else preempts it. It can be preempted if the provider becomes certain
+ * (or does anything else that calls {@link #suggestImmediate(GeolocationTimeZoneSuggestion)})
+ * within UNCERTAINTY_DELAY. Preemption causes the scheduled "uncertain" event to be cancelled.
+ * If the provider repeatedly sends uncertainty events within UNCERTAINTY_DELAY, those events
+ * are effectively ignored (i.e. the timer is not reset each time).
+ */
+ private void scheduleUncertainSuggestionIfNeeded(@Nullable LocationTimeZoneEvent event) {
+ if (mPendingSuggestion == null || mPendingSuggestion.getZoneIds() != null) {
+ GeolocationTimeZoneSuggestion suggestion = createEmptySuggestion(
+ "provider=" + mProvider + " became uncertain, event=" + event);
+ suggestDelayed(suggestion, UNCERTAINTY_DELAY);
+ }
+ }
+
+ @Override
+ public void dump(@NonNull IndentingPrintWriter ipw, @Nullable String[] args) {
+ synchronized (mSharedLock) {
+ ipw.println("LocationTimeZoneProviderController:");
+
+ ipw.increaseIndent(); // level 1
+ ipw.println("mCurrentUserConfiguration=" + mCurrentUserConfiguration);
+ ipw.println("mPendingSuggestion=" + mPendingSuggestion);
+ ipw.println("mLastSuggestion=" + mLastSuggestion);
+
+ ipw.println("Provider:");
+ ipw.increaseIndent(); // level 2
+ mProvider.dump(ipw, args);
+ ipw.decreaseIndent(); // level 2
+
+ ipw.decreaseIndent(); // level 1
+ }
+ }
+
+ /** Sends an immediate suggestion, cancelling any pending suggestion. */
+ @GuardedBy("mSharedLock")
+ private void suggestImmediate(@NonNull GeolocationTimeZoneSuggestion suggestion) {
+ debugLog("suggestImmediate: Executing suggestion=" + suggestion);
+ mDelayedSuggestionQueue.runSynchronously(() -> mCallback.suggest(suggestion));
+ mPendingSuggestion = null;
+ mLastSuggestion = suggestion;
+ }
+
+ /** Clears any pending suggestion. */
+ @GuardedBy("mSharedLock")
+ private void clearDelayedSuggestion() {
+ mDelayedSuggestionQueue.cancel();
+ mPendingSuggestion = null;
+ }
+
+
+ /**
+ * Schedules a delayed suggestion. There can only be one delayed suggestion at a time.
+ * If there is a pending scheduled suggestion equal to the one passed, it will not be replaced.
+ * Replacing a previous delayed suggestion has the effect of cancelling the timeout associated
+ * with that previous suggestion.
+ */
+ @GuardedBy("mSharedLock")
+ private void suggestDelayed(@NonNull GeolocationTimeZoneSuggestion suggestion,
+ @NonNull Duration delay) {
+ Objects.requireNonNull(suggestion);
+ Objects.requireNonNull(delay);
+
+ if (Objects.equals(mPendingSuggestion, suggestion)) {
+ // Do not reset the timer.
+ debugLog("suggestDelayed: Suggestion=" + suggestion + " is equal to existing."
+ + " Not scheduled.");
+ return;
+ }
+
+ debugLog("suggestDelayed: Scheduling suggestion=" + suggestion);
+ mPendingSuggestion = suggestion;
+
+ mDelayedSuggestionQueue.runDelayed(() -> {
+ debugLog("suggestDelayed: Executing suggestion=" + suggestion);
+ mCallback.suggest(suggestion);
+ mPendingSuggestion = null;
+ mLastSuggestion = suggestion;
+ }, delay.toMillis());
+ }
+
+ private static GeolocationTimeZoneSuggestion createEmptySuggestion(String reason) {
+ GeolocationTimeZoneSuggestion suggestion = new GeolocationTimeZoneSuggestion(null);
+ suggestion.addDebugInfo(reason);
+ return suggestion;
+ }
+
+ /**
+ * Asynchronously passes a {@link SimulatedBinderProviderEvent] to the appropriate provider.
+ * If the provider name does not match a known provider, then the event is logged and discarded.
+ */
+ void simulateBinderProviderEvent(SimulatedBinderProviderEvent event) {
+ if (!Objects.equals(mProvider.getName(), event.getProviderName())) {
+ warnLog("Unable to process simulated binder provider event,"
+ + " unknown providerName in event=" + event);
+ return;
+ }
+ if (!(mProvider instanceof BinderLocationTimeZoneProvider)) {
+ warnLog("Unable to process simulated binder provider event,"
+ + " provider is not a " + BinderLocationTimeZoneProvider.class
+ + ", event=" + event);
+ return;
+ }
+ ((BinderLocationTimeZoneProvider) mProvider).simulateBinderProviderEvent(event);
+ }
+}
diff --git a/services/core/java/com/android/server/location/timezone/HandlerThreadingDomain.java b/services/core/java/com/android/server/location/timezone/HandlerThreadingDomain.java
new file mode 100644
index 000000000000..17e719ed2cb0
--- /dev/null
+++ b/services/core/java/com/android/server/location/timezone/HandlerThreadingDomain.java
@@ -0,0 +1,73 @@
+/*
+ * 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.location.timezone;
+
+import android.annotation.NonNull;
+import android.os.Handler;
+
+import java.util.Objects;
+
+/**
+ * The real implementation of {@link ThreadingDomain} that uses a {@link Handler}.
+ */
+final class HandlerThreadingDomain extends ThreadingDomain {
+
+ @NonNull private final Handler mHandler;
+
+ HandlerThreadingDomain(Handler handler) {
+ mHandler = Objects.requireNonNull(handler);
+ }
+
+ /**
+ * Returns the {@link Handler} associated with this threading domain. The same {@link Handler}
+ * may be associated with multiple threading domains, e.g. multiple threading domains could
+ * choose to use the {@link com.android.server.FgThread} handler.
+ *
+ * <p>If you find yourself making this public because you need a {@link Handler}, then it may
+ * cause problems with testability. Try to avoid using this method and use methods like {@link
+ * #post(Runnable)} instead.
+ */
+ @NonNull
+ Handler getHandler() {
+ return mHandler;
+ }
+
+ @NonNull
+ Thread getThread() {
+ return getHandler().getLooper().getThread();
+ }
+
+ @Override
+ void post(@NonNull Runnable r) {
+ getHandler().post(r);
+ }
+
+ @Override
+ void postDelayed(@NonNull Runnable r, long delayMillis) {
+ getHandler().postDelayed(r, delayMillis);
+ }
+
+ @Override
+ void postDelayed(Runnable r, Object token, long delayMillis) {
+ getHandler().postDelayed(r, token, delayMillis);
+ }
+
+ @Override
+ void removeQueuedRunnables(Object token) {
+ getHandler().removeCallbacksAndMessages(token);
+ }
+}
diff --git a/services/core/java/com/android/server/location/timezone/LocationTimeZoneManagerService.java b/services/core/java/com/android/server/location/timezone/LocationTimeZoneManagerService.java
new file mode 100644
index 000000000000..238f999ff8a6
--- /dev/null
+++ b/services/core/java/com/android/server/location/timezone/LocationTimeZoneManagerService.java
@@ -0,0 +1,269 @@
+/*
+ * 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.location.timezone;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.content.Context;
+import android.os.Binder;
+import android.os.ResultReceiver;
+import android.os.ShellCallback;
+import android.os.SystemProperties;
+import android.util.IndentingPrintWriter;
+import android.util.Log;
+import android.util.Slog;
+
+import com.android.internal.annotations.GuardedBy;
+import com.android.internal.util.DumpUtils;
+import com.android.server.FgThread;
+import com.android.server.SystemService;
+import com.android.server.timezonedetector.TimeZoneDetectorInternal;
+import com.android.server.timezonedetector.TimeZoneDetectorService;
+
+import java.io.FileDescriptor;
+import java.io.PrintWriter;
+import java.util.Objects;
+
+/**
+ * A service class that acts as a container for the {@link LocationTimeZoneProviderController},
+ * which determines what {@link com.android.server.timezonedetector.GeolocationTimeZoneSuggestion}
+ * are made to the {@link TimeZoneDetectorInternal}, and the {@link LocationTimeZoneProvider}s that
+ * offer {@link android.location.timezone.LocationTimeZoneEvent}s.
+ *
+ * TODO(b/152744911): This implementation currently only supports a primary provider. Support for a
+ * secondary provider must be added in a later commit.
+ *
+ * <p>Implementation details:
+ *
+ * <p>For simplicity, with the exception of a few outliers like {@link #dump}, all processing in
+ * this service (and package-private helper objects) takes place on a single thread / handler, the
+ * one indicated by {@link ThreadingDomain}. Because methods like {@link #dump} can be invoked on
+ * another thread, the service and its related objects must still be thread-safe.
+ *
+ * <p>For testing / reproduction of bugs, it is possible to put providers into "simulation
+ * mode" where the real binder clients are replaced by {@link
+ * SimulatedLocationTimeZoneProviderProxy}. This means that the real client providers are never
+ * bound (ensuring no real location events will be received) and simulated events / behaviors
+ * can be injected via the command line. To enter simulation mode for a provider, use
+ * "{@code adb shell setprop persist.sys.location_tz_simulation_mode.<provider name> 1}" and reboot.
+ * e.g. "{@code adb shell setprop persist.sys.location_tz_simulation_mode.primary 1}}"
+ * Then use "{@code adb shell cmd location_time_zone_manager help}" for injection. Set the system
+ * properties to "0" and reboot to return to exit simulation mode.
+ */
+public class LocationTimeZoneManagerService extends Binder {
+
+ /**
+ * Controls lifecycle of the {@link LocationTimeZoneManagerService}.
+ */
+ public static class Lifecycle extends SystemService {
+
+ private LocationTimeZoneManagerService mService;
+
+ public Lifecycle(@NonNull Context context) {
+ super(Objects.requireNonNull(context));
+ }
+
+ @Override
+ public void onStart() {
+ if (TimeZoneDetectorService.GEOLOCATION_TIME_ZONE_DETECTION_ENABLED) {
+ Context context = getContext();
+ mService = new LocationTimeZoneManagerService(context);
+
+ // The service currently exposes no LocalService or Binder API, but it extends
+ // Binder and is registered as a binder service so it can receive shell commands.
+ publishBinderService("location_time_zone_manager", mService);
+ } else {
+ Slog.i(TAG, getClass() + " is compile-time disabled");
+ }
+ }
+
+ @Override
+ public void onBootPhase(int phase) {
+ if (TimeZoneDetectorService.GEOLOCATION_TIME_ZONE_DETECTION_ENABLED) {
+ if (phase == PHASE_SYSTEM_SERVICES_READY) {
+ // The location service must be functioning after this boot phase.
+ mService.onSystemReady();
+ } else if (phase == PHASE_THIRD_PARTY_APPS_CAN_START) {
+ // Some providers rely on non-platform code (e.g. gcore), so we wait to
+ // initialize providers until third party code is allowed to run.
+ mService.onSystemThirdPartyAppsCanStart();
+ }
+ }
+ }
+ }
+
+ static final String TAG = "LocationTZDetector";
+
+ static final String PRIMARY_PROVIDER_NAME = "primary";
+
+ private static final String SIMULATION_MODE_SYSTEM_PROPERTY_PREFIX =
+ "persist.sys.location_tz_simulation_mode.";
+
+ private static final String ATTRIBUTION_TAG = "LocationTimeZoneService";
+
+ private static final String PRIMARY_LOCATION_TIME_ZONE_SERVICE_ACTION =
+ "com.android.location.timezone.service.v1.PrimaryLocationTimeZoneProvider";
+
+
+ @NonNull private final Context mContext;
+
+ /**
+ * The {@link ThreadingDomain} used to supply the {@link android.os.Handler} and shared lock
+ * object used by the controller and related components.
+ *
+ * <p>Most operations are executed on the associated handler thread <em>but not all</em>, hence
+ * the requirement for additional synchronization using a shared lock.
+ */
+ @NonNull private final ThreadingDomain mThreadingDomain;
+
+ /** The shared lock from {@link #mThreadingDomain}. */
+ @NonNull private final Object mSharedLock;
+
+ // Lazily initialized. Non-null and effectively final after onSystemThirdPartyAppsCanStart().
+ @GuardedBy("mSharedLock")
+ private ControllerImpl mLocationTimeZoneDetectorController;
+
+ LocationTimeZoneManagerService(Context context) {
+ mContext = context.createAttributionContext(ATTRIBUTION_TAG);
+ mThreadingDomain = new HandlerThreadingDomain(FgThread.getHandler());
+ mSharedLock = mThreadingDomain.getLockObject();
+ }
+
+ void onSystemReady() {
+ // Called on an arbitrary thread during initialization.
+ synchronized (mSharedLock) {
+ // TODO(b/152744911): LocationManagerService watches for packages disappearing. Need to
+ // do anything here?
+
+ // TODO(b/152744911): LocationManagerService watches for foreground app changes. Need to
+ // do anything here?
+ // TODO(b/152744911): LocationManagerService watches screen state. Need to do anything
+ // here?
+ }
+ }
+
+ void onSystemThirdPartyAppsCanStart() {
+ // Called on an arbitrary thread during initialization.
+ synchronized (mSharedLock) {
+ LocationTimeZoneProvider primary = createPrimaryProvider();
+ mLocationTimeZoneDetectorController = new ControllerImpl(mThreadingDomain, primary);
+ ControllerCallbackImpl callback = new ControllerCallbackImpl(mThreadingDomain);
+ ControllerEnvironmentImpl environment = new ControllerEnvironmentImpl(
+ mThreadingDomain, mLocationTimeZoneDetectorController);
+
+ // Initialize the controller on the mThreadingDomain thread: this ensures that the
+ // ThreadingDomain requirements for the controller / environment methods are honored.
+ mThreadingDomain.post(() ->
+ mLocationTimeZoneDetectorController.initialize(environment, callback));
+ }
+ }
+
+ private LocationTimeZoneProvider createPrimaryProvider() {
+ LocationTimeZoneProviderProxy proxy;
+ if (isInSimulationMode(PRIMARY_PROVIDER_NAME)) {
+ proxy = new SimulatedLocationTimeZoneProviderProxy(mContext, mThreadingDomain);
+ } else {
+ // TODO Uncomment this code in a later commit.
+ throw new UnsupportedOperationException("Not implemented");
+ /*
+ proxy = RealLocationTimeZoneProviderProxy.createAndRegister(
+ mContext,
+ mThreadingDomain,
+ PRIMARY_LOCATION_TIME_ZONE_SERVICE_ACTION,
+ com.android.internal.R.bool.config_enablePrimaryLocationTimeZoneOverlay,
+ com.android.internal.R.string.config_primaryLocationTimeZoneProviderPackageName
+ );
+ */
+ }
+ return createLocationTimeZoneProvider(PRIMARY_PROVIDER_NAME, proxy);
+ }
+
+ private boolean isInSimulationMode(String providerName) {
+ return SystemProperties.getBoolean(
+ SIMULATION_MODE_SYSTEM_PROPERTY_PREFIX + providerName, false);
+ }
+
+ private LocationTimeZoneProvider createLocationTimeZoneProvider(
+ @NonNull String providerName, @NonNull LocationTimeZoneProviderProxy proxy) {
+ LocationTimeZoneProvider provider;
+ if (proxy != null) {
+ debugLog("LocationTimeZoneProvider found for providerName=" + providerName);
+ provider = new BinderLocationTimeZoneProvider(mThreadingDomain,
+ providerName, proxy);
+ } else {
+ debugLog("No LocationTimeZoneProvider found for providerName=" + providerName
+ + ": stubbing");
+ provider = new NullLocationTimeZoneProvider(mThreadingDomain, providerName);
+ }
+ return provider;
+ }
+
+ @Override
+ public void onShellCommand(FileDescriptor in, FileDescriptor out,
+ FileDescriptor err, String[] args, ShellCallback callback,
+ ResultReceiver resultReceiver) {
+ (new LocationTimeZoneManagerShellCommand(this)).exec(
+ this, in, out, err, args, callback, resultReceiver);
+ }
+
+ /**
+ * Asynchronously passes a {@link SimulatedBinderProviderEvent] to the appropriate provider.
+ * The device must be in simulation mode, otherwise an {@link IllegalStateException} will be
+ * thrown.
+ */
+ void simulateBinderProviderEvent(SimulatedBinderProviderEvent event)
+ throws IllegalStateException {
+ if (!isInSimulationMode(event.getProviderName())) {
+ throw new IllegalStateException("Use \"setprop "
+ + SIMULATION_MODE_SYSTEM_PROPERTY_PREFIX + event.getProviderName()
+ + " 1\" and reboot before injecting simulated binder events.");
+ }
+ mThreadingDomain.post(
+ () -> mLocationTimeZoneDetectorController.simulateBinderProviderEvent(event));
+ }
+
+ @Override
+ protected void dump(@NonNull FileDescriptor fd, @NonNull PrintWriter pw,
+ @Nullable String[] args) {
+ if (!DumpUtils.checkDumpPermission(mContext, TAG, pw)) return;
+
+ IndentingPrintWriter ipw = new IndentingPrintWriter(pw);
+ // Called on an arbitrary thread at any time.
+ synchronized (mSharedLock) {
+ ipw.println("LocationTimeZoneManagerService:");
+ ipw.increaseIndent();
+ if (mLocationTimeZoneDetectorController == null) {
+ ipw.println("{Uninitialized}");
+ } else {
+ mLocationTimeZoneDetectorController.dump(ipw, args);
+ }
+ ipw.decreaseIndent();
+ }
+ }
+
+ static void debugLog(String msg) {
+ if (Log.isLoggable(TAG, Log.DEBUG)) {
+ Slog.d(TAG, msg);
+ }
+ }
+
+ static void warnLog(String msg) {
+ if (Log.isLoggable(TAG, Log.WARN)) {
+ Slog.w(TAG, msg);
+ }
+ }
+}
diff --git a/services/core/java/com/android/server/location/timezone/LocationTimeZoneManagerShellCommand.java b/services/core/java/com/android/server/location/timezone/LocationTimeZoneManagerShellCommand.java
new file mode 100644
index 000000000000..7c3b891743cc
--- /dev/null
+++ b/services/core/java/com/android/server/location/timezone/LocationTimeZoneManagerShellCommand.java
@@ -0,0 +1,80 @@
+/*
+ * 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.location.timezone;
+
+import android.os.ShellCommand;
+
+import java.io.PrintWriter;
+
+/** Implements the shell command interface for {@link LocationTimeZoneManagerService}. */
+class LocationTimeZoneManagerShellCommand extends ShellCommand {
+
+ private final LocationTimeZoneManagerService mService;
+
+ LocationTimeZoneManagerShellCommand(LocationTimeZoneManagerService service) {
+ mService = service;
+ }
+
+ @Override
+ public int onCommand(String cmd) {
+ if (cmd == null) {
+ return handleDefaultCommands(cmd);
+ }
+
+ switch (cmd) {
+ case "simulate_binder": {
+ return runSimulateBinderEvent();
+ }
+ default: {
+ return handleDefaultCommands(cmd);
+ }
+ }
+ }
+
+ private int runSimulateBinderEvent() {
+ PrintWriter outPrintWriter = getOutPrintWriter();
+
+ SimulatedBinderProviderEvent simulatedProviderBinderEvent;
+ try {
+ simulatedProviderBinderEvent = SimulatedBinderProviderEvent.createFromArgs(this);
+ } catch (IllegalArgumentException e) {
+ outPrintWriter.println("Error: " + e.getMessage());
+ return 1;
+ }
+
+ outPrintWriter.println("Injecting: " + simulatedProviderBinderEvent);
+ try {
+ mService.simulateBinderProviderEvent(simulatedProviderBinderEvent);
+ } catch (IllegalStateException e) {
+ outPrintWriter.println("Error: " + e.getMessage());
+ return 2;
+ }
+ return 0;
+ }
+
+ @Override
+ public void onHelp() {
+ final PrintWriter pw = getOutPrintWriter();
+ pw.println("Location Time Zone Manager (location_time_zone_manager) commands:");
+ pw.println(" help");
+ pw.println(" Print this help text.");
+ pw.println(" simulate_binder");
+ pw.println(" <simulated provider binder event>");
+ pw.println();
+ SimulatedBinderProviderEvent.printCommandLineOpts(pw);
+ pw.println();
+ }
+}
diff --git a/services/core/java/com/android/server/location/timezone/LocationTimeZoneProvider.java b/services/core/java/com/android/server/location/timezone/LocationTimeZoneProvider.java
new file mode 100644
index 000000000000..3743779b20c9
--- /dev/null
+++ b/services/core/java/com/android/server/location/timezone/LocationTimeZoneProvider.java
@@ -0,0 +1,511 @@
+/*
+ * 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.location.timezone;
+
+import static android.location.timezone.LocationTimeZoneEvent.EVENT_TYPE_PERMANENT_FAILURE;
+import static android.location.timezone.LocationTimeZoneEvent.EVENT_TYPE_SUCCESS;
+import static android.location.timezone.LocationTimeZoneEvent.EVENT_TYPE_UNCERTAIN;
+
+import static com.android.server.location.timezone.LocationTimeZoneManagerService.debugLog;
+import static com.android.server.location.timezone.LocationTimeZoneProvider.ProviderState.PROVIDER_STATE_DISABLED;
+import static com.android.server.location.timezone.LocationTimeZoneProvider.ProviderState.PROVIDER_STATE_ENABLED;
+import static com.android.server.location.timezone.LocationTimeZoneProvider.ProviderState.PROVIDER_STATE_PERM_FAILED;
+
+import android.annotation.IntDef;
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.location.timezone.LocationTimeZoneEvent;
+import android.os.Handler;
+import android.os.SystemClock;
+
+import com.android.internal.annotations.GuardedBy;
+import com.android.server.timezonedetector.ConfigurationInternal;
+import com.android.server.timezonedetector.Dumpable;
+import com.android.server.timezonedetector.ReferenceWithHistory;
+
+import java.util.Objects;
+
+/**
+ * A facade used by the {@link LocationTimeZoneProviderController} to interact with a location time
+ * zone provider. The provider could have a binder implementation with logic running in another
+ * process, or could be a stubbed instance when no real provider is registered.
+ *
+ * <p>The provider is supplied with a {@link ProviderListener} via {@link
+ * #initialize(ProviderListener)}. This enables it to communicates asynchronous detection / error
+ * events back to the {@link LocationTimeZoneProviderController} via the {@link
+ * ProviderListener#onProviderStateChange} method. This call must be made on the
+ * {@link Handler} thread from the {@link ThreadingDomain} passed to the constructor.
+ *
+ * <p>All incoming calls from the controller except for {@link
+ * LocationTimeZoneProvider#dump(android.util.IndentingPrintWriter, String[])} will be made on the
+ * {@link Handler} thread of the {@link ThreadingDomain} passed to the constructor.
+ */
+abstract class LocationTimeZoneProvider implements Dumpable {
+
+ /**
+ * Listener interface used by the {@link LocationTimeZoneProviderController} to register an
+ * interest in provider events.
+ */
+ interface ProviderListener {
+ /**
+ * Indicated that a provider changed states. The {@code providerState} indicates which one
+ */
+ void onProviderStateChange(@NonNull ProviderState providerState);
+ }
+
+ /**
+ * Information about the provider's current state.
+ */
+ static class ProviderState {
+
+ @IntDef({ PROVIDER_STATE_UNKNOWN, PROVIDER_STATE_ENABLED, PROVIDER_STATE_DISABLED,
+ PROVIDER_STATE_PERM_FAILED })
+ @interface ProviderStateEnum {}
+
+ /**
+ * Uninitialized value. Must not be used afte {@link LocationTimeZoneProvider#initialize}.
+ */
+ static final int PROVIDER_STATE_UNKNOWN = 0;
+
+ /**
+ * The provider is currently enabled.
+ */
+ static final int PROVIDER_STATE_ENABLED = 1;
+
+ /**
+ * The provider is currently disabled.
+ * This is the state after {@link #initialize} is called.
+ */
+ static final int PROVIDER_STATE_DISABLED = 2;
+
+ /**
+ * The provider has failed and cannot be re-enabled.
+ *
+ * Providers may enter this state after a provider is enabled.
+ */
+ static final int PROVIDER_STATE_PERM_FAILED = 3;
+
+ /** The {@link LocationTimeZoneProvider} the state is for. */
+ public final @NonNull LocationTimeZoneProvider provider;
+
+ /** The state enum value of the current state. */
+ public final @ProviderStateEnum int stateEnum;
+
+ /**
+ * The last {@link LocationTimeZoneEvent} received. Only populated when {@link #stateEnum}
+ * is {@link #PROVIDER_STATE_ENABLED}, but it can be {@code null} then too if no event has
+ * yet been received.
+ */
+ @Nullable public final LocationTimeZoneEvent event;
+
+ /**
+ * The user configuration associated with the current state. Only and always present when
+ * {@link #stateEnum} is {@link #PROVIDER_STATE_ENABLED}.
+ */
+ @Nullable public final ConfigurationInternal currentUserConfiguration;
+
+ /**
+ * The time according to the elapsed realtime clock when the provider entered the current
+ * state. Included for debugging, not used for equality.
+ */
+ private final long mStateEntryTimeMillis;
+
+ /**
+ * Debug information providing context for the transition to this state. Included for
+ * debugging, not used for equality.
+ */
+ @Nullable private final String mDebugInfo;
+
+
+ private ProviderState(@NonNull LocationTimeZoneProvider provider,
+ @ProviderStateEnum int stateEnum, @Nullable LocationTimeZoneEvent event,
+ @Nullable ConfigurationInternal currentUserConfiguration,
+ @Nullable String debugInfo) {
+ this.provider = Objects.requireNonNull(provider);
+ this.stateEnum = stateEnum;
+ this.event = event;
+ this.currentUserConfiguration = currentUserConfiguration;
+ this.mStateEntryTimeMillis = SystemClock.elapsedRealtime();
+ this.mDebugInfo = debugInfo;
+ }
+
+ /** Creates the bootstrap state, uses {@link #PROVIDER_STATE_UNKNOWN}. */
+ static ProviderState createStartingState(
+ @NonNull LocationTimeZoneProvider provider) {
+ return new ProviderState(
+ provider, PROVIDER_STATE_UNKNOWN, null, null, "Initial state");
+ }
+
+ /**
+ * Create a new state from this state. Validates that the state transition is valid
+ * and that the required parameters for the new state are present / absent.
+ */
+ ProviderState newState(@ProviderStateEnum int newStateEnum,
+ @Nullable LocationTimeZoneEvent event,
+ @Nullable ConfigurationInternal currentUserConfig,
+ @Nullable String debugInfo) {
+
+ // Check valid "from" transitions.
+ switch (this.stateEnum) {
+ case PROVIDER_STATE_UNKNOWN: {
+ if (newStateEnum != PROVIDER_STATE_DISABLED) {
+ throw new IllegalArgumentException(
+ "Must transition from " + prettyPrintStateEnum(
+ PROVIDER_STATE_UNKNOWN)
+ + " to " + prettyPrintStateEnum(PROVIDER_STATE_DISABLED));
+ }
+ break;
+ }
+ case PROVIDER_STATE_DISABLED:
+ case PROVIDER_STATE_ENABLED: {
+ // These can go to each other or PROVIDER_STATE_PERM_FAILED.
+ break;
+ }
+ case PROVIDER_STATE_PERM_FAILED: {
+ throw new IllegalArgumentException("Illegal transition out of "
+ + prettyPrintStateEnum(PROVIDER_STATE_UNKNOWN));
+ }
+ default: {
+ throw new IllegalArgumentException("Invalid this.stateEnum=" + this.stateEnum);
+ }
+ }
+
+ // Validate "to" transitions / arguments.
+ switch (newStateEnum) {
+ case PROVIDER_STATE_UNKNOWN: {
+ throw new IllegalArgumentException("Cannot transition to "
+ + prettyPrintStateEnum(PROVIDER_STATE_UNKNOWN));
+ }
+ case PROVIDER_STATE_DISABLED: {
+ if (event != null || currentUserConfig != null) {
+ throw new IllegalArgumentException(
+ "Disabled state: event and currentUserConfig must be null"
+ + ", event=" + event
+ + ", currentUserConfig=" + currentUserConfig);
+ }
+ break;
+ }
+ case PROVIDER_STATE_ENABLED: {
+ if (currentUserConfig == null) {
+ throw new IllegalArgumentException(
+ "Enabled state: currentUserConfig must not be null");
+ }
+ break;
+ }
+ case PROVIDER_STATE_PERM_FAILED: {
+ if (event != null || currentUserConfig != null) {
+ throw new IllegalArgumentException(
+ "Perf failed state: event and currentUserConfig must be null"
+ + ", event=" + event
+ + ", currentUserConfig=" + currentUserConfig);
+ }
+ break;
+ }
+ default: {
+ throw new IllegalArgumentException("Unknown newStateEnum=" + newStateEnum);
+ }
+ }
+ return new ProviderState(provider, newStateEnum, event, currentUserConfig, debugInfo);
+ }
+
+ @Override
+ public String toString() {
+ return "State{"
+ + "stateEnum=" + prettyPrintStateEnum(stateEnum)
+ + ", event=" + event
+ + ", currentUserConfiguration=" + currentUserConfiguration
+ + ", mStateEntryTimeMillis=" + mStateEntryTimeMillis
+ + ", mDebugInfo=" + mDebugInfo
+ + '}';
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ if (this == o) {
+ return true;
+ }
+ if (o == null || getClass() != o.getClass()) {
+ return false;
+ }
+ ProviderState state = (ProviderState) o;
+ return stateEnum == state.stateEnum
+ && Objects.equals(event, state.event)
+ && Objects.equals(currentUserConfiguration, state.currentUserConfiguration);
+ }
+
+ @Override
+ public int hashCode() {
+ return Objects.hash(stateEnum, event, currentUserConfiguration);
+ }
+
+ private static String prettyPrintStateEnum(@ProviderStateEnum int state) {
+ switch (state) {
+ case PROVIDER_STATE_DISABLED:
+ return "Disabled (" + PROVIDER_STATE_DISABLED + ")";
+ case PROVIDER_STATE_ENABLED:
+ return "Enabled (" + PROVIDER_STATE_ENABLED + ")";
+ case PROVIDER_STATE_PERM_FAILED:
+ return "Perm failure (" + PROVIDER_STATE_PERM_FAILED + ")";
+ case PROVIDER_STATE_UNKNOWN:
+ default:
+ return "Unknown (" + state + ")";
+ }
+ }
+ }
+
+ @NonNull final ThreadingDomain mThreadingDomain;
+ @NonNull final Object mSharedLock;
+ @NonNull final String mProviderName;
+
+ /**
+ * The current state (with history for debugging).
+ */
+ @GuardedBy("mSharedLock")
+ final ReferenceWithHistory<ProviderState> mCurrentState =
+ new ReferenceWithHistory<>(10);
+
+ // Non-null and effectively final after initialize() is called.
+ ProviderListener mProviderListener;
+
+ /** Creates the instance. */
+ LocationTimeZoneProvider(@NonNull ThreadingDomain threadingDomain,
+ @NonNull String providerName) {
+ mThreadingDomain = Objects.requireNonNull(threadingDomain);
+ mSharedLock = threadingDomain.getLockObject();
+ mProviderName = Objects.requireNonNull(providerName);
+ }
+
+ /**
+ * Called before the provider is first used.
+ */
+ final void initialize(@NonNull ProviderListener providerListener) {
+ mThreadingDomain.assertCurrentThread();
+
+ synchronized (mSharedLock) {
+ if (mProviderListener != null) {
+ throw new IllegalStateException("initialize already called");
+ }
+ mProviderListener = Objects.requireNonNull(providerListener);
+ ProviderState currentState = ProviderState.createStartingState(this);
+ ProviderState newState = currentState.newState(
+ PROVIDER_STATE_DISABLED, null, null, "initialize() called");
+ setCurrentState(newState, false);
+
+ onInitialize();
+ }
+ }
+
+ /**
+ * Implemented by subclasses to do work during {@link #initialize}.
+ */
+ abstract void onInitialize();
+
+ /**
+ * Set the current state, for use by this class and subclasses only. If {@code #notifyChanges}
+ * is {@code true} and {@code newState} is not equal to the old state, then {@link
+ * ProviderListener#onProviderStateChange(ProviderState)} must be called on
+ * {@link #mProviderListener}.
+ */
+ final void setCurrentState(@NonNull ProviderState newState, boolean notifyChanges) {
+ mThreadingDomain.assertCurrentThread();
+ synchronized (mSharedLock) {
+ ProviderState oldState = mCurrentState.get();
+ mCurrentState.set(newState);
+ onSetCurrentState(newState);
+ if (notifyChanges) {
+ if (!Objects.equals(newState, oldState)) {
+ mProviderListener.onProviderStateChange(newState);
+ }
+ }
+ }
+ }
+
+ /**
+ * Overridden by subclasses to do work during {@link #setCurrentState}.
+ */
+ @GuardedBy("mSharedLock")
+ void onSetCurrentState(ProviderState newState) {
+ // Default no-op.
+ }
+
+ /**
+ * Returns the current state of the provider. This method must be called using the handler
+ * thread from the {@link ThreadingDomain}.
+ */
+ @NonNull
+ final ProviderState getCurrentState() {
+ mThreadingDomain.assertCurrentThread();
+ synchronized (mSharedLock) {
+ return mCurrentState.get();
+ }
+ }
+
+ /**
+ * Returns the name of the provider. This method must be called using the handler thread from
+ * the {@link ThreadingDomain}.
+ */
+ final String getName() {
+ mThreadingDomain.assertCurrentThread();
+ return mProviderName;
+ }
+
+ /**
+ * Enables the provider. It is an error to call this method except when the {@link
+ * #getCurrentState()} is at {@link ProviderState#PROVIDER_STATE_DISABLED}. This method must be
+ * called using the handler thread from the {@link ThreadingDomain}.
+ */
+ final void enable(@NonNull ConfigurationInternal currentUserConfiguration) {
+ mThreadingDomain.assertCurrentThread();
+
+ synchronized (mSharedLock) {
+ assertCurrentState(PROVIDER_STATE_DISABLED);
+
+ ProviderState currentState = getCurrentState();
+ ProviderState newState = currentState.newState(
+ PROVIDER_STATE_ENABLED, null, currentUserConfiguration, "enable() called");
+ setCurrentState(newState, false);
+ onEnable();
+ }
+ }
+
+ /**
+ * Implemented by subclasses to do work during {@link #enable}.
+ */
+ abstract void onEnable();
+
+ /**
+ * Disables the provider. It is an error* to call this method except when the {@link
+ * #getCurrentState()} is at {@link ProviderState#PROVIDER_STATE_ENABLED}. This method must be
+ * called using the handler thread from the {@link ThreadingDomain}.
+ */
+ final void disable() {
+ mThreadingDomain.assertCurrentThread();
+
+ synchronized (mSharedLock) {
+ assertCurrentState(PROVIDER_STATE_ENABLED);
+
+ ProviderState currentState = getCurrentState();
+ ProviderState newState =
+ currentState.newState(PROVIDER_STATE_DISABLED, null, null, "disable() called");
+ setCurrentState(newState, false);
+
+ onDisable();
+ }
+ }
+
+ /**
+ * Implemented by subclasses to do work during {@link #disable}.
+ */
+ abstract void onDisable();
+
+ /** For subclasses to invoke when a {@link LocationTimeZoneEvent} has been received. */
+ final void handleLocationTimeZoneEvent(
+ @NonNull LocationTimeZoneEvent locationTimeZoneEvent) {
+ mThreadingDomain.assertCurrentThread();
+ Objects.requireNonNull(locationTimeZoneEvent);
+
+ synchronized (mSharedLock) {
+ debugLog("handleLocationTimeZoneEvent: mProviderName=" + mProviderName
+ + ", locationTimeZoneEvent=" + locationTimeZoneEvent);
+
+ ProviderState currentState = getCurrentState();
+ int eventType = locationTimeZoneEvent.getEventType();
+ switch (currentState.stateEnum) {
+ case PROVIDER_STATE_PERM_FAILED: {
+ // After entering perm failed, there is nothing to do. The remote peer is
+ // supposed to stop sending events after it has reported perm failure.
+ logWarn("handleLocationTimeZoneEvent: Event=" + locationTimeZoneEvent
+ + " received for provider=" + this + " when in failed state");
+ return;
+ }
+ case PROVIDER_STATE_DISABLED: {
+ switch (eventType) {
+ case EVENT_TYPE_PERMANENT_FAILURE: {
+ String msg = "handleLocationTimeZoneEvent:"
+ + " Failure event=" + locationTimeZoneEvent
+ + " received for disabled provider=" + this
+ + ", entering permanently failed state";
+ logWarn(msg);
+ ProviderState newState = currentState.newState(
+ PROVIDER_STATE_PERM_FAILED, null, null, msg);
+ setCurrentState(newState, true);
+ return;
+ }
+ case EVENT_TYPE_SUCCESS:
+ case EVENT_TYPE_UNCERTAIN: {
+ // Any geolocation-related events received for a disabled provider are
+ // ignored: they should not happen.
+ logWarn("handleLocationTimeZoneEvent:"
+ + " event=" + locationTimeZoneEvent
+ + " received for disabled provider=" + this
+ + ", ignoring");
+
+ return;
+ }
+ default: {
+ throw new IllegalStateException(
+ "Unknown eventType=" + locationTimeZoneEvent);
+ }
+ }
+ }
+ case PROVIDER_STATE_ENABLED: {
+ switch (eventType) {
+ case EVENT_TYPE_PERMANENT_FAILURE: {
+ String msg = "handleLocationTimeZoneEvent:"
+ + " Failure event=" + locationTimeZoneEvent
+ + " received for provider=" + this
+ + ", entering permanently failed state";
+ logWarn(msg);
+ ProviderState newState = currentState.newState(
+ PROVIDER_STATE_PERM_FAILED, null, null, msg);
+ setCurrentState(newState, true);
+ return;
+ }
+ case EVENT_TYPE_UNCERTAIN:
+ case EVENT_TYPE_SUCCESS: {
+ ProviderState newState = currentState.newState(PROVIDER_STATE_ENABLED,
+ locationTimeZoneEvent, currentState.currentUserConfiguration,
+ "handleLocationTimeZoneEvent() when enabled");
+ setCurrentState(newState, true);
+ return;
+ }
+ default: {
+ throw new IllegalStateException(
+ "Unknown eventType=" + locationTimeZoneEvent);
+ }
+ }
+ }
+ default: {
+ throw new IllegalStateException("Unknown providerType=" + currentState);
+ }
+ }
+ }
+ }
+
+ /**
+ * Implemented by subclasses.
+ */
+ abstract void logWarn(String msg);
+
+ private void assertCurrentState(@ProviderState.ProviderStateEnum int requiredState) {
+ ProviderState currentState = getCurrentState();
+ if (currentState.stateEnum != requiredState) {
+ throw new IllegalStateException(
+ "Required stateEnum=" + requiredState + ", but was " + currentState);
+ }
+ }
+}
diff --git a/services/core/java/com/android/server/location/timezone/LocationTimeZoneProviderController.java b/services/core/java/com/android/server/location/timezone/LocationTimeZoneProviderController.java
new file mode 100644
index 000000000000..2f75c43594df
--- /dev/null
+++ b/services/core/java/com/android/server/location/timezone/LocationTimeZoneProviderController.java
@@ -0,0 +1,124 @@
+/*
+ * 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.location.timezone;
+
+import android.annotation.NonNull;
+import android.os.Handler;
+
+import com.android.server.location.timezone.LocationTimeZoneProvider.ProviderState;
+import com.android.server.timezonedetector.ConfigurationInternal;
+import com.android.server.timezonedetector.Dumpable;
+import com.android.server.timezonedetector.GeolocationTimeZoneSuggestion;
+
+import java.util.Objects;
+
+/**
+ * An base class for the component responsible handling events from {@link
+ * LocationTimeZoneProvider}s and synthesizing time zone ID suggestions for sending to the time zone
+ * detector. This interface primarily exists to extract testable detection logic, i.e. with
+ * a minimal number of threading considerations or dependencies on Android infrastructure.
+ *
+ * <p>The controller interacts with the following components:
+ * <ul>
+ * <li>The surrounding service, which calls {@link #initialize(Environment, Callback)} and
+ * {@link #onConfigChanged()}.</li>
+ * <li>The {@link Environment} through which obtains information it needs.</li>
+ * <li>The {@link Callback} through which it makes time zone suggestions.</li>
+ * <li>Any {@link LocationTimeZoneProvider} instances it owns, which communicate via the
+ * {@link LocationTimeZoneProvider.ProviderListener#onProviderStateChange(ProviderState)}
+ * method.</li>
+ * </ul>
+ *
+ * <p>All incoming calls except for {@link
+ * LocationTimeZoneProviderController#dump(android.util.IndentingPrintWriter, String[])} must be
+ * made on the {@link Handler} thread of the {@link ThreadingDomain} passed to {@link
+ * #LocationTimeZoneProviderController(ThreadingDomain)}.
+ *
+ * <p>Provider / controller integration notes:
+ *
+ * <p>Providers distinguish between "unknown unknowns" ("uncertain") and "known unknowns"
+ * ("certain"), i.e. a provider can be uncertain and not know what the time zone is, which is
+ * different from the certainty that there are no time zone IDs for the current location. A provider
+ * can be certain about there being no time zone IDs for a location for good reason, e.g. for
+ * disputed areas and oceans. Distinguishing uncertainty allows the controller to try other
+ * providers (or give up), where as certainty means it should not.
+ *
+ * <p>A provider can fail permanently. A permanent failure will disable the provider until next
+ * boot.
+ */
+abstract class LocationTimeZoneProviderController implements Dumpable {
+
+ @NonNull protected final ThreadingDomain mThreadingDomain;
+ @NonNull protected final Object mSharedLock;
+
+ LocationTimeZoneProviderController(@NonNull ThreadingDomain threadingDomain) {
+ mThreadingDomain = Objects.requireNonNull(threadingDomain);
+ mSharedLock = threadingDomain.getLockObject();
+ }
+
+ /**
+ * Called to initialize the controller during boot. Called once only.
+ * {@link LocationTimeZoneProvider#initialize} must be called by this method.
+ */
+ abstract void initialize(@NonNull Environment environment, @NonNull Callback callback);
+
+ /**
+ * Called when any settings or other device state that affect location-based time zone detection
+ * have changed. The receiver should call {@link
+ * Environment#getCurrentUserConfigurationInternal()} to get the current user's config. This
+ * call must be made on the {@link ThreadingDomain} handler thread.
+ */
+ abstract void onConfigChanged();
+
+ /**
+ * Used by {@link LocationTimeZoneProviderController} to obtain information from the surrounding
+ * service. It can easily be faked for tests.
+ */
+ abstract static class Environment {
+
+ @NonNull protected final ThreadingDomain mThreadingDomain;
+ @NonNull protected final Object mSharedLock;
+
+ Environment(@NonNull ThreadingDomain threadingDomain) {
+ mThreadingDomain = Objects.requireNonNull(threadingDomain);
+ mSharedLock = threadingDomain.getLockObject();
+ }
+
+ /** Returns the {@link ConfigurationInternal} for the current user of the device. */
+ abstract ConfigurationInternal getCurrentUserConfigurationInternal();
+ }
+
+ /**
+ * Used by {@link LocationTimeZoneProviderController} to interact with the surrounding service.
+ * It can easily be faked for tests.
+ */
+ abstract static class Callback {
+
+ @NonNull protected final ThreadingDomain mThreadingDomain;
+ @NonNull protected final Object mSharedLock;
+
+ Callback(@NonNull ThreadingDomain threadingDomain) {
+ mThreadingDomain = Objects.requireNonNull(threadingDomain);
+ mSharedLock = threadingDomain.getLockObject();
+ }
+
+ /**
+ * Suggests the latest time zone state for the device.
+ */
+ abstract void suggest(@NonNull GeolocationTimeZoneSuggestion suggestion);
+ }
+}
diff --git a/services/core/java/com/android/server/location/timezone/LocationTimeZoneProviderProxy.java b/services/core/java/com/android/server/location/timezone/LocationTimeZoneProviderProxy.java
new file mode 100644
index 000000000000..3d889ae1856a
--- /dev/null
+++ b/services/core/java/com/android/server/location/timezone/LocationTimeZoneProviderProxy.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.location.timezone;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.content.Context;
+import android.location.timezone.LocationTimeZoneEvent;
+import android.os.Handler;
+import android.util.IndentingPrintWriter;
+
+import com.android.internal.annotations.GuardedBy;
+import com.android.internal.location.timezone.LocationTimeZoneProviderRequest;
+import com.android.server.timezonedetector.Dumpable;
+
+import java.util.Objects;
+
+/**
+ * System server-side proxy for ILocationTimeZoneProvider implementations, i.e. this provides the
+ * system server object used to communicate with a remote LocationTimeZoneProvider over Binder,
+ * which could be running in a different process. As LocationTimeZoneProviders are bound / unbound
+ * this proxy will rebind to the "best" available remote process.
+ *
+ * <p>Threading guarantees provided / required by this interface:
+ * <ul>
+ * <li>All public methods defined by this class must be invoked using the {@link Handler} thread
+ * from the {@link ThreadingDomain} passed to the constructor, excluding
+ * {@link #dump(IndentingPrintWriter, String[])}</li>
+ * <li>Non-static public methods that make binder calls to remote processes (e.g.
+ * {@link #setRequest(LocationTimeZoneProviderRequest)}) are executed asynchronously and will
+ * return immediately.</li>
+ * <li>Callbacks received via binder are delivered via {@link Listener} are delivered on the
+ * {@link Handler} thread from the {@link ThreadingDomain} passed to the constructor.
+ * </ul>
+ *
+ * <p>This class exists to enable the introduction of test implementations of {@link
+ * LocationTimeZoneProviderProxy} that can be used when a device is in a test mode to inject test
+ * events / behavior that are otherwise difficult to simulate.
+ */
+abstract class LocationTimeZoneProviderProxy implements Dumpable {
+
+ @NonNull protected final Context mContext;
+ @NonNull protected final ThreadingDomain mThreadingDomain;
+ @NonNull protected final Object mSharedLock;
+
+ // Non-null and effectively final after setListener() is called.
+ @GuardedBy("mSharedLock")
+ @Nullable
+ protected Listener mListener;
+
+ LocationTimeZoneProviderProxy(
+ @NonNull Context context, @NonNull ThreadingDomain threadingDomain) {
+ mContext = Objects.requireNonNull(context);
+ mThreadingDomain = Objects.requireNonNull(threadingDomain);
+ mSharedLock = threadingDomain.getLockObject();
+ }
+
+ /**
+ * Sets the listener. The listener can expect to receive all events after this point.
+ */
+ void setListener(@NonNull Listener listener) {
+ Objects.requireNonNull(listener);
+ synchronized (mSharedLock) {
+ if (mListener != null) {
+ throw new IllegalStateException("listener already set");
+ }
+ this.mListener = listener;
+ }
+ }
+
+ /**
+ * Sets a new request for the provider.
+ */
+ abstract void setRequest(@NonNull LocationTimeZoneProviderRequest request);
+
+ /**
+ * Handles a {@link LocationTimeZoneEvent} from a remote process.
+ */
+ final void handleLocationTimeZoneEvent(
+ @NonNull LocationTimeZoneEvent locationTimeZoneEvent) {
+ // These calls are invoked on a binder thread. Move to the mThreadingDomain thread as
+ // required by the guarantees for this class.
+ mThreadingDomain.post(() -> mListener.onReportLocationTimeZoneEvent(locationTimeZoneEvent));
+ }
+
+ /**
+ * Interface for listening to location time zone providers. See {@link
+ * LocationTimeZoneProviderProxy} for threading guarantees.
+ */
+ interface Listener {
+
+ /**
+ * Called when a provider receives a {@link LocationTimeZoneEvent}.
+ */
+ void onReportLocationTimeZoneEvent(@NonNull LocationTimeZoneEvent locationTimeZoneEvent);
+
+ /**
+ * Called when a provider is (re)bound.
+ */
+ void onProviderBound();
+
+ /** Called when a provider is unbound. */
+ void onProviderUnbound();
+ }
+}
diff --git a/services/core/java/com/android/server/location/timezone/NullLocationTimeZoneProvider.java b/services/core/java/com/android/server/location/timezone/NullLocationTimeZoneProvider.java
new file mode 100644
index 000000000000..79e2b975089d
--- /dev/null
+++ b/services/core/java/com/android/server/location/timezone/NullLocationTimeZoneProvider.java
@@ -0,0 +1,97 @@
+/*
+ * 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.location.timezone;
+
+import static com.android.server.location.timezone.LocationTimeZoneProvider.ProviderState.PROVIDER_STATE_PERM_FAILED;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.util.IndentingPrintWriter;
+import android.util.Slog;
+
+/**
+ * A {@link LocationTimeZoneProvider} that provides minimal responses needed for the {@link
+ * LocationTimeZoneProviderController} to operate correctly when there is no "real" provider
+ * configured. This can be used during development / testing, or in a production build when the
+ * platform supports more providers than are needed for an Android deployment.
+ *
+ * <p>For example, if the {@link LocationTimeZoneProviderController} supports a primary
+ * and a secondary {@link LocationTimeZoneProvider}, but only a primary is configured, the secondary
+ * config will be left null and the {@link LocationTimeZoneProvider} implementation will be
+ * defaulted to a {@link NullLocationTimeZoneProvider}. The {@link NullLocationTimeZoneProvider}
+ * enters a {@link ProviderState#PROVIDER_STATE_PERM_FAILED} state immediately after being enabled
+ * for the first time and sends the appropriate event, which ensures the {@link
+ * LocationTimeZoneProviderController} won't expect any further {@link
+ * android.location.timezone.LocationTimeZoneEvent}s to come from it, and won't attempt to use it
+ * again.
+ */
+class NullLocationTimeZoneProvider extends LocationTimeZoneProvider {
+
+ private static final String TAG = "NullLocationTimeZoneProvider";
+
+ /** Creates the instance. */
+ NullLocationTimeZoneProvider(@NonNull ThreadingDomain threadingDomain,
+ @NonNull String providerName) {
+ super(threadingDomain, providerName);
+ }
+
+ @Override
+ void onInitialize() {
+ // No-op
+ }
+
+ @Override
+ void onEnable() {
+ // Report a failure (asynchronously using the mThreadingDomain thread to avoid recursion).
+ mThreadingDomain.post(()-> {
+ // Enter the perm-failed state.
+ ProviderState currentState = mCurrentState.get();
+ ProviderState failedState = currentState.newState(
+ PROVIDER_STATE_PERM_FAILED, null, null, "Stubbed provider");
+ setCurrentState(failedState, true);
+ });
+ }
+
+ @Override
+ void onDisable() {
+ // Ignored - NullLocationTimeZoneProvider is always permanently failed.
+ }
+
+ @Override
+ void logWarn(String msg) {
+ Slog.w(TAG, msg);
+ }
+
+ @Override
+ public void dump(@NonNull IndentingPrintWriter ipw, @Nullable String[] args) {
+ synchronized (mSharedLock) {
+ ipw.println("{Stubbed LocationTimeZoneProvider}");
+ ipw.println("mProviderName=" + mProviderName);
+ ipw.println("mCurrentState=" + mCurrentState);
+ }
+ }
+
+ @Override
+ public String toString() {
+ synchronized (mSharedLock) {
+ return "NullLocationTimeZoneProvider{"
+ + "mProviderName='" + mProviderName + '\''
+ + "mCurrentState='" + mCurrentState + '\''
+ + '}';
+ }
+ }
+}
diff --git a/services/core/java/com/android/server/location/timezone/SimulatedBinderProviderEvent.java b/services/core/java/com/android/server/location/timezone/SimulatedBinderProviderEvent.java
new file mode 100644
index 000000000000..ef2e349fceaa
--- /dev/null
+++ b/services/core/java/com/android/server/location/timezone/SimulatedBinderProviderEvent.java
@@ -0,0 +1,162 @@
+/*
+ * 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.location.timezone;
+
+import static android.location.timezone.LocationTimeZoneEvent.EVENT_TYPE_PERMANENT_FAILURE;
+import static android.location.timezone.LocationTimeZoneEvent.EVENT_TYPE_SUCCESS;
+import static android.location.timezone.LocationTimeZoneEvent.EVENT_TYPE_UNCERTAIN;
+
+import static com.android.server.location.timezone.LocationTimeZoneManagerService.PRIMARY_PROVIDER_NAME;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.app.ActivityManager;
+import android.location.timezone.LocationTimeZoneEvent;
+import android.os.ShellCommand;
+import android.os.SystemClock;
+import android.os.UserHandle;
+
+import java.io.PrintWriter;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.List;
+import java.util.Objects;
+
+/**
+ * An event used for simulating real binder proxy behavior using a {@link
+ * SimulatedLocationTimeZoneProviderProxy}.
+ */
+final class SimulatedBinderProviderEvent {
+
+ private static final List<String> VALID_PROVIDER_NAMES = Arrays.asList(PRIMARY_PROVIDER_NAME);
+
+ static final int INJECTED_EVENT_TYPE_ON_BIND = 1;
+ static final int INJECTED_EVENT_TYPE_ON_UNBIND = 2;
+ static final int INJECTED_EVENT_TYPE_LOCATION_TIME_ZONE_EVENT = 3;
+
+
+ @NonNull private final String mProviderName;
+ private final int mEventType;
+ @Nullable private final LocationTimeZoneEvent mLocationTimeZoneEvent;
+
+ private SimulatedBinderProviderEvent(@NonNull String providerName, int eventType,
+ @Nullable LocationTimeZoneEvent locationTimeZoneEvent) {
+ this.mProviderName = Objects.requireNonNull(providerName);
+ this.mEventType = eventType;
+ this.mLocationTimeZoneEvent = locationTimeZoneEvent;
+ }
+
+ @NonNull
+ String getProviderName() {
+ return mProviderName;
+ }
+
+ @Nullable
+ LocationTimeZoneEvent getLocationTimeZoneEvent() {
+ return mLocationTimeZoneEvent;
+ }
+
+ int getEventType() {
+ return mEventType;
+ }
+
+ /** Prints the command line options that {@link #createFromArgs(ShellCommand)} understands. */
+ static void printCommandLineOpts(PrintWriter pw) {
+ pw.println("Simulated provider binder event:");
+ pw.println();
+ pw.println("<provider name> [onBind|onUnbind|locationTimeZoneEvent"
+ + " <location time zone event args>]");
+ pw.println();
+ pw.println("<provider name> = " + VALID_PROVIDER_NAMES);
+ pw.println("<location time zone event args> ="
+ + " [PERMANENT_FAILURE|UNCERTAIN|SUCCESS <time zone ids>*]");
+ }
+
+ /**
+ * Constructs a {@link SimulatedBinderProviderEvent} from the arguments of {@code shellCommand}.
+ */
+ static SimulatedBinderProviderEvent createFromArgs(ShellCommand shellCommand) {
+ String providerName = shellCommand.getNextArgRequired();
+ if (!VALID_PROVIDER_NAMES.contains(providerName)) {
+ throw new IllegalArgumentException("Unknown provider name=" + providerName);
+ }
+ String injectedEvent = shellCommand.getNextArgRequired();
+ switch (injectedEvent) {
+ case "onBind": {
+ return new SimulatedBinderProviderEvent(
+ providerName, INJECTED_EVENT_TYPE_ON_BIND, null);
+ }
+ case "onUnbind": {
+ return new SimulatedBinderProviderEvent(
+ providerName, INJECTED_EVENT_TYPE_ON_UNBIND, null);
+ }
+ case "locationTimeZoneEvent": {
+ LocationTimeZoneEvent event = parseLocationTimeZoneEventArgs(shellCommand);
+ return new SimulatedBinderProviderEvent(providerName,
+ INJECTED_EVENT_TYPE_LOCATION_TIME_ZONE_EVENT, event);
+ }
+ default: {
+ throw new IllegalArgumentException("Unknown simulated event type=" + injectedEvent);
+ }
+ }
+ }
+
+ private static LocationTimeZoneEvent parseLocationTimeZoneEventArgs(ShellCommand shellCommand) {
+ LocationTimeZoneEvent.Builder eventBuilder = new LocationTimeZoneEvent.Builder()
+ .setElapsedRealtimeNanos(SystemClock.elapsedRealtime())
+ .setUserHandle(UserHandle.of(ActivityManager.getCurrentUser()));
+
+ String eventTypeString = shellCommand.getNextArgRequired();
+ switch (eventTypeString.toUpperCase()) {
+ case "PERMANENT_FAILURE": {
+ eventBuilder.setEventType(EVENT_TYPE_PERMANENT_FAILURE);
+ break;
+ }
+ case "UNCERTAIN": {
+ eventBuilder.setEventType(EVENT_TYPE_UNCERTAIN);
+ break;
+ }
+ case "SUCCESS": {
+ eventBuilder.setEventType(EVENT_TYPE_SUCCESS)
+ .setTimeZoneIds(parseTimeZoneArgs(shellCommand));
+ break;
+ }
+ default: {
+ throw new IllegalArgumentException("Error: Unknown eventType: " + eventTypeString);
+ }
+ }
+ return eventBuilder.build();
+ }
+
+ private static List<String> parseTimeZoneArgs(ShellCommand shellCommand) {
+ List<String> timeZoneIds = new ArrayList<>();
+ String timeZoneId;
+ while ((timeZoneId = shellCommand.getNextArg()) != null) {
+ timeZoneIds.add(timeZoneId);
+ }
+ return timeZoneIds;
+ }
+
+ @Override
+ public String toString() {
+ return "SimulatedBinderProviderEvent{"
+ + "mProviderName=" + mProviderName
+ + ", mEventType=" + mEventType
+ + ", mLocationTimeZoneEvent=" + mLocationTimeZoneEvent
+ + '}';
+ }
+}
diff --git a/services/core/java/com/android/server/location/timezone/SimulatedLocationTimeZoneProviderProxy.java b/services/core/java/com/android/server/location/timezone/SimulatedLocationTimeZoneProviderProxy.java
new file mode 100644
index 000000000000..462bcab80c1b
--- /dev/null
+++ b/services/core/java/com/android/server/location/timezone/SimulatedLocationTimeZoneProviderProxy.java
@@ -0,0 +1,118 @@
+/*
+ * 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.location.timezone;
+
+import static com.android.server.location.timezone.SimulatedBinderProviderEvent.INJECTED_EVENT_TYPE_LOCATION_TIME_ZONE_EVENT;
+import static com.android.server.location.timezone.SimulatedBinderProviderEvent.INJECTED_EVENT_TYPE_ON_BIND;
+import static com.android.server.location.timezone.SimulatedBinderProviderEvent.INJECTED_EVENT_TYPE_ON_UNBIND;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.content.Context;
+import android.util.IndentingPrintWriter;
+
+import com.android.internal.annotations.GuardedBy;
+import com.android.internal.location.timezone.LocationTimeZoneProviderRequest;
+import com.android.server.timezonedetector.ReferenceWithHistory;
+
+import java.util.Objects;
+
+/**
+ * A replacement for a real binder proxy for use during integration testing
+ * that can be used to inject simulated {@link LocationTimeZoneProviderProxy} behavior.
+ */
+class SimulatedLocationTimeZoneProviderProxy extends LocationTimeZoneProviderProxy {
+
+ @GuardedBy("mProxyLock")
+ @NonNull private LocationTimeZoneProviderRequest mRequest;
+
+ @NonNull private ReferenceWithHistory<String> mLastEvent = new ReferenceWithHistory<>(50);
+
+ SimulatedLocationTimeZoneProviderProxy(
+ @NonNull Context context, @NonNull ThreadingDomain threadingDomain) {
+ super(context, threadingDomain);
+ mRequest = LocationTimeZoneProviderRequest.EMPTY_REQUEST;
+ }
+
+ void simulate(@NonNull SimulatedBinderProviderEvent event) {
+ switch (event.getEventType()) {
+ case INJECTED_EVENT_TYPE_ON_BIND: {
+ mLastEvent.set("Simulating onProviderBound(), event=" + event);
+ mThreadingDomain.post(this::onBindOnHandlerThread);
+ break;
+ }
+ case INJECTED_EVENT_TYPE_ON_UNBIND: {
+ mLastEvent.set("Simulating onProviderUnbound(), event=" + event);
+ mThreadingDomain.post(this::onUnbindOnHandlerThread);
+ break;
+ }
+ case INJECTED_EVENT_TYPE_LOCATION_TIME_ZONE_EVENT: {
+ if (!mRequest.getReportLocationTimeZone()) {
+ mLastEvent.set("Test event=" + event + " is testing an invalid case:"
+ + " reporting is off. mRequest=" + mRequest);
+ }
+ mLastEvent.set("Simulating LocationTimeZoneEvent, event=" + event);
+ handleLocationTimeZoneEvent(event.getLocationTimeZoneEvent());
+ break;
+ }
+ default: {
+ mLastEvent.set("Unknown simulated event type. event=" + event);
+ throw new IllegalArgumentException("Unknown simulated event type. event=" + event);
+ }
+ }
+ }
+
+ private void onBindOnHandlerThread() {
+ mThreadingDomain.assertCurrentThread();
+
+ synchronized (mSharedLock) {
+ mListener.onProviderBound();
+ }
+ }
+
+ private void onUnbindOnHandlerThread() {
+ mThreadingDomain.assertCurrentThread();
+
+ synchronized (mSharedLock) {
+ mListener.onProviderUnbound();
+ }
+ }
+
+ @Override
+ final void setRequest(@NonNull LocationTimeZoneProviderRequest request) {
+ mThreadingDomain.assertCurrentThread();
+
+ Objects.requireNonNull(request);
+ synchronized (mSharedLock) {
+ mLastEvent.set("Request received: " + request);
+ mRequest = request;
+ }
+ }
+
+ @Override
+ public void dump(@NonNull IndentingPrintWriter ipw, @Nullable String[] args) {
+ synchronized (mSharedLock) {
+ ipw.println("mRequest=" + mRequest);
+ ipw.println("mLastEvent=" + mLastEvent);
+
+ ipw.increaseIndent();
+ ipw.println("Last event history:");
+ mLastEvent.dump(ipw);
+ ipw.decreaseIndent();
+ }
+ }
+}
diff --git a/services/core/java/com/android/server/location/timezone/ThreadingDomain.java b/services/core/java/com/android/server/location/timezone/ThreadingDomain.java
new file mode 100644
index 000000000000..9b9c82358974
--- /dev/null
+++ b/services/core/java/com/android/server/location/timezone/ThreadingDomain.java
@@ -0,0 +1,120 @@
+/*
+ * 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.location.timezone;
+
+import android.annotation.NonNull;
+
+import com.android.internal.util.Preconditions;
+
+/**
+ * A class that can be used to enforce / indicate a set of components that need to share threading
+ * behavior such as a shared lock object and a common thread, with async execution support.
+ *
+ * <p>It is <em>not</em> essential that the object returned by {@link #getLockObject()} is only used
+ * when executing on the domain's thread, but users should be careful to avoid deadlocks when
+ * multiple locks / threads are in use. Generally sticking to a single thread / lock is safest.
+ */
+abstract class ThreadingDomain {
+
+ @NonNull private final Object mLockObject;
+
+ ThreadingDomain() {
+ mLockObject = new Object();
+ }
+
+ /**
+ * Returns the common lock object for this threading domain that can be used for synchronized ()
+ * blocks. The lock is unique to this threading domain.
+ */
+ @NonNull
+ Object getLockObject() {
+ return mLockObject;
+ }
+
+ /**
+ * Returns the Thread associated with this threading domain.
+ */
+ @NonNull
+ abstract Thread getThread();
+
+ /**
+ * Asserts the currently executing thread is the one associated with this threading domain.
+ * Generally useful for documenting expectations in the code. By asserting a single thread is
+ * being used within a set of components, a lot of races can be avoided.
+ */
+ void assertCurrentThread() {
+ Preconditions.checkArgument(Thread.currentThread() == getThread());
+ }
+
+ /**
+ * Execute the supplied runnable on the threading domain's thread.
+ */
+ abstract void post(@NonNull Runnable runnable);
+
+ /**
+ * Execute the supplied runnable on the threading domain's thread with a delay.
+ */
+ abstract void postDelayed(@NonNull Runnable runnable, long delayMillis);
+
+ abstract void postDelayed(Runnable r, Object token, long delayMillis);
+
+ abstract void removeQueuedRunnables(Object token);
+
+ /**
+ * Creates a new {@link SingleRunnableQueue} that can be used to ensure that (at most) a
+ * single runnable for a given purpose is ever queued. Create new ones for different purposes.
+ */
+ SingleRunnableQueue createSingleRunnableQueue() {
+ return new SingleRunnableQueue();
+ }
+
+ /**
+ * A class that allows up to one {@link Runnable} to be queued on the handler, i.e. calling any
+ * of the methods will cancel the execution of any previously queued / delayed runnable. All
+ * methods must be called from the {@link ThreadingDomain}'s thread.
+ */
+ final class SingleRunnableQueue {
+
+ /**
+ * Runs the supplied {@link Runnable} synchronously on the threading domain's thread,
+ * cancelling any queued but not-yet-executed {@link Runnable} previously added by this.
+ * This method must be called from the threading domain's thread.
+ */
+ void runSynchronously(Runnable r) {
+ cancel();
+ r.run();
+ }
+
+ /**
+ * Posts the supplied {@link Runnable} asynchronously and delayed on the threading domain
+ * handler thread, cancelling any queued but not-yet-executed {@link Runnable} previously
+ * added by this. This method must be called from the threading domain's thread.
+ */
+ void runDelayed(Runnable r, long delayMillis) {
+ cancel();
+ ThreadingDomain.this.postDelayed(r, this, delayMillis);
+ }
+
+ /**
+ * Cancels any queued but not-yet-executed {@link Runnable} previously added by this.
+ */
+ public void cancel() {
+ assertCurrentThread();
+ removeQueuedRunnables(this);
+ }
+ }
+}
diff --git a/services/core/java/com/android/server/locksettings/LockSettingsService.java b/services/core/java/com/android/server/locksettings/LockSettingsService.java
index 1b43db2a421d..7e53e6f3e2c3 100644
--- a/services/core/java/com/android/server/locksettings/LockSettingsService.java
+++ b/services/core/java/com/android/server/locksettings/LockSettingsService.java
@@ -114,6 +114,7 @@ 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.IndentingPrintWriter;
+import com.android.internal.util.Preconditions;
import com.android.internal.widget.ICheckCredentialProgressCallback;
import com.android.internal.widget.ILockSettings;
import com.android.internal.widget.LockPatternUtils;
@@ -2609,9 +2610,12 @@ public class LockSettingsService extends ILockSettings.Stub {
protected AuthenticationToken initializeSyntheticPasswordLocked(byte[] credentialHash,
LockscreenCredential credential, int userId) {
Slog.i(TAG, "Initialize SyntheticPassword for user: " + userId);
+ Preconditions.checkState(
+ getSyntheticPasswordHandleLocked(userId) == SyntheticPasswordManager.DEFAULT_HANDLE,
+ "Cannot reinitialize SP");
+
final AuthenticationToken auth = mSpManager.newSyntheticPasswordAndSid(
getGateKeeperService(), credentialHash, credential, userId);
- onAuthTokenKnownForUser(userId, auth);
if (auth == null) {
Slog.wtf(TAG, "initializeSyntheticPasswordLocked returns null auth token");
return null;
@@ -2634,6 +2638,7 @@ public class LockSettingsService extends ILockSettings.Stub {
}
fixateNewestUserKeyAuth(userId);
setSyntheticPasswordHandleLocked(handle, userId);
+ onAuthTokenKnownForUser(userId, auth);
return auth;
}
@@ -2669,7 +2674,7 @@ public class LockSettingsService extends ILockSettings.Stub {
@VisibleForTesting
protected boolean shouldMigrateToSyntheticPasswordLocked(int userId) {
- return true;
+ return getSyntheticPasswordHandleLocked(userId) == SyntheticPasswordManager.DEFAULT_HANDLE;
}
private VerifyCredentialResponse spBasedDoVerifyCredential(LockscreenCredential userCredential,
@@ -3501,6 +3506,9 @@ public class LockSettingsService extends ILockSettings.Stub {
SyntheticPasswordManager.AuthenticationToken
authToken = new SyntheticPasswordManager.AuthenticationToken(spVersion);
authToken.recreateDirectly(syntheticPassword);
+ synchronized (mSpManager) {
+ mSpManager.verifyChallenge(getGateKeeperService(), authToken, 0L, userId);
+ }
onCredentialVerified(authToken, loadPasswordMetrics(authToken, userId), userId);
}
}
diff --git a/services/core/java/com/android/server/media/MediaButtonReceiverHolder.java b/services/core/java/com/android/server/media/MediaButtonReceiverHolder.java
index b9997e0c2ec8..0a4d17f20aec 100644
--- a/services/core/java/com/android/server/media/MediaButtonReceiverHolder.java
+++ b/services/core/java/com/android/server/media/MediaButtonReceiverHolder.java
@@ -259,7 +259,7 @@ final class MediaButtonReceiverHolder {
return "";
}
return String.join(COMPONENT_NAME_USER_ID_DELIM,
- mComponentName.toString(),
+ mComponentName.flattenToString(),
String.valueOf(mUserId),
String.valueOf(mComponentType));
}
diff --git a/services/core/java/com/android/server/notification/NotificationManagerService.java b/services/core/java/com/android/server/notification/NotificationManagerService.java
index 7401403de127..1fd7a73f1174 100755
--- a/services/core/java/com/android/server/notification/NotificationManagerService.java
+++ b/services/core/java/com/android/server/notification/NotificationManagerService.java
@@ -309,6 +309,7 @@ import java.util.Set;
import java.util.concurrent.Executor;
import java.util.concurrent.TimeUnit;
import java.util.function.BiConsumer;
+import java.util.stream.Collectors;
/** {@hide} */
public class NotificationManagerService extends SystemService {
@@ -1528,10 +1529,10 @@ public class NotificationManagerService extends SystemService {
cancelAllNotificationsInt(MY_UID, MY_PID, pkgName, null, 0, 0,
!queryRestart, changeUserId, reason, null);
}
- } else if (hideNotifications) {
- hideNotificationsForPackages(pkgList);
- } else if (unhideNotifications) {
- unhideNotificationsForPackages(pkgList);
+ } else if (hideNotifications && uidList != null && (uidList.length > 0)) {
+ hideNotificationsForPackages(pkgList, uidList);
+ } else if (unhideNotifications && uidList != null && (uidList.length > 0)) {
+ unhideNotificationsForPackages(pkgList, uidList);
}
}
@@ -2076,6 +2077,68 @@ public class NotificationManagerService extends SystemService {
mMsgPkgsAllowedAsConvos = Set.of(getStringArrayResource(
com.android.internal.R.array.config_notificationMsgPkgsAllowedAsConvos));
mStatsManager = statsManager;
+
+ // register for various Intents.
+ // If this is called within a test, make sure to unregister the intent receivers by
+ // calling onDestroy()
+ IntentFilter filter = new IntentFilter();
+ filter.addAction(Intent.ACTION_SCREEN_ON);
+ filter.addAction(Intent.ACTION_SCREEN_OFF);
+ filter.addAction(TelephonyManager.ACTION_PHONE_STATE_CHANGED);
+ filter.addAction(Intent.ACTION_USER_PRESENT);
+ filter.addAction(Intent.ACTION_USER_STOPPED);
+ filter.addAction(Intent.ACTION_USER_SWITCHED);
+ filter.addAction(Intent.ACTION_USER_ADDED);
+ filter.addAction(Intent.ACTION_USER_REMOVED);
+ filter.addAction(Intent.ACTION_USER_UNLOCKED);
+ filter.addAction(Intent.ACTION_MANAGED_PROFILE_UNAVAILABLE);
+ getContext().registerReceiverAsUser(mIntentReceiver, UserHandle.ALL, filter, null, null);
+
+ IntentFilter pkgFilter = new IntentFilter();
+ pkgFilter.addAction(Intent.ACTION_PACKAGE_ADDED);
+ pkgFilter.addAction(Intent.ACTION_PACKAGE_REMOVED);
+ pkgFilter.addAction(Intent.ACTION_PACKAGE_CHANGED);
+ pkgFilter.addAction(Intent.ACTION_PACKAGE_RESTARTED);
+ pkgFilter.addAction(Intent.ACTION_QUERY_PACKAGE_RESTART);
+ pkgFilter.addDataScheme("package");
+ getContext().registerReceiverAsUser(mPackageIntentReceiver, UserHandle.ALL, pkgFilter, null,
+ null);
+
+ IntentFilter suspendedPkgFilter = new IntentFilter();
+ suspendedPkgFilter.addAction(Intent.ACTION_PACKAGES_SUSPENDED);
+ suspendedPkgFilter.addAction(Intent.ACTION_PACKAGES_UNSUSPENDED);
+ suspendedPkgFilter.addAction(Intent.ACTION_DISTRACTING_PACKAGES_CHANGED);
+ getContext().registerReceiverAsUser(mPackageIntentReceiver, UserHandle.ALL,
+ suspendedPkgFilter, null, null);
+
+ IntentFilter sdFilter = new IntentFilter(Intent.ACTION_EXTERNAL_APPLICATIONS_UNAVAILABLE);
+ getContext().registerReceiverAsUser(mPackageIntentReceiver, UserHandle.ALL, sdFilter, null,
+ null);
+
+ IntentFilter timeoutFilter = new IntentFilter(ACTION_NOTIFICATION_TIMEOUT);
+ timeoutFilter.addDataScheme(SCHEME_TIMEOUT);
+ getContext().registerReceiver(mNotificationTimeoutReceiver, timeoutFilter);
+
+ IntentFilter settingsRestoredFilter = new IntentFilter(Intent.ACTION_SETTING_RESTORED);
+ getContext().registerReceiver(mRestoreReceiver, settingsRestoredFilter);
+
+ IntentFilter localeChangedFilter = new IntentFilter(Intent.ACTION_LOCALE_CHANGED);
+ getContext().registerReceiver(mLocaleChangeReceiver, localeChangedFilter);
+ }
+
+ /**
+ * Cleanup broadcast receivers change listeners.
+ */
+ public void onDestroy() {
+ getContext().unregisterReceiver(mIntentReceiver);
+ getContext().unregisterReceiver(mPackageIntentReceiver);
+ getContext().unregisterReceiver(mNotificationTimeoutReceiver);
+ getContext().unregisterReceiver(mRestoreReceiver);
+ getContext().unregisterReceiver(mLocaleChangeReceiver);
+
+ if (mDeviceConfigChangedListener != null) {
+ DeviceConfig.removeOnPropertiesChangedListener(mDeviceConfigChangedListener);
+ }
}
protected String[] getStringArrayResource(int key) {
@@ -2126,51 +2189,6 @@ public class NotificationManagerService extends SystemService {
Context.STATS_MANAGER),
getContext().getSystemService(TelephonyManager.class));
- // register for various Intents
- IntentFilter filter = new IntentFilter();
- filter.addAction(Intent.ACTION_SCREEN_ON);
- filter.addAction(Intent.ACTION_SCREEN_OFF);
- filter.addAction(TelephonyManager.ACTION_PHONE_STATE_CHANGED);
- filter.addAction(Intent.ACTION_USER_PRESENT);
- filter.addAction(Intent.ACTION_USER_STOPPED);
- filter.addAction(Intent.ACTION_USER_SWITCHED);
- filter.addAction(Intent.ACTION_USER_ADDED);
- filter.addAction(Intent.ACTION_USER_REMOVED);
- filter.addAction(Intent.ACTION_USER_UNLOCKED);
- filter.addAction(Intent.ACTION_MANAGED_PROFILE_UNAVAILABLE);
- getContext().registerReceiverAsUser(mIntentReceiver, UserHandle.ALL, filter, null, null);
-
- IntentFilter pkgFilter = new IntentFilter();
- pkgFilter.addAction(Intent.ACTION_PACKAGE_ADDED);
- pkgFilter.addAction(Intent.ACTION_PACKAGE_REMOVED);
- pkgFilter.addAction(Intent.ACTION_PACKAGE_CHANGED);
- pkgFilter.addAction(Intent.ACTION_PACKAGE_RESTARTED);
- pkgFilter.addAction(Intent.ACTION_QUERY_PACKAGE_RESTART);
- pkgFilter.addDataScheme("package");
- getContext().registerReceiverAsUser(mPackageIntentReceiver, UserHandle.ALL, pkgFilter, null,
- null);
-
- IntentFilter suspendedPkgFilter = new IntentFilter();
- suspendedPkgFilter.addAction(Intent.ACTION_PACKAGES_SUSPENDED);
- suspendedPkgFilter.addAction(Intent.ACTION_PACKAGES_UNSUSPENDED);
- suspendedPkgFilter.addAction(Intent.ACTION_DISTRACTING_PACKAGES_CHANGED);
- getContext().registerReceiverAsUser(mPackageIntentReceiver, UserHandle.ALL,
- suspendedPkgFilter, null, null);
-
- IntentFilter sdFilter = new IntentFilter(Intent.ACTION_EXTERNAL_APPLICATIONS_UNAVAILABLE);
- getContext().registerReceiverAsUser(mPackageIntentReceiver, UserHandle.ALL, sdFilter, null,
- null);
-
- IntentFilter timeoutFilter = new IntentFilter(ACTION_NOTIFICATION_TIMEOUT);
- timeoutFilter.addDataScheme(SCHEME_TIMEOUT);
- getContext().registerReceiver(mNotificationTimeoutReceiver, timeoutFilter);
-
- IntentFilter settingsRestoredFilter = new IntentFilter(Intent.ACTION_SETTING_RESTORED);
- getContext().registerReceiver(mRestoreReceiver, settingsRestoredFilter);
-
- IntentFilter localeChangedFilter = new IntentFilter(Intent.ACTION_LOCALE_CHANGED);
- getContext().registerReceiver(mLocaleChangeReceiver, localeChangedFilter);
-
publishBinderService(Context.NOTIFICATION_SERVICE, mService, /* allowIsolated= */ false,
DUMP_FLAG_PRIORITY_CRITICAL | DUMP_FLAG_PRIORITY_NORMAL);
publishLocalService(NotificationManagerInternal.class, mInternalService);
@@ -2193,12 +2211,6 @@ public class NotificationManagerService extends SystemService {
mDeviceConfigChangedListener);
}
- void unregisterDeviceConfigChange() {
- if (mDeviceConfigChangedListener != null) {
- DeviceConfig.removeOnPropertiesChangedListener(mDeviceConfigChangedListener);
- }
- }
-
private void registerNotificationPreferencesPullers() {
mPullAtomCallback = new StatsPullAtomCallbackImpl();
mStatsManager.setPullAtomCallback(
@@ -8339,15 +8351,16 @@ public class NotificationManagerService extends SystemService {
return -1;
}
- @VisibleForTesting
- protected void hideNotificationsForPackages(String[] pkgs) {
+ private void hideNotificationsForPackages(@NonNull String[] pkgs, @NonNull int[] uidList) {
synchronized (mNotificationLock) {
+ Set<Integer> uidSet = Arrays.stream(uidList).boxed().collect(Collectors.toSet());
List<String> pkgList = Arrays.asList(pkgs);
List<NotificationRecord> changedNotifications = new ArrayList<>();
int numNotifications = mNotificationList.size();
for (int i = 0; i < numNotifications; i++) {
NotificationRecord rec = mNotificationList.get(i);
- if (pkgList.contains(rec.getSbn().getPackageName())) {
+ if (pkgList.contains(rec.getSbn().getPackageName())
+ && uidSet.contains(rec.getUid())) {
rec.setHidden(true);
changedNotifications.add(rec);
}
@@ -8357,15 +8370,17 @@ public class NotificationManagerService extends SystemService {
}
}
- @VisibleForTesting
- protected void unhideNotificationsForPackages(String[] pkgs) {
+ private void unhideNotificationsForPackages(@NonNull String[] pkgs,
+ @NonNull int[] uidList) {
synchronized (mNotificationLock) {
+ Set<Integer> uidSet = Arrays.stream(uidList).boxed().collect(Collectors.toSet());
List<String> pkgList = Arrays.asList(pkgs);
List<NotificationRecord> changedNotifications = new ArrayList<>();
int numNotifications = mNotificationList.size();
for (int i = 0; i < numNotifications; i++) {
NotificationRecord rec = mNotificationList.get(i);
- if (pkgList.contains(rec.getSbn().getPackageName())) {
+ if (pkgList.contains(rec.getSbn().getPackageName())
+ && uidSet.contains(rec.getUid())) {
rec.setHidden(false);
changedNotifications.add(rec);
}
@@ -9939,38 +9954,6 @@ public class NotificationManagerService extends SystemService {
return CollectionUtils.firstOrNull(allowedComponents);
}
- @VisibleForTesting
- protected void simulatePackageSuspendBroadcast(boolean suspend, String pkg) {
- checkCallerIsSystemOrShell();
- // only use for testing: mimic receive broadcast that package is (un)suspended
- // but does not actually (un)suspend the package
- final Bundle extras = new Bundle();
- extras.putStringArray(Intent.EXTRA_CHANGED_PACKAGE_LIST,
- new String[]{pkg});
-
- final String action = suspend ? Intent.ACTION_PACKAGES_SUSPENDED
- : Intent.ACTION_PACKAGES_UNSUSPENDED;
- final Intent intent = new Intent(action);
- intent.putExtras(extras);
-
- mPackageIntentReceiver.onReceive(getContext(), intent);
- }
-
- @VisibleForTesting
- protected void simulatePackageDistractionBroadcast(int flag, String[] pkgs) {
- checkCallerIsSystemOrShell();
- // only use for testing: mimic receive broadcast that package is (un)distracting
- // but does not actually register that info with packagemanager
- final Bundle extras = new Bundle();
- extras.putStringArray(Intent.EXTRA_CHANGED_PACKAGE_LIST, pkgs);
- extras.putInt(Intent.EXTRA_DISTRACTION_RESTRICTIONS, flag);
-
- final Intent intent = new Intent(Intent.ACTION_DISTRACTING_PACKAGES_CHANGED);
- intent.putExtras(extras);
-
- mPackageIntentReceiver.onReceive(getContext(), intent);
- }
-
/**
* Wrapper for a StatusBarNotification object that allows transfer across a oneway
* binder without sending large amounts of data over a oneway transaction.
diff --git a/services/core/java/com/android/server/notification/NotificationShellCmd.java b/services/core/java/com/android/server/notification/NotificationShellCmd.java
index da7864ba32c4..927dc25ac6ce 100644
--- a/services/core/java/com/android/server/notification/NotificationShellCmd.java
+++ b/services/core/java/com/android/server/notification/NotificationShellCmd.java
@@ -66,8 +66,6 @@ public class NotificationShellCmd extends ShellCommand {
+ " set_dnd [on|none (same as on)|priority|alarms|all|off (same as all)]"
+ " allow_dnd PACKAGE [user_id (current user if not specified)]\n"
+ " disallow_dnd PACKAGE [user_id (current user if not specified)]\n"
- + " suspend_package PACKAGE\n"
- + " unsuspend_package PACKAGE\n"
+ " reset_assistant_user_set [user_id (current user if not specified)]\n"
+ " get_approved_assistant [user_id (current user if not specified)]\n"
+ " post [--help | flags] TAG TEXT\n"
@@ -258,25 +256,6 @@ public class NotificationShellCmd extends ShellCommand {
mBinderService.setNotificationAssistantAccessGrantedForUser(cn, userId, false);
}
break;
- case "suspend_package": {
- // only use for testing
- mDirectService.simulatePackageSuspendBroadcast(true, getNextArgRequired());
- }
- break;
- case "unsuspend_package": {
- // only use for testing
- mDirectService.simulatePackageSuspendBroadcast(false, getNextArgRequired());
- }
- break;
- case "distract_package": {
- // only use for testing
- // Flag values are in
- // {@link android.content.pm.PackageManager.DistractionRestriction}.
- mDirectService.simulatePackageDistractionBroadcast(
- Integer.parseInt(getNextArgRequired()),
- getNextArgRequired().split(","));
- break;
- }
case "reset_assistant_user_set": {
int userId = ActivityManager.getCurrentUser();
if (peekNextArg() != null) {
diff --git a/services/core/java/com/android/server/pm/ApkChecksums.java b/services/core/java/com/android/server/pm/ApkChecksums.java
index c6c80aef4432..8640dbcab33c 100644
--- a/services/core/java/com/android/server/pm/ApkChecksums.java
+++ b/services/core/java/com/android/server/pm/ApkChecksums.java
@@ -64,7 +64,6 @@ import java.io.DataInputStream;
import java.io.DataOutputStream;
import java.io.File;
import java.io.FileInputStream;
-import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
@@ -168,12 +167,11 @@ public class ApkChecksums {
}
/**
- * Serialize checksums to file in binary format.
+ * Serialize checksums to the stream in binary format.
*/
- public static void writeChecksums(File file, ApkChecksum[] checksums)
+ public static void writeChecksums(OutputStream os, ApkChecksum[] checksums)
throws IOException, CertificateException {
- try (OutputStream os = new FileOutputStream(file);
- DataOutputStream dos = new DataOutputStream(os)) {
+ try (DataOutputStream dos = new DataOutputStream(os)) {
dos.writeInt(checksums.length);
for (ApkChecksum checksum : checksums) {
final String splitName = checksum.getSplitName();
@@ -190,6 +188,14 @@ public class ApkChecksums {
dos.writeInt(valueBytes.length);
dos.write(valueBytes);
+ final String packageName = checksum.getSourcePackageName();
+ if (packageName == null) {
+ dos.writeInt(-1);
+ } else {
+ dos.writeInt(packageName.length());
+ dos.writeUTF(packageName);
+ }
+
final Certificate cert = checksum.getSourceCertificate();
final byte[] certBytes = (cert == null) ? null : cert.getEncoded();
if (certBytes == null) {
@@ -218,9 +224,19 @@ public class ApkChecksums {
} else {
splitName = dis.readUTF();
}
+
final int kind = dis.readInt();
+
final byte[] valueBytes = new byte[dis.readInt()];
dis.read(valueBytes);
+
+ final String packageName;
+ if (dis.readInt() < 0) {
+ packageName = null;
+ } else {
+ packageName = dis.readUTF();
+ }
+
final byte[] certBytes;
final int certBytesLength = dis.readInt();
if (certBytesLength < 0) {
@@ -230,7 +246,7 @@ public class ApkChecksums {
dis.read(certBytes);
}
checksums[i] = new ApkChecksum(splitName, new Checksum(kind, valueBytes),
- certBytes);
+ packageName, certBytes);
}
return checksums;
}
diff --git a/services/core/java/com/android/server/pm/PackageInstallerService.java b/services/core/java/com/android/server/pm/PackageInstallerService.java
index f52db5fb5f2f..31ee59717dba 100644
--- a/services/core/java/com/android/server/pm/PackageInstallerService.java
+++ b/services/core/java/com/android/server/pm/PackageInstallerService.java
@@ -618,6 +618,13 @@ public class PackageInstallerService extends IPackageInstaller.Stub implements
}
}
+ if ((params.installFlags & PackageManager.INSTALL_INSTANT_APP) != 0
+ && !isCalledBySystemOrShell(callingUid)
+ && (mPm.getFlagsForUid(callingUid) & ApplicationInfo.FLAG_SYSTEM) == 0) {
+ throw new SecurityException(
+ "Only system apps could use the PackageManager.INSTALL_INSTANT_APP flag.");
+ }
+
if (params.isStaged && !isCalledBySystemOrShell(callingUid)) {
if (mBypassNextStagedInstallerCheck) {
mBypassNextStagedInstallerCheck = false;
diff --git a/services/core/java/com/android/server/pm/PackageInstallerSession.java b/services/core/java/com/android/server/pm/PackageInstallerSession.java
index 8468f23b0a6b..60737bf77c48 100644
--- a/services/core/java/com/android/server/pm/PackageInstallerSession.java
+++ b/services/core/java/com/android/server/pm/PackageInstallerSession.java
@@ -52,6 +52,7 @@ import static com.android.server.pm.PackageInstallerService.prepareStageDir;
import android.Manifest;
import android.annotation.NonNull;
import android.annotation.Nullable;
+import android.app.AppOpsManager;
import android.app.Notification;
import android.app.NotificationManager;
import android.app.admin.DevicePolicyEventLogger;
@@ -153,6 +154,7 @@ import org.xmlpull.v1.XmlPullParser;
import org.xmlpull.v1.XmlPullParserException;
import org.xmlpull.v1.XmlSerializer;
+import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.FileDescriptor;
import java.io.FileFilter;
@@ -167,6 +169,7 @@ import java.util.Objects;
import java.util.Set;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.concurrent.atomic.AtomicInteger;
+import java.util.function.Predicate;
public class PackageInstallerSession extends IPackageInstallerSession.Stub {
private static final String TAG = "PackageInstallerSession";
@@ -240,6 +243,7 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub {
private static final String ATTR_SIGNATURE = "signature";
private static final String ATTR_CHECKSUM_KIND = "checksumKind";
private static final String ATTR_CHECKSUM_VALUE = "checksumValue";
+ private static final String ATTR_CHECKSUM_PACKAGE = "checksumPackage";
private static final String ATTR_CHECKSUM_CERTIFICATE = "checksumCertificate";
private static final String PROPERTY_NAME_INHERIT_NATIVE = "pi.inherit_native_on_dont_kill";
@@ -394,10 +398,13 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub {
static class CertifiedChecksum {
final @NonNull Checksum mChecksum;
+ final @NonNull String mPackageName;
final @NonNull byte[] mCertificate;
- CertifiedChecksum(@NonNull Checksum checksum, @NonNull byte[] certificate) {
+ CertifiedChecksum(@NonNull Checksum checksum, @NonNull String packageName,
+ @NonNull byte[] certificate) {
mChecksum = checksum;
+ mPackageName = packageName;
mCertificate = certificate;
}
@@ -405,6 +412,10 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub {
return mChecksum;
}
+ String getPackageName() {
+ return mPackageName;
+ }
+
byte[] getCertificate() {
return mCertificate;
}
@@ -679,7 +690,7 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub {
stagedSessionErrorMessage != null ? stagedSessionErrorMessage : "";
if (isDataLoaderInstallation()) {
- if (isApexInstallation()) {
+ if (isApexSession()) {
throw new IllegalArgumentException(
"DataLoader installation of APEX modules is not allowed.");
}
@@ -745,7 +756,7 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub {
info.mode = params.mode;
info.installReason = params.installReason;
info.sizeBytes = params.sizeBytes;
- info.appPackageName = params.appPackageName;
+ info.appPackageName = mPackageName != null ? mPackageName : params.appPackageName;
if (includeIcon) {
info.appIcon = params.appIcon;
}
@@ -951,23 +962,26 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub {
return;
}
- final PackageManagerInternal pmi = LocalServices.getService(
- PackageManagerInternal.class);
- final AndroidPackage callingInstaller = pmi.getPackage(Binder.getCallingUid());
+ final String initiatingPackageName = mInstallSource.initiatingPackageName;
+
+ final AppOpsManager appOps = mContext.getSystemService(AppOpsManager.class);
+ appOps.checkPackage(Binder.getCallingUid(), initiatingPackageName);
+ final PackageManagerInternal pmi = LocalServices.getService(PackageManagerInternal.class);
+ final AndroidPackage callingInstaller = pmi.getPackage(initiatingPackageName);
if (callingInstaller == null) {
throw new IllegalStateException("Can't obtain calling installer's package.");
}
// Obtaining array of certificates used for signing the installer package.
- // According to V2/V3 signing schema, the first certificate corresponds to public key
- // in the signing block.
- Signature[] certs = callingInstaller.getSigningDetails().signatures;
+ final Signature[] certs = callingInstaller.getSigningDetails().signatures;
if (certs == null || certs.length == 0 || certs[0] == null) {
throw new IllegalStateException(
"Can't obtain calling installer package's certificates.");
}
- byte[] mainCertificateBytes = certs[0].toByteArray();
+ // According to V2/V3 signing schema, the first certificate corresponds to the public key
+ // in the signing block.
+ final byte[] mainCertificateBytes = certs[0].toByteArray();
synchronized (mLock) {
assertCallerIsOwnerOrRootLocked();
@@ -979,7 +993,8 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub {
fileChecksums = new ArrayList<>();
mChecksums.put(name, fileChecksums);
}
- fileChecksums.add(new CertifiedChecksum(checksum, mainCertificateBytes));
+ fileChecksums.add(new CertifiedChecksum(checksum, initiatingPackageName,
+ mainCertificateBytes));
}
}
}
@@ -1562,7 +1577,7 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub {
return false;
}
- if (isApexInstallation()) {
+ if (isApexSession()) {
validateApexInstallLocked();
} else {
validateApkInstallLocked();
@@ -1719,7 +1734,7 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub {
try {
sealLocked();
- if (isApexInstallation()) {
+ if (isApexSession()) {
// APEX installations rely on certain fields to be populated after reboot.
// E.g. mPackageName.
validateApexInstallLocked();
@@ -1795,7 +1810,7 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub {
return;
}
- if (isApexInstallation()) {
+ if (isApexSession()) {
destroyInternal();
dispatchSessionFinished(PackageManager.INSTALL_FAILED_INTERNAL_ERROR,
"APEX packages can only be installed using staged sessions.", null);
@@ -1972,7 +1987,7 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub {
// TODO(b/136257624): Some logic in this if block probably belongs in
// makeInstallParams().
- if (!params.isMultiPackage && !isApexInstallation()) {
+ if (!params.isMultiPackage && !isApexSession()) {
Objects.requireNonNull(mPackageName);
Objects.requireNonNull(mSigningDetails);
Objects.requireNonNull(mResolvedBaseFile);
@@ -2201,10 +2216,34 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub {
/**
* Returns true if the session is installing an APEX package.
*/
- private boolean isApexInstallation() {
+ boolean isApexSession() {
return (params.installFlags & PackageManager.INSTALL_APEX) != 0;
}
+ private boolean sessionContains(Predicate<PackageInstallerSession> filter) {
+ if (!isMultiPackage()) {
+ return filter.test(this);
+ }
+ final List<PackageInstallerSession> childSessions;
+ synchronized (mLock) {
+ childSessions = getChildSessionsLocked();
+ }
+ for (PackageInstallerSession child: childSessions) {
+ if (filter.test(child)) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ boolean containsApexSession() {
+ return sessionContains((s) -> s.isApexSession());
+ }
+
+ boolean containsApkSession() {
+ return sessionContains((s) -> !s.isApexSession());
+ }
+
/**
* Validate apex install.
* <p>
@@ -2357,6 +2396,17 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub {
"Invalid filename: " + targetName);
}
+ // Yell loudly if installers drop attribute installLocation when apps explicitly set.
+ if (apk.installLocation != PackageInfo.INSTALL_LOCATION_UNSPECIFIED) {
+ final String installerPackageName = getInstallerPackageName();
+ if (installerPackageName != null
+ && (params.installLocation != apk.installLocation)) {
+ Slog.wtf(TAG, installerPackageName
+ + " drops manifest attribute android:installLocation in " + targetName
+ + " for " + mPackageName);
+ }
+ }
+
final File targetFile = new File(stageDir, targetName);
resolveAndStageFileLocked(addedFile, targetFile, apk.splitName);
@@ -2622,7 +2672,7 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub {
for (int i = 0, size = checksums.size(); i < size; ++i) {
CertifiedChecksum checksum = checksums.get(i);
result[i] = new ApkChecksum(splitName, checksum.getChecksum(),
- checksum.getCertificate());
+ checksum.getPackageName(), checksum.getCertificate());
}
return result;
}
@@ -2640,11 +2690,17 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub {
return;
}
- final File targetDigestsFile = new File(stageDir,
- ApkChecksums.buildDigestsPathForApk(targetFile.getName()));
- try {
- ApkChecksums.writeChecksums(targetDigestsFile,
- createApkChecksums(splitName, checksums));
+ final String targetDigestsPath = ApkChecksums.buildDigestsPathForApk(targetFile.getName());
+ final File targetDigestsFile = new File(stageDir, targetDigestsPath);
+ try (ByteArrayOutputStream os = new ByteArrayOutputStream()) {
+ ApkChecksums.writeChecksums(os, createApkChecksums(splitName, checksums));
+ final byte[] checksumsBytes = os.toByteArray();
+
+ if (!isIncrementalInstallation() || mIncrementalFileStorages == null) {
+ FileUtils.bytesToFile(targetDigestsFile.getAbsolutePath(), checksumsBytes);
+ } else {
+ mIncrementalFileStorages.makeFile(targetDigestsPath, checksumsBytes);
+ }
} catch (CertificateException e) {
throw new PackageManagerException(INSTALL_PARSE_FAILED_CERTIFICATE_ENCODING,
"Failed to encode certificate for " + mPackageName, e);
@@ -3964,6 +4020,7 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub {
writeIntAttribute(out, ATTR_CHECKSUM_KIND, checksum.getChecksum().getKind());
writeByteArrayAttribute(out, ATTR_CHECKSUM_VALUE,
checksum.getChecksum().getValue());
+ writeStringAttribute(out, ATTR_CHECKSUM_PACKAGE, checksum.getPackageName());
writeByteArrayAttribute(out, ATTR_CHECKSUM_CERTIFICATE,
checksum.getCertificate());
out.endTag(null, TAG_SESSION_CHECKSUM);
@@ -4116,6 +4173,7 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub {
final CertifiedChecksum certifiedChecksum = new CertifiedChecksum(
new Checksum(readIntAttribute(in, ATTR_CHECKSUM_KIND, 0),
readByteArrayAttribute(in, ATTR_CHECKSUM_VALUE)),
+ readStringAttribute(in, ATTR_CHECKSUM_PACKAGE),
readByteArrayAttribute(in, ATTR_CHECKSUM_CERTIFICATE));
List<CertifiedChecksum> certifiedChecksums = checksums.get(fileName);
diff --git a/services/core/java/com/android/server/pm/PackageManagerService.java b/services/core/java/com/android/server/pm/PackageManagerService.java
index 0f455fdf764b..b451eaf198f8 100644
--- a/services/core/java/com/android/server/pm/PackageManagerService.java
+++ b/services/core/java/com/android/server/pm/PackageManagerService.java
@@ -282,6 +282,7 @@ import android.os.storage.StorageManagerInternal;
import android.os.storage.VolumeInfo;
import android.os.storage.VolumeRecord;
import android.permission.IPermissionManager;
+import android.provider.ContactsContract;
import android.provider.DeviceConfig;
import android.provider.Settings.Global;
import android.provider.Settings.Secure;
@@ -349,6 +350,7 @@ import com.android.server.ServiceThread;
import com.android.server.SystemConfig;
import com.android.server.SystemServerInitThreadPool;
import com.android.server.Watchdog;
+import com.android.server.compat.CompatChange;
import com.android.server.compat.PlatformCompat;
import com.android.server.net.NetworkPolicyManagerInternal;
import com.android.server.pm.Installer.InstallerException;
@@ -1736,15 +1738,14 @@ public class PackageManagerService extends IPackageManager.Stub
int i = 0; // filling out the above arrays
for (int n = 0; n < mPendingBroadcasts.userIdCount(); n++) {
- int packageUserId = mPendingBroadcasts.userIdAt(n);
- Iterator<Map.Entry<String, ArrayList<String>>> it
- = mPendingBroadcasts.packagesForUserId(packageUserId)
- .entrySet().iterator();
- while (it.hasNext() && i < size) {
- Map.Entry<String, ArrayList<String>> ent = it.next();
- packages[i] = ent.getKey();
- components[i] = ent.getValue();
- PackageSetting ps = mSettings.mPackages.get(ent.getKey());
+ final int packageUserId = mPendingBroadcasts.userIdAt(n);
+ final ArrayMap<String, ArrayList<String>> componentsToBroadcast =
+ mPendingBroadcasts.packagesForUserId(packageUserId);
+ final int numComponents = componentsToBroadcast.size();
+ for (int index = 0; i < size && index < numComponents; index++) {
+ packages[i] = componentsToBroadcast.keyAt(index);
+ components[i] = componentsToBroadcast.valueAt(index);
+ final PackageSetting ps = mSettings.mPackages.get(packages[i]);
uids[i] = (ps != null)
? UserHandle.getUid(packageUserId, ps.appId)
: -1;
@@ -1850,7 +1851,6 @@ public class PackageManagerService extends IPackageManager.Stub
Process.setThreadPriority(Process.THREAD_PRIORITY_DEFAULT);
synchronized (mLock) {
removeMessages(WRITE_PACKAGE_LIST);
- mPermissionManager.writePermissionsStateToPackageSettingsTEMP();
mSettings.writePackageListLPr(msg.arg1);
}
Process.setThreadPriority(Process.THREAD_PRIORITY_BACKGROUND);
@@ -2128,11 +2128,21 @@ public class PackageManagerService extends IPackageManager.Stub
final boolean update = res.removedInfo != null && res.removedInfo.removedPackage != null;
final String packageName = res.name;
final PackageSetting pkgSetting = succeeded ? getPackageSetting(packageName) : null;
- if (succeeded && pkgSetting == null) {
+ final boolean removedBeforeUpdate = (pkgSetting == null)
+ || (pkgSetting.isSystem() && !pkgSetting.getPathString().equals(res.pkg.getPath()));
+ if (succeeded && removedBeforeUpdate) {
Slog.e(TAG, packageName + " was removed before handlePackagePostInstall "
+ "could be executed");
res.returnCode = INSTALL_FAILED_PACKAGE_CHANGED;
res.returnMsg = "Package was removed before install could complete.";
+
+ // Remove the update failed package's older resources safely now
+ InstallArgs args = res.removedInfo != null ? res.removedInfo.args : null;
+ if (args != null) {
+ synchronized (mInstallLock) {
+ args.doPostDeleteLI(true);
+ }
+ }
notifyInstallObserver(res, installObserver);
return;
}
@@ -2687,7 +2697,8 @@ public class PackageManagerService extends IPackageManager.Stub
(i, pm) ->
new Settings(Environment.getDataDirectory(),
i.getPermissionManagerServiceInternal().getPermissionSettings(),
- RuntimePermissionsPersistence.createInstance(), lock),
+ RuntimePermissionsPersistence.createInstance(),
+ i.getPermissionManagerServiceInternal(), lock),
new Injector.LocalServicesProducer<>(ActivityTaskManagerInternal.class),
new Injector.LocalServicesProducer<>(ActivityManagerInternal.class),
new Injector.LocalServicesProducer<>(DeviceIdleInternal.class),
@@ -2704,39 +2715,43 @@ public class PackageManagerService extends IPackageManager.Stub
PackageManagerService m = new PackageManagerService(injector, onlyCore, factoryTest);
t.traceEnd(); // "create package manager"
- injector.getCompatibility().registerListener(SELinuxMMAC.SELINUX_LATEST_CHANGES,
- packageName -> {
- synchronized (m.mInstallLock) {
- final AndroidPackage pkg;
- final PackageSetting ps;
- final SharedUserSetting sharedUser;
- final String oldSeInfo;
- synchronized (m.mLock) {
- ps = m.mSettings.getPackageLPr(packageName);
- if (ps == null) {
- Slog.e(TAG, "Failed to find package setting " + packageName);
- return;
- }
- pkg = ps.pkg;
- sharedUser = ps.getSharedUser();
- oldSeInfo = AndroidPackageUtils.getSeInfo(pkg, ps);
- }
-
- if (pkg == null) {
- Slog.e(TAG, "Failed to find package " + packageName);
- return;
- }
- final String newSeInfo = SELinuxMMAC.getSeInfo(pkg, sharedUser,
- m.mInjector.getCompatibility());
-
- if (!newSeInfo.equals(oldSeInfo)) {
- Slog.i(TAG, "Updating seInfo for package " + packageName + " from: "
- + oldSeInfo + " to: " + newSeInfo);
- ps.getPkgState().setOverrideSeInfo(newSeInfo);
- m.prepareAppDataAfterInstallLIF(pkg);
- }
+ final CompatChange.ChangeListener selinuxChangeListener = packageName -> {
+ synchronized (m.mInstallLock) {
+ final AndroidPackage pkg;
+ final PackageSetting ps;
+ final SharedUserSetting sharedUser;
+ final String oldSeInfo;
+ synchronized (m.mLock) {
+ ps = m.mSettings.getPackageLPr(packageName);
+ if (ps == null) {
+ Slog.e(TAG, "Failed to find package setting " + packageName);
+ return;
}
- });
+ pkg = ps.pkg;
+ sharedUser = ps.getSharedUser();
+ oldSeInfo = AndroidPackageUtils.getSeInfo(pkg, ps);
+ }
+
+ if (pkg == null) {
+ Slog.e(TAG, "Failed to find package " + packageName);
+ return;
+ }
+ final String newSeInfo = SELinuxMMAC.getSeInfo(pkg, sharedUser,
+ m.mInjector.getCompatibility());
+
+ if (!newSeInfo.equals(oldSeInfo)) {
+ Slog.i(TAG, "Updating seInfo for package " + packageName + " from: "
+ + oldSeInfo + " to: " + newSeInfo);
+ ps.getPkgState().setOverrideSeInfo(newSeInfo);
+ m.prepareAppDataAfterInstallLIF(pkg);
+ }
+ }
+ };
+
+ injector.getCompatibility().registerListener(SELinuxMMAC.SELINUX_LATEST_CHANGES,
+ selinuxChangeListener);
+ injector.getCompatibility().registerListener(SELinuxMMAC.SELINUX_R_CHANGES,
+ selinuxChangeListener);
m.installWhitelistedSystemPackages();
ServiceManager.addService("package", m);
@@ -3235,17 +3250,18 @@ public class PackageManagerService extends IPackageManager.Stub
final List<String> stubSystemApps = new ArrayList<>();
if (!mOnlyCore) {
// do this first before mucking with mPackages for the "expecting better" case
- final Iterator<AndroidPackage> pkgIterator = mPackages.values().iterator();
- while (pkgIterator.hasNext()) {
- final AndroidPackage pkg = pkgIterator.next();
+ final int numPackages = mPackages.size();
+ for (int index = 0; index < numPackages; index++) {
+ final AndroidPackage pkg = mPackages.valueAt(index);
if (pkg.isStub()) {
stubSystemApps.add(pkg.getPackageName());
}
}
- final Iterator<PackageSetting> psit = mSettings.mPackages.values().iterator();
- while (psit.hasNext()) {
- PackageSetting ps = psit.next();
+ // Iterates PackageSettings in reversed order because the item could be removed
+ // during the iteration.
+ for (int index = mSettings.mPackages.size() - 1; index >= 0; index--) {
+ final PackageSetting ps = mSettings.mPackages.valueAt(index);
/*
* If this is not a system app, it can't be a
@@ -3282,7 +3298,7 @@ public class PackageManagerService extends IPackageManager.Stub
}
if (!mSettings.isDisabledSystemPackageLPr(ps.name)) {
- psit.remove();
+ mSettings.mPackages.removeAt(index);
logCriticalInfo(Log.WARN, "System package " + ps.name
+ " no longer exists; it's data will be wiped");
@@ -3524,7 +3540,7 @@ public class PackageManagerService extends IPackageManager.Stub
+ ((SystemClock.uptimeMillis()-startTime)/1000f)
+ " seconds");
- mPermissionManager.readPermissionsStateFromPackageSettingsTEMP();
+ mPermissionManager.readStateFromPackageSettingsTEMP();
// If the platform SDK has changed since the last time we booted,
// we need to re-grant app permission to catch any new ones that
// appear. This is really a hack, and means that apps can in some
@@ -4486,8 +4502,8 @@ public class PackageManagerService extends IPackageManager.Stub
AndroidPackage p = ps.pkg;
if (p != null) {
// Compute GIDs only if requested
- final int[] gids = (flags & PackageManager.GET_GIDS) == 0
- ? EMPTY_INT_ARRAY : mPermissionManager.getPackageGids(ps.name, userId);
+ final int[] gids = (flags & PackageManager.GET_GIDS) == 0 ? EMPTY_INT_ARRAY
+ : mPermissionManager.getGidsForUid(UserHandle.getUid(userId, ps.appId));
// Compute granted permissions only if package has requested permissions
final Set<String> permissions = ArrayUtils.isEmpty(p.getRequestedPermissions())
? Collections.emptySet()
@@ -4962,13 +4978,13 @@ public class PackageManagerService extends IPackageManager.Stub
}
// TODO: Shouldn't this be checking for package installed state for userId and
// return null?
- return mPermissionManager.getPackageGids(packageName, userId);
+ return mPermissionManager.getGidsForUid(UserHandle.getUid(userId, ps.appId));
}
if ((flags & MATCH_KNOWN_PACKAGES) != 0) {
final PackageSetting ps = mSettings.mPackages.get(packageName);
if (ps != null && ps.isMatch(flags)
&& !shouldFilterApplicationLocked(ps, callingUid, userId)) {
- return mPermissionManager.getPackageGids(packageName, userId);
+ return mPermissionManager.getGidsForUid(UserHandle.getUid(userId, ps.appId));
}
}
}
@@ -5427,6 +5443,23 @@ public class PackageManagerService extends IPackageManager.Stub
}
@Override
+ public int getTargetSdkVersion(String packageName) {
+ synchronized (mLock) {
+ final AndroidPackage pkg = mPackages.get(packageName);
+ if (pkg == null) {
+ return -1;
+ }
+
+ final PackageSetting ps = getPackageSetting(pkg.getPackageName());
+ if (shouldFilterApplicationLocked(ps, Binder.getCallingUid(),
+ UserHandle.getCallingUserId())) {
+ return -1;
+ }
+ return pkg.getTargetSdkVersion();
+ }
+ }
+
+ @Override
public ActivityInfo getActivityInfo(ComponentName component, int flags, int userId) {
return getActivityInfoInternal(component, flags, Binder.getCallingUid(), userId);
}
@@ -6332,10 +6365,9 @@ public class PackageManagerService extends IPackageManager.Stub
final SharedUserSetting sus = (SharedUserSetting) obj;
final int N = sus.packages.size();
String[] res = new String[N];
- final Iterator<PackageSetting> it = sus.packages.iterator();
int i = 0;
- while (it.hasNext()) {
- PackageSetting ps = it.next();
+ for (int index = 0; index < N; index++) {
+ final PackageSetting ps = sus.packages.valueAt(index);
if (ps.getInstalled(userId)) {
res[i++] = ps.name;
}
@@ -6491,9 +6523,10 @@ public class PackageManagerService extends IPackageManager.Stub
final Object obj = mSettings.getSettingLPr(appId);
if (obj instanceof SharedUserSetting) {
final SharedUserSetting sus = (SharedUserSetting) obj;
- final Iterator<PackageSetting> it = sus.packages.iterator();
- while (it.hasNext()) {
- if (it.next().isPrivileged()) {
+ final int numPackages = sus.packages.size();
+ for (int index = 0; index < numPackages; index++) {
+ final PackageSetting ps = sus.packages.valueAt(index);
+ if (ps.isPrivileged()) {
return true;
}
}
@@ -8992,10 +9025,10 @@ public class PackageManagerService extends IPackageManager.Stub
// reader
synchronized (mLock) {
- final Iterator<AndroidPackage> i = mPackages.values().iterator();
+ final int numPackages = mPackages.size();
final int userId = UserHandle.getCallingUserId();
- while (i.hasNext()) {
- final AndroidPackage p = i.next();
+ for (int index = 0; index < numPackages; index++) {
+ final AndroidPackage p = mPackages.valueAt(index);
final boolean matchesUnaware = ((flags & MATCH_DIRECT_BOOT_UNAWARE) != 0)
&& !p.isDirectBootAware();
@@ -9153,9 +9186,9 @@ public class PackageManagerService extends IPackageManager.Stub
// reader
synchronized (mLock) {
- final Iterator<ParsedInstrumentation> i = mInstrumentation.values().iterator();
- while (i.hasNext()) {
- final ParsedInstrumentation p = i.next();
+ final int numInstrumentations = mInstrumentation.size();
+ for (int index = 0; index < numInstrumentations; index++) {
+ final ParsedInstrumentation p = mInstrumentation.valueAt(index);
if (targetPackage == null
|| targetPackage.equals(p.getTargetPackage())) {
String packageName = p.getPackageName();
@@ -10486,7 +10519,6 @@ public class PackageManagerService extends IPackageManager.Stub
private void clearAppDataLIF(AndroidPackage pkg, int userId, int flags) {
if (pkg == null) {
- Slog.wtf(TAG, "Package was null!", new Throwable());
return;
}
clearAppDataLeafLIF(pkg, userId, flags);
@@ -18967,7 +18999,7 @@ public class PackageManagerService extends IPackageManager.Stub
}
if ((deletedPs.sharedUser == null || deletedPs.sharedUser.packages.size() == 0)
&& !isUpdatedSystemApp(deletedPs)) {
- mPermissionManager.removePermissionsStateTEMP(removedAppId);
+ mPermissionManager.removeAppIdStateTEMP(removedAppId);
}
mPermissionManager.updatePermissions(deletedPs.name, null);
if (deletedPs.sharedUser != null) {
@@ -19806,9 +19838,9 @@ public class PackageManagerService extends IPackageManager.Stub
if (obj instanceof SharedUserSetting) {
final SharedUserSetting sus = (SharedUserSetting) obj;
int vers = Build.VERSION_CODES.CUR_DEVELOPMENT;
- final Iterator<PackageSetting> it = sus.packages.iterator();
- while (it.hasNext()) {
- final PackageSetting ps = it.next();
+ final int numPackages = sus.packages.size();
+ for (int index = 0; index < numPackages; index++) {
+ final PackageSetting ps = sus.packages.valueAt(index);
if (ps.pkg != null) {
int v = ps.pkg.getTargetSdkVersion();
if (v < vers) vers = v;
@@ -21226,7 +21258,8 @@ public class PackageManagerService extends IPackageManager.Stub
// Prior to enabling the package, we need to decompress the APK(s) to the
// data partition and then replace the version on the system partition.
final AndroidPackage deletedPkg = pkgSetting.pkg;
- final boolean isSystemStub = deletedPkg.isStub()
+ final boolean isSystemStub = (deletedPkg != null)
+ && deletedPkg.isStub()
&& deletedPkg.isSystem();
if (isSystemStub
&& (newState == PackageManager.COMPONENT_ENABLED_STATE_DEFAULT
@@ -21838,8 +21871,6 @@ public class PackageManagerService extends IPackageManager.Stub
protected void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
if (!DumpUtils.checkDumpAndUsageStatsPermission(mContext, TAG, pw)) return;
- mPermissionManager.writePermissionsStateToPackageSettingsTEMP();
-
DumpState dumpState = new DumpState();
boolean fullPreferred = false;
boolean checkin = false;
@@ -22104,9 +22135,9 @@ public class PackageManagerService extends IPackageManager.Stub
if (dumpState.isDumping(DumpState.DUMP_LIBS) && packageName == null) {
boolean printedHeader = false;
- final Iterator<String> it = mSharedLibraries.keySet().iterator();
- while (it.hasNext()) {
- String libName = it.next();
+ final int numSharedLibraries = mSharedLibraries.size();
+ for (int index = 0; index < numSharedLibraries; index++) {
+ final String libName = mSharedLibraries.keyAt(index);
LongSparseArray<SharedLibraryInfo> versionedLib
= mSharedLibraries.get(libName);
if (versionedLib == null) {
@@ -23718,7 +23749,6 @@ public class PackageManagerService extends IPackageManager.Stub
mDirtyUsers.remove(userId);
mUserNeedsBadging.delete(userId);
mPermissionManager.onUserRemoved(userId);
- mPermissionManager.writePermissionsStateToPackageSettingsTEMP();
mSettings.removeUserLPw(userId);
mPendingBroadcasts.remove(userId);
mInstantAppRegistry.onUserRemovedLPw(userId);
@@ -23735,9 +23765,9 @@ public class PackageManagerService extends IPackageManager.Stub
private void removeUnusedPackagesLPw(UserManagerService userManager, final int userId) {
final boolean DEBUG_CLEAN_APKS = false;
int [] users = userManager.getUserIds();
- Iterator<PackageSetting> psit = mSettings.mPackages.values().iterator();
- while (psit.hasNext()) {
- PackageSetting ps = psit.next();
+ final int numPackages = mSettings.mPackages.size();
+ for (int index = 0; index < numPackages; index++) {
+ final PackageSetting ps = mSettings.mPackages.valueAt(index);
if (ps.pkg == null) {
continue;
}
@@ -23819,9 +23849,9 @@ public class PackageManagerService extends IPackageManager.Stub
boolean readPermissionStateForUser(@UserIdInt int userId) {
synchronized (mPackages) {
- mPermissionManager.writePermissionsStateToPackageSettingsTEMP();
+ mPermissionManager.writeStateToPackageSettingsTEMP();
mSettings.readPermissionStateForUserSyncLPr(userId);
- mPermissionManager.readPermissionsStateFromPackageSettingsTEMP();
+ mPermissionManager.readStateFromPackageSettingsTEMP();
return mPmInternal.isPermissionUpgradeNeeded(userId);
}
}
@@ -24210,15 +24240,13 @@ public class PackageManagerService extends IPackageManager.Stub
}
@Override
- public int getTargetSdkVersionForPackage(String packageName)
- throws RemoteException {
- int callingUser = UserHandle.getUserId(Binder.getCallingUid());
- ApplicationInfo info = getApplicationInfo(packageName, 0, callingUser);
- if (info == null) {
- throw new RemoteException(
- "Couldn't get ApplicationInfo for package " + packageName);
+ public int getTargetSdkVersionForPackage(String packageName) throws RemoteException {
+ int targetSdk = getTargetSdkVersion(packageName);
+ if (targetSdk != -1) {
+ return targetSdk;
}
- return info.targetSdkVersion;
+
+ throw new RemoteException("Couldn't get targetSdkVersion for package " + packageName);
}
@Override
@@ -25442,11 +25470,11 @@ public class PackageManagerService extends IPackageManager.Stub
}
ArraySet<PackageSetting> packages = packageSetting.sharedUser.packages;
- String[] res = new String[packages.size()];
- final Iterator<PackageSetting> it = packages.iterator();
+ final int numPackages = packages.size();
+ String[] res = new String[numPackages];
int i = 0;
- while (it.hasNext()) {
- PackageSetting ps = it.next();
+ for (int index = 0; index < numPackages; index++) {
+ final PackageSetting ps = packages.valueAt(index);
if (ps.getInstalled(userId)) {
res[i++] = ps.name;
}
@@ -25702,6 +25730,32 @@ public class PackageManagerService extends IPackageManager.Stub
}
}
+ @Override
+ public void grantImplicitAccess(int recipientUid, String visibleAuthority) {
+ // This API is exposed temporarily to only the contacts provider. (b/158688602)
+ final int callingUid = Binder.getCallingUid();
+ ProviderInfo contactsProvider = resolveContentProviderInternal(
+ ContactsContract.AUTHORITY, 0, UserHandle.USER_SYSTEM);
+ if (contactsProvider == null || contactsProvider.applicationInfo == null
+ || !UserHandle.isSameApp(contactsProvider.applicationInfo.uid, callingUid)) {
+ throw new SecurityException(callingUid + " is not allow to call grantImplicitAccess");
+ }
+ final int userId = UserHandle.getUserId(recipientUid);
+ final long token = Binder.clearCallingIdentity();
+ final ProviderInfo providerInfo;
+ try {
+ providerInfo = resolveContentProvider(visibleAuthority, 0 /*flags*/, userId);
+ } finally {
+ Binder.restoreCallingIdentity(token);
+ }
+ if (providerInfo == null) {
+ return;
+ }
+ int visibleUid = providerInfo.applicationInfo.uid;
+ mPmInternal.grantImplicitAccess(userId, null /*Intent*/, UserHandle.getAppId(recipientUid),
+ visibleUid, false);
+ }
+
boolean canHaveOatDir(String packageName) {
synchronized (mLock) {
AndroidPackage p = mPackages.get(packageName);
@@ -25885,12 +25939,12 @@ public class PackageManagerService extends IPackageManager.Stub
/**
* Temporary method that wraps mSettings.writeLPr() and calls
- * mPermissionManager.writePermissionsStateToPackageSettingsTEMP() beforehand.
+ * mPermissionManager.writeStateToPackageSettingsTEMP() beforehand.
*
* TODO(zhanghai): This should be removed once we finish migration of permission storage.
*/
private void writeSettingsLPrTEMP() {
- mPermissionManager.writePermissionsStateToPackageSettingsTEMP();
+ mPermissionManager.writeStateToPackageSettingsTEMP();
mSettings.writeLPr();
}
}
diff --git a/services/core/java/com/android/server/pm/PackageManagerShellCommand.java b/services/core/java/com/android/server/pm/PackageManagerShellCommand.java
index d77683e8ba61..afbf7d3a35af 100644
--- a/services/core/java/com/android/server/pm/PackageManagerShellCommand.java
+++ b/services/core/java/com/android/server/pm/PackageManagerShellCommand.java
@@ -2323,7 +2323,8 @@ class PackageManagerShellCommand extends ShellCommand {
private boolean isVendorApp(String pkg) {
try {
- final PackageInfo info = mInterface.getPackageInfo(pkg, 0, UserHandle.USER_SYSTEM);
+ final PackageInfo info = mInterface.getPackageInfo(
+ pkg, PackageManager.MATCH_ANY_USER, UserHandle.USER_SYSTEM);
return info != null && info.applicationInfo.isVendor();
} catch (RemoteException e) {
return false;
@@ -2332,7 +2333,8 @@ class PackageManagerShellCommand extends ShellCommand {
private boolean isProductApp(String pkg) {
try {
- final PackageInfo info = mInterface.getPackageInfo(pkg, 0, UserHandle.USER_SYSTEM);
+ final PackageInfo info = mInterface.getPackageInfo(
+ pkg, PackageManager.MATCH_ANY_USER, UserHandle.USER_SYSTEM);
return info != null && info.applicationInfo.isProduct();
} catch (RemoteException e) {
return false;
@@ -2341,7 +2343,8 @@ class PackageManagerShellCommand extends ShellCommand {
private boolean isSystemExtApp(String pkg) {
try {
- final PackageInfo info = mInterface.getPackageInfo(pkg, 0, UserHandle.USER_SYSTEM);
+ final PackageInfo info = mInterface.getPackageInfo(
+ pkg, PackageManager.MATCH_ANY_USER, UserHandle.USER_SYSTEM);
return info != null && info.applicationInfo.isSystemExt();
} catch (RemoteException e) {
return false;
diff --git a/services/core/java/com/android/server/pm/PackageManagerShellCommandDataLoader.java b/services/core/java/com/android/server/pm/PackageManagerShellCommandDataLoader.java
index 2bbca79741bd..1814a8e6322e 100644
--- a/services/core/java/com/android/server/pm/PackageManagerShellCommandDataLoader.java
+++ b/services/core/java/com/android/server/pm/PackageManagerShellCommandDataLoader.java
@@ -27,6 +27,8 @@ import android.service.dataloader.DataLoaderService;
import android.util.Slog;
import android.util.SparseArray;
+import com.android.internal.annotations.VisibleForTesting;
+
import libcore.io.IoUtils;
import java.io.IOException;
@@ -112,7 +114,9 @@ public class PackageManagerShellCommandDataLoader extends DataLoaderService {
}
}
- static class Metadata {
+ /** @hide */
+ @VisibleForTesting
+ public static class Metadata {
/**
* Full files read from stdin.
*/
@@ -137,7 +141,9 @@ public class PackageManagerShellCommandDataLoader extends DataLoaderService {
return new Metadata(STDIN, fileId);
}
- static Metadata forLocalFile(String filePath) {
+ /** @hide */
+ @VisibleForTesting
+ public static Metadata forLocalFile(String filePath) {
return new Metadata(LOCAL_FILE, filePath);
}
@@ -163,7 +169,9 @@ public class PackageManagerShellCommandDataLoader extends DataLoaderService {
return new Metadata(mode, data);
}
- byte[] toByteArray() {
+ /** @hide */
+ @VisibleForTesting
+ public byte[] toByteArray() {
byte[] dataBytes = this.mData.getBytes(StandardCharsets.UTF_8);
byte[] result = new byte[1 + dataBytes.length];
result[0] = this.mMode;
diff --git a/services/core/java/com/android/server/pm/PackageSetting.java b/services/core/java/com/android/server/pm/PackageSetting.java
index 276f88082df0..855a5ff524fd 100644
--- a/services/core/java/com/android/server/pm/PackageSetting.java
+++ b/services/core/java/com/android/server/pm/PackageSetting.java
@@ -28,7 +28,7 @@ import android.util.proto.ProtoOutputStream;
import com.android.internal.annotations.VisibleForTesting;
import com.android.server.pm.parsing.pkg.AndroidPackage;
-import com.android.server.pm.permission.PermissionsState;
+import com.android.server.pm.permission.AppIdPermissionState;
import com.android.server.pm.pkg.PackageStateUnserialized;
import java.io.File;
@@ -215,7 +215,7 @@ public class PackageSetting extends PackageSettingBase {
}
@Override
- public PermissionsState getPermissionsState() {
+ public AppIdPermissionState getPermissionsState() {
return (sharedUser != null)
? sharedUser.getPermissionsState()
: super.getPermissionsState();
diff --git a/services/core/java/com/android/server/pm/SELinuxMMAC.java b/services/core/java/com/android/server/pm/SELinuxMMAC.java
index fdd9636ae7b2..c5fbfba9b049 100644
--- a/services/core/java/com/android/server/pm/SELinuxMMAC.java
+++ b/services/core/java/com/android/server/pm/SELinuxMMAC.java
@@ -18,6 +18,7 @@ package com.android.server.pm;
import android.compat.annotation.ChangeId;
import android.compat.annotation.EnabledAfter;
+import android.content.pm.ApplicationInfo;
import android.content.pm.PackageParser.SigningDetails;
import android.content.pm.Signature;
import android.os.Environment;
@@ -77,9 +78,21 @@ public final class SELinuxMMAC {
private static final String TARGETSDKVERSION_STR = ":targetSdkVersion=";
/**
- * This change gates apps access to untrusted_app_R-targetSDk SELinux domain. Allows opt-in
+ * Allows opt-in to the latest targetSdkVersion enforced changes without changing target SDK.
+ * Turning this change off for an app targeting the latest SDK is a no-op.
+ *
+ * <p>Has no effect for apps using shared user id.
+ *
+ * TODO(b/143539591): Update description with relevant SELINUX changes this opts in to.
+ */
+ @EnabledAfter(targetSdkVersion = android.os.Build.VERSION_CODES.R)
+ @ChangeId
+ static final long SELINUX_LATEST_CHANGES = 143539591L;
+
+ /**
+ * This change gates apps access to untrusted_app_R-targetSDK SELinux domain. Allows opt-in
* to R targetSdkVersion enforced changes without changing target SDK. Turning this change
- * off for an app targeting R is a no-op.
+ * off for an app targeting S is a no-op.
*
* <p>Has no effect for apps using shared user id.
*
@@ -87,7 +100,7 @@ public final class SELinuxMMAC {
*/
@EnabledAfter(targetSdkVersion = android.os.Build.VERSION_CODES.Q)
@ChangeId
- static final long SELINUX_LATEST_CHANGES = 143539591L;
+ static final long SELINUX_R_CHANGES = 168782947L;
// Only initialize sMacPermissions once.
static {
@@ -349,9 +362,11 @@ public final class SELinuxMMAC {
if ((sharedUserSetting != null) && (sharedUserSetting.packages.size() != 0)) {
return sharedUserSetting.seInfoTargetSdkVersion;
}
- if (compatibility.isChangeEnabledInternal(SELINUX_LATEST_CHANGES,
- pkg.toAppInfoWithoutState())) {
- return android.os.Build.VERSION_CODES.R;
+ final ApplicationInfo appInfo = pkg.toAppInfoWithoutState();
+ if (compatibility.isChangeEnabledInternal(SELINUX_LATEST_CHANGES, appInfo)) {
+ return android.os.Build.VERSION_CODES.S;
+ } else if (compatibility.isChangeEnabledInternal(SELINUX_R_CHANGES, appInfo)) {
+ return Math.max(android.os.Build.VERSION_CODES.R, pkg.getTargetSdkVersion());
}
return pkg.getTargetSdkVersion();
diff --git a/services/core/java/com/android/server/pm/SettingBase.java b/services/core/java/com/android/server/pm/SettingBase.java
index 3e2ab05e83ec..c1258b1efd48 100644
--- a/services/core/java/com/android/server/pm/SettingBase.java
+++ b/services/core/java/com/android/server/pm/SettingBase.java
@@ -19,23 +19,23 @@ package com.android.server.pm;
import android.content.pm.ApplicationInfo;
import com.android.internal.annotations.VisibleForTesting;
-import com.android.server.pm.permission.PermissionsState;
+import com.android.server.pm.permission.AppIdPermissionState;
@VisibleForTesting(visibility = VisibleForTesting.Visibility.PACKAGE)
public abstract class SettingBase {
int pkgFlags;
int pkgPrivateFlags;
- protected final PermissionsState mPermissionsState;
+ protected final AppIdPermissionState mPermissionsState;
SettingBase(int pkgFlags, int pkgPrivateFlags) {
setFlags(pkgFlags);
setPrivateFlags(pkgPrivateFlags);
- mPermissionsState = new PermissionsState();
+ mPermissionsState = new AppIdPermissionState();
}
SettingBase(SettingBase orig) {
- mPermissionsState = new PermissionsState();
+ mPermissionsState = new AppIdPermissionState();
doCopy(orig);
}
@@ -49,7 +49,7 @@ public abstract class SettingBase {
mPermissionsState.copyFrom(orig.mPermissionsState);
}
- public PermissionsState getPermissionsState() {
+ public AppIdPermissionState getPermissionsState() {
return mPermissionsState;
}
diff --git a/services/core/java/com/android/server/pm/Settings.java b/services/core/java/com/android/server/pm/Settings.java
index 965430727d3c..bae36b2ad353 100644
--- a/services/core/java/com/android/server/pm/Settings.java
+++ b/services/core/java/com/android/server/pm/Settings.java
@@ -79,6 +79,7 @@ import android.text.TextUtils;
import android.util.ArrayMap;
import android.util.ArraySet;
import android.util.AtomicFile;
+import android.util.IntArray;
import android.util.Log;
import android.util.LogPrinter;
import android.util.Pair;
@@ -106,10 +107,11 @@ import com.android.server.pm.Installer.InstallerException;
import com.android.server.pm.parsing.PackageInfoUtils;
import com.android.server.pm.parsing.pkg.AndroidPackage;
import com.android.server.pm.parsing.pkg.AndroidPackageUtils;
+import com.android.server.pm.permission.AppIdPermissionState;
+import com.android.server.pm.permission.AppIdPermissionState.PermissionState;
import com.android.server.pm.permission.BasePermission;
+import com.android.server.pm.permission.LegacyPermissionDataProvider;
import com.android.server.pm.permission.PermissionSettings;
-import com.android.server.pm.permission.PermissionsState;
-import com.android.server.pm.permission.PermissionsState.PermissionState;
import com.android.server.utils.TimingsTraceAndSlog;
import libcore.io.IoUtils;
@@ -416,9 +418,12 @@ public final class Settings {
private final File mSystemDir;
public final KeySetManagerService mKeySetManagerService = new KeySetManagerService(mPackages);
+
/** Settings and other information about permissions */
final PermissionSettings mPermissions;
+ private final LegacyPermissionDataProvider mPermissionDataProvider;
+
@VisibleForTesting(visibility = VisibleForTesting.Visibility.PRIVATE)
public Settings(Map<String, PackageSetting> pkgSettings) {
mLock = new Object();
@@ -426,6 +431,7 @@ public final class Settings {
mSystemDir = null;
mPermissions = null;
mRuntimePermissionsPersistence = null;
+ mPermissionDataProvider = null;
mSettingsFilename = null;
mBackupSettingsFilename = null;
mPackageListFilename = null;
@@ -434,12 +440,14 @@ public final class Settings {
mKernelMappingFilename = null;
}
- Settings(File dataDir, PermissionSettings permission,
- RuntimePermissionsPersistence runtimePermissionsPersistence, Object lock) {
+ Settings(File dataDir, PermissionSettings permissionSettings,
+ RuntimePermissionsPersistence runtimePermissionsPersistence,
+ LegacyPermissionDataProvider permissionDataProvider, Object lock) {
mLock = lock;
- mPermissions = permission;
+ mPermissions = permissionSettings;
mRuntimePermissionsPersistence = new RuntimePermissionPersistence(
- runtimePermissionsPersistence, mLock);
+ runtimePermissionsPersistence);
+ mPermissionDataProvider = permissionDataProvider;
mSystemDir = new File(dataDir, "system");
mSystemDir.mkdirs();
@@ -1239,7 +1247,7 @@ public final class Settings {
void writeAllRuntimePermissionsLPr() {
for (int userId : UserManagerService.getInstance().getUserIds()) {
- mRuntimePermissionsPersistence.writePermissionsForUserAsyncLPr(userId);
+ mRuntimePermissionsPersistence.writeStateForUserAsyncLPr(userId);
}
}
@@ -2102,7 +2110,7 @@ public final class Settings {
}
void readInstallPermissionsLPr(XmlPullParser parser,
- PermissionsState permissionsState) throws IOException, XmlPullParserException {
+ AppIdPermissionState permissionsState) throws IOException, XmlPullParserException {
int outerDepth = parser.getDepth();
int type;
while ((type=parser.next()) != XmlPullParser.END_DOCUMENT
@@ -2131,25 +2139,7 @@ public final class Settings {
final int flags = (flagsStr != null)
? Integer.parseInt(flagsStr, 16) : 0;
- if (granted) {
- if (permissionsState.grantInstallPermission(bp) ==
- PermissionsState.PERMISSION_OPERATION_FAILURE) {
- Slog.w(PackageManagerService.TAG, "Permission already added: " + name);
- XmlUtils.skipCurrentTag(parser);
- } else {
- permissionsState.updatePermissionFlags(bp, UserHandle.USER_ALL,
- PackageManager.MASK_PERMISSION_FLAGS_ALL, flags);
- }
- } else {
- if (permissionsState.revokeInstallPermission(bp) ==
- PermissionsState.PERMISSION_OPERATION_FAILURE) {
- Slog.w(PackageManagerService.TAG, "Permission already added: " + name);
- XmlUtils.skipCurrentTag(parser);
- } else {
- permissionsState.updatePermissionFlags(bp, UserHandle.USER_ALL,
- PackageManager.MASK_PERMISSION_FLAGS_ALL, flags);
- }
- }
+ permissionsState.putInstallPermissionState(new PermissionState(bp, granted, flags));
} else {
Slog.w(PackageManagerService.TAG, "Unknown element under <permissions>: "
+ parser.getName());
@@ -2158,7 +2148,7 @@ public final class Settings {
}
}
- void writePermissionsLPr(XmlSerializer serializer, List<PermissionState> permissionStates)
+ void writePermissionsLPr(XmlSerializer serializer, Collection<PermissionState> permissionStates)
throws IOException {
if (permissionStates.isEmpty()) {
return;
@@ -2641,7 +2631,11 @@ public final class Settings {
}
final boolean isDebug = pkg.pkg.isDebuggable();
- final int[] gids = pkg.getPermissionsState().computeGids(userIds);
+ final IntArray gids = new IntArray();
+ for (final int userId : userIds) {
+ gids.addAll(mPermissionDataProvider.getGidsForUid(UserHandle.getUid(userId,
+ pkg.appId)));
+ }
// Avoid any application that has a space in its path.
if (dataPath.indexOf(' ') >= 0)
@@ -2673,11 +2667,12 @@ public final class Settings {
sb.append(" ");
sb.append(AndroidPackageUtils.getSeInfo(pkg.pkg, pkg));
sb.append(" ");
- if (gids != null && gids.length > 0) {
- sb.append(gids[0]);
- for (int i = 1; i < gids.length; i++) {
+ final int gidsSize = gids.size();
+ if (gids != null && gids.size() > 0) {
+ sb.append(gids.get(0));
+ for (int i = 1; i < gidsSize; i++) {
sb.append(",");
- sb.append(gids[i]);
+ sb.append(gids.get(i));
}
} else {
sb.append("none");
@@ -4482,8 +4477,9 @@ public final class Settings {
}
void dumpPackageLPr(PrintWriter pw, String prefix, String checkinTag,
- ArraySet<String> permissionNames, PackageSetting ps, SimpleDateFormat sdf,
- Date date, List<UserInfo> users, boolean dumpAll, boolean dumpAllComponents) {
+ ArraySet<String> permissionNames, PackageSetting ps,
+ AppIdPermissionState permissionsState, SimpleDateFormat sdf, Date date,
+ List<UserInfo> users, boolean dumpAll, boolean dumpAllComponents) {
AndroidPackage pkg = ps.pkg;
if (checkinTag != null) {
pw.print(checkinTag);
@@ -4810,7 +4806,6 @@ public final class Settings {
}
if (ps.sharedUser == null || permissionNames != null || dumpAll) {
- PermissionsState permissionsState = ps.getPermissionsState();
dumpInstallPermissionsLPr(pw, prefix + " ", permissionNames, permissionsState);
}
@@ -4889,8 +4884,8 @@ public final class Settings {
}
if (ps.sharedUser == null) {
- PermissionsState permissionsState = ps.getPermissionsState();
- dumpGidsLPr(pw, prefix + " ", permissionsState.computeGids(user.id));
+ dumpGidsLPr(pw, prefix + " ", mPermissionDataProvider.getGidsForUid(
+ UserHandle.getUid(user.id, ps.appId)));
dumpRuntimePermissionsLPr(pw, prefix + " ", permissionNames, permissionsState
.getRuntimePermissionStates(user.id), dumpAll);
}
@@ -4933,8 +4928,10 @@ public final class Settings {
&& !packageName.equals(ps.name)) {
continue;
}
+ final AppIdPermissionState permissionsState =
+ mPermissionDataProvider.getAppIdPermissionState(ps.appId);
if (permissionNames != null
- && !ps.getPermissionsState().hasRequestedPermission(permissionNames)) {
+ && !permissionsState.hasPermissionState(permissionNames)) {
continue;
}
@@ -4948,8 +4945,8 @@ public final class Settings {
pw.println("Packages:");
printedSomething = true;
}
- dumpPackageLPr(pw, " ", checkin ? "pkg" : null, permissionNames, ps, sdf, date, users,
- packageName != null, dumpAllComponents);
+ dumpPackageLPr(pw, " ", checkin ? "pkg" : null, permissionNames, ps, permissionsState,
+ sdf, date, users, packageName != null, dumpAllComponents);
}
printedSomething = false;
@@ -4989,8 +4986,10 @@ public final class Settings {
pw.println("Hidden system packages:");
printedSomething = true;
}
- dumpPackageLPr(pw, " ", checkin ? "dis" : null, permissionNames, ps, sdf, date,
- users, packageName != null, dumpAllComponents);
+ final AppIdPermissionState permissionsState =
+ mPermissionDataProvider.getAppIdPermissionState(ps.appId);
+ dumpPackageLPr(pw, " ", checkin ? "dis" : null, permissionNames, ps,
+ permissionsState, sdf, date, users, packageName != null, dumpAllComponents);
}
}
}
@@ -5018,8 +5017,10 @@ public final class Settings {
if (packageName != null && su != dumpState.getSharedUser()) {
continue;
}
+ final AppIdPermissionState permissionsState =
+ mPermissionDataProvider.getAppIdPermissionState(su.userId);
if (permissionNames != null
- && !su.getPermissionsState().hasRequestedPermission(permissionNames)) {
+ && !permissionsState.hasPermissionState(permissionNames)) {
continue;
}
if (!checkin) {
@@ -5054,12 +5055,12 @@ public final class Settings {
continue;
}
- final PermissionsState permissionsState = su.getPermissionsState();
dumpInstallPermissionsLPr(pw, prefix, permissionNames, permissionsState);
for (int userId : UserManagerService.getInstance().getUserIds()) {
- final int[] gids = permissionsState.computeGids(userId);
- final List<PermissionState> permissions =
+ final int[] gids = mPermissionDataProvider.getGidsForUid(UserHandle.getUid(
+ userId, su.userId));
+ final Collection<PermissionState> permissions =
permissionsState.getRuntimePermissionStates(userId);
if (!ArrayUtils.isEmpty(gids) || !permissions.isEmpty()) {
pw.print(prefix); pw.print("User "); pw.print(userId); pw.println(": ");
@@ -5120,7 +5121,7 @@ public final class Settings {
}
void dumpRuntimePermissionsLPr(PrintWriter pw, String prefix, ArraySet<String> permissionNames,
- List<PermissionState> permissionStates, boolean dumpAll) {
+ Collection<PermissionState> permissionStates, boolean dumpAll) {
if (!permissionStates.isEmpty() || dumpAll) {
pw.print(prefix); pw.println("runtime permissions:");
for (PermissionState permissionState : permissionStates) {
@@ -5161,8 +5162,9 @@ public final class Settings {
}
void dumpInstallPermissionsLPr(PrintWriter pw, String prefix, ArraySet<String> permissionNames,
- PermissionsState permissionsState) {
- List<PermissionState> permissionStates = permissionsState.getInstallPermissionStates();
+ AppIdPermissionState permissionsState) {
+ Collection<PermissionState> permissionStates =
+ permissionsState.getInstallPermissionStates();
if (!permissionStates.isEmpty()) {
pw.print(prefix); pw.println("install permissions:");
for (PermissionState permissionState : permissionStates) {
@@ -5202,9 +5204,9 @@ public final class Settings {
public void writeRuntimePermissionsForUserLPr(int userId, boolean sync) {
if (sync) {
- mRuntimePermissionsPersistence.writePermissionsForUserSyncLPr(userId);
+ mRuntimePermissionsPersistence.writeStateForUserSyncLPr(userId);
} else {
- mRuntimePermissionsPersistence.writePermissionsForUserAsyncLPr(userId);
+ mRuntimePermissionsPersistence.writeStateForUserAsyncLPr(userId);
}
}
@@ -5292,8 +5294,6 @@ public final class Settings {
private final Handler mHandler = new MyHandler();
- private final Object mPersistenceLock;
-
@GuardedBy("mLock")
private final SparseBooleanArray mWriteScheduled = new SparseBooleanArray();
@@ -5313,10 +5313,8 @@ public final class Settings {
// The mapping keys are user ids.
private final SparseBooleanArray mPermissionUpgradeNeeded = new SparseBooleanArray();
- public RuntimePermissionPersistence(RuntimePermissionsPersistence persistence,
- Object persistenceLock) {
+ public RuntimePermissionPersistence(RuntimePermissionsPersistence persistence) {
mPersistence = persistence;
- mPersistenceLock = persistenceLock;
}
@GuardedBy("Settings.this.mLock")
@@ -5327,7 +5325,7 @@ public final class Settings {
@GuardedBy("Settings.this.mLock")
void setVersionLPr(int version, int userId) {
mVersions.put(userId, version);
- writePermissionsForUserAsyncLPr(userId);
+ writeStateForUserAsyncLPr(userId);
}
@GuardedBy("Settings.this.mLock")
@@ -5342,7 +5340,7 @@ public final class Settings {
+ "set before trying to update the fingerprint.");
}
mFingerprints.put(userId, mExtendedFingerprint);
- writePermissionsForUserAsyncLPr(userId);
+ writeStateForUserAsyncLPr(userId);
}
public void setPermissionControllerVersion(long version) {
@@ -5361,13 +5359,7 @@ public final class Settings {
return Build.FINGERPRINT + "?pc_version=" + version;
}
- public void writePermissionsForUserSyncLPr(int userId) {
- mHandler.removeMessages(userId);
- writePermissionsSync(userId);
- }
-
- @GuardedBy("Settings.this.mLock")
- public void writePermissionsForUserAsyncLPr(int userId) {
+ public void writeStateForUserAsyncLPr(int userId) {
final long currentTimeMillis = SystemClock.uptimeMillis();
if (mWriteScheduled.get(userId)) {
@@ -5399,59 +5391,53 @@ public final class Settings {
}
}
- private void writePermissionsSync(int userId) {
- RuntimePermissionsState runtimePermissions;
- synchronized (mPersistenceLock) {
- mWriteScheduled.delete(userId);
-
- int version = mVersions.get(userId, INITIAL_VERSION);
+ public void writeStateForUserSyncLPr(int userId) {
+ mHandler.removeMessages(userId);
+ mWriteScheduled.delete(userId);
- String fingerprint = mFingerprints.get(userId);
+ int version = mVersions.get(userId, INITIAL_VERSION);
- Map<String, List<RuntimePermissionsState.PermissionState>> packagePermissions =
- new ArrayMap<>();
- int packagesSize = mPackages.size();
- for (int i = 0; i < packagesSize; i++) {
- String packageName = mPackages.keyAt(i);
- PackageSetting packageSetting = mPackages.valueAt(i);
- if (packageSetting.sharedUser == null) {
- List<RuntimePermissionsState.PermissionState> permissions =
- getPermissionsFromPermissionsState(
- packageSetting.getPermissionsState(), userId);
- packagePermissions.put(packageName, permissions);
- }
- }
+ String fingerprint = mFingerprints.get(userId);
- Map<String, List<RuntimePermissionsState.PermissionState>> sharedUserPermissions =
- new ArrayMap<>();
- final int sharedUsersSize = mSharedUsers.size();
- for (int i = 0; i < sharedUsersSize; i++) {
- String sharedUserName = mSharedUsers.keyAt(i);
- SharedUserSetting sharedUserSetting = mSharedUsers.valueAt(i);
+ Map<String, List<RuntimePermissionsState.PermissionState>> packagePermissions =
+ new ArrayMap<>();
+ int packagesSize = mPackages.size();
+ for (int i = 0; i < packagesSize; i++) {
+ String packageName = mPackages.keyAt(i);
+ PackageSetting packageSetting = mPackages.valueAt(i);
+ if (packageSetting.sharedUser == null) {
List<RuntimePermissionsState.PermissionState> permissions =
getPermissionsFromPermissionsState(
- sharedUserSetting.getPermissionsState(), userId);
- sharedUserPermissions.put(sharedUserName, permissions);
+ packageSetting.getPermissionsState(), userId);
+ packagePermissions.put(packageName, permissions);
}
+ }
- runtimePermissions = new RuntimePermissionsState(version, fingerprint,
- packagePermissions, sharedUserPermissions);
+ Map<String, List<RuntimePermissionsState.PermissionState>> sharedUserPermissions =
+ new ArrayMap<>();
+ final int sharedUsersSize = mSharedUsers.size();
+ for (int i = 0; i < sharedUsersSize; i++) {
+ String sharedUserName = mSharedUsers.keyAt(i);
+ SharedUserSetting sharedUserSetting = mSharedUsers.valueAt(i);
+ List<RuntimePermissionsState.PermissionState> permissions =
+ getPermissionsFromPermissionsState(
+ sharedUserSetting.getPermissionsState(), userId);
+ sharedUserPermissions.put(sharedUserName, permissions);
}
+ RuntimePermissionsState runtimePermissions = new RuntimePermissionsState(version,
+ fingerprint, packagePermissions, sharedUserPermissions);
+
mPersistence.writeForUser(runtimePermissions, UserHandle.of(userId));
}
@NonNull
private List<RuntimePermissionsState.PermissionState> getPermissionsFromPermissionsState(
- @NonNull PermissionsState permissionsState, @UserIdInt int userId) {
- List<PermissionState> permissionStates = permissionsState.getRuntimePermissionStates(
- userId);
- List<RuntimePermissionsState.PermissionState> permissions =
- new ArrayList<>();
- int permissionStatesSize = permissionStates.size();
- for (int i = 0; i < permissionStatesSize; i++) {
- PermissionState permissionState = permissionStates.get(i);
-
+ @NonNull AppIdPermissionState permissionsState, @UserIdInt int userId) {
+ Collection<PermissionState> permissionStates =
+ permissionsState.getRuntimePermissionStates(userId);
+ List<RuntimePermissionsState.PermissionState> permissions = new ArrayList<>();
+ for (PermissionState permissionState : permissionStates) {
RuntimePermissionsState.PermissionState permission =
new RuntimePermissionsState.PermissionState(permissionState.getName(),
permissionState.isGranted(), permissionState.getFlags());
@@ -5480,7 +5466,7 @@ public final class Settings {
userId));
if (runtimePermissions == null) {
readLegacyStateForUserSyncLPr(userId);
- writePermissionsForUserAsyncLPr(userId);
+ writeStateForUserAsyncLPr(userId);
return;
}
@@ -5536,7 +5522,7 @@ public final class Settings {
private void readPermissionsStateLpr(
@NonNull List<RuntimePermissionsState.PermissionState> permissions,
- @NonNull PermissionsState permissionsState, @UserIdInt int userId) {
+ @NonNull AppIdPermissionState permissionsState, @UserIdInt int userId) {
int permissionsSize = permissions.size();
for (int i = 0; i < permissionsSize; i++) {
RuntimePermissionsState.PermissionState permission = permissions.get(i);
@@ -5550,14 +5536,8 @@ public final class Settings {
boolean granted = permission.isGranted();
int flags = permission.getFlags();
- if (granted) {
- permissionsState.grantRuntimePermission(basePermission, userId);
- permissionsState.updatePermissionFlags(basePermission, userId,
- PackageManager.MASK_PERMISSION_FLAGS_ALL, flags);
- } else {
- permissionsState.updatePermissionFlags(basePermission, userId,
- PackageManager.MASK_PERMISSION_FLAGS_ALL, flags);
- }
+ permissionsState.putRuntimePermissionState(new PermissionState(basePermission,
+ granted, flags), userId);
}
}
@@ -5638,8 +5618,9 @@ public final class Settings {
}
}
- private void parsePermissionsLPr(XmlPullParser parser, PermissionsState permissionsState,
- int userId) throws IOException, XmlPullParserException {
+ private void parsePermissionsLPr(XmlPullParser parser,
+ AppIdPermissionState permissionsState, int userId)
+ throws IOException, XmlPullParserException {
final int outerDepth = parser.getDepth();
int type;
while ((type = parser.next()) != XmlPullParser.END_DOCUMENT
@@ -5666,15 +5647,8 @@ public final class Settings {
final int flags = (flagsStr != null)
? Integer.parseInt(flagsStr, 16) : 0;
- if (granted) {
- permissionsState.grantRuntimePermission(bp, userId);
- permissionsState.updatePermissionFlags(bp, userId,
- PackageManager.MASK_PERMISSION_FLAGS_ALL, flags);
- } else {
- permissionsState.updatePermissionFlags(bp, userId,
- PackageManager.MASK_PERMISSION_FLAGS_ALL, flags);
- }
-
+ permissionsState.putRuntimePermissionState(new PermissionState(bp, granted,
+ flags), userId);
}
break;
}
@@ -5690,7 +5664,9 @@ public final class Settings {
public void handleMessage(Message message) {
final int userId = message.what;
Runnable callback = (Runnable) message.obj;
- writePermissionsSync(userId);
+ synchronized (mLock) {
+ writeStateForUserSyncLPr(userId);
+ }
if (callback != null) {
callback.run();
}
diff --git a/services/core/java/com/android/server/pm/StagingManager.java b/services/core/java/com/android/server/pm/StagingManager.java
index b12a8f89979b..35c26d6cd54e 100644
--- a/services/core/java/com/android/server/pm/StagingManager.java
+++ b/services/core/java/com/android/server/pm/StagingManager.java
@@ -86,7 +86,6 @@ import java.util.List;
import java.util.Set;
import java.util.concurrent.LinkedBlockingQueue;
import java.util.concurrent.TimeUnit;
-import java.util.function.Predicate;
import java.util.function.Supplier;
/**
@@ -164,17 +163,6 @@ public class StagingManager {
}
}
- private void updateStoredSession(@NonNull PackageInstallerSession sessionInfo) {
- synchronized (mStagedSessions) {
- PackageInstallerSession storedSession = mStagedSessions.get(sessionInfo.sessionId);
- // storedSession might be null if a call to abortSession was made before the session
- // is updated.
- if (storedSession != null) {
- mStagedSessions.put(sessionInfo.sessionId, sessionInfo);
- }
- }
- }
-
private void markBootCompleted() {
mApexManager.markBootCompleted();
}
@@ -237,7 +225,7 @@ public class StagingManager {
final IntArray childSessionIds = new IntArray();
if (session.isMultiPackage()) {
for (PackageInstallerSession s : session.getChildSessions()) {
- if (isApexSession(s)) {
+ if (s.isApexSession()) {
childSessionIds.add(s.sessionId);
}
}
@@ -340,31 +328,6 @@ public class StagingManager {
}
}
- private static boolean isApexSession(@NonNull PackageInstallerSession session) {
- return (session.params.installFlags & PackageManager.INSTALL_APEX) != 0;
- }
-
- private boolean sessionContains(@NonNull PackageInstallerSession session,
- Predicate<PackageInstallerSession> filter) {
- if (!session.isMultiPackage()) {
- return filter.test(session);
- }
- for (PackageInstallerSession s : session.getChildSessions()) {
- if (filter.test(s)) {
- return true;
- }
- }
- return false;
- }
-
- private boolean sessionContainsApex(@NonNull PackageInstallerSession session) {
- return sessionContains(session, (s) -> isApexSession(s));
- }
-
- private boolean sessionContainsApk(@NonNull PackageInstallerSession session) {
- return sessionContains(session, (s) -> !isApexSession(s));
- }
-
// Reverts apex sessions and user data (if checkpoint is supported). Also reboots the device.
private void abortCheckpoint(int sessionId, String errorMsg) {
String failureReason = "Failed to install sessionId: " + sessionId + " Error: " + errorMsg;
@@ -411,7 +374,7 @@ public class StagingManager {
List<PackageInstallerSession> apexSessions = new ArrayList<>();
if (session.isMultiPackage()) {
for (PackageInstallerSession s : session.getChildSessions()) {
- if (sessionContainsApex(s)) {
+ if (s.containsApexSession()) {
apexSessions.add(s);
}
}
@@ -532,7 +495,7 @@ public class StagingManager {
private void resumeSession(@NonNull PackageInstallerSession session) {
Slog.d(TAG, "Resuming session " + session.sessionId);
- final boolean hasApex = sessionContainsApex(session);
+ final boolean hasApex = session.containsApexSession();
ApexSessionInfo apexSessionInfo = null;
if (hasApex) {
// Check with apexservice whether the apex packages have been activated.
@@ -751,7 +714,7 @@ public class StagingManager {
@Nullable
private PackageInstallerSession extractApksInSession(PackageInstallerSession session)
throws PackageManagerException {
- if (!session.isMultiPackage() && !isApexSession(session)) {
+ if (!session.isMultiPackage() && !session.isApexSession()) {
return createAndWriteApkSession(session);
} else if (session.isMultiPackage()) {
// For multi-package staged sessions containing APKs, we identify which child sessions
@@ -760,7 +723,7 @@ public class StagingManager {
// sessions will be installed atomically.
final List<PackageInstallerSession> childSessions = new ArrayList<>();
for (PackageInstallerSession s : session.getChildSessions()) {
- if (!isApexSession(s)) {
+ if (!s.isApexSession()) {
childSessions.add(s);
}
}
@@ -809,7 +772,7 @@ public class StagingManager {
}
final Set<String> apkNames = new ArraySet<>();
for (PackageInstallerSession s : session.getChildSessions()) {
- if (!isApexSession(s)) {
+ if (!s.isApexSession()) {
apkNames.add(s.getPackageName());
}
}
@@ -865,7 +828,6 @@ public class StagingManager {
}
void commitSession(@NonNull PackageInstallerSession session) {
- updateStoredSession(session);
mPreRebootVerificationHandler.startPreRebootVerification(session);
}
@@ -1032,7 +994,7 @@ public class StagingManager {
* @return returns true if it is ensured that there is no active apex session, otherwise false
*/
private boolean ensureActiveApexSessionIsAborted(PackageInstallerSession session) {
- if (!sessionContainsApex(session)) {
+ if (!session.containsApexSession()) {
return true;
}
final ApexSessionInfo apexSession = mApexManager.getStagedSessionInfo(session.sessionId);
@@ -1354,7 +1316,7 @@ public class StagingManager {
*/
private void handlePreRebootVerification_Apex(
@NonNull PackageInstallerSession session, int rollbackId) {
- final boolean hasApex = sessionContainsApex(session);
+ final boolean hasApex = session.containsApexSession();
// APEX checks. For single-package sessions, check if they contain an APEX. For
// multi-package sessions, find all the child sessions that contain an APEX.
@@ -1384,7 +1346,7 @@ public class StagingManager {
* {@link #notifyPreRebootVerification_Apk_Complete}
*/
private void handlePreRebootVerification_Apk(@NonNull PackageInstallerSession session) {
- if (!sessionContainsApk(session)) {
+ if (!session.containsApkSession()) {
notifyPreRebootVerification_Apk_Complete(session);
return;
}
@@ -1433,7 +1395,7 @@ public class StagingManager {
Slog.d(TAG, "Marking session " + session.sessionId + " as ready");
session.setStagedSessionReady();
if (session.isStagedSessionReady()) {
- final boolean hasApex = sessionContainsApex(session);
+ final boolean hasApex = session.containsApexSession();
if (hasApex) {
try {
mApexManager.markStagedSessionReady(session.sessionId);
diff --git a/services/core/java/com/android/server/pm/TEST_MAPPING b/services/core/java/com/android/server/pm/TEST_MAPPING
index 485868237895..7d48d0a6e266 100644
--- a/services/core/java/com/android/server/pm/TEST_MAPPING
+++ b/services/core/java/com/android/server/pm/TEST_MAPPING
@@ -33,16 +33,13 @@
"name": "CtsContentTestCases",
"options": [
{
- "include-filter": "android.content.pm.cts.ChecksumsTest"
- },
- {
- "include-filter": "android.content.pm.cts.PackageManagerShellCommandTest"
+ "exclude-annotation": "androidx.test.filters.FlakyTest"
},
{
- "include-filter": "android.content.pm.cts.PackageManagerShellCommandIncrementalTest"
+ "exclude-annotation": "org.junit.Ignore"
},
{
- "include-filter": "android.content.pm.cts.PackageManagerTest"
+ "include-filter": "android.content.pm.cts"
}
]
},
diff --git a/services/core/java/com/android/server/pm/UserManagerService.java b/services/core/java/com/android/server/pm/UserManagerService.java
index e848bb7092da..4c58b06e79c6 100644
--- a/services/core/java/com/android/server/pm/UserManagerService.java
+++ b/services/core/java/com/android/server/pm/UserManagerService.java
@@ -221,10 +221,12 @@ public class UserManagerService extends IUserManager.Stub {
private static final int ALLOWED_FLAGS_FOR_CREATE_USERS_PERMISSION =
UserInfo.FLAG_MANAGED_PROFILE
+ | UserInfo.FLAG_PROFILE
| UserInfo.FLAG_EPHEMERAL
| UserInfo.FLAG_RESTRICTED
| UserInfo.FLAG_GUEST
- | UserInfo.FLAG_DEMO;
+ | UserInfo.FLAG_DEMO
+ | UserInfo.FLAG_FULL;
@VisibleForTesting
static final int MIN_USER_ID = UserHandle.MIN_SECONDARY_USER_ID;
@@ -765,8 +767,6 @@ public class UserManagerService extends IUserManager.Stub {
return null;
}
- // TODO(b/157921703): replace by getAliveUsers() or remove (so callers
- // explicitly call the 3-booleans version)
public @NonNull List<UserInfo> getUsers(boolean excludeDying) {
return getUsers(/*excludePartial= */ true, excludeDying, /* excludePreCreated= */
true);
diff --git a/services/core/java/com/android/server/pm/permission/AppIdPermissionState.java b/services/core/java/com/android/server/pm/permission/AppIdPermissionState.java
new file mode 100644
index 000000000000..aabdafdd453a
--- /dev/null
+++ b/services/core/java/com/android/server/pm/permission/AppIdPermissionState.java
@@ -0,0 +1,320 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.pm.permission;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.annotation.UserIdInt;
+import android.os.UserHandle;
+import android.util.ArrayMap;
+import android.util.SparseArray;
+import android.util.SparseBooleanArray;
+
+import java.util.Collection;
+import java.util.Collections;
+import java.util.Objects;
+
+/**
+ * Legacy permission state that was associated with packages or shared users.
+ */
+//@SystemApi(client = SystemApi.Client.SYSTEM_SERVER)
+public final class AppIdPermissionState {
+ // Maps from user IDs to user states.
+ @NonNull
+ private final SparseArray<UserState> mUserStates = new SparseArray<>();
+
+ // Keyed by user IDs.
+ @NonNull
+ private final SparseBooleanArray mMissing = new SparseBooleanArray();
+
+ /**
+ * Copy from another permission state.
+ *
+ * @param other the other permission state.
+ *
+ * @hide
+ */
+ public void copyFrom(@NonNull AppIdPermissionState other) {
+ if (other == this) {
+ return;
+ }
+
+ mUserStates.clear();
+ final int userStatesSize = other.mUserStates.size();
+ for (int i = 0; i < userStatesSize; i++) {
+ mUserStates.put(other.mUserStates.keyAt(i),
+ new UserState(other.mUserStates.valueAt(i)));
+ }
+
+ mMissing.clear();
+ final int missingSize = other.mMissing.size();
+ for (int i = 0; i < missingSize; i++) {
+ mMissing.put(other.mMissing.keyAt(i), other.mMissing.valueAt(i));
+ }
+ }
+
+ /**
+ * Reset this permission state.
+ *
+ * @hide
+ */
+ public void reset() {
+ mUserStates.clear();
+ mMissing.clear();
+ }
+
+ @Override
+ public boolean equals(@Nullable Object object) {
+ if (this == object) {
+ return true;
+ }
+ if (object == null) {
+ return false;
+ }
+ if (getClass() != object.getClass()) {
+ return false;
+ }
+ final AppIdPermissionState other = (AppIdPermissionState) object;
+ return Objects.equals(mUserStates, other.mUserStates)
+ && Objects.equals(mMissing, other.mMissing);
+ }
+
+ /**
+ * Put a install permission state.
+ *
+ * @param permissionState the permission state
+ */
+ public void putInstallPermissionState(@NonNull PermissionState permissionState) {
+ putPermissionState(permissionState, UserHandle.USER_ALL);
+ }
+
+ /**
+ * Put a runtime permission state for a user.
+ *
+ * @param permissionState the permission state
+ * @param userId the user ID
+ */
+ public void putRuntimePermissionState(@NonNull PermissionState permissionState,
+ @UserIdInt int userId) {
+ checkUserId(userId);
+ putPermissionState(permissionState, userId);
+ }
+
+ private void putPermissionState(@NonNull PermissionState permissionState,
+ @UserIdInt int userId) {
+ UserState userState = mUserStates.get(userId);
+ if (userState == null) {
+ userState = new UserState();
+ mUserStates.put(userId, userState);
+ }
+ userState.putPermissionState(permissionState);
+ }
+
+ /**
+ * Check whether there are any permission states for the given permissions.
+ *
+ * @param permissionNames the permission names
+ * @return whether there are any permission states
+ *
+ * @hide
+ */
+ public boolean hasPermissionState(@NonNull Collection<String> permissionNames) {
+ final int userStatesSize = mUserStates.size();
+ for (int i = 0; i < userStatesSize; i++) {
+ final UserState userState = mUserStates.valueAt(i);
+ for (final String permissionName : permissionNames) {
+ if (userState.getPermissionState(permissionName) != null) {
+ return true;
+ }
+ }
+ }
+ return false;
+ }
+
+ /**
+ * Get all the install permission states.
+ *
+ * @return the install permission states
+ */
+ @NonNull
+ public Collection<PermissionState> getInstallPermissionStates() {
+ return getPermissionStates(UserHandle.USER_ALL);
+ }
+
+ /**
+ * Get all the runtime permission states for a user.
+ *
+ * @param userId the user ID
+ * @return the runtime permission states
+ */
+ @NonNull
+ public Collection<PermissionState> getRuntimePermissionStates(@UserIdInt int userId) {
+ checkUserId(userId);
+ return getPermissionStates(userId);
+ }
+
+ @NonNull
+ private Collection<PermissionState> getPermissionStates(@UserIdInt int userId) {
+ final UserState userState = mUserStates.get(userId);
+ if (userState == null) {
+ return Collections.emptyList();
+ }
+ return userState.getPermissionStates();
+ }
+
+ /**
+ * Check whether the permission state is missing for a user.
+ * <p>
+ * This can happen if permission state is rolled back and we'll need to generate a reasonable
+ * default state to keep the app usable.
+ *
+ * @param userId the user ID
+ * @return whether the permission state is missing
+ */
+ public boolean isMissing(@UserIdInt int userId) {
+ checkUserId(userId);
+ return mMissing.get(userId);
+ }
+
+ /**
+ * Set whether the permission state is missing for a user.
+ * <p>
+ * This can happen if permission state is rolled back and we'll need to generate a reasonable
+ * default state to keep the app usable.
+ *
+ * @param missing whether the permission state is missing
+ * @param userId the user ID
+ */
+ public void setMissing(boolean missing, @UserIdInt int userId) {
+ checkUserId(userId);
+ if (missing) {
+ mMissing.put(userId, true);
+ } else {
+ mMissing.delete(userId);
+ }
+ }
+
+ private static void checkUserId(@UserIdInt int userId) {
+ if (userId < 0) {
+ throw new IllegalArgumentException("Invalid user ID " + userId);
+ }
+ }
+
+ /**
+ * Legacy state for permissions for a user.
+ */
+ private static final class UserState {
+ // Maps from permission names to permission states.
+ @NonNull
+ private final ArrayMap<String, PermissionState> mPermissionStates = new ArrayMap<>();
+
+ public UserState() {}
+
+ public UserState(@NonNull UserState other) {
+ final int permissionStatesSize = other.mPermissionStates.size();
+ for (int i = 0; i < permissionStatesSize; i++) {
+ mPermissionStates.put(other.mPermissionStates.keyAt(i),
+ new PermissionState(other.mPermissionStates.valueAt(i)));
+ }
+ }
+
+ @Nullable
+ public PermissionState getPermissionState(@NonNull String permissionName) {
+ return mPermissionStates.get(permissionName);
+ }
+
+ public void putPermissionState(@NonNull PermissionState permissionState) {
+ mPermissionStates.put(permissionState.getName(), permissionState);
+ }
+
+ @NonNull
+ public Collection<PermissionState> getPermissionStates() {
+ return Collections.unmodifiableCollection(mPermissionStates.values());
+ }
+ }
+
+ /**
+ * Legacy state for a single permission.
+ */
+ public static final class PermissionState {
+ @NonNull
+ private final BasePermission mPermission;
+
+ private final boolean mGranted;
+
+ private final int mFlags;
+
+ /**
+ * Create a new instance of this class.
+ *
+ * @param permission the {@link BasePermission} for the permission
+ * @param granted whether the permission is granted
+ * @param flags the permission flags
+ */
+ public PermissionState(@NonNull BasePermission permission, boolean granted, int flags) {
+ mPermission = permission;
+ mGranted = granted;
+ mFlags = flags;
+ }
+
+ private PermissionState(@NonNull PermissionState other) {
+ mPermission = other.mPermission;
+ mGranted = other.mGranted;
+ mFlags = other.mFlags;
+ }
+
+ /**
+ * Get the {@link BasePermission} for the permission.
+ *
+ * @return the {@link BasePermission}
+ */
+ @NonNull
+ public BasePermission getPermission() {
+ return mPermission;
+ }
+
+ /**
+ * Get the permission name.
+ *
+ * @return the permission name
+ */
+ @NonNull
+ public String getName() {
+ return mPermission.getName();
+ }
+
+ /**
+ * Get whether the permission is granted.
+ *
+ * @return whether the permission is granted
+ */
+ @NonNull
+ public boolean isGranted() {
+ return mGranted;
+ }
+
+ /**
+ * Get the permission flags.
+ *
+ * @return the permission flags
+ */
+ @NonNull
+ public int getFlags() {
+ return mFlags;
+ }
+ }
+}
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 962638b4f63c..865b8a1e97eb 100644
--- a/services/core/java/com/android/server/pm/permission/BasePermission.java
+++ b/services/core/java/com/android/server/pm/permission/BasePermission.java
@@ -36,6 +36,7 @@ import android.os.UserHandle;
import android.util.Log;
import android.util.Slog;
+import com.android.internal.util.ArrayUtils;
import com.android.server.pm.DumpState;
import com.android.server.pm.PackageManagerService;
import com.android.server.pm.PackageSettingBase;
@@ -139,6 +140,10 @@ public final class BasePermission {
this.perm = perm;
}
+ public boolean hasGids() {
+ return !ArrayUtils.isEmpty(gids);
+ }
+
public int[] computeGids(int userId) {
if (perUser) {
final int[] userGids = new int[gids.length];
@@ -419,9 +424,9 @@ public final class BasePermission {
}
public void enforceDeclaredUsedAndRuntimeOrDevelopment(AndroidPackage pkg,
- PermissionsState permsState) {
+ UidPermissionState uidState) {
int index = pkg.getRequestedPermissions().indexOf(name);
- if (!permsState.hasRequestedPermission(name) && index == -1) {
+ if (!uidState.hasRequestedPermission(name) && index == -1) {
throw new SecurityException("Package " + pkg.getPackageName()
+ " has not requested permission " + name);
}
diff --git a/services/core/java/com/android/server/pm/permission/DevicePermissionState.java b/services/core/java/com/android/server/pm/permission/DevicePermissionState.java
new file mode 100644
index 000000000000..b9456acfced5
--- /dev/null
+++ b/services/core/java/com/android/server/pm/permission/DevicePermissionState.java
@@ -0,0 +1,77 @@
+/*
+ * 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.pm.permission;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.annotation.UserIdInt;
+import android.util.SparseArray;
+
+import com.android.internal.annotations.GuardedBy;
+
+/**
+ * Permission state for this device.
+ */
+public final class DevicePermissionState {
+ @GuardedBy("mLock")
+ @NonNull
+ private final SparseArray<UserPermissionState> mUserStates = new SparseArray<>();
+
+ @NonNull
+ private final Object mLock;
+
+ public DevicePermissionState(@NonNull Object lock) {
+ mLock = lock;
+ }
+
+ @Nullable
+ public UserPermissionState getUserState(@UserIdInt int userId) {
+ synchronized (mLock) {
+ return mUserStates.get(userId);
+ }
+ }
+
+ @NonNull
+ public UserPermissionState getOrCreateUserState(@UserIdInt int userId) {
+ synchronized (mLock) {
+ UserPermissionState userState = mUserStates.get(userId);
+ if (userState == null) {
+ userState = new UserPermissionState(mLock);
+ mUserStates.put(userId, userState);
+ }
+ return userState;
+ }
+ }
+
+ public void removeUserState(@UserIdInt int userId) {
+ synchronized (mLock) {
+ mUserStates.delete(userId);
+ }
+ }
+
+ public int[] getUserIds() {
+ synchronized (mLock) {
+ final int userStatesSize = mUserStates.size();
+ final int[] userIds = new int[userStatesSize];
+ for (int i = 0; i < userStatesSize; i++) {
+ final int userId = mUserStates.keyAt(i);
+ userIds[i] = userId;
+ }
+ return userIds;
+ }
+ }
+}
diff --git a/services/core/java/com/android/server/pm/permission/LegacyPermissionDataProvider.java b/services/core/java/com/android/server/pm/permission/LegacyPermissionDataProvider.java
new file mode 100644
index 000000000000..7452b522b20a
--- /dev/null
+++ b/services/core/java/com/android/server/pm/permission/LegacyPermissionDataProvider.java
@@ -0,0 +1,44 @@
+/*
+ * 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.pm.permission;
+
+import android.annotation.AppIdInt;
+import android.annotation.NonNull;
+
+/**
+ * An interface for legacy code to read permission data in order to maintain compatibility.
+ */
+//@SystemApi(client = SystemApi.Client.SYSTEM_SERVER)
+public interface LegacyPermissionDataProvider {
+ /**
+ * Get the legacy permission state of an app ID, either a package or a shared user.
+ *
+ * @param appId the app ID
+ * @return the legacy permission state
+ */
+ @NonNull
+ public abstract AppIdPermissionState getAppIdPermissionState(@AppIdInt int appId);
+
+ /**
+ * Get the GIDs computed from the permission state of a UID, either a package or a shared user.
+ *
+ * @param uid the UID
+ * @return the GIDs for the UID
+ */
+ @NonNull
+ public abstract int[] getGidsForUid(int uid);
+}
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 ccf850fea7fd..840b233902f6 100644
--- a/services/core/java/com/android/server/pm/permission/PermissionManagerService.java
+++ b/services/core/java/com/android/server/pm/permission/PermissionManagerService.java
@@ -53,11 +53,12 @@ import static com.android.server.pm.PackageManagerService.DEBUG_PACKAGE_SCANNING
import static com.android.server.pm.PackageManagerService.DEBUG_PERMISSIONS;
import static com.android.server.pm.PackageManagerService.DEBUG_REMOVE;
import static com.android.server.pm.PackageManagerService.PLATFORM_PACKAGE_NAME;
-import static com.android.server.pm.permission.PermissionsState.PERMISSION_OPERATION_FAILURE;
+import static com.android.server.pm.permission.UidPermissionState.PERMISSION_OPERATION_FAILURE;
import static java.util.concurrent.TimeUnit.SECONDS;
import android.Manifest;
+import android.annotation.AppIdInt;
import android.annotation.IntDef;
import android.annotation.NonNull;
import android.annotation.Nullable;
@@ -148,12 +149,9 @@ import com.android.server.pm.permission.PermissionManagerServiceInternal.Default
import com.android.server.pm.permission.PermissionManagerServiceInternal.DefaultDialerProvider;
import com.android.server.pm.permission.PermissionManagerServiceInternal.DefaultHomeProvider;
import com.android.server.pm.permission.PermissionManagerServiceInternal.PermissionCallback;
-import com.android.server.pm.permission.PermissionsState.PermissionState;
import com.android.server.policy.PermissionPolicyInternal;
import com.android.server.policy.SoftRestrictedPermissionPolicy;
-import libcore.util.EmptyArray;
-
import java.io.FileDescriptor;
import java.io.PrintWriter;
import java.lang.annotation.Retention;
@@ -185,8 +183,6 @@ public class PermissionManagerService extends IPermissionManager.Stub {
private static final int GRANT_INSTALL = 2;
/** Permission grant: grant the permission as a runtime one. */
private static final int GRANT_RUNTIME = 3;
- /** Permission grant: grant as runtime a permission that was granted as an install time one. */
- private static final int GRANT_UPGRADE = 4;
private static final long BACKUP_TIMEOUT_MILLIS = SECONDS.toMillis(60);
@@ -226,8 +222,8 @@ public class PermissionManagerService extends IPermissionManager.Stub {
/** Internal connection to the user manager */
private final UserManagerInternal mUserManagerInt;
- /** Maps from App ID to PermissionsState */
- private final SparseArray<PermissionsState> mAppIdStates = new SparseArray<>();
+ @NonNull
+ private final DevicePermissionState mState;
/** Permission controller: User space permission management */
private PermissionControllerManager mPermissionControllerManager;
@@ -395,6 +391,7 @@ public class PermissionManagerService extends IPermissionManager.Stub {
mPackageManagerInt = LocalServices.getService(PackageManagerInternal.class);
mUserManagerInt = LocalServices.getService(UserManagerInternal.class);
mSettings = new PermissionSettings(mLock);
+ mState = new DevicePermissionState(mLock);
mAppOpsManager = context.getSystemService(AppOpsManager.class);
mHandlerThread = new ServiceThread(TAG,
@@ -681,12 +678,12 @@ public class PermissionManagerService extends IPermissionManager.Stub {
if (mPackageManagerInt.filterAppAccess(pkg, callingUid, userId)) {
return 0;
}
- final PermissionsState permissionsState = getPermissionsState(pkg);
- if (permissionsState == null) {
- Slog.e(TAG, "Missing permissions state for " + packageName);
+ final UidPermissionState uidState = getUidState(pkg, userId);
+ if (uidState == null) {
+ Slog.e(TAG, "Missing permissions state for " + packageName + " and user " + userId);
return 0;
}
- return permissionsState.getPermissionFlags(permName, userId);
+ return uidState.getPermissionFlags(permName);
}
@Override
@@ -788,14 +785,13 @@ public class PermissionManagerService extends IPermissionManager.Stub {
throw new IllegalArgumentException("Unknown permission: " + permName);
}
- final PermissionsState permissionsState = getPermissionsState(pkg);
- if (permissionsState == null) {
- Slog.e(TAG, "Missing permissions state for " + packageName);
+ final UidPermissionState uidState = getUidState(pkg, userId);
+ if (uidState == null) {
+ Slog.e(TAG, "Missing permissions state for " + packageName + " and user " + userId);
return;
}
- final boolean hadState =
- permissionsState.getRuntimePermissionState(permName, userId) != null;
+ final boolean hadState = uidState.getPermissionState(permName) != null;
if (!hadState) {
boolean isRequested = false;
// Fast path, the current package has requested the permission.
@@ -822,20 +818,18 @@ public class PermissionManagerService extends IPermissionManager.Stub {
}
}
final boolean permissionUpdated =
- permissionsState.updatePermissionFlags(bp, userId, flagMask, flagValues);
+ uidState.updatePermissionFlags(bp, flagMask, flagValues);
if (permissionUpdated && bp.isRuntime()) {
notifyRuntimePermissionStateChanged(packageName, userId);
}
if (permissionUpdated && callback != null) {
// Install and runtime permissions are stored in different places,
// so figure out what permission changed and persist the change.
- if (permissionsState.getInstallPermissionState(permName) != null) {
+ if (!bp.isRuntime()) {
int userUid = UserHandle.getUid(userId, UserHandle.getAppId(pkg.getUid()));
callback.onInstallPermissionUpdatedNotifyListener(userUid);
- } else if (permissionsState.getRuntimePermissionState(permName, userId) != null
- || hadState) {
- callback.onPermissionUpdatedNotifyListener(new int[]{userId}, false,
- pkg.getUid());
+ } else {
+ callback.onPermissionUpdatedNotifyListener(new int[]{userId}, false, pkg.getUid());
}
}
}
@@ -868,13 +862,14 @@ public class PermissionManagerService extends IPermissionManager.Stub {
final boolean[] changed = new boolean[1];
mPackageManagerInt.forEachPackage(pkg -> {
- final PermissionsState permissionsState = getPermissionsState(pkg);
- if (permissionsState == null) {
- Slog.e(TAG, "Missing permissions state for " + pkg.getPackageName());
+ final UidPermissionState uidState = getUidState(pkg, userId);
+ if (uidState == null) {
+ Slog.e(TAG, "Missing permissions state for " + pkg.getPackageName() + " and user "
+ + userId);
return;
}
- changed[0] |= permissionsState.updatePermissionFlagsForAllPermissions(
- userId, effectiveFlagMask, effectiveFlagValues);
+ changed[0] |= uidState.updatePermissionFlagsForAllPermissions(
+ effectiveFlagMask, effectiveFlagValues);
mOnPermissionChangeListeners.onPermissionsChanged(pkg.getUid());
});
@@ -926,19 +921,20 @@ public class PermissionManagerService extends IPermissionManager.Stub {
}
final int uid = UserHandle.getUid(userId, pkg.getUid());
- final PermissionsState permissionsState = getPermissionsState(pkg);
- if (permissionsState == null) {
- Slog.e(TAG, "Missing permissions state for " + pkg.getPackageName());
+ final UidPermissionState uidState = getUidState(pkg, userId);
+ if (uidState == null) {
+ Slog.e(TAG, "Missing permissions state for " + pkg.getPackageName() + " and user "
+ + userId);
return PackageManager.PERMISSION_DENIED;
}
- if (checkSinglePermissionInternal(uid, permissionsState, permissionName)) {
+ if (checkSinglePermissionInternal(uid, uidState, permissionName)) {
return PackageManager.PERMISSION_GRANTED;
}
final String fullerPermissionName = FULLER_PERMISSION_MAP.get(permissionName);
if (fullerPermissionName != null
- && checkSinglePermissionInternal(uid, permissionsState, fullerPermissionName)) {
+ && checkSinglePermissionInternal(uid, uidState, fullerPermissionName)) {
return PackageManager.PERMISSION_GRANTED;
}
@@ -946,8 +942,8 @@ public class PermissionManagerService extends IPermissionManager.Stub {
}
private boolean checkSinglePermissionInternal(int uid,
- @NonNull PermissionsState permissionsState, @NonNull String permissionName) {
- if (!permissionsState.hasPermission(permissionName, UserHandle.getUserId(uid))) {
+ @NonNull UidPermissionState uidState, @NonNull String permissionName) {
+ if (!uidState.hasPermission(permissionName)) {
return false;
}
@@ -1141,9 +1137,9 @@ public class PermissionManagerService extends IPermissionManager.Stub {
final long identity = Binder.clearCallingIdentity();
try {
- final PermissionsState permissionsState = getPermissionsState(pkg);
- if (permissionsState == null) {
- Slog.e(TAG, "Missing permissions state for " + packageName);
+ final UidPermissionState uidState = getUidState(pkg, userId);
+ if (uidState == null) {
+ Slog.e(TAG, "Missing permissions state for " + packageName + " and user " + userId);
return null;
}
@@ -1164,7 +1160,7 @@ public class PermissionManagerService extends IPermissionManager.Stub {
for (int i = 0; i < permissionCount; i++) {
final String permissionName = pkg.getRequestedPermissions().get(i);
final int currentFlags =
- permissionsState.getPermissionFlags(permissionName, userId);
+ uidState.getPermissionFlags(permissionName);
if ((currentFlags & queryFlags) != 0) {
if (whitelistedPermissions == null) {
whitelistedPermissions = new ArrayList<>();
@@ -1453,13 +1449,14 @@ public class PermissionManagerService extends IPermissionManager.Stub {
throw new IllegalArgumentException("Unknown package: " + packageName);
}
- final PermissionsState permissionsState = getPermissionsState(pkg);
- if (permissionsState == null) {
- Slog.e(TAG, "Missing permissions state for " + pkg.getPackageName());
+ final UidPermissionState uidState = getUidState(pkg, userId);
+ if (uidState == null) {
+ Slog.e(TAG, "Missing permissions state for " + pkg.getPackageName() + " and user "
+ + userId);
return;
}
- bp.enforceDeclaredUsedAndRuntimeOrDevelopment(pkg, permissionsState);
+ bp.enforceDeclaredUsedAndRuntimeOrDevelopment(pkg, uidState);
// If a permission review is required for legacy apps we represent
// their permissions as always granted runtime ones since we need
@@ -1472,7 +1469,7 @@ public class PermissionManagerService extends IPermissionManager.Stub {
final int uid = UserHandle.getUid(userId, UserHandle.getAppId(pkg.getUid()));
- final int flags = permissionsState.getPermissionFlags(permName, userId);
+ final int flags = uidState.getPermissionFlags(permName);
if ((flags & PackageManager.FLAG_PERMISSION_SYSTEM_FIXED) != 0) {
Log.e(TAG, "Cannot grant system fixed permission "
+ permName + " for package " + packageName);
@@ -1502,8 +1499,9 @@ public class PermissionManagerService extends IPermissionManager.Stub {
if (bp.isDevelopment()) {
// Development permissions must be handled specially, since they are not
// normal runtime permissions. For now they apply to all users.
- if (permissionsState.grantInstallPermission(bp)
- != PERMISSION_OPERATION_FAILURE) {
+ // TODO(zhanghai): We are breaking the behavior above by making all permission state
+ // per-user. It isn't documented behavior and relatively rarely used anyway.
+ if (uidState.grantPermission(bp) != PERMISSION_OPERATION_FAILURE) {
if (callback != null) {
callback.onInstallPermissionGranted();
}
@@ -1521,13 +1519,13 @@ public class PermissionManagerService extends IPermissionManager.Stub {
return;
}
- final int result = permissionsState.grantRuntimePermission(bp, userId);
+ final int result = uidState.grantPermission(bp);
switch (result) {
case PERMISSION_OPERATION_FAILURE: {
return;
}
- case PermissionsState.PERMISSION_OPERATION_SUCCESS_GIDS_CHANGED: {
+ case UidPermissionState.PERMISSION_OPERATION_SUCCESS_GIDS_CHANGED: {
if (callback != null) {
callback.onGidsChanged(UserHandle.getAppId(pkg.getUid()), userId);
}
@@ -1617,13 +1615,14 @@ public class PermissionManagerService extends IPermissionManager.Stub {
throw new IllegalArgumentException("Unknown permission: " + permName);
}
- final PermissionsState permissionsState = getPermissionsState(pkg);
- if (permissionsState == null) {
- Slog.e(TAG, "Missing permissions state for " + pkg.getPackageName());
+ final UidPermissionState uidState = getUidState(pkg, userId);
+ if (uidState == null) {
+ Slog.e(TAG, "Missing permissions state for " + pkg.getPackageName() + " and user "
+ + userId);
return;
}
- bp.enforceDeclaredUsedAndRuntimeOrDevelopment(pkg, permissionsState);
+ bp.enforceDeclaredUsedAndRuntimeOrDevelopment(pkg, uidState);
// If a permission review is required for legacy apps we represent
// their permissions as always granted runtime ones since we need
@@ -1634,7 +1633,7 @@ public class PermissionManagerService extends IPermissionManager.Stub {
return;
}
- final int flags = permissionsState.getPermissionFlags(permName, userId);
+ final int flags = uidState.getPermissionFlags(permName);
// Only the system may revoke SYSTEM_FIXED permissions.
if ((flags & PackageManager.FLAG_PERMISSION_SYSTEM_FIXED) != 0
&& UserHandle.getCallingAppId() != Process.SYSTEM_UID) {
@@ -1649,8 +1648,9 @@ public class PermissionManagerService extends IPermissionManager.Stub {
if (bp.isDevelopment()) {
// Development permissions must be handled specially, since they are not
// normal runtime permissions. For now they apply to all users.
- if (permissionsState.revokeInstallPermission(bp)
- != PERMISSION_OPERATION_FAILURE) {
+ // TODO(zhanghai): We are breaking the behavior above by making all permission state
+ // per-user. It isn't documented behavior and relatively rarely used anyway.
+ if (uidState.revokePermission(bp) != PERMISSION_OPERATION_FAILURE) {
if (callback != null) {
mDefaultPermissionCallback.onInstallPermissionRevoked();
}
@@ -1659,12 +1659,11 @@ public class PermissionManagerService extends IPermissionManager.Stub {
}
// Permission is already revoked, no need to do anything.
- if (!permissionsState.hasRuntimePermission(permName, userId)) {
+ if (!uidState.hasPermission(permName)) {
return;
}
- if (permissionsState.revokeRuntimePermission(bp, userId)
- == PERMISSION_OPERATION_FAILURE) {
+ if (uidState.revokePermission(bp) == PERMISSION_OPERATION_FAILURE) {
return;
}
@@ -2466,19 +2465,7 @@ public class PermissionManagerService extends IPermissionManager.Stub {
private void onUserRemoved(@UserIdInt int userId) {
synchronized (mLock) {
- final int appIdStatesSize = mAppIdStates.size();
- for (int i = 0; i < appIdStatesSize; i++) {
- PermissionsState permissionsState = mAppIdStates.valueAt(i);
- for (PermissionState permissionState
- : permissionsState.getRuntimePermissionStates(userId)) {
- BasePermission bp = mSettings.getPermission(permissionState.getName());
- if (bp != null) {
- permissionsState.revokeRuntimePermission(bp, userId);
- permissionsState.updatePermissionFlags(bp, userId,
- PackageManager.MASK_PERMISSION_FLAGS_ALL, 0);
- }
- }
- }
+ mState.removeUserState(userId);
}
}
@@ -2489,19 +2476,18 @@ public class PermissionManagerService extends IPermissionManager.Stub {
if (ps == null) {
return Collections.emptySet();
}
- final PermissionsState permissionsState = getPermissionsState(ps);
- if (permissionsState == null) {
- Slog.e(TAG, "Missing permissions state for " + packageName);
+ final UidPermissionState uidState = getUidState(ps, userId);
+ if (uidState == null) {
+ Slog.e(TAG, "Missing permissions state for " + packageName + " and user " + userId);
return Collections.emptySet();
}
if (!ps.getInstantApp(userId)) {
- return permissionsState.getPermissions(userId);
+ return uidState.getPermissions();
} else {
// Install permission state is shared among all users, but instant app state is
// per-user, so we can only filter it here unless we make install permission state
// per-user as well.
- final Set<String> instantPermissions = new ArraySet<>(permissionsState.getPermissions(
- userId));
+ final Set<String> instantPermissions = new ArraySet<>(uidState.getPermissions());
instantPermissions.removeIf(permissionName -> {
BasePermission permission = mSettings.getPermission(permissionName);
if (permission == null) {
@@ -2527,20 +2513,6 @@ public class PermissionManagerService extends IPermissionManager.Stub {
return permission.computeGids(userId);
}
- @Nullable
- private int[] getPackageGids(@NonNull String packageName, @UserIdInt int userId) {
- final PackageSetting ps = mPackageManagerInt.getPackageSetting(packageName);
- if (ps == null) {
- return null;
- }
- final PermissionsState permissionsState = getPermissionsState(ps);
- if (permissionsState == null) {
- Slog.e(TAG, "Missing permissions state for " + packageName);
- return null;
- }
- return permissionsState.computeGids(userId);
- }
-
/**
* Restore the permission state for a package.
*
@@ -2575,15 +2547,17 @@ public class PermissionManagerService extends IPermissionManager.Stub {
if (ps == null) {
return;
}
- final PermissionsState permissionsState = getOrCreatePermissionsState(ps);
final int[] userIds = getAllUserIds();
boolean runtimePermissionsRevoked = false;
int[] updatedUserIds = EMPTY_INT_ARRAY;
- for (int userId : userIds) {
- if (permissionsState.isMissing(userId)) {
+ for (final int userId : userIds) {
+ final UserPermissionState userState = mState.getOrCreateUserState(userId);
+ final UidPermissionState uidState = userState.getOrCreateUidState(ps.getAppId());
+
+ if (uidState.isMissing()) {
Collection<String> requestedPermissions;
int targetSdkVersion;
if (!ps.isSharedUser()) {
@@ -2611,175 +2585,167 @@ public class PermissionManagerService extends IPermissionManager.Stub {
&& permission.isRuntime() && !permission.isRemoved()) {
if (permission.isHardOrSoftRestricted()
|| permission.isImmutablyRestricted()) {
- permissionsState.updatePermissionFlags(permission, userId,
+ uidState.updatePermissionFlags(permission,
FLAG_PERMISSION_RESTRICTION_UPGRADE_EXEMPT,
FLAG_PERMISSION_RESTRICTION_UPGRADE_EXEMPT);
}
if (targetSdkVersion < Build.VERSION_CODES.M) {
- permissionsState.updatePermissionFlags(permission, userId,
+ uidState.updatePermissionFlags(permission,
PackageManager.FLAG_PERMISSION_REVIEW_REQUIRED
| PackageManager.FLAG_PERMISSION_REVOKED_COMPAT,
PackageManager.FLAG_PERMISSION_REVIEW_REQUIRED
| PackageManager.FLAG_PERMISSION_REVOKED_COMPAT);
- permissionsState.grantRuntimePermission(permission, userId);
+ uidState.grantPermission(permission);
}
}
}
- permissionsState.setMissing(false, userId);
+ uidState.setMissing(false);
updatedUserIds = ArrayUtils.appendInt(updatedUserIds, userId);
}
- }
- PermissionsState origPermissions = permissionsState;
+ UidPermissionState origState = uidState;
- boolean changedInstallPermission = false;
+ boolean changedInstallPermission = false;
- if (replace) {
- ps.setInstallPermissionsFixed(false);
- if (!ps.isSharedUser()) {
- origPermissions = new PermissionsState(permissionsState);
- permissionsState.reset();
- } else {
- // We need to know only about runtime permission changes since the
- // calling code always writes the install permissions state but
- // the runtime ones are written only if changed. The only cases of
- // changed runtime permissions here are promotion of an install to
- // runtime and revocation of a runtime from a shared user.
- synchronized (mLock) {
- updatedUserIds = revokeUnusedSharedUserPermissionsLocked(
- ps.getSharedUser().getPackages(), permissionsState, userIds);
- if (!ArrayUtils.isEmpty(updatedUserIds)) {
- runtimePermissionsRevoked = true;
+ if (replace) {
+ userState.setInstallPermissionsFixed(ps.name, false);
+ if (!ps.isSharedUser()) {
+ origState = new UidPermissionState(uidState);
+ uidState.reset();
+ } else {
+ // We need to know only about runtime permission changes since the
+ // calling code always writes the install permissions state but
+ // the runtime ones are written only if changed. The only cases of
+ // changed runtime permissions here are promotion of an install to
+ // runtime and revocation of a runtime from a shared user.
+ synchronized (mLock) {
+ if (revokeUnusedSharedUserPermissionsLocked(
+ ps.getSharedUser().getPackages(), uidState)) {
+ updatedUserIds = ArrayUtils.appendInt(updatedUserIds, userId);
+ runtimePermissionsRevoked = true;
+ }
}
}
}
- }
- permissionsState.setGlobalGids(mGlobalGids);
+ uidState.setGlobalGids(mGlobalGids);
- ArraySet<String> newImplicitPermissions = new ArraySet<>();
-
- final int N = pkg.getRequestedPermissions().size();
- for (int i = 0; i < N; i++) {
- final String permName = pkg.getRequestedPermissions().get(i);
+ ArraySet<String> newImplicitPermissions = new ArraySet<>();
final String friendlyName = pkg.getPackageName() + "(" + pkg.getUid() + ")";
- final BasePermission bp = mSettings.getPermission(permName);
- final boolean appSupportsRuntimePermissions =
- pkg.getTargetSdkVersion() >= Build.VERSION_CODES.M;
- String upgradedActivityRecognitionPermission = null;
- if (DEBUG_INSTALL && bp != null) {
- Log.i(TAG, "Package " + friendlyName
- + " checking " + permName + ": " + bp);
- }
+ final int N = pkg.getRequestedPermissions().size();
+ for (int i = 0; i < N; i++) {
+ final String permName = pkg.getRequestedPermissions().get(i);
+ final BasePermission bp = mSettings.getPermission(permName);
+ final boolean appSupportsRuntimePermissions =
+ pkg.getTargetSdkVersion() >= Build.VERSION_CODES.M;
+ String upgradedActivityRecognitionPermission = null;
+
+ if (DEBUG_INSTALL && bp != null) {
+ Log.i(TAG, "Package " + friendlyName
+ + " checking " + permName + ": " + bp);
+ }
- if (bp == null || getSourcePackageSetting(bp) == null) {
- if (packageOfInterest == null || packageOfInterest.equals(
- pkg.getPackageName())) {
- if (DEBUG_PERMISSIONS) {
- Slog.i(TAG, "Unknown permission " + permName
- + " in package " + friendlyName);
+ if (bp == null || getSourcePackageSetting(bp) == null) {
+ if (packageOfInterest == null || packageOfInterest.equals(
+ pkg.getPackageName())) {
+ if (DEBUG_PERMISSIONS) {
+ Slog.i(TAG, "Unknown permission " + permName
+ + " in package " + friendlyName);
+ }
}
+ continue;
}
- continue;
- }
- // Cache newImplicitPermissions before modifing permissionsState as for the shared
- // uids the original and new state are the same object
- if (!origPermissions.hasRequestedPermission(permName)
- && (pkg.getImplicitPermissions().contains(permName)
- || (permName.equals(Manifest.permission.ACTIVITY_RECOGNITION)))) {
- if (pkg.getImplicitPermissions().contains(permName)) {
- // If permName is an implicit permission, try to auto-grant
- newImplicitPermissions.add(permName);
+ // Cache newImplicitPermissions before modifing permissionsState as for the shared
+ // uids the original and new state are the same object
+ if (!origState.hasRequestedPermission(permName)
+ && (pkg.getImplicitPermissions().contains(permName)
+ || (permName.equals(Manifest.permission.ACTIVITY_RECOGNITION)))) {
+ if (pkg.getImplicitPermissions().contains(permName)) {
+ // If permName is an implicit permission, try to auto-grant
+ newImplicitPermissions.add(permName);
- if (DEBUG_PERMISSIONS) {
- Slog.i(TAG, permName + " is newly added for " + friendlyName);
- }
- } else {
- // Special case for Activity Recognition permission. Even if AR permission
- // is not an implicit permission we want to add it to the list (try to
- // auto-grant it) if the app was installed on a device before AR permission
- // was split, regardless of if the app now requests the new AR permission
- // or has updated its target SDK and AR is no longer implicit to it.
- // This is a compatibility workaround for apps when AR permission was
- // split in Q.
- final List<SplitPermissionInfoParcelable> permissionList =
- getSplitPermissions();
- int numSplitPerms = permissionList.size();
- for (int splitPermNum = 0; splitPermNum < numSplitPerms; splitPermNum++) {
- SplitPermissionInfoParcelable sp = permissionList.get(splitPermNum);
- String splitPermName = sp.getSplitPermission();
- if (sp.getNewPermissions().contains(permName)
- && origPermissions.hasInstallPermission(splitPermName)) {
- upgradedActivityRecognitionPermission = splitPermName;
- newImplicitPermissions.add(permName);
+ if (DEBUG_PERMISSIONS) {
+ Slog.i(TAG, permName + " is newly added for " + friendlyName);
+ }
+ } else {
+ // Special case for Activity Recognition permission. Even if AR permission
+ // is not an implicit permission we want to add it to the list (try to
+ // auto-grant it) if the app was installed on a device before AR permission
+ // was split, regardless of if the app now requests the new AR permission
+ // or has updated its target SDK and AR is no longer implicit to it.
+ // This is a compatibility workaround for apps when AR permission was
+ // split in Q.
+ final List<SplitPermissionInfoParcelable> permissionList =
+ getSplitPermissions();
+ int numSplitPerms = permissionList.size();
+ for (int splitPermNum = 0; splitPermNum < numSplitPerms; splitPermNum++) {
+ SplitPermissionInfoParcelable sp = permissionList.get(splitPermNum);
+ String splitPermName = sp.getSplitPermission();
+ if (sp.getNewPermissions().contains(permName)
+ && origState.hasPermission(splitPermName)) {
+ upgradedActivityRecognitionPermission = splitPermName;
+ newImplicitPermissions.add(permName);
- if (DEBUG_PERMISSIONS) {
- Slog.i(TAG, permName + " is newly added for " + friendlyName);
+ if (DEBUG_PERMISSIONS) {
+ Slog.i(TAG, permName + " is newly added for "
+ + friendlyName);
+ }
+ break;
}
- break;
}
}
}
- }
- // TODO(b/140256621): The package instant app method has been removed
- // as part of work in b/135203078, so this has been commented out in the meantime
- // Limit ephemeral apps to ephemeral allowed permissions.
-// if (/*pkg.isInstantApp()*/ false && !bp.isInstant()) {
-// if (DEBUG_PERMISSIONS) {
-// Log.i(TAG, "Denying non-ephemeral permission " + bp.getName()
-// + " for package " + friendlyName);
-// }
-// continue;
-// }
-
- if (bp.isRuntimeOnly() && !appSupportsRuntimePermissions) {
- if (DEBUG_PERMISSIONS) {
- Log.i(TAG, "Denying runtime-only permission " + bp.getName()
- + " for package " + friendlyName);
+ // TODO(b/140256621): The package instant app method has been removed
+ // as part of work in b/135203078, so this has been commented out in the meantime
+ // Limit ephemeral apps to ephemeral allowed permissions.
+ // if (/*pkg.isInstantApp()*/ false && !bp.isInstant()) {
+ // if (DEBUG_PERMISSIONS) {
+ // Log.i(TAG, "Denying non-ephemeral permission " + bp.getName()
+ // + " for package " + pkg.getPackageName());
+ // }
+ // continue;
+ // }
+
+ if (bp.isRuntimeOnly() && !appSupportsRuntimePermissions) {
+ if (DEBUG_PERMISSIONS) {
+ Log.i(TAG, "Denying runtime-only permission " + bp.getName()
+ + " for package " + friendlyName);
+ }
+ continue;
}
- continue;
- }
- final String perm = bp.getName();
- boolean allowedSig = false;
- int grant = GRANT_DENIED;
+ final String perm = bp.getName();
+ boolean allowedSig = false;
+ int grant = GRANT_DENIED;
- // Keep track of app op permissions.
- if (bp.isAppOp()) {
- mSettings.addAppOpPackage(perm, pkg.getPackageName());
- }
+ // Keep track of app op permissions.
+ if (bp.isAppOp()) {
+ mSettings.addAppOpPackage(perm, pkg.getPackageName());
+ }
- if (bp.isNormal()) {
- // For all apps normal permissions are install time ones.
- grant = GRANT_INSTALL;
- } else if (bp.isRuntime()) {
- if (origPermissions.hasInstallPermission(bp.getName())
- || upgradedActivityRecognitionPermission != null) {
- // Before Q we represented some runtime permissions as install permissions,
- // in Q we cannot do this anymore. Hence upgrade them all.
- grant = GRANT_UPGRADE;
- } else {
+ if (bp.isNormal()) {
+ // For all apps normal permissions are install time ones.
+ grant = GRANT_INSTALL;
+ } else if (bp.isRuntime()) {
// For modern apps keep runtime permissions unchanged.
grant = GRANT_RUNTIME;
+ } else if (bp.isSignature()) {
+ // For all apps signature permissions are install time ones.
+ allowedSig = shouldGrantSignaturePermission(perm, pkg, ps, bp, origState);
+ if (allowedSig) {
+ grant = GRANT_INSTALL;
+ }
}
- } else if (bp.isSignature()) {
- // For all apps signature permissions are install time ones.
- allowedSig = shouldGrantSignaturePermission(perm, pkg, ps, bp, origPermissions);
- if (allowedSig) {
- grant = GRANT_INSTALL;
- }
- }
- if (grant != GRANT_DENIED) {
- if (!ps.isSystem() && ps.areInstallPermissionsFixed() && !bp.isRuntime()) {
+ if (grant == GRANT_INSTALL && !allowedSig && !origState.hasPermission(perm)) {
// If this is an existing, non-system package, then
// we can't add any new permissions to it. Runtime
- // permissions can be added any time - they ad dynamic.
- if (!allowedSig && !origPermissions.hasInstallPermission(perm)) {
+ // permissions can be added any time - they are dynamic.
+ if (!ps.isSystem() && userState.areInstallPermissionsFixed(ps.name)) {
// Except... if this is a permission that was added
// to the platform (note: need to only do this when
// updating the platform).
@@ -2788,45 +2754,26 @@ public class PermissionManagerService extends IPermissionManager.Stub {
}
}
}
- }
- if (DEBUG_PERMISSIONS) {
- Slog.i(TAG, "Considering granting permission " + perm + " to package "
- + pkg.getPackageName());
- }
+ if (DEBUG_PERMISSIONS) {
+ Slog.i(TAG, "Considering granting permission " + perm + " to package "
+ + pkg.getPackageName());
+ }
- synchronized (mLock) {
- if (grant != GRANT_DENIED) {
- switch (grant) {
- case GRANT_INSTALL: {
- // Revoke this as runtime permission to handle the case of
- // a runtime permission being downgraded to an install one.
- // Also in permission review mode we keep dangerous permissions
- // for legacy apps
- for (int userId : userIds) {
- if (origPermissions.getRuntimePermissionState(
- perm, userId) != null) {
- // Revoke the runtime permission and clear the flags.
- origPermissions.revokeRuntimePermission(bp, userId);
- origPermissions.updatePermissionFlags(bp, userId,
- PackageManager.MASK_PERMISSION_FLAGS_ALL, 0);
- // If we revoked a permission permission, we have to write.
- updatedUserIds = ArrayUtils.appendInt(
- updatedUserIds, userId);
+ synchronized (mLock) {
+ if (grant != GRANT_DENIED) {
+ switch (grant) {
+ case GRANT_INSTALL: {
+ // Grant an install permission.
+ if (uidState.grantPermission(bp) != PERMISSION_OPERATION_FAILURE) {
+ changedInstallPermission = true;
}
- }
- // Grant an install permission.
- if (permissionsState.grantInstallPermission(bp) !=
- PERMISSION_OPERATION_FAILURE) {
- changedInstallPermission = true;
- }
- } break;
+ } break;
- case GRANT_RUNTIME: {
- boolean hardRestricted = bp.isHardRestricted();
- boolean softRestricted = bp.isSoftRestricted();
+ case GRANT_RUNTIME: {
+ boolean hardRestricted = bp.isHardRestricted();
+ boolean softRestricted = bp.isSoftRestricted();
- for (int userId : userIds) {
// If permission policy is not ready we don't deal with restricted
// permissions as the policy may whitelist some permissions. Once
// the policy is initialized we would re-evaluate permissions.
@@ -2834,25 +2781,24 @@ public class PermissionManagerService extends IPermissionManager.Stub {
mPermissionPolicyInternal != null
&& mPermissionPolicyInternal.isInitialized(userId);
- PermissionState permState = origPermissions
- .getRuntimePermissionState(perm, userId);
- int flags = permState != null ? permState.getFlags() : 0;
+ PermissionState origPermState = origState.getPermissionState(perm);
+ int flags = origPermState != null ? origPermState.getFlags() : 0;
boolean wasChanged = false;
boolean restrictionExempt =
- (origPermissions.getPermissionFlags(bp.name, userId)
+ (origState.getPermissionFlags(bp.name)
& FLAGS_PERMISSION_RESTRICTION_ANY_EXEMPT) != 0;
- boolean restrictionApplied = (origPermissions.getPermissionFlags(
- bp.name, userId) & FLAG_PERMISSION_APPLY_RESTRICTION) != 0;
+ boolean restrictionApplied = (origState.getPermissionFlags(
+ bp.name) & FLAG_PERMISSION_APPLY_RESTRICTION) != 0;
if (appSupportsRuntimePermissions) {
// If hard restricted we don't allow holding it
if (permissionPolicyInitialized && hardRestricted) {
if (!restrictionExempt) {
- if (permState != null && permState.isGranted()
- && permissionsState.revokeRuntimePermission(
- bp, userId) != PERMISSION_OPERATION_FAILURE) {
+ if (origPermState != null && origPermState.isGranted()
+ && uidState.revokePermission(
+ bp) != PERMISSION_OPERATION_FAILURE) {
wasChanged = true;
}
if (!restrictionApplied) {
@@ -2882,15 +2828,16 @@ public class PermissionManagerService extends IPermissionManager.Stub {
// Hard restricted permissions cannot be held.
} else if (!permissionPolicyInitialized
|| (!hardRestricted || restrictionExempt)) {
- if (permState != null && permState.isGranted()) {
- if (permissionsState.grantRuntimePermission(bp, userId)
+ if ((origPermState != null && origPermState.isGranted())
+ || upgradedActivityRecognitionPermission != null) {
+ if (uidState.grantPermission(bp)
== PERMISSION_OPERATION_FAILURE) {
wasChanged = true;
}
}
}
} else {
- if (permState == null) {
+ if (origPermState == null) {
// New permission
if (PLATFORM_PACKAGE_NAME.equals(
bp.getSourcePackageName())) {
@@ -2902,8 +2849,8 @@ public class PermissionManagerService extends IPermissionManager.Stub {
}
}
- if (!permissionsState.hasRuntimePermission(bp.name, userId)
- && permissionsState.grantRuntimePermission(bp, userId)
+ if (!uidState.hasPermission(bp.name)
+ && uidState.grantPermission(bp)
!= PERMISSION_OPERATION_FAILURE) {
wasChanged = true;
}
@@ -2936,194 +2883,75 @@ public class PermissionManagerService extends IPermissionManager.Stub {
updatedUserIds = ArrayUtils.appendInt(updatedUserIds, userId);
}
- permissionsState.updatePermissionFlags(bp, userId,
- MASK_PERMISSION_FLAGS_ALL, flags);
- }
- } break;
-
- case GRANT_UPGRADE: {
- // Upgrade from Pre-Q to Q permission model. Make all permissions
- // runtime
- PermissionState permState = origPermissions
- .getInstallPermissionState(perm);
- int flags = (permState != null) ? permState.getFlags() : 0;
-
- BasePermission bpToRevoke =
- upgradedActivityRecognitionPermission == null
- ? bp : mSettings.getPermissionLocked(
- upgradedActivityRecognitionPermission);
- // Remove install permission
- if (origPermissions.revokeInstallPermission(bpToRevoke)
- != PERMISSION_OPERATION_FAILURE) {
- origPermissions.updatePermissionFlags(bpToRevoke,
- UserHandle.USER_ALL,
- (MASK_PERMISSION_FLAGS_ALL
- & ~FLAG_PERMISSION_APPLY_RESTRICTION), 0);
- changedInstallPermission = true;
- }
-
- boolean hardRestricted = bp.isHardRestricted();
- boolean softRestricted = bp.isSoftRestricted();
-
- for (int userId : userIds) {
- // If permission policy is not ready we don't deal with restricted
- // permissions as the policy may whitelist some permissions. Once
- // the policy is initialized we would re-evaluate permissions.
- final boolean permissionPolicyInitialized =
- mPermissionPolicyInternal != null
- && mPermissionPolicyInternal.isInitialized(userId);
-
- boolean wasChanged = false;
-
- boolean restrictionExempt =
- (origPermissions.getPermissionFlags(bp.name, userId)
- & FLAGS_PERMISSION_RESTRICTION_ANY_EXEMPT) != 0;
- boolean restrictionApplied = (origPermissions.getPermissionFlags(
- bp.name, userId) & FLAG_PERMISSION_APPLY_RESTRICTION) != 0;
-
- if (appSupportsRuntimePermissions) {
- // If hard restricted we don't allow holding it
- if (permissionPolicyInitialized && hardRestricted) {
- if (!restrictionExempt) {
- if (permState != null && permState.isGranted()
- && permissionsState.revokeRuntimePermission(
- bp, userId) != PERMISSION_OPERATION_FAILURE) {
- wasChanged = true;
- }
- if (!restrictionApplied) {
- flags |= FLAG_PERMISSION_APPLY_RESTRICTION;
- wasChanged = true;
- }
- }
- // If soft restricted we allow holding in a restricted form
- } else if (permissionPolicyInitialized && softRestricted) {
- // Regardless if granted set the restriction flag as it
- // may affect app treatment based on this permission.
- if (!restrictionExempt && !restrictionApplied) {
- flags |= FLAG_PERMISSION_APPLY_RESTRICTION;
- wasChanged = true;
- }
- }
-
- // Remove review flag as it is not necessary anymore
- if ((flags & FLAG_PERMISSION_REVIEW_REQUIRED) != 0) {
- flags &= ~FLAG_PERMISSION_REVIEW_REQUIRED;
- wasChanged = true;
- }
-
- if ((flags & FLAG_PERMISSION_REVOKED_COMPAT) != 0) {
- flags &= ~FLAG_PERMISSION_REVOKED_COMPAT;
- wasChanged = true;
- // Hard restricted permissions cannot be held.
- } else if (!permissionPolicyInitialized ||
- (!hardRestricted || restrictionExempt)) {
- if (permissionsState.grantRuntimePermission(bp, userId) !=
- PERMISSION_OPERATION_FAILURE) {
- wasChanged = true;
- }
- }
- } else {
- if (!permissionsState.hasRuntimePermission(bp.name, userId)
- && permissionsState.grantRuntimePermission(bp,
- userId) != PERMISSION_OPERATION_FAILURE) {
- flags |= FLAG_PERMISSION_REVIEW_REQUIRED;
- wasChanged = true;
- }
-
- // If legacy app always grant the permission but if restricted
- // and not exempt take a note a restriction should be applied.
- if (permissionPolicyInitialized
- && (hardRestricted || softRestricted)
- && !restrictionExempt && !restrictionApplied) {
- flags |= FLAG_PERMISSION_APPLY_RESTRICTION;
- wasChanged = true;
- }
- }
+ uidState.updatePermissionFlags(bp, MASK_PERMISSION_FLAGS_ALL,
+ flags);
+ } break;
- // If unrestricted or restriction exempt, don't apply restriction.
- if (permissionPolicyInitialized) {
- if (!(hardRestricted || softRestricted) || restrictionExempt) {
- if (restrictionApplied) {
- flags &= ~FLAG_PERMISSION_APPLY_RESTRICTION;
- // Dropping restriction on a legacy app implies a review
- if (!appSupportsRuntimePermissions) {
- flags |= FLAG_PERMISSION_REVIEW_REQUIRED;
- }
- wasChanged = true;
- }
+ default: {
+ if (packageOfInterest == null
+ || packageOfInterest.equals(pkg.getPackageName())) {
+ if (DEBUG_PERMISSIONS) {
+ Slog.i(TAG, "Not granting permission " + perm
+ + " to package " + friendlyName
+ + " because it was previously installed without");
}
}
-
- if (wasChanged) {
- updatedUserIds = ArrayUtils.appendInt(updatedUserIds, userId);
- }
-
- permissionsState.updatePermissionFlags(bp, userId,
- MASK_PERMISSION_FLAGS_ALL, flags);
+ } break;
+ }
+ } else {
+ if (uidState.revokePermission(bp) != PERMISSION_OPERATION_FAILURE) {
+ // Also drop the permission flags.
+ uidState.updatePermissionFlags(bp,
+ MASK_PERMISSION_FLAGS_ALL, 0);
+ changedInstallPermission = true;
+ if (DEBUG_PERMISSIONS) {
+ Slog.i(TAG, "Un-granting permission " + perm
+ + " from package " + friendlyName
+ + " (protectionLevel=" + bp.getProtectionLevel()
+ + " flags=0x"
+ + Integer.toHexString(PackageInfoUtils.appInfoFlags(pkg,
+ ps))
+ + ")");
}
- } break;
-
- default: {
- if (packageOfInterest == null
- || packageOfInterest.equals(pkg.getPackageName())) {
- if (DEBUG_PERMISSIONS) {
- Slog.i(TAG, "Not granting permission " + perm
- + " to package " + friendlyName
- + " because it was previously installed without");
- }
+ } else if (bp.isAppOp()) {
+ // Don't print warning for app op permissions, since it is fine for them
+ // not to be granted, there is a UI for the user to decide.
+ if (DEBUG_PERMISSIONS
+ && (packageOfInterest == null
+ || packageOfInterest.equals(pkg.getPackageName()))) {
+ Slog.i(TAG, "Not granting permission " + perm
+ + " to package " + friendlyName
+ + " (protectionLevel=" + bp.getProtectionLevel()
+ + " flags=0x"
+ + Integer.toHexString(PackageInfoUtils.appInfoFlags(pkg,
+ ps))
+ + ")");
}
- } break;
- }
- } else {
- if (permissionsState.revokeInstallPermission(bp) !=
- PERMISSION_OPERATION_FAILURE) {
- // Also drop the permission flags.
- permissionsState.updatePermissionFlags(bp, UserHandle.USER_ALL,
- MASK_PERMISSION_FLAGS_ALL, 0);
- changedInstallPermission = true;
- if (DEBUG_PERMISSIONS) {
- Slog.i(TAG, "Un-granting permission " + perm
- + " from package " + friendlyName
- + " (protectionLevel=" + bp.getProtectionLevel()
- + " flags=0x"
- + Integer.toHexString(PackageInfoUtils.appInfoFlags(pkg, ps))
- + ")");
- }
- } else if (bp.isAppOp()) {
- // Don't print warning for app op permissions, since it is fine for them
- // not to be granted, there is a UI for the user to decide.
- if (DEBUG_PERMISSIONS
- && (packageOfInterest == null
- || packageOfInterest.equals(pkg.getPackageName()))) {
- Slog.i(TAG, "Not granting permission " + perm
- + " to package " + friendlyName
- + " (protectionLevel=" + bp.getProtectionLevel()
- + " flags=0x"
- + Integer.toHexString(PackageInfoUtils.appInfoFlags(pkg, ps))
- + ")");
}
}
}
}
- }
- if ((changedInstallPermission || replace) && !ps.areInstallPermissionsFixed() &&
- !ps.isSystem() || ps.getPkgState().isUpdatedSystemApp()) {
- // This is the first that we have heard about this package, so the
- // permissions we have now selected are fixed until explicitly
- // changed.
- ps.setInstallPermissionsFixed(true);
- }
+ if ((changedInstallPermission || replace)
+ && !userState.areInstallPermissionsFixed(ps.name)
+ && !ps.isSystem() || ps.getPkgState().isUpdatedSystemApp()) {
+ // This is the first that we have heard about this package, so the
+ // permissions we have now selected are fixed until explicitly
+ // changed.
+ userState.setInstallPermissionsFixed(ps.name, true);
+ }
- synchronized (mLock) {
- updatedUserIds = revokePermissionsNoLongerImplicitLocked(permissionsState, pkg,
- userIds, updatedUserIds);
- updatedUserIds = setInitialGrantForNewImplicitPermissionsLocked(origPermissions,
- permissionsState, pkg, newImplicitPermissions, userIds, updatedUserIds);
- updatedUserIds = checkIfLegacyStorageOpsNeedToBeUpdated(pkg, replace, userIds,
- updatedUserIds);
+ synchronized (mLock) {
+ updatedUserIds = revokePermissionsNoLongerImplicitLocked(uidState, pkg,
+ userId, updatedUserIds);
+ updatedUserIds = setInitialGrantForNewImplicitPermissionsLocked(origState,
+ uidState, pkg, newImplicitPermissions, userId, updatedUserIds);
+ }
}
+ updatedUserIds = checkIfLegacyStorageOpsNeedToBeUpdated(pkg, replace, userIds,
+ updatedUserIds);
+
// TODO: Kill UIDs whose GIDs or runtime permissions changed. This might be more important
// for shared users.
// Persist the runtime permissions state for users with changes. If permissions
@@ -3159,40 +2987,38 @@ public class PermissionManagerService extends IPermissionManager.Stub {
*
* @return The updated value of the {@code updatedUserIds} parameter
*/
- private @NonNull int[] revokePermissionsNoLongerImplicitLocked(@NonNull PermissionsState ps,
- @NonNull AndroidPackage pkg, @NonNull int[] userIds, @NonNull int[] updatedUserIds) {
+ private @NonNull int[] revokePermissionsNoLongerImplicitLocked(@NonNull UidPermissionState ps,
+ @NonNull AndroidPackage pkg, int userId, @NonNull int[] updatedUserIds) {
String pkgName = pkg.getPackageName();
boolean supportsRuntimePermissions = pkg.getTargetSdkVersion()
>= Build.VERSION_CODES.M;
- for (int userId : userIds) {
- for (String permission : ps.getPermissions(userId)) {
- if (!pkg.getImplicitPermissions().contains(permission)) {
- if (!ps.hasInstallPermission(permission)) {
- int flags = ps.getRuntimePermissionState(permission, userId).getFlags();
+ for (String permission : ps.getPermissions()) {
+ if (!pkg.getImplicitPermissions().contains(permission)) {
+ BasePermission bp = mSettings.getPermissionLocked(permission);
+ if (bp.isRuntime()) {
+ int flags = ps.getPermissionFlags(permission);
- if ((flags & FLAG_PERMISSION_REVOKE_WHEN_REQUESTED) != 0) {
- BasePermission bp = mSettings.getPermissionLocked(permission);
+ if ((flags & FLAG_PERMISSION_REVOKE_WHEN_REQUESTED) != 0) {
- int flagsToRemove = FLAG_PERMISSION_REVOKE_WHEN_REQUESTED;
+ int flagsToRemove = FLAG_PERMISSION_REVOKE_WHEN_REQUESTED;
- if ((flags & BLOCKING_PERMISSION_FLAGS) == 0
- && supportsRuntimePermissions) {
- int revokeResult = ps.revokeRuntimePermission(bp, userId);
- if (revokeResult != PERMISSION_OPERATION_FAILURE) {
- if (DEBUG_PERMISSIONS) {
- Slog.i(TAG, "Revoking runtime permission "
- + permission + " for " + pkgName
- + " as it is now requested");
- }
+ if ((flags & BLOCKING_PERMISSION_FLAGS) == 0
+ && supportsRuntimePermissions) {
+ int revokeResult = ps.revokePermission(bp);
+ if (revokeResult != PERMISSION_OPERATION_FAILURE) {
+ if (DEBUG_PERMISSIONS) {
+ Slog.i(TAG, "Revoking runtime permission "
+ + permission + " for " + pkgName
+ + " as it is now requested");
}
-
- flagsToRemove |= USER_PERMISSION_FLAGS;
}
- ps.updatePermissionFlags(bp, userId, flagsToRemove, 0);
- updatedUserIds = ArrayUtils.appendInt(updatedUserIds, userId);
+ flagsToRemove |= USER_PERMISSION_FLAGS;
}
+
+ ps.updatePermissionFlags(bp, flagsToRemove, 0);
+ updatedUserIds = ArrayUtils.appendInt(updatedUserIds, userId);
}
}
}
@@ -3213,12 +3039,10 @@ public class PermissionManagerService extends IPermissionManager.Stub {
* @param newPerm The permission to inherit to
* @param ps The permission state of the package
* @param pkg The package requesting the permissions
- * @param userId The user the permission belongs to
*/
private void inheritPermissionStateToNewImplicitPermissionLocked(
@NonNull ArraySet<String> sourcePerms, @NonNull String newPerm,
- @NonNull PermissionsState ps, @NonNull AndroidPackage pkg,
- @UserIdInt int userId) {
+ @NonNull UidPermissionState ps, @NonNull AndroidPackage pkg) {
String pkgName = pkg.getPackageName();
boolean isGranted = false;
int flags = 0;
@@ -3226,17 +3050,16 @@ public class PermissionManagerService extends IPermissionManager.Stub {
int numSourcePerm = sourcePerms.size();
for (int i = 0; i < numSourcePerm; i++) {
String sourcePerm = sourcePerms.valueAt(i);
- if ((ps.hasRuntimePermission(sourcePerm, userId))
- || ps.hasInstallPermission(sourcePerm)) {
+ if (ps.hasPermission(sourcePerm)) {
if (!isGranted) {
flags = 0;
}
isGranted = true;
- flags |= ps.getPermissionFlags(sourcePerm, userId);
+ flags |= ps.getPermissionFlags(sourcePerm);
} else {
if (!isGranted) {
- flags |= ps.getPermissionFlags(sourcePerm, userId);
+ flags |= ps.getPermissionFlags(sourcePerm);
}
}
}
@@ -3247,11 +3070,11 @@ public class PermissionManagerService extends IPermissionManager.Stub {
+ " for " + pkgName);
}
- ps.grantRuntimePermission(mSettings.getPermissionLocked(newPerm), userId);
+ ps.grantPermission(mSettings.getPermissionLocked(newPerm));
}
// Add permission flags
- ps.updatePermissionFlags(mSettings.getPermission(newPerm), userId, flags, flags);
+ ps.updatePermissionFlags(mSettings.getPermission(newPerm), flags, flags);
}
/**
@@ -3283,15 +3106,15 @@ public class PermissionManagerService extends IPermissionManager.Stub {
* @param origPs The permission state of the package before the split
* @param ps The new permission state
* @param pkg The package the permission belongs to
- * @param userIds All user IDs in the system, must be passed in because this method is locked
+ * @param userId The user ID
* @param updatedUserIds List of users for which the permission state has already been changed
*
* @return List of users for which the permission state has been changed
*/
private @NonNull int[] setInitialGrantForNewImplicitPermissionsLocked(
- @NonNull PermissionsState origPs, @NonNull PermissionsState ps,
+ @NonNull UidPermissionState origPs, @NonNull UidPermissionState ps,
@NonNull AndroidPackage pkg, @NonNull ArraySet<String> newImplicitPermissions,
- @NonNull int[] userIds, @NonNull int[] updatedUserIds) {
+ @UserIdInt int userId, @NonNull int[] updatedUserIds) {
String pkgName = pkg.getPackageName();
ArrayMap<String, ArraySet<String>> newToSplitPerms = new ArrayMap<>();
@@ -3322,39 +3145,41 @@ public class PermissionManagerService extends IPermissionManager.Stub {
ArraySet<String> sourcePerms = newToSplitPerms.get(newPerm);
if (sourcePerms != null) {
- if (!ps.hasInstallPermission(newPerm)) {
- BasePermission bp = mSettings.getPermissionLocked(newPerm);
-
- for (int userId : userIds) {
- if (!newPerm.equals(Manifest.permission.ACTIVITY_RECOGNITION)) {
- ps.updatePermissionFlags(bp, userId,
- FLAG_PERMISSION_REVOKE_WHEN_REQUESTED,
- FLAG_PERMISSION_REVOKE_WHEN_REQUESTED);
- }
- updatedUserIds = ArrayUtils.appendInt(updatedUserIds, userId);
+ BasePermission bp = mSettings.getPermissionLocked(newPerm);
+ if (bp.isRuntime()) {
+ if (!newPerm.equals(Manifest.permission.ACTIVITY_RECOGNITION)) {
+ ps.updatePermissionFlags(bp,
+ FLAG_PERMISSION_REVOKE_WHEN_REQUESTED,
+ FLAG_PERMISSION_REVOKE_WHEN_REQUESTED);
+ }
+ updatedUserIds = ArrayUtils.appendInt(updatedUserIds, userId);
+
+ if (!origPs.hasRequestedPermission(sourcePerms)) {
boolean inheritsFromInstallPerm = false;
for (int sourcePermNum = 0; sourcePermNum < sourcePerms.size();
sourcePermNum++) {
- if (ps.hasInstallPermission(sourcePerms.valueAt(sourcePermNum))) {
+ final String sourcePerm = sourcePerms.valueAt(sourcePermNum);
+ BasePermission sourceBp = mSettings.getPermissionLocked(sourcePerm);
+ if (!sourceBp.isRuntime()) {
inheritsFromInstallPerm = true;
break;
}
}
- if (!origPs.hasRequestedPermission(sourcePerms)
- && !inheritsFromInstallPerm) {
+ if (!inheritsFromInstallPerm) {
// Both permissions are new so nothing to inherit.
if (DEBUG_PERMISSIONS) {
Slog.i(TAG, newPerm + " does not inherit from " + sourcePerms
+ " for " + pkgName + " as split permission is also new");
}
- } else {
- // Inherit from new install or existing runtime permissions
- inheritPermissionStateToNewImplicitPermissionLocked(sourcePerms,
- newPerm, ps, pkg, userId);
+ continue;
}
}
+
+ // Inherit from new install or existing runtime permissions
+ inheritPermissionStateToNewImplicitPermissionLocked(sourcePerms, newPerm, ps,
+ pkg);
}
}
}
@@ -3484,7 +3309,7 @@ public class PermissionManagerService extends IPermissionManager.Stub {
}
private boolean shouldGrantSignaturePermission(String perm, AndroidPackage pkg,
- PackageSetting pkgSetting, BasePermission bp, PermissionsState origPermissions) {
+ PackageSetting pkgSetting, BasePermission bp, UidPermissionState origPermissions) {
boolean oemPermission = bp.isOEM();
boolean vendorPrivilegedPermission = bp.isVendorPrivileged();
boolean privilegedPermission = bp.isPrivileged() || bp.isVendorPrivileged();
@@ -3640,7 +3465,7 @@ public class PermissionManagerService extends IPermissionManager.Stub {
if (!allowed && bp.isDevelopment()) {
// For development permissions, a development permission
// is granted only if it was already granted.
- allowed = origPermissions.hasInstallPermission(perm);
+ allowed = origPermissions.hasPermission(perm);
}
if (!allowed && bp.isSetup()
&& ArrayUtils.contains(mPackageManagerInt.getKnownPackageNames(
@@ -3760,12 +3585,13 @@ public class PermissionManagerService extends IPermissionManager.Stub {
}
// Legacy apps have the permission and get user consent on launch.
- final PermissionsState permissionsState = getPermissionsState(pkg);
- if (permissionsState == null) {
- Slog.e(TAG, "Missing permissions state for " + pkg.getPackageName());
+ final UidPermissionState uidState = getUidState(pkg, userId);
+ if (uidState == null) {
+ Slog.e(TAG, "Missing permissions state for " + pkg.getPackageName() + " and user "
+ + userId);
return false;
}
- return permissionsState.isPermissionReviewRequired(userId);
+ return uidState.isPermissionReviewRequired();
}
private boolean isPackageRequestingPermission(AndroidPackage pkg, String permission) {
@@ -3789,9 +3615,10 @@ public class PermissionManagerService extends IPermissionManager.Stub {
private void grantRequestedRuntimePermissionsForUser(AndroidPackage pkg, int userId,
String[] grantedPermissions, int callingUid, PermissionCallback callback) {
- final PermissionsState permissionsState = getPermissionsState(pkg);
- if (permissionsState == null) {
- Slog.e(TAG, "Missing permissions state for " + pkg.getPackageName());
+ final UidPermissionState uidState = getUidState(pkg, userId);
+ if (uidState == null) {
+ Slog.e(TAG, "Missing permissions state for " + pkg.getPackageName() + " and user "
+ + userId);
return;
}
@@ -3816,7 +3643,7 @@ public class PermissionManagerService extends IPermissionManager.Stub {
&& (supportsRuntimePermissions || !bp.isRuntimeOnly())
&& (grantedPermissions == null
|| ArrayUtils.contains(grantedPermissions, permission))) {
- final int flags = permissionsState.getPermissionFlags(permission, userId);
+ final int flags = uidState.getPermissionFlags(permission);
if (supportsRuntimePermissions) {
// Installer cannot change immutable permissions.
if ((flags & immutableFlags) == 0) {
@@ -3838,18 +3665,19 @@ public class PermissionManagerService extends IPermissionManager.Stub {
private void setWhitelistedRestrictedPermissionsForUsers(@NonNull AndroidPackage pkg,
@UserIdInt int[] userIds, @Nullable List<String> permissions, int callingUid,
@PermissionWhitelistFlags int whitelistFlags, PermissionCallback callback) {
- final PermissionsState permissionsState = getPermissionsState(pkg);
- if (permissionsState == null) {
- Slog.e(TAG, "Missing permissions state for " + pkg.getPackageName());
- return;
- }
-
SparseArray<ArraySet<String>> oldGrantedRestrictedPermissions = new SparseArray<>();
boolean updatePermissions = false;
final int permissionCount = pkg.getRequestedPermissions().size();
for (int i = 0; i < userIds.length; i++) {
int userId = userIds[i];
+ final UidPermissionState uidState = getUidState(pkg, userId);
+ if (uidState == null) {
+ Slog.e(TAG, "Missing permissions state for " + pkg.getPackageName() + " and user "
+ + userId);
+ continue;
+ }
+
for (int j = 0; j < permissionCount; j++) {
final String permissionName = pkg.getRequestedPermissions().get(j);
@@ -3859,14 +3687,14 @@ public class PermissionManagerService extends IPermissionManager.Stub {
continue;
}
- if (permissionsState.hasPermission(permissionName, userId)) {
+ if (uidState.hasPermission(permissionName)) {
if (oldGrantedRestrictedPermissions.get(userId) == null) {
oldGrantedRestrictedPermissions.put(userId, new ArraySet<>());
}
oldGrantedRestrictedPermissions.get(userId).add(permissionName);
}
- final int oldFlags = permissionsState.getPermissionFlags(permissionName, userId);
+ final int oldFlags = uidState.getPermissionFlags(permissionName);
int newFlags = oldFlags;
int mask = 0;
@@ -3921,8 +3749,7 @@ public class PermissionManagerService extends IPermissionManager.Stub {
// as whitelisting trumps policy i.e. policy cannot grant a non
// grantable permission.
if ((oldFlags & PackageManager.FLAG_PERMISSION_POLICY_FIXED) != 0) {
- final boolean isGranted = permissionsState.hasPermission(permissionName,
- userId);
+ final boolean isGranted = uidState.hasPermission(permissionName);
if (!isWhitelisted && isGranted) {
mask |= PackageManager.FLAG_PERMISSION_POLICY_FIXED;
newFlags &= ~PackageManager.FLAG_PERMISSION_POLICY_FIXED;
@@ -3958,12 +3785,13 @@ public class PermissionManagerService extends IPermissionManager.Stub {
for (int j = 0; j < oldGrantedCount; j++) {
final String permission = oldPermsForUser.valueAt(j);
// Sometimes we create a new permission state instance during update.
- final PermissionsState newPermissionsState = getPermissionsState(pkg);
- if (permissionsState == null) {
- Slog.e(TAG, "Missing permissions state for " + pkg.getPackageName());
+ final UidPermissionState newUidState = getUidState(pkg, userId);
+ if (newUidState == null) {
+ Slog.e(TAG, "Missing permissions state for " + pkg.getPackageName()
+ + " and user " + userId);
continue;
}
- if (!newPermissionsState.hasPermission(permission, userId)) {
+ if (!newUidState.hasPermission(permission)) {
callback.onPermissionRevoked(pkg.getUid(), userId, null);
break;
}
@@ -4012,9 +3840,10 @@ public class PermissionManagerService extends IPermissionManager.Stub {
continue;
}
- PermissionsState permissionsState = getPermissionsState(deletedPs.pkg);
- if (permissionsState == null) {
- Slog.e(TAG, "Missing permissions state for " + deletedPs.pkg.getPackageName());
+ UidPermissionState uidState = getUidState(deletedPs.pkg, userId);
+ if (uidState == null) {
+ Slog.e(TAG, "Missing permissions state for " + deletedPs.pkg.getPackageName()
+ + " and user " + userId);
continue;
}
@@ -4036,25 +3865,15 @@ public class PermissionManagerService extends IPermissionManager.Stub {
}
}
- // Try to revoke as an install permission which is for all users.
// The package is gone - no need to keep flags for applying policy.
- permissionsState.updatePermissionFlags(bp, userId,
- PackageManager.MASK_PERMISSION_FLAGS_ALL, 0);
-
- if (permissionsState.revokeInstallPermission(bp)
- == PermissionsState.PERMISSION_OPERATION_SUCCESS_GIDS_CHANGED) {
- affectedUserId = UserHandle.USER_ALL;
- }
+ uidState.updatePermissionFlags(bp, PackageManager.MASK_PERMISSION_FLAGS_ALL, 0);
// Try to revoke as a runtime permission which is per user.
- if (permissionsState.revokeRuntimePermission(bp, userId)
- == PermissionsState.PERMISSION_OPERATION_SUCCESS_GIDS_CHANGED) {
- if (affectedUserId == UserHandle.USER_NULL) {
- affectedUserId = userId;
- } else if (affectedUserId != userId) {
- // Multiple users affected.
- affectedUserId = UserHandle.USER_ALL;
- }
+ // TODO(zhanghai): This doesn't make sense. revokePermission() doesn't fail, and why are
+ // we only killing the uid when gids changed, instead of any permission change?
+ if (uidState.revokePermission(bp)
+ == UidPermissionState.PERMISSION_OPERATION_SUCCESS_GIDS_CHANGED) {
+ affectedUserId = userId;
}
}
@@ -4062,12 +3881,12 @@ public class PermissionManagerService extends IPermissionManager.Stub {
}
@GuardedBy("mLock")
- private int[] revokeUnusedSharedUserPermissionsLocked(
- List<AndroidPackage> pkgList, PermissionsState permissionsState, int[] allUserIds) {
+ private boolean revokeUnusedSharedUserPermissionsLocked(
+ List<AndroidPackage> pkgList, UidPermissionState uidState) {
// Collect all used permissions in the UID
final ArraySet<String> usedPermissions = new ArraySet<>();
if (pkgList == null || pkgList.size() == 0) {
- return EmptyArray.INT;
+ return false;
}
for (AndroidPackage pkg : pkgList) {
if (pkg.getRequestedPermissions().isEmpty()) {
@@ -4083,44 +3902,27 @@ public class PermissionManagerService extends IPermissionManager.Stub {
}
}
- // Prune install permissions
- List<PermissionState> installPermStates = permissionsState.getInstallPermissionStates();
- final int installPermCount = installPermStates.size();
- for (int i = installPermCount - 1; i >= 0; i--) {
- PermissionState permissionState = installPermStates.get(i);
+ boolean runtimePermissionChanged = false;
+
+ // Prune permissions
+ final List<com.android.server.pm.permission.PermissionState> permissionStates =
+ uidState.getPermissionStates();
+ final int permissionStatesSize = permissionStates.size();
+ for (int i = permissionStatesSize - 1; i >= 0; i--) {
+ PermissionState permissionState = permissionStates.get(i);
if (!usedPermissions.contains(permissionState.getName())) {
BasePermission bp = mSettings.getPermissionLocked(permissionState.getName());
if (bp != null) {
- permissionsState.revokeInstallPermission(bp);
- permissionsState.updatePermissionFlags(bp, UserHandle.USER_ALL,
- MASK_PERMISSION_FLAGS_ALL, 0);
- }
- }
- }
-
- int[] runtimePermissionChangedUserIds = EmptyArray.INT;
-
- // Prune runtime permissions
- for (int userId : allUserIds) {
- List<PermissionState> runtimePermStates = permissionsState
- .getRuntimePermissionStates(userId);
- final int runtimePermCount = runtimePermStates.size();
- for (int i = runtimePermCount - 1; i >= 0; i--) {
- PermissionState permissionState = runtimePermStates.get(i);
- if (!usedPermissions.contains(permissionState.getName())) {
- BasePermission bp = mSettings.getPermissionLocked(permissionState.getName());
- if (bp != null) {
- permissionsState.revokeRuntimePermission(bp, userId);
- permissionsState.updatePermissionFlags(bp, userId,
- MASK_PERMISSION_FLAGS_ALL, 0);
- runtimePermissionChangedUserIds = ArrayUtils.appendInt(
- runtimePermissionChangedUserIds, userId);
+ uidState.revokePermission(bp);
+ uidState.updatePermissionFlags(bp, MASK_PERMISSION_FLAGS_ALL, 0);
+ if (permissionState.isRuntime()) {
+ runtimePermissionChanged = true;
}
}
}
}
- return runtimePermissionChangedUserIds;
+ return runtimePermissionChanged;
}
/**
@@ -4368,15 +4170,19 @@ public class PermissionManagerService extends IPermissionManager.Stub {
}
} else {
mPackageManagerInt.forEachPackage(p -> {
- final PermissionsState permissionsState = getPermissionsState(p);
- if (permissionsState == null) {
- Slog.e(TAG, "Missing permissions state for " + p.getPackageName());
- return;
- }
- if (permissionsState.getInstallPermissionState(bp.getName()) != null) {
- permissionsState.revokeInstallPermission(bp);
- permissionsState.updatePermissionFlags(bp, UserHandle.USER_ALL,
- MASK_PERMISSION_FLAGS_ALL, 0);
+ final int[] userIds = mUserManagerInt.getUserIds();
+ for (final int userId : userIds) {
+ final UidPermissionState uidState = getUidState(p, userId);
+ if (uidState == null) {
+ Slog.e(TAG, "Missing permissions state for "
+ + p.getPackageName() + " and user " + userId);
+ return;
+ }
+ if (uidState.getPermissionState(bp.getName()) != null) {
+ uidState.revokePermission(bp);
+ uidState.updatePermissionFlags(bp, MASK_PERMISSION_FLAGS_ALL,
+ 0);
+ }
}
});
}
@@ -4784,66 +4590,160 @@ public class PermissionManagerService extends IPermissionManager.Stub {
}
@Nullable
- private PermissionsState getPermissionsState(@NonNull PackageSetting ps) {
- return getPermissionsState(ps.getAppId());
- }
-
- @Nullable
- private PermissionsState getPermissionsState(@NonNull AndroidPackage pkg) {
- return getPermissionsState(pkg.getUid());
- }
-
- @Nullable
- private PermissionsState getPermissionsState(int appId) {
- synchronized (mLock) {
- return mAppIdStates.get(appId);
- }
+ private UidPermissionState getUidState(@NonNull PackageSetting ps,
+ @UserIdInt int userId) {
+ return getUidState(ps.getAppId(), userId);
}
@Nullable
- private PermissionsState getOrCreatePermissionsState(@NonNull PackageSetting ps) {
- return getOrCreatePermissionsState(ps.getAppId());
+ private UidPermissionState getUidState(@NonNull AndroidPackage pkg,
+ @UserIdInt int userId) {
+ return getUidState(pkg.getUid(), userId);
}
@Nullable
- private PermissionsState getOrCreatePermissionsState(int appId) {
+ private UidPermissionState getUidState(int appId, @UserIdInt int userId) {
synchronized (mLock) {
- PermissionsState state = mAppIdStates.get(appId);
- if (state == null) {
- state = new PermissionsState();
- mAppIdStates.put(appId, state);
+ final UserPermissionState userState = mState.getUserState(userId);
+ if (userState == null) {
+ return null;
}
- return state;
+ return userState.getUidState(appId);
}
}
- private void removePermissionsState(int appId) {
+ private void removeAppIdState(@AppIdInt int appId) {
synchronized (mLock) {
- mAppIdStates.remove(appId);
+ final int[] userIds = mState.getUserIds();
+ for (final int userId : userIds) {
+ final UserPermissionState userState = mState.getUserState(userId);
+ userState.removeUidState(appId);
+ }
}
}
- private void readPermissionsStateFromPackageSettings() {
+ private void readStateFromPackageSettings() {
+ final int[] userIds = getAllUserIds();
mPackageManagerInt.forEachPackageSetting(ps -> {
+ final int appId = ps.getAppId();
+ final AppIdPermissionState appIdState = ps.getPermissionsState();
+
synchronized (mLock) {
- mAppIdStates.put(ps.getAppId(), new PermissionsState(ps.getPermissionsState()));
+ for (final int userId : userIds) {
+ final UserPermissionState userState = mState.getOrCreateUserState(userId);
+
+ userState.setInstallPermissionsFixed(ps.name, ps.areInstallPermissionsFixed());
+ final UidPermissionState uidState = userState.getOrCreateUidState(appId);
+ uidState.reset();
+ uidState.setMissing(appIdState.isMissing(userId));
+ readStateFromPermissionStates(uidState,
+ appIdState.getInstallPermissionStates());
+ readStateFromPermissionStates(uidState,
+ appIdState.getRuntimePermissionStates(userId));
+ }
}
});
}
- private void writePermissionsStateToPackageSettings() {
+ private void readStateFromPermissionStates(@NonNull UidPermissionState uidState,
+ @NonNull Collection<AppIdPermissionState.PermissionState> permissionStates) {
+ for (final AppIdPermissionState.PermissionState permissionState : permissionStates) {
+ uidState.putPermissionState(permissionState.getPermission(),
+ permissionState.isGranted(), permissionState.getFlags());
+ }
+ }
+
+ private void writeStateToPackageSettings() {
+ final int[] userIds = mState.getUserIds();
mPackageManagerInt.forEachPackageSetting(ps -> {
+ ps.setInstallPermissionsFixed(false);
+ final AppIdPermissionState appIdState = ps.getPermissionsState();
+ appIdState.reset();
+ final int appId = ps.getAppId();
+
synchronized (mLock) {
- final PermissionsState permissionsState = mAppIdStates.get(ps.getAppId());
- if (permissionsState == null) {
- Slog.e(TAG, "Missing permissions state for " + ps.name);
- return;
+ for (final int userId : userIds) {
+ final UserPermissionState userState = mState.getUserState(userId);
+ if (userState == null) {
+ Slog.e(TAG, "Missing user state for " + userId);
+ continue;
+ }
+
+ if (userState.areInstallPermissionsFixed(ps.name)) {
+ ps.setInstallPermissionsFixed(true);
+ }
+
+ final UidPermissionState uidState = userState.getUidState(appId);
+ if (uidState == null) {
+ Slog.e(TAG, "Missing permission state for " + ps.name + " and user "
+ + userId);
+ continue;
+ }
+
+ appIdState.setMissing(uidState.isMissing(), userId);
+ final List<PermissionState> permissionStates = uidState.getPermissionStates();
+ final int permissionStatesSize = permissionStates.size();
+ for (int i = 0; i < permissionStatesSize; i++) {
+ final PermissionState permissionState = permissionStates.get(i);
+
+ final AppIdPermissionState.PermissionState legacyPermissionState =
+ new AppIdPermissionState.PermissionState(
+ permissionState.getPermission(),
+ permissionState.isGranted(), permissionState.getFlags());
+ if (permissionState.isRuntime()) {
+ appIdState.putRuntimePermissionState(legacyPermissionState,
+ userId);
+ } else {
+ appIdState.putInstallPermissionState(legacyPermissionState);
+ }
+ }
}
- ps.getPermissionsState().copyFrom(permissionsState);
}
});
}
+ @NonNull
+ private AppIdPermissionState getAppIdPermissionState(@AppIdInt int appId) {
+ final AppIdPermissionState appIdState = new AppIdPermissionState();
+ final int[] userIds = mState.getUserIds();
+ for (final int userId : userIds) {
+ final UidPermissionState uidState = getUidState(appId, userId);
+ if (uidState == null) {
+ Slog.e(TAG, "Missing permissions state for app ID " + appId + " and user ID "
+ + userId);
+ continue;
+ }
+
+ final List<PermissionState> permissionStates = uidState.getPermissionStates();
+ final int permissionStatesSize = permissionStates.size();
+ for (int i = 0; i < permissionStatesSize; i++) {
+ final PermissionState permissionState = permissionStates.get(i);
+
+ final AppIdPermissionState.PermissionState legacyPermissionState =
+ new AppIdPermissionState.PermissionState(permissionState.getPermission(),
+ permissionState.isGranted(), permissionState.getFlags());
+ if (permissionState.isRuntime()) {
+ appIdState.putRuntimePermissionState(legacyPermissionState, userId);
+ } else if (userId == UserHandle.USER_SYSTEM) {
+ appIdState.putInstallPermissionState(legacyPermissionState);
+ }
+ }
+ }
+ return appIdState;
+ }
+
+ @NonNull
+ private int[] getGidsForUid(int uid) {
+ final int appId = UserHandle.getAppId(uid);
+ final int userId = UserHandle.getUserId(uid);
+ final UidPermissionState uidState = getUidState(appId, userId);
+ if (uidState == null) {
+ Slog.e(TAG, "Missing permissions state for app ID " + appId + " and user ID " + userId);
+ return EMPTY_INT_ARRAY;
+ }
+ return uidState.computeGids(userId);
+ }
+
private class PermissionManagerServiceInternalImpl extends PermissionManagerServiceInternal {
@Override
public void systemReady() {
@@ -4876,20 +4776,20 @@ public class PermissionManagerService extends IPermissionManager.Stub {
PermissionManagerService.this.removeAllPermissions(pkg, chatty);
}
@Override
- public void readPermissionsStateFromPackageSettingsTEMP() {
- PermissionManagerService.this.readPermissionsStateFromPackageSettings();
+ public void readStateFromPackageSettingsTEMP() {
+ PermissionManagerService.this.readStateFromPackageSettings();
}
@Override
- public void writePermissionsStateToPackageSettingsTEMP() {
- PermissionManagerService.this.writePermissionsStateToPackageSettings();
+ public void writeStateToPackageSettingsTEMP() {
+ PermissionManagerService.this.writeStateToPackageSettings();
}
@Override
public void onUserRemoved(@UserIdInt int userId) {
PermissionManagerService.this.onUserRemoved(userId);
}
@Override
- public void removePermissionsStateTEMP(int appId) {
- PermissionManagerService.this.removePermissionsState(appId);
+ public void removeAppIdStateTEMP(@AppIdInt int appId) {
+ PermissionManagerService.this.removeAppIdState(appId);
}
@Override
@UserIdInt
@@ -4909,11 +4809,6 @@ public class PermissionManagerService extends IPermissionManager.Stub {
public int[] getPermissionGids(@NonNull String permissionName, @UserIdInt int userId) {
return PermissionManagerService.this.getPermissionGids(permissionName, userId);
}
- @Nullable
- @Override
- public int[] getPackageGids(@NonNull String packageName, @UserIdInt int userId) {
- return PermissionManagerService.this.getPackageGids(packageName, userId);
- }
@Override
public void grantRequestedRuntimePermissions(AndroidPackage pkg, int[] userIds,
String[] grantedPermissions, int callingUid) {
@@ -5222,10 +5117,10 @@ public class PermissionManagerService extends IPermissionManager.Stub {
@Override
public void onNewUserCreated(int userId) {
- mDefaultPermissionGrantPolicy.grantDefaultPermissions(userId);
// NOTE: This adds UPDATE_PERMISSIONS_REPLACE_PKG
PermissionManagerService.this.updateAllPermissions(StorageManager.UUID_PRIVATE_INTERNAL,
true, mDefaultPermissionCallback);
+ mDefaultPermissionGrantPolicy.grantDefaultPermissions(userId);
}
@Override
@@ -5241,6 +5136,16 @@ public class PermissionManagerService extends IPermissionManager.Stub {
}
}
}
+
+ @NonNull
+ public AppIdPermissionState getAppIdPermissionState(@AppIdInt int appId) {
+ return PermissionManagerService.this.getAppIdPermissionState(appId);
+ }
+
+ @NonNull
+ public int[] getGidsForUid(int uid) {
+ return PermissionManagerService.this.getGidsForUid(uid);
+ }
}
private static final class OnPermissionChangeListeners extends Handler {
diff --git a/services/core/java/com/android/server/pm/permission/PermissionManagerServiceInternal.java b/services/core/java/com/android/server/pm/permission/PermissionManagerServiceInternal.java
index ca207d8f0d41..5ea3458fcbfa 100644
--- a/services/core/java/com/android/server/pm/permission/PermissionManagerServiceInternal.java
+++ b/services/core/java/com/android/server/pm/permission/PermissionManagerServiceInternal.java
@@ -37,8 +37,8 @@ import java.util.function.Consumer;
*
* TODO: Should be merged into PermissionManagerInternal, but currently uses internal classes.
*/
-public abstract class PermissionManagerServiceInternal extends PermissionManagerInternal {
-
+public abstract class PermissionManagerServiceInternal extends PermissionManagerInternal
+ implements LegacyPermissionDataProvider {
/**
* Provider for package names.
*/
@@ -266,21 +266,21 @@ public abstract class PermissionManagerServiceInternal extends PermissionManager
public abstract void removeAllPermissions(@NonNull AndroidPackage pkg, boolean chatty);
/**
- * Read {@code PermissionsState} from package settings.
+ * Read permission state from package settings.
*
* TODO(zhanghai): This is a temporary method because we should not expose
* {@code PackageSetting} which is a implementation detail that permission should not know.
* Instead, it should retrieve the legacy state via a defined API.
*/
- public abstract void readPermissionsStateFromPackageSettingsTEMP();
+ public abstract void readStateFromPackageSettingsTEMP();
/**
- * Write {@code PermissionsState} from to settings.
+ * Write permission state to package settings.
*
* TODO(zhanghai): This is a temporary method and should be removed once we migrated persistence
* for permission.
*/
- public abstract void writePermissionsStateToPackageSettingsTEMP();
+ public abstract void writeStateToPackageSettingsTEMP();
/**
* Notify that a user has been removed and its permission state should be removed as well.
@@ -288,13 +288,13 @@ public abstract class PermissionManagerServiceInternal extends PermissionManager
public abstract void onUserRemoved(@UserIdInt int userId);
/**
- * Remove the {@code PermissionsState} associated with an app ID, called the same time as the
+ * Remove the permission state associated with an app ID, called the same time as the
* removal of a {@code PackageSetitng}.
*
* TODO(zhanghai): This is a temporary method before we figure out a way to get notified of app
* ID removal via API.
*/
- public abstract void removePermissionsStateTEMP(int appId);
+ public abstract void removeAppIdStateTEMP(@AppIdInt int appId);
/**
* Update the shared user setting when a package with a shared user id is removed. The gids
@@ -324,12 +324,6 @@ public abstract class PermissionManagerServiceInternal extends PermissionManager
@Nullable
public abstract int[] getPermissionGids(@NonNull String permissionName, @UserIdInt int userId);
- /**
- * Get the GIDs computed from the permission state of a package.
- */
- @Nullable
- public abstract int[] getPackageGids(@NonNull String packageName, @UserIdInt int userId);
-
/** Retrieve the packages that have requested the given app op permission */
public abstract @Nullable String[] getAppOpPermissionPackages(
@NonNull String permName, int callingUid);
diff --git a/services/core/java/com/android/server/pm/permission/PermissionState.java b/services/core/java/com/android/server/pm/permission/PermissionState.java
new file mode 100644
index 000000000000..38264c83a15c
--- /dev/null
+++ b/services/core/java/com/android/server/pm/permission/PermissionState.java
@@ -0,0 +1,128 @@
+/*
+ * 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.pm.permission;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.annotation.UserIdInt;
+
+import com.android.internal.annotations.GuardedBy;
+
+/**
+ * State for a single permission.
+ */
+public final class PermissionState {
+
+ @NonNull
+ private final BasePermission mPermission;
+
+ private final Object mLock = new Object();
+
+ @GuardedBy("mLock")
+ private boolean mRuntime;
+
+ @GuardedBy("mLock")
+ private boolean mGranted;
+
+ @GuardedBy("mLock")
+ private int mFlags;
+
+ public PermissionState(@NonNull BasePermission permission) {
+ mPermission = permission;
+ }
+
+ public PermissionState(@NonNull PermissionState other) {
+ this(other.mPermission);
+
+ mGranted = other.mGranted;
+ mFlags = other.mFlags;
+ }
+
+ @NonNull
+ public BasePermission getPermission() {
+ return mPermission;
+ }
+
+ @NonNull
+ public String getName() {
+ return mPermission.getName();
+ }
+
+ @Nullable
+ public int[] computeGids(@UserIdInt int userId) {
+ return mPermission.computeGids(userId);
+ }
+
+ public boolean isRuntime() {
+ synchronized (mLock) {
+ return mPermission.isRuntime();
+ }
+ }
+
+ public boolean isGranted() {
+ synchronized (mLock) {
+ return mGranted;
+ }
+ }
+
+ public boolean grant() {
+ synchronized (mLock) {
+ if (mGranted) {
+ return false;
+ }
+ mGranted = true;
+ UidPermissionState.invalidateCache();
+ return true;
+ }
+ }
+
+ public boolean revoke() {
+ synchronized (mLock) {
+ if (!mGranted) {
+ return false;
+ }
+ mGranted = false;
+ UidPermissionState.invalidateCache();
+ return true;
+ }
+ }
+
+ public int getFlags() {
+ synchronized (mLock) {
+ return mFlags;
+ }
+ }
+
+ public boolean updateFlags(int flagMask, int flagValues) {
+ synchronized (mLock) {
+ final int newFlags = flagValues & flagMask;
+
+ // Okay to do before the modification because we hold the lock.
+ UidPermissionState.invalidateCache();
+
+ final int oldFlags = mFlags;
+ mFlags = (mFlags & ~flagMask) | newFlags;
+ return mFlags != oldFlags;
+ }
+ }
+
+ public boolean isDefault() {
+ synchronized (mLock) {
+ return !mGranted && mFlags == 0;
+ }
+ }
+}
diff --git a/services/core/java/com/android/server/pm/permission/PermissionsState.java b/services/core/java/com/android/server/pm/permission/PermissionsState.java
deleted file mode 100644
index bad59cb1b567..000000000000
--- a/services/core/java/com/android/server/pm/permission/PermissionsState.java
+++ /dev/null
@@ -1,962 +0,0 @@
-/*
- * Copyright (C) 2015 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.server.pm.permission;
-
-import android.annotation.Nullable;
-import android.annotation.UserIdInt;
-import android.content.pm.PackageManager;
-import android.os.UserHandle;
-import android.util.ArrayMap;
-import android.util.ArraySet;
-import android.util.SparseArray;
-import android.util.SparseBooleanArray;
-
-import com.android.internal.annotations.GuardedBy;
-import com.android.internal.util.ArrayUtils;
-
-import java.util.ArrayList;
-import java.util.Arrays;
-import java.util.Collections;
-import java.util.List;
-import java.util.Objects;
-import java.util.Set;
-
-/**
- * This class encapsulates the permissions for a package or a shared user.
- * <p>
- * There are two types of permissions: install (granted at installation)
- * and runtime (granted at runtime). Install permissions are granted to
- * all device users while runtime permissions are granted explicitly to
- * specific users.
- * </p>
- * <p>
- * The permissions are kept on a per device user basis. For example, an
- * application may have some runtime permissions granted under the device
- * owner but not granted under the secondary user.
- * <p>
- * This class is also responsible for keeping track of the Linux gids per
- * user for a package or a shared user. The gids are computed as a set of
- * the gids for all granted permissions' gids on a per user basis.
- * </p>
- */
-public final class PermissionsState {
-
- /** The permission operation failed. */
- public static final int PERMISSION_OPERATION_FAILURE = -1;
-
- /** The permission operation succeeded and no gids changed. */
- public static final int PERMISSION_OPERATION_SUCCESS = 0;
-
- /** The permission operation succeeded and gids changed. */
- public static final int PERMISSION_OPERATION_SUCCESS_GIDS_CHANGED = 1;
-
- private static final int[] NO_GIDS = {};
-
- private final Object mLock = new Object();
-
- @GuardedBy("mLock")
- private ArrayMap<String, PermissionData> mPermissions;
-
- private int[] mGlobalGids = NO_GIDS;
-
- @Nullable
- private SparseBooleanArray mMissing;
-
- private SparseBooleanArray mPermissionReviewRequired;
-
- public PermissionsState() {
- /* do nothing */
- }
-
- public PermissionsState(PermissionsState prototype) {
- copyFrom(prototype);
- }
-
- /**
- * Sets the global gids, applicable to all users.
- *
- * @param globalGids The global gids.
- */
- public void setGlobalGids(int[] globalGids) {
- if (!ArrayUtils.isEmpty(globalGids)) {
- mGlobalGids = Arrays.copyOf(globalGids, globalGids.length);
- }
- }
-
- private static void invalidateCache() {
- PackageManager.invalidatePackageInfoCache();
- }
-
- /**
- * Initialized this instance from another one.
- *
- * @param other The other instance.
- */
- public void copyFrom(PermissionsState other) {
- if (other == this) {
- return;
- }
-
- synchronized (mLock) {
- if (mPermissions != null) {
- if (other.mPermissions == null) {
- mPermissions = null;
- } else {
- mPermissions.clear();
- }
- }
- if (other.mPermissions != null) {
- if (mPermissions == null) {
- mPermissions = new ArrayMap<>();
- }
- final int permissionCount = other.mPermissions.size();
- for (int i = 0; i < permissionCount; i++) {
- String name = other.mPermissions.keyAt(i);
- PermissionData permissionData = other.mPermissions.valueAt(i);
- mPermissions.put(name, new PermissionData(permissionData));
- }
- }
- }
-
- mGlobalGids = NO_GIDS;
- if (other.mGlobalGids != NO_GIDS) {
- mGlobalGids = Arrays.copyOf(other.mGlobalGids,
- other.mGlobalGids.length);
- }
-
- if (mMissing != null) {
- if (other.mMissing == null) {
- mMissing = null;
- } else {
- mMissing.clear();
- }
- }
- if (other.mMissing != null) {
- if (mMissing == null) {
- mMissing = new SparseBooleanArray();
- }
- final int missingSize = other.mMissing.size();
- for (int i = 0; i < missingSize; i++) {
- mMissing.put(other.mMissing.keyAt(i), other.mMissing.valueAt(i));
- }
- }
-
- if (mPermissionReviewRequired != null) {
- if (other.mPermissionReviewRequired == null) {
- mPermissionReviewRequired = null;
- } else {
- mPermissionReviewRequired.clear();
- }
- }
- if (other.mPermissionReviewRequired != null) {
- if (mPermissionReviewRequired == null) {
- mPermissionReviewRequired = new SparseBooleanArray();
- }
- final int userCount = other.mPermissionReviewRequired.size();
- for (int i = 0; i < userCount; i++) {
- final boolean reviewRequired = other.mPermissionReviewRequired.valueAt(i);
- mPermissionReviewRequired.put(other.mPermissionReviewRequired.keyAt(i),
- reviewRequired);
- }
- }
- }
-
- @Override
- public boolean equals(Object obj) {
- if (this == obj) {
- return true;
- }
- if (obj == null) {
- return false;
- }
- if (getClass() != obj.getClass()) {
- return false;
- }
- final PermissionsState other = (PermissionsState) obj;
-
- synchronized (mLock) {
- if (mPermissions == null) {
- if (other.mPermissions != null) {
- return false;
- }
- } else if (!mPermissions.equals(other.mPermissions)) {
- return false;
- }
- }
-
- if (!Objects.equals(mMissing, other.mMissing)) {
- return false;
- }
-
- if (mPermissionReviewRequired == null) {
- if (other.mPermissionReviewRequired != null) {
- return false;
- }
- } else if (!mPermissionReviewRequired.equals(other.mPermissionReviewRequired)) {
- return false;
- }
- return Arrays.equals(mGlobalGids, other.mGlobalGids);
- }
-
- /**
- * Check whether the permissions state is missing for a user. This can happen if permission
- * state is rolled back and we'll need to generate a reasonable default state to keep the app
- * usable.
- */
- public boolean isMissing(@UserIdInt int userId) {
- return mMissing != null && mMissing.get(userId);
- }
-
- /**
- * Set whether the permissions state is missing for a user. This can happen if permission state
- * is rolled back and we'll need to generate a reasonable default state to keep the app usable.
- */
- public void setMissing(boolean missing, @UserIdInt int userId) {
- if (missing) {
- if (mMissing == null) {
- mMissing = new SparseBooleanArray();
- }
- mMissing.put(userId, true);
- } else {
- if (mMissing != null) {
- mMissing.delete(userId);
- if (mMissing.size() == 0) {
- mMissing = null;
- }
- }
- }
- }
-
- public boolean isPermissionReviewRequired(int userId) {
- return mPermissionReviewRequired != null && mPermissionReviewRequired.get(userId);
- }
-
- /**
- * Grant an install permission.
- *
- * @param permission The permission to grant.
- * @return The operation result which is either {@link #PERMISSION_OPERATION_SUCCESS},
- * or {@link #PERMISSION_OPERATION_SUCCESS_GIDS_CHANGED}, or {@link
- * #PERMISSION_OPERATION_FAILURE}.
- */
- public int grantInstallPermission(BasePermission permission) {
- return grantPermission(permission, UserHandle.USER_ALL);
- }
-
- /**
- * Revoke an install permission.
- *
- * @param permission The permission to revoke.
- * @return The operation result which is either {@link #PERMISSION_OPERATION_SUCCESS},
- * or {@link #PERMISSION_OPERATION_SUCCESS_GIDS_CHANGED}, or {@link
- * #PERMISSION_OPERATION_FAILURE}.
- */
- public int revokeInstallPermission(BasePermission permission) {
- return revokePermission(permission, UserHandle.USER_ALL);
- }
-
- /**
- * Grant a runtime permission for a given device user.
- *
- * @param permission The permission to grant.
- * @param userId The device user id.
- * @return The operation result which is either {@link #PERMISSION_OPERATION_SUCCESS},
- * or {@link #PERMISSION_OPERATION_SUCCESS_GIDS_CHANGED}, or {@link
- * #PERMISSION_OPERATION_FAILURE}.
- */
- public int grantRuntimePermission(BasePermission permission, int userId) {
- enforceValidUserId(userId);
- if (userId == UserHandle.USER_ALL) {
- return PERMISSION_OPERATION_FAILURE;
- }
- return grantPermission(permission, userId);
- }
-
- /**
- * Revoke a runtime permission for a given device user.
- *
- * @param permission The permission to revoke.
- * @param userId The device user id.
- * @return The operation result which is either {@link #PERMISSION_OPERATION_SUCCESS},
- * or {@link #PERMISSION_OPERATION_SUCCESS_GIDS_CHANGED}, or {@link
- * #PERMISSION_OPERATION_FAILURE}.
- */
- public int revokeRuntimePermission(BasePermission permission, int userId) {
- enforceValidUserId(userId);
- if (userId == UserHandle.USER_ALL) {
- return PERMISSION_OPERATION_FAILURE;
- }
- return revokePermission(permission, userId);
- }
-
- /**
- * Gets whether this state has a given runtime permission for a
- * given device user id.
- *
- * @param name The permission name.
- * @param userId The device user id.
- * @return Whether this state has the permission.
- */
- public boolean hasRuntimePermission(String name, int userId) {
- enforceValidUserId(userId);
- return !hasInstallPermission(name) && hasPermission(name, userId);
- }
-
- /**
- * Gets whether this state has a given install permission.
- *
- * @param name The permission name.
- * @return Whether this state has the permission.
- */
- public boolean hasInstallPermission(String name) {
- return hasPermission(name, UserHandle.USER_ALL);
- }
-
- /**
- * Gets whether the state has a given permission for the specified
- * user, regardless if this is an install or a runtime permission.
- *
- * @param name The permission name.
- * @param userId The device user id.
- * @return Whether the user has the permission.
- */
- public boolean hasPermission(String name, int userId) {
- enforceValidUserId(userId);
-
- synchronized (mLock) {
- if (mPermissions == null) {
- return false;
- }
- PermissionData permissionData = mPermissions.get(name);
-
- return permissionData != null && permissionData.isGranted(userId);
- }
-
- }
-
- /**
- * Returns whether the state has any known request for the given permission name,
- * whether or not it has been granted.
- */
- public boolean hasRequestedPermission(ArraySet<String> names) {
- synchronized (mLock) {
- if (mPermissions == null) {
- return false;
- }
- for (int i=names.size()-1; i>=0; i--) {
- if (mPermissions.get(names.valueAt(i)) != null) {
- return true;
- }
- }
- }
-
- return false;
- }
-
- /**
- * Returns whether the state has any known request for the given permission name,
- * whether or not it has been granted.
- */
- public boolean hasRequestedPermission(String name) {
- return mPermissions != null && (mPermissions.get(name) != null);
- }
- /**
- * Gets all permissions for a given device user id regardless if they
- * are install time or runtime permissions.
- *
- * @param userId The device user id.
- * @return The permissions or an empty set.
- */
- public Set<String> getPermissions(int userId) {
- enforceValidUserId(userId);
-
- synchronized (mLock) {
- if (mPermissions == null) {
- return Collections.emptySet();
- }
-
- Set<String> permissions = new ArraySet<>(mPermissions.size());
-
- final int permissionCount = mPermissions.size();
- for (int i = 0; i < permissionCount; i++) {
- String permission = mPermissions.keyAt(i);
-
- if (hasInstallPermission(permission)) {
- permissions.add(permission);
- continue;
- }
-
- if (userId != UserHandle.USER_ALL) {
- if (hasRuntimePermission(permission, userId)) {
- permissions.add(permission);
- }
- }
- }
-
- return permissions;
- }
- }
-
- /**
- * Gets the state for an install permission or null if no such.
- *
- * @param name The permission name.
- * @return The permission state.
- */
- public PermissionState getInstallPermissionState(String name) {
- return getPermissionState(name, UserHandle.USER_ALL);
- }
-
- /**
- * Gets the state for a runtime permission or null if no such.
- *
- * @param name The permission name.
- * @param userId The device user id.
- * @return The permission state.
- */
- public PermissionState getRuntimePermissionState(String name, int userId) {
- enforceValidUserId(userId);
- return getPermissionState(name, userId);
- }
-
- /**
- * Gets all install permission states.
- *
- * @return The permission states or an empty set.
- */
- public List<PermissionState> getInstallPermissionStates() {
- return getPermissionStatesInternal(UserHandle.USER_ALL);
- }
-
- /**
- * Gets all runtime permission states.
- *
- * @return The permission states or an empty set.
- */
- public List<PermissionState> getRuntimePermissionStates(int userId) {
- enforceValidUserId(userId);
- return getPermissionStatesInternal(userId);
- }
-
- /**
- * Gets the flags for a permission regardless if it is install or
- * runtime permission.
- *
- * @param name The permission name.
- * @return The permission state or null if no such.
- */
- public int getPermissionFlags(String name, int userId) {
- PermissionState installPermState = getInstallPermissionState(name);
- if (installPermState != null) {
- return installPermState.getFlags();
- }
- PermissionState runtimePermState = getRuntimePermissionState(name, userId);
- if (runtimePermState != null) {
- return runtimePermState.getFlags();
- }
- return 0;
- }
-
- /**
- * Update the flags associated with a given permission.
- * @param permission The permission whose flags to update.
- * @param userId The user for which to update.
- * @param flagMask Mask for which flags to change.
- * @param flagValues New values for the mask flags.
- * @return Whether the permission flags changed.
- */
- public boolean updatePermissionFlags(BasePermission permission, int userId,
- int flagMask, int flagValues) {
- enforceValidUserId(userId);
-
- final boolean mayChangeFlags = flagValues != 0 || flagMask != 0;
-
- synchronized (mLock) {
- if (mPermissions == null) {
- if (!mayChangeFlags) {
- return false;
- }
- ensurePermissionData(permission);
- }
- }
-
- PermissionData permissionData = null;
- synchronized (mLock) {
- permissionData = mPermissions.get(permission.getName());
- }
-
- if (permissionData == null) {
- if (!mayChangeFlags) {
- return false;
- }
- permissionData = ensurePermissionData(permission);
- }
-
- final int oldFlags = permissionData.getFlags(userId);
-
- final boolean updated = permissionData.updateFlags(userId, flagMask, flagValues);
- if (updated) {
- final int newFlags = permissionData.getFlags(userId);
- if ((oldFlags & PackageManager.FLAG_PERMISSION_REVIEW_REQUIRED) == 0
- && (newFlags & PackageManager.FLAG_PERMISSION_REVIEW_REQUIRED) != 0) {
- if (mPermissionReviewRequired == null) {
- mPermissionReviewRequired = new SparseBooleanArray();
- }
- mPermissionReviewRequired.put(userId, true);
- } else if ((oldFlags & PackageManager.FLAG_PERMISSION_REVIEW_REQUIRED) != 0
- && (newFlags & PackageManager.FLAG_PERMISSION_REVIEW_REQUIRED) == 0) {
- if (mPermissionReviewRequired != null && !hasPermissionRequiringReview(userId)) {
- mPermissionReviewRequired.delete(userId);
- if (mPermissionReviewRequired.size() <= 0) {
- mPermissionReviewRequired = null;
- }
- }
- }
- }
- return updated;
- }
-
- private boolean hasPermissionRequiringReview(int userId) {
- synchronized (mLock) {
- final int permissionCount = mPermissions.size();
- for (int i = 0; i < permissionCount; i++) {
- final PermissionData permission = mPermissions.valueAt(i);
- if ((permission.getFlags(userId)
- & PackageManager.FLAG_PERMISSION_REVIEW_REQUIRED) != 0) {
- return true;
- }
- }
- }
-
- return false;
- }
-
- public boolean updatePermissionFlagsForAllPermissions(
- int userId, int flagMask, int flagValues) {
- enforceValidUserId(userId);
-
- synchronized (mLock) {
- if (mPermissions == null) {
- return false;
- }
- boolean changed = false;
- final int permissionCount = mPermissions.size();
- for (int i = 0; i < permissionCount; i++) {
- PermissionData permissionData = mPermissions.valueAt(i);
- changed |= permissionData.updateFlags(userId, flagMask, flagValues);
- }
-
- return changed;
- }
- }
-
- /**
- * Compute the Linux gids for a given device user from the permissions
- * granted to this user. Note that these are computed to avoid additional
- * state as they are rarely accessed.
- *
- * @param userId The device user id.
- * @return The gids for the device user.
- */
- public int[] computeGids(int userId) {
- enforceValidUserId(userId);
-
- int[] gids = mGlobalGids;
-
- synchronized (mLock) {
- if (mPermissions != null) {
- final int permissionCount = mPermissions.size();
- for (int i = 0; i < permissionCount; i++) {
- String permission = mPermissions.keyAt(i);
- if (!hasPermission(permission, userId)) {
- continue;
- }
- PermissionData permissionData = mPermissions.valueAt(i);
- final int[] permGids = permissionData.computeGids(userId);
- if (permGids != NO_GIDS) {
- gids = appendInts(gids, permGids);
- }
- }
- }
- }
-
- return gids;
- }
-
- /**
- * Compute the Linux gids for all device users from the permissions
- * granted to these users.
- *
- * @return The gids for all device users.
- */
- public int[] computeGids(int[] userIds) {
- int[] gids = mGlobalGids;
-
- for (int userId : userIds) {
- final int[] userGids = computeGids(userId);
- gids = appendInts(gids, userGids);
- }
-
- return gids;
- }
-
- /**
- * Resets the internal state of this object.
- */
- public void reset() {
- mGlobalGids = NO_GIDS;
-
- synchronized (mLock) {
- mPermissions = null;
- invalidateCache();
- }
-
- mMissing = null;
- mPermissionReviewRequired = null;
- }
-
- private PermissionState getPermissionState(String name, int userId) {
- synchronized (mLock) {
- if (mPermissions == null) {
- return null;
- }
- PermissionData permissionData = mPermissions.get(name);
- if (permissionData == null) {
- return null;
- }
-
- return permissionData.getPermissionState(userId);
- }
- }
-
- private List<PermissionState> getPermissionStatesInternal(int userId) {
- enforceValidUserId(userId);
-
- synchronized (mLock) {
- if (mPermissions == null) {
- return Collections.emptyList();
- }
-
- List<PermissionState> permissionStates = new ArrayList<>();
-
- final int permissionCount = mPermissions.size();
- for (int i = 0; i < permissionCount; i++) {
- PermissionData permissionData = mPermissions.valueAt(i);
-
- PermissionState permissionState = permissionData.getPermissionState(userId);
- if (permissionState != null) {
- permissionStates.add(permissionState);
- }
- }
-
- return permissionStates;
- }
- }
-
- private int grantPermission(BasePermission permission, int userId) {
- if (hasPermission(permission.getName(), userId)) {
- return PERMISSION_OPERATION_SUCCESS;
- }
-
- final boolean hasGids = !ArrayUtils.isEmpty(permission.computeGids(userId));
- final int[] oldGids = hasGids ? computeGids(userId) : NO_GIDS;
-
- PermissionData permissionData = ensurePermissionData(permission);
-
- if (!permissionData.grant(userId)) {
- return PERMISSION_OPERATION_FAILURE;
- }
-
- if (hasGids) {
- final int[] newGids = computeGids(userId);
- if (oldGids.length != newGids.length) {
- return PERMISSION_OPERATION_SUCCESS_GIDS_CHANGED;
- }
- }
-
- return PERMISSION_OPERATION_SUCCESS;
- }
-
- private int revokePermission(BasePermission permission, int userId) {
- final String permName = permission.getName();
- if (!hasPermission(permName, userId)) {
- return PERMISSION_OPERATION_SUCCESS;
- }
-
- final boolean hasGids = !ArrayUtils.isEmpty(permission.computeGids(userId));
- final int[] oldGids = hasGids ? computeGids(userId) : NO_GIDS;
-
- PermissionData permissionData = null;
- synchronized (mLock) {
- permissionData = mPermissions.get(permName);
- }
-
- if (!permissionData.revoke(userId)) {
- return PERMISSION_OPERATION_FAILURE;
- }
-
- if (permissionData.isDefault()) {
- ensureNoPermissionData(permName);
- }
-
- if (hasGids) {
- final int[] newGids = computeGids(userId);
- if (oldGids.length != newGids.length) {
- return PERMISSION_OPERATION_SUCCESS_GIDS_CHANGED;
- }
- }
-
- return PERMISSION_OPERATION_SUCCESS;
- }
-
- // TODO: fix this to use arraycopy and append all ints in one go
- private static int[] appendInts(int[] current, int[] added) {
- if (current != null && added != null) {
- for (int guid : added) {
- current = ArrayUtils.appendInt(current, guid);
- }
- }
- return current;
- }
-
- private static void enforceValidUserId(int userId) {
- if (userId != UserHandle.USER_ALL && userId < 0) {
- throw new IllegalArgumentException("Invalid userId:" + userId);
- }
- }
-
- private PermissionData ensurePermissionData(BasePermission permission) {
- final String permName = permission.getName();
-
- synchronized (mLock) {
- if (mPermissions == null) {
- mPermissions = new ArrayMap<>();
- }
- PermissionData permissionData = mPermissions.get(permName);
- if (permissionData == null) {
- permissionData = new PermissionData(permission);
- mPermissions.put(permName, permissionData);
- }
- return permissionData;
- }
-
- }
-
- private void ensureNoPermissionData(String name) {
- synchronized (mLock) {
- if (mPermissions == null) {
- return;
- }
- mPermissions.remove(name);
- if (mPermissions.isEmpty()) {
- mPermissions = null;
- }
- }
-
- }
-
- private static final class PermissionData {
-
- private final Object mLock = new Object();
-
- private final BasePermission mPerm;
- @GuardedBy("mLock")
- private SparseArray<PermissionState> mUserStates = new SparseArray<>();
-
- public PermissionData(BasePermission perm) {
- mPerm = perm;
- }
-
- public PermissionData(PermissionData other) {
- this(other.mPerm);
-
- synchronized (mLock) {
- final int otherStateCount = other.mUserStates.size();
- for (int i = 0; i < otherStateCount; i++) {
- final int otherUserId = other.mUserStates.keyAt(i);
- PermissionState otherState = other.mUserStates.valueAt(i);
- mUserStates.put(otherUserId, new PermissionState(otherState));
- }
- }
- }
-
- public int[] computeGids(int userId) {
- return mPerm.computeGids(userId);
- }
-
- public boolean isGranted(int userId) {
- synchronized (mLock) {
- if (isInstallPermission()) {
- userId = UserHandle.USER_ALL;
- }
-
- PermissionState userState = mUserStates.get(userId);
- if (userState == null) {
- return false;
- }
-
- return userState.mGranted;
- }
- }
-
- public boolean grant(int userId) {
- synchronized (mLock) {
- if (!isCompatibleUserId(userId)) {
- return false;
- }
-
- if (isGranted(userId)) {
- return false;
- }
-
- PermissionState userState = mUserStates.get(userId);
- if (userState == null) {
- userState = new PermissionState(mPerm.getName());
- mUserStates.put(userId, userState);
- }
-
- userState.mGranted = true;
-
- invalidateCache();
- return true;
- }
- }
-
- public boolean revoke(int userId) {
- synchronized (mLock) {
- if (!isCompatibleUserId(userId)) {
- return false;
- }
-
- if (!isGranted(userId)) {
- return false;
- }
-
- PermissionState userState = mUserStates.get(userId);
- userState.mGranted = false;
-
- if (userState.isDefault()) {
- mUserStates.remove(userId);
- }
-
- invalidateCache();
- return true;
- }
- }
-
- public PermissionState getPermissionState(int userId) {
- synchronized (mLock) {
- return mUserStates.get(userId);
- }
- }
-
- public int getFlags(int userId) {
- synchronized (mLock) {
- PermissionState userState = mUserStates.get(userId);
- if (userState != null) {
- return userState.mFlags;
- }
- return 0;
- }
- }
-
- public boolean isDefault() {
- synchronized (mLock) {
- return mUserStates.size() <= 0;
- }
- }
-
- public static boolean isInstallPermissionKey(int userId) {
- return userId == UserHandle.USER_ALL;
- }
-
- public boolean updateFlags(int userId, int flagMask, int flagValues) {
- synchronized (mLock) {
- if (isInstallPermission()) {
- userId = UserHandle.USER_ALL;
- }
-
- if (!isCompatibleUserId(userId)) {
- return false;
- }
-
- final int newFlags = flagValues & flagMask;
-
- // Okay to do before the modification because we hold the lock.
- invalidateCache();
-
- PermissionState userState = mUserStates.get(userId);
- if (userState != null) {
- final int oldFlags = userState.mFlags;
- userState.mFlags = (userState.mFlags & ~flagMask) | newFlags;
- if (userState.isDefault()) {
- mUserStates.remove(userId);
- }
- return userState.mFlags != oldFlags;
- } else if (newFlags != 0) {
- userState = new PermissionState(mPerm.getName());
- userState.mFlags = newFlags;
- mUserStates.put(userId, userState);
- return true;
- }
-
- return false;
- }
- }
-
- private boolean isCompatibleUserId(int userId) {
- return isDefault() || !(isInstallPermission() ^ isInstallPermissionKey(userId));
- }
-
- private boolean isInstallPermission() {
- return mUserStates.size() == 1
- && mUserStates.get(UserHandle.USER_ALL) != null;
- }
- }
-
- public static final class PermissionState {
- private final String mName;
- private boolean mGranted;
- private int mFlags;
-
- public PermissionState(String name) {
- mName = name;
- }
-
- public PermissionState(PermissionState other) {
- mName = other.mName;
- mGranted = other.mGranted;
- mFlags = other.mFlags;
- }
-
- public boolean isDefault() {
- return !mGranted && mFlags == 0;
- }
-
- public String getName() {
- return mName;
- }
-
- public boolean isGranted() {
- return mGranted;
- }
-
- public int getFlags() {
- return mFlags;
- }
- }
-}
diff --git a/services/core/java/com/android/server/pm/permission/UidPermissionState.java b/services/core/java/com/android/server/pm/permission/UidPermissionState.java
new file mode 100644
index 000000000000..b45176b720b5
--- /dev/null
+++ b/services/core/java/com/android/server/pm/permission/UidPermissionState.java
@@ -0,0 +1,551 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.pm.permission;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.annotation.UserIdInt;
+import android.content.pm.PackageManager;
+import android.util.ArrayMap;
+import android.util.ArraySet;
+
+import com.android.internal.annotations.GuardedBy;
+import com.android.internal.util.ArrayUtils;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.List;
+import java.util.Set;
+
+/**
+ * Permission state for a UID.
+ * <p>
+ * This class is also responsible for keeping track of the Linux GIDs per
+ * user for a package or a shared user. The GIDs are computed as a set of
+ * the GIDs for all granted permissions' GIDs on a per user basis.
+ */
+public final class UidPermissionState {
+ /** The permission operation failed. */
+ public static final int PERMISSION_OPERATION_FAILURE = -1;
+
+ /** The permission operation succeeded and no gids changed. */
+ public static final int PERMISSION_OPERATION_SUCCESS = 0;
+
+ /** The permission operation succeeded and gids changed. */
+ public static final int PERMISSION_OPERATION_SUCCESS_GIDS_CHANGED = 1;
+
+ private static final int[] NO_GIDS = {};
+
+ @NonNull
+ private final Object mLock = new Object();
+
+ @GuardedBy("mLock")
+ private ArrayMap<String, PermissionState> mPermissions;
+
+ @NonNull
+ private int[] mGlobalGids = NO_GIDS;
+
+ private boolean mMissing;
+
+ private boolean mPermissionReviewRequired;
+
+ public UidPermissionState() {
+ /* do nothing */
+ }
+
+ public UidPermissionState(@NonNull UidPermissionState prototype) {
+ copyFrom(prototype);
+ }
+
+ /**
+ * Gets the global gids, applicable to all users.
+ */
+ @NonNull
+ public int[] getGlobalGids() {
+ return mGlobalGids;
+ }
+
+ /**
+ * Sets the global gids, applicable to all users.
+ *
+ * @param globalGids The global gids.
+ */
+ public void setGlobalGids(@NonNull int[] globalGids) {
+ if (!ArrayUtils.isEmpty(globalGids)) {
+ mGlobalGids = Arrays.copyOf(globalGids, globalGids.length);
+ }
+ }
+
+ static void invalidateCache() {
+ PackageManager.invalidatePackageInfoCache();
+ }
+
+ /**
+ * Initialized this instance from another one.
+ *
+ * @param other The other instance.
+ */
+ public void copyFrom(@NonNull UidPermissionState other) {
+ if (other == this) {
+ return;
+ }
+
+ synchronized (mLock) {
+ if (mPermissions != null) {
+ if (other.mPermissions == null) {
+ mPermissions = null;
+ } else {
+ mPermissions.clear();
+ }
+ }
+ if (other.mPermissions != null) {
+ if (mPermissions == null) {
+ mPermissions = new ArrayMap<>();
+ }
+ final int permissionCount = other.mPermissions.size();
+ for (int i = 0; i < permissionCount; i++) {
+ String name = other.mPermissions.keyAt(i);
+ PermissionState permissionState = other.mPermissions.valueAt(i);
+ mPermissions.put(name, new PermissionState(permissionState));
+ }
+ }
+ }
+
+ mGlobalGids = NO_GIDS;
+ if (other.mGlobalGids != NO_GIDS) {
+ mGlobalGids = other.mGlobalGids.clone();
+ }
+
+ mMissing = other.mMissing;
+
+ mPermissionReviewRequired = other.mPermissionReviewRequired;
+ }
+
+ @Override
+ public boolean equals(Object obj) {
+ if (this == obj) {
+ return true;
+ }
+ if (obj == null) {
+ return false;
+ }
+ if (getClass() != obj.getClass()) {
+ return false;
+ }
+ final UidPermissionState other = (UidPermissionState) obj;
+
+ synchronized (mLock) {
+ if (mPermissions == null) {
+ if (other.mPermissions != null) {
+ return false;
+ }
+ } else if (!mPermissions.equals(other.mPermissions)) {
+ return false;
+ }
+ }
+
+ if (mMissing != other.mMissing) {
+ return false;
+ }
+
+ if (mPermissionReviewRequired != other.mPermissionReviewRequired) {
+ return false;
+ }
+ return Arrays.equals(mGlobalGids, other.mGlobalGids);
+ }
+
+ /**
+ * Check whether the permissions state is missing for a user. This can happen if permission
+ * state is rolled back and we'll need to generate a reasonable default state to keep the app
+ * usable.
+ */
+ public boolean isMissing() {
+ return mMissing;
+ }
+
+ /**
+ * Set whether the permissions state is missing for a user. This can happen if permission state
+ * is rolled back and we'll need to generate a reasonable default state to keep the app usable.
+ */
+ public void setMissing(boolean missing) {
+ mMissing = missing;
+ }
+
+ public boolean isPermissionReviewRequired() {
+ return mPermissionReviewRequired;
+ }
+
+ /**
+ * Gets whether the state has a given permission.
+ *
+ * @param name The permission name.
+ * @return Whether the state has the permission.
+ */
+ public boolean hasPermission(@NonNull String name) {
+ synchronized (mLock) {
+ if (mPermissions == null) {
+ return false;
+ }
+ PermissionState permissionState = mPermissions.get(name);
+ return permissionState != null && permissionState.isGranted();
+ }
+ }
+
+ /**
+ * Returns whether the state has any known request for the given permission name,
+ * whether or not it has been granted.
+ *
+ * @deprecated Not all requested permissions may be here.
+ */
+ @Deprecated
+ public boolean hasRequestedPermission(@NonNull ArraySet<String> names) {
+ synchronized (mLock) {
+ if (mPermissions == null) {
+ return false;
+ }
+ for (int i = names.size() - 1; i >= 0; i--) {
+ if (mPermissions.get(names.valueAt(i)) != null) {
+ return true;
+ }
+ }
+ }
+
+ return false;
+ }
+
+ /**
+ * Returns whether the state has any known request for the given permission name,
+ * whether or not it has been granted.
+ *
+ * @deprecated Not all requested permissions may be here.
+ */
+ @Deprecated
+ public boolean hasRequestedPermission(@NonNull String name) {
+ return mPermissions != null && (mPermissions.get(name) != null);
+ }
+
+ /**
+ * Gets all permissions for a given device user id regardless if they
+ * are install time or runtime permissions.
+ *
+ * @return The permissions or an empty set.
+ */
+ @NonNull
+ public Set<String> getPermissions() {
+ synchronized (mLock) {
+ if (mPermissions == null) {
+ return Collections.emptySet();
+ }
+
+ Set<String> permissions = new ArraySet<>(mPermissions.size());
+
+ final int permissionCount = mPermissions.size();
+ for (int i = 0; i < permissionCount; i++) {
+ String permission = mPermissions.keyAt(i);
+
+ if (hasPermission(permission)) {
+ permissions.add(permission);
+ }
+ }
+
+ return permissions;
+ }
+ }
+
+ /**
+ * Gets the flags for a permission.
+ *
+ * @param name The permission name.
+ * @return The permission state or null if no such.
+ */
+ public int getPermissionFlags(@NonNull String name) {
+ PermissionState permState = getPermissionState(name);
+ if (permState != null) {
+ return permState.getFlags();
+ }
+ return 0;
+ }
+
+ /**
+ * Update the flags associated with a given permission.
+ * @param permission The permission whose flags to update.
+ * @param flagMask Mask for which flags to change.
+ * @param flagValues New values for the mask flags.
+ * @return Whether the permission flags changed.
+ */
+ public boolean updatePermissionFlags(@NonNull BasePermission permission, int flagMask,
+ int flagValues) {
+ if (flagMask == 0) {
+ return false;
+ }
+
+ PermissionState permissionState = ensurePermissionState(permission);
+
+ final int oldFlags = permissionState.getFlags();
+
+ synchronized (mLock) {
+ final boolean updated = permissionState.updateFlags(flagMask, flagValues);
+ if (updated) {
+ final int newFlags = permissionState.getFlags();
+ if ((oldFlags & PackageManager.FLAG_PERMISSION_REVIEW_REQUIRED) == 0
+ && (newFlags & PackageManager.FLAG_PERMISSION_REVIEW_REQUIRED) != 0) {
+ mPermissionReviewRequired = true;
+ } else if ((oldFlags & PackageManager.FLAG_PERMISSION_REVIEW_REQUIRED) != 0
+ && (newFlags & PackageManager.FLAG_PERMISSION_REVIEW_REQUIRED) == 0) {
+ if (mPermissionReviewRequired && !hasPermissionRequiringReview()) {
+ mPermissionReviewRequired = false;
+ }
+ }
+ }
+ return updated;
+ }
+ }
+
+ private boolean hasPermissionRequiringReview() {
+ synchronized (mLock) {
+ final int permissionCount = mPermissions.size();
+ for (int i = 0; i < permissionCount; i++) {
+ final PermissionState permission = mPermissions.valueAt(i);
+ if ((permission.getFlags() & PackageManager.FLAG_PERMISSION_REVIEW_REQUIRED) != 0) {
+ return true;
+ }
+ }
+ }
+ return false;
+ }
+
+ public boolean updatePermissionFlagsForAllPermissions(int flagMask, int flagValues) {
+ synchronized (mLock) {
+ if (mPermissions == null) {
+ return false;
+ }
+ boolean changed = false;
+ final int permissionCount = mPermissions.size();
+ for (int i = 0; i < permissionCount; i++) {
+ PermissionState permissionState = mPermissions.valueAt(i);
+ changed |= permissionState.updateFlags(flagMask, flagValues);
+ }
+ return changed;
+ }
+ }
+
+ /**
+ * Compute the Linux gids for a given device user from the permissions
+ * granted to this user. Note that these are computed to avoid additional
+ * state as they are rarely accessed.
+ *
+ * @param userId The device user id.
+ * @return The gids for the device user.
+ */
+ @NonNull
+ public int[] computeGids(@UserIdInt int userId) {
+ int[] gids = mGlobalGids;
+
+ synchronized (mLock) {
+ if (mPermissions != null) {
+ final int permissionCount = mPermissions.size();
+ for (int i = 0; i < permissionCount; i++) {
+ PermissionState permissionState = mPermissions.valueAt(i);
+ if (!permissionState.isGranted()) {
+ continue;
+ }
+ final int[] permGids = permissionState.computeGids(userId);
+ if (permGids != NO_GIDS) {
+ gids = appendInts(gids, permGids);
+ }
+ }
+ }
+ }
+
+ return gids;
+ }
+
+ /**
+ * Compute the Linux gids for all device users from the permissions
+ * granted to these users.
+ *
+ * @return The gids for all device users.
+ */
+ @NonNull
+ public int[] computeGids(@NonNull int[] userIds) {
+ int[] gids = mGlobalGids;
+
+ for (int userId : userIds) {
+ final int[] userGids = computeGids(userId);
+ gids = appendInts(gids, userGids);
+ }
+
+ return gids;
+ }
+
+ /**
+ * Resets the internal state of this object.
+ */
+ public void reset() {
+ mGlobalGids = NO_GIDS;
+
+ synchronized (mLock) {
+ mPermissions = null;
+ invalidateCache();
+ }
+
+ mMissing = false;
+ mPermissionReviewRequired = false;
+ }
+
+ /**
+ * Gets the state for a permission or null if no such.
+ *
+ * @param name The permission name.
+ * @return The permission state.
+ */
+ @Nullable
+ public PermissionState getPermissionState(@NonNull String name) {
+ synchronized (mLock) {
+ if (mPermissions == null) {
+ return null;
+ }
+ return mPermissions.get(name);
+ }
+ }
+
+ /**
+ * Gets all permission states.
+ *
+ * @return The permission states or an empty set.
+ */
+ @NonNull
+ public List<PermissionState> getPermissionStates() {
+ synchronized (mLock) {
+ if (mPermissions == null) {
+ return Collections.emptyList();
+ }
+ return new ArrayList<>(mPermissions.values());
+ }
+ }
+
+ /**
+ * Put a permission state.
+ */
+ public void putPermissionState(@NonNull BasePermission permission, boolean isGranted,
+ int flags) {
+ synchronized (mLock) {
+ ensureNoPermissionState(permission.name);
+ PermissionState permissionState = ensurePermissionState(permission);
+ if (isGranted) {
+ permissionState.grant();
+ }
+ permissionState.updateFlags(flags, flags);
+ if ((flags & PackageManager.FLAG_PERMISSION_REVIEW_REQUIRED) != 0) {
+ mPermissionReviewRequired = true;
+ }
+ }
+ }
+
+ /**
+ * Grant a permission.
+ *
+ * @param permission The permission to grant.
+ * @return The operation result which is either {@link #PERMISSION_OPERATION_SUCCESS},
+ * or {@link #PERMISSION_OPERATION_SUCCESS_GIDS_CHANGED}, or {@link
+ * #PERMISSION_OPERATION_FAILURE}.
+ */
+ public int grantPermission(@NonNull BasePermission permission) {
+ if (hasPermission(permission.getName())) {
+ return PERMISSION_OPERATION_SUCCESS;
+ }
+
+ PermissionState permissionState = ensurePermissionState(permission);
+
+ if (!permissionState.grant()) {
+ return PERMISSION_OPERATION_FAILURE;
+ }
+
+ return permission.hasGids() ? PERMISSION_OPERATION_SUCCESS_GIDS_CHANGED
+ : PERMISSION_OPERATION_SUCCESS;
+ }
+
+ /**
+ * Revoke a permission.
+ *
+ * @param permission The permission to revoke.
+ * @return The operation result which is either {@link #PERMISSION_OPERATION_SUCCESS},
+ * or {@link #PERMISSION_OPERATION_SUCCESS_GIDS_CHANGED}, or {@link
+ * #PERMISSION_OPERATION_FAILURE}.
+ */
+ public int revokePermission(@NonNull BasePermission permission) {
+ final String permissionName = permission.getName();
+ if (!hasPermission(permissionName)) {
+ return PERMISSION_OPERATION_SUCCESS;
+ }
+
+ PermissionState permissionState;
+ synchronized (mLock) {
+ permissionState = mPermissions.get(permissionName);
+ }
+
+ if (!permissionState.revoke()) {
+ return PERMISSION_OPERATION_FAILURE;
+ }
+
+ if (permissionState.isDefault()) {
+ ensureNoPermissionState(permissionName);
+ }
+
+ return permission.hasGids() ? PERMISSION_OPERATION_SUCCESS_GIDS_CHANGED
+ : PERMISSION_OPERATION_SUCCESS;
+ }
+
+ // TODO: fix this to use arraycopy and append all ints in one go
+ private static int[] appendInts(int[] current, int[] added) {
+ if (current != null && added != null) {
+ for (int guid : added) {
+ current = ArrayUtils.appendInt(current, guid);
+ }
+ }
+ return current;
+ }
+
+ @NonNull
+ private PermissionState ensurePermissionState(@NonNull BasePermission permission) {
+ final String permissionName = permission.getName();
+ synchronized (mLock) {
+ if (mPermissions == null) {
+ mPermissions = new ArrayMap<>();
+ }
+ PermissionState permissionState = mPermissions.get(permissionName);
+ if (permissionState == null) {
+ permissionState = new PermissionState(permission);
+ mPermissions.put(permissionName, permissionState);
+ }
+ return permissionState;
+ }
+ }
+
+ private void ensureNoPermissionState(@NonNull String name) {
+ synchronized (mLock) {
+ if (mPermissions == null) {
+ return;
+ }
+ mPermissions.remove(name);
+ if (mPermissions.isEmpty()) {
+ mPermissions = null;
+ }
+ }
+ }
+}
diff --git a/services/core/java/com/android/server/pm/permission/UserPermissionState.java b/services/core/java/com/android/server/pm/permission/UserPermissionState.java
new file mode 100644
index 000000000000..7f55cb161e40
--- /dev/null
+++ b/services/core/java/com/android/server/pm/permission/UserPermissionState.java
@@ -0,0 +1,103 @@
+/*
+ * 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.pm.permission;
+
+import android.annotation.AppIdInt;
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.os.UserHandle;
+import android.util.ArraySet;
+import android.util.SparseArray;
+
+import com.android.internal.annotations.GuardedBy;
+
+/**
+ * Permission state for a user.
+ */
+public final class UserPermissionState {
+ /**
+ * Whether the install permissions have been granted to a package, so that no install
+ * permissions should be added to it unless the package is upgraded.
+ */
+ @GuardedBy("mLock")
+ @NonNull
+ private final ArraySet<String> mInstallPermissionsFixed = new ArraySet<>();
+
+ /**
+ * Maps from app ID to {@link UidPermissionState}.
+ */
+ @GuardedBy("mLock")
+ @NonNull
+ private final SparseArray<UidPermissionState> mUidStates = new SparseArray<>();
+
+ @NonNull
+ private final Object mLock;
+
+ public UserPermissionState(@NonNull Object lock) {
+ mLock = lock;
+ }
+
+ public boolean areInstallPermissionsFixed(@NonNull String packageName) {
+ synchronized (mLock) {
+ return mInstallPermissionsFixed.contains(packageName);
+ }
+ }
+
+ public void setInstallPermissionsFixed(@NonNull String packageName, boolean fixed) {
+ synchronized (mLock) {
+ if (fixed) {
+ mInstallPermissionsFixed.add(packageName);
+ } else {
+ mInstallPermissionsFixed.remove(packageName);
+ }
+ }
+ }
+
+ @Nullable
+ public UidPermissionState getUidState(@AppIdInt int appId) {
+ checkAppId(appId);
+ synchronized (mLock) {
+ return mUidStates.get(appId);
+ }
+ }
+
+ @NonNull
+ public UidPermissionState getOrCreateUidState(@AppIdInt int appId) {
+ checkAppId(appId);
+ synchronized (mLock) {
+ UidPermissionState uidState = mUidStates.get(appId);
+ if (uidState == null) {
+ uidState = new UidPermissionState();
+ mUidStates.put(appId, uidState);
+ }
+ return uidState;
+ }
+ }
+
+ public void removeUidState(@AppIdInt int appId) {
+ checkAppId(appId);
+ synchronized (mLock) {
+ mUidStates.delete(appId);
+ }
+ }
+
+ private void checkAppId(@AppIdInt int appId) {
+ if (UserHandle.getUserId(appId) != 0) {
+ throw new IllegalArgumentException(appId + " is not an app ID");
+ }
+ }
+}
diff --git a/services/core/java/com/android/server/soundtrigger_middleware/ConversionUtil.java b/services/core/java/com/android/server/soundtrigger_middleware/ConversionUtil.java
index 1d31285ed9c7..de8823c4b7f3 100644
--- a/services/core/java/com/android/server/soundtrigger_middleware/ConversionUtil.java
+++ b/services/core/java/com/android/server/soundtrigger_middleware/ConversionUtil.java
@@ -40,7 +40,10 @@ import android.media.soundtrigger_middleware.SoundModel;
import android.media.soundtrigger_middleware.SoundModelType;
import android.media.soundtrigger_middleware.SoundTriggerModuleProperties;
import android.os.HidlMemoryUtil;
+import android.os.ParcelFileDescriptor;
+import java.io.FileDescriptor;
+import java.io.IOException;
import java.util.regex.Matcher;
/**
@@ -196,8 +199,18 @@ class ConversionUtil {
hidlModel.header.type = aidl2hidlSoundModelType(aidlModel.type);
hidlModel.header.uuid = aidl2hidlUuid(aidlModel.uuid);
hidlModel.header.vendorUuid = aidl2hidlUuid(aidlModel.vendorUuid);
- hidlModel.data = HidlMemoryUtil.fileDescriptorToHidlMemory(aidlModel.data,
- aidlModel.dataSize);
+
+ // Extract a dup of the underlying FileDescriptor out of aidlModel.data without changing
+ // the original.
+ FileDescriptor fd = new FileDescriptor();
+ try {
+ ParcelFileDescriptor dup = aidlModel.data.dup();
+ fd.setInt$(dup.detachFd());
+ hidlModel.data = HidlMemoryUtil.fileDescriptorToHidlMemory(fd, aidlModel.dataSize);
+ } catch (IOException e) {
+ throw new RuntimeException(e);
+ }
+
return hidlModel;
}
diff --git a/services/core/java/com/android/server/soundtrigger_middleware/ISoundTriggerMiddlewareInternal.java b/services/core/java/com/android/server/soundtrigger_middleware/ISoundTriggerMiddlewareInternal.java
index 5def7621c148..a90053a23dea 100644
--- a/services/core/java/com/android/server/soundtrigger_middleware/ISoundTriggerMiddlewareInternal.java
+++ b/services/core/java/com/android/server/soundtrigger_middleware/ISoundTriggerMiddlewareInternal.java
@@ -17,11 +17,28 @@
package com.android.server.soundtrigger_middleware;
import android.media.ICaptureStateListener;
-import android.media.soundtrigger_middleware.ISoundTriggerMiddlewareService;
+import android.media.soundtrigger_middleware.ISoundTriggerCallback;
+import android.media.soundtrigger_middleware.ISoundTriggerModule;
+import android.media.soundtrigger_middleware.SoundTriggerModuleDescriptor;
/**
- * This interface unifies ISoundTriggerMiddlewareService with ICaptureStateListener.
+ * This interface unifies methods from ISoundTriggerMiddlewareService and ICaptureStateListener.
+ *
+ * The ISoundTriggerMiddlewareService have been modified to exclude identity information and the
+ * RemoteException signature, both of which are only relevant at the service boundary layer.
*/
-public interface ISoundTriggerMiddlewareInternal extends ISoundTriggerMiddlewareService,
- ICaptureStateListener {
+public interface ISoundTriggerMiddlewareInternal extends ICaptureStateListener {
+ /**
+ * Query the available modules and their capabilities.
+ */
+ public SoundTriggerModuleDescriptor[] listModules();
+
+ /**
+ * Attach to one of the available modules.
+ *
+ * listModules() must be called prior to calling this method and the provided handle must be
+ * one of the handles from the returned list.
+ */
+ public ISoundTriggerModule attach(int handle,
+ ISoundTriggerCallback callback);
}
diff --git a/services/core/java/com/android/server/soundtrigger_middleware/SoundTriggerHw2Enforcer.java b/services/core/java/com/android/server/soundtrigger_middleware/SoundTriggerHw2Enforcer.java
index 761858ccd238..eced8940947a 100644
--- a/services/core/java/com/android/server/soundtrigger_middleware/SoundTriggerHw2Enforcer.java
+++ b/services/core/java/com/android/server/soundtrigger_middleware/SoundTriggerHw2Enforcer.java
@@ -21,6 +21,7 @@ import android.hardware.soundtrigger.V2_1.ISoundTriggerHwCallback;
import android.hardware.soundtrigger.V2_3.ModelParameterRange;
import android.hardware.soundtrigger.V2_3.Properties;
import android.hardware.soundtrigger.V2_3.RecognitionConfig;
+import android.media.soundtrigger_middleware.Status;
import android.os.DeadObjectException;
import android.os.IHwBinder;
import android.os.RemoteException;
@@ -195,10 +196,10 @@ public class SoundTriggerHw2Enforcer implements ISoundTriggerHw2 {
if (e.getCause() instanceof DeadObjectException) {
// Server is dead, no need to reboot.
Log.e(TAG, "HAL died");
- } else {
- Log.e(TAG, "Exception caught from HAL, rebooting HAL");
- rebootHal();
+ throw new RecoverableException(Status.DEAD_OBJECT);
}
+ Log.e(TAG, "Exception caught from HAL, rebooting HAL");
+ rebootHal();
throw e;
}
diff --git a/services/core/java/com/android/server/soundtrigger_middleware/SoundTriggerMiddlewareLogging.java b/services/core/java/com/android/server/soundtrigger_middleware/SoundTriggerMiddlewareLogging.java
index 8b6ed1ff5081..2ef0759719fc 100644
--- a/services/core/java/com/android/server/soundtrigger_middleware/SoundTriggerMiddlewareLogging.java
+++ b/services/core/java/com/android/server/soundtrigger_middleware/SoundTriggerMiddlewareLogging.java
@@ -18,8 +18,9 @@ package com.android.server.soundtrigger_middleware;
import android.annotation.NonNull;
import android.annotation.Nullable;
+import android.media.permission.Identity;
+import android.media.permission.IdentityContext;
import android.media.soundtrigger_middleware.ISoundTriggerCallback;
-import android.media.soundtrigger_middleware.ISoundTriggerMiddlewareService;
import android.media.soundtrigger_middleware.ISoundTriggerModule;
import android.media.soundtrigger_middleware.ModelParameterRange;
import android.media.soundtrigger_middleware.PhraseRecognitionEvent;
@@ -28,17 +29,15 @@ import android.media.soundtrigger_middleware.RecognitionConfig;
import android.media.soundtrigger_middleware.RecognitionEvent;
import android.media.soundtrigger_middleware.SoundModel;
import android.media.soundtrigger_middleware.SoundTriggerModuleDescriptor;
-import android.os.Binder;
import android.os.IBinder;
-import android.os.Parcelable;
import android.os.RemoteException;
import android.util.Log;
import java.io.PrintWriter;
import java.text.SimpleDateFormat;
-import java.util.Arrays;
import java.util.Date;
import java.util.LinkedList;
+import java.util.Objects;
/**
* An ISoundTriggerMiddlewareService decorator, which adds logging of all API calls (and
@@ -71,7 +70,8 @@ public class SoundTriggerMiddlewareLogging implements ISoundTriggerMiddlewareInt
}
@Override
- public @NonNull SoundTriggerModuleDescriptor[] listModules() throws RemoteException {
+ public @NonNull
+ SoundTriggerModuleDescriptor[] listModules() {
try {
SoundTriggerModuleDescriptor[] result = mDelegate.listModules();
logReturn("listModules", result);
@@ -83,12 +83,13 @@ public class SoundTriggerMiddlewareLogging implements ISoundTriggerMiddlewareInt
}
@Override
- public @NonNull ISoundTriggerModule attach(int handle, ISoundTriggerCallback callback)
- throws RemoteException {
+ public @NonNull
+ ISoundTriggerModule attach(int handle, ISoundTriggerCallback callback) {
try {
- ISoundTriggerModule result = mDelegate.attach(handle, new CallbackLogging(callback));
+ ModuleLogging result = new ModuleLogging(callback);
+ result.attach(mDelegate.attach(handle, result.getCallbackWrapper()));
logReturn("attach", result, handle, callback);
- return new ModuleLogging(result);
+ return result;
} catch (Exception e) {
logException("attach", e, handle, callback);
throw e;
@@ -106,7 +107,8 @@ public class SoundTriggerMiddlewareLogging implements ISoundTriggerMiddlewareInt
}
}
- @Override public IBinder asBinder() {
+ @Override
+ public IBinder asBinder() {
throw new UnsupportedOperationException(
"This implementation is not inteded to be used directly with Binder.");
}
@@ -118,94 +120,33 @@ public class SoundTriggerMiddlewareLogging implements ISoundTriggerMiddlewareInt
}
private void logException(String methodName, Exception ex, Object... args) {
- logExceptionWithObject(this, methodName, ex, args);
+ logExceptionWithObject(this, IdentityContext.get(), methodName, ex, args);
}
private void logReturn(String methodName, Object retVal, Object... args) {
- logReturnWithObject(this, methodName, retVal, args);
+ logReturnWithObject(this, IdentityContext.get(), methodName, retVal, args);
}
private void logVoidReturn(String methodName, Object... args) {
- logVoidReturnWithObject(this, methodName, args);
+ logVoidReturnWithObject(this, IdentityContext.get(), methodName, args);
}
- private class CallbackLogging implements ISoundTriggerCallback {
- private final ISoundTriggerCallback mDelegate;
-
- private CallbackLogging(ISoundTriggerCallback delegate) {
- mDelegate = delegate;
- }
-
- @Override
- public void onRecognition(int modelHandle, RecognitionEvent event) throws RemoteException {
- try {
- mDelegate.onRecognition(modelHandle, event);
- logVoidReturn("onRecognition", modelHandle, event);
- } catch (Exception e) {
- logException("onRecognition", e, modelHandle, event);
- throw e;
- }
- }
-
- @Override
- public void onPhraseRecognition(int modelHandle, PhraseRecognitionEvent event)
- throws RemoteException {
- try {
- mDelegate.onPhraseRecognition(modelHandle, event);
- logVoidReturn("onPhraseRecognition", modelHandle, event);
- } catch (Exception e) {
- logException("onPhraseRecognition", e, modelHandle, event);
- throw e;
- }
- }
-
- @Override
- public void onRecognitionAvailabilityChange(boolean available) throws RemoteException {
- try {
- mDelegate.onRecognitionAvailabilityChange(available);
- logVoidReturn("onRecognitionAvailabilityChange", available);
- } catch (Exception e) {
- logException("onRecognitionAvailabilityChange", e, available);
- throw e;
- }
- }
-
- @Override
- public void onModuleDied() throws RemoteException {
- try {
- mDelegate.onModuleDied();
- logVoidReturn("onModuleDied");
- } catch (Exception e) {
- logException("onModuleDied", e);
- throw e;
- }
- }
-
- private void logException(String methodName, Exception ex, Object... args) {
- logExceptionWithObject(this, methodName, ex, args);
- }
+ private class ModuleLogging implements ISoundTriggerModule {
+ private ISoundTriggerModule mDelegate;
+ private final @NonNull CallbackLogging mCallbackWrapper;
+ private final @NonNull Identity mOriginatorIdentity;
- private void logVoidReturn(String methodName, Object... args) {
- logVoidReturnWithObject(this, methodName, args);
+ ModuleLogging(@NonNull ISoundTriggerCallback callback) {
+ mCallbackWrapper = new CallbackLogging(callback);
+ mOriginatorIdentity = IdentityContext.getNonNull();
}
- @Override
- public IBinder asBinder() {
- return mDelegate.asBinder();
- }
-
- // Override toString() in order to have the delegate's ID in it.
- @Override
- public String toString() {
- return mDelegate.toString();
+ void attach(@NonNull ISoundTriggerModule delegate) {
+ mDelegate = delegate;
}
- }
-
- private class ModuleLogging implements ISoundTriggerModule {
- private final ISoundTriggerModule mDelegate;
- private ModuleLogging(ISoundTriggerModule delegate) {
- mDelegate = delegate;
+ ISoundTriggerCallback getCallbackWrapper() {
+ return mCallbackWrapper;
}
@Override
@@ -334,19 +275,92 @@ public class SoundTriggerMiddlewareLogging implements ISoundTriggerMiddlewareInt
// Override toString() in order to have the delegate's ID in it.
@Override
public String toString() {
- return mDelegate.toString();
+ return Objects.toString(mDelegate);
}
private void logException(String methodName, Exception ex, Object... args) {
- logExceptionWithObject(this, methodName, ex, args);
+ logExceptionWithObject(this, mOriginatorIdentity, methodName, ex, args);
}
private void logReturn(String methodName, Object retVal, Object... args) {
- logReturnWithObject(this, methodName, retVal, args);
+ logReturnWithObject(this, mOriginatorIdentity, methodName, retVal, args);
}
private void logVoidReturn(String methodName, Object... args) {
- logVoidReturnWithObject(this, methodName, args);
+ logVoidReturnWithObject(this, mOriginatorIdentity, methodName, args);
+ }
+
+ private class CallbackLogging implements ISoundTriggerCallback {
+ private final ISoundTriggerCallback mCallbackDelegate;
+
+ private CallbackLogging(ISoundTriggerCallback delegate) {
+ mCallbackDelegate = delegate;
+ }
+
+ @Override
+ public void onRecognition(int modelHandle, RecognitionEvent event)
+ throws RemoteException {
+ try {
+ mCallbackDelegate.onRecognition(modelHandle, event);
+ logVoidReturn("onRecognition", modelHandle, event);
+ } catch (Exception e) {
+ logException("onRecognition", e, modelHandle, event);
+ throw e;
+ }
+ }
+
+ @Override
+ public void onPhraseRecognition(int modelHandle, PhraseRecognitionEvent event)
+ throws RemoteException {
+ try {
+ mCallbackDelegate.onPhraseRecognition(modelHandle, event);
+ logVoidReturn("onPhraseRecognition", modelHandle, event);
+ } catch (Exception e) {
+ logException("onPhraseRecognition", e, modelHandle, event);
+ throw e;
+ }
+ }
+
+ @Override
+ public void onRecognitionAvailabilityChange(boolean available) throws RemoteException {
+ try {
+ mCallbackDelegate.onRecognitionAvailabilityChange(available);
+ logVoidReturn("onRecognitionAvailabilityChange", available);
+ } catch (Exception e) {
+ logException("onRecognitionAvailabilityChange", e, available);
+ throw e;
+ }
+ }
+
+ @Override
+ public void onModuleDied() throws RemoteException {
+ try {
+ mCallbackDelegate.onModuleDied();
+ logVoidReturn("onModuleDied");
+ } catch (Exception e) {
+ logException("onModuleDied", e);
+ throw e;
+ }
+ }
+
+ private void logException(String methodName, Exception ex, Object... args) {
+ logExceptionWithObject(this, mOriginatorIdentity, methodName, ex, args);
+ }
+
+ private void logVoidReturn(String methodName, Object... args) {
+ logVoidReturnWithObject(this, mOriginatorIdentity, methodName, args);
+ }
+
+ @Override
+ public IBinder asBinder() {
+ return mCallbackDelegate.asBinder();
+ }
+
+ // Override toString() in order to have the delegate's ID in it.
+ @Override
+ public String toString() {
+ return Objects.toString(mCallbackDelegate);
+ }
}
}
@@ -386,34 +400,37 @@ public class SoundTriggerMiddlewareLogging implements ISoundTriggerMiddlewareInt
return builder.toString();
}
- private void logReturnWithObject(@NonNull Object object, String methodName,
+ private void logReturnWithObject(@NonNull Object object, @Nullable Identity originatorIdentity,
+ String methodName,
@Nullable Object retVal,
@NonNull Object[] args) {
- final String message = String.format("%s[this=%s, caller=%d/%d](%s) -> %s", methodName,
+ final String message = String.format("%s[this=%s, client=%s](%s) -> %s", methodName,
object,
- Binder.getCallingUid(), Binder.getCallingPid(),
+ printObject(originatorIdentity),
printArgs(args),
printObject(retVal));
Log.i(TAG, message);
appendMessage(message);
}
- private void logVoidReturnWithObject(@NonNull Object object, @NonNull String methodName,
+ private void logVoidReturnWithObject(@NonNull Object object,
+ @Nullable Identity originatorIdentity, @NonNull String methodName,
@NonNull Object[] args) {
- final String message = String.format("%s[this=%s, caller=%d/%d](%s)", methodName,
+ final String message = String.format("%s[this=%s, client=%s](%s)", methodName,
object,
- Binder.getCallingUid(), Binder.getCallingPid(),
+ printObject(originatorIdentity),
printArgs(args));
Log.i(TAG, message);
appendMessage(message);
}
- private void logExceptionWithObject(@NonNull Object object, @NonNull String methodName,
+ private void logExceptionWithObject(@NonNull Object object,
+ @Nullable Identity originatorIdentity, @NonNull String methodName,
@NonNull Exception ex,
Object[] args) {
- final String message = String.format("%s[this=%s, caller=%d/%d](%s) threw", methodName,
+ final String message = String.format("%s[this=%s, client=%s](%s) threw", methodName,
object,
- Binder.getCallingUid(), Binder.getCallingPid(),
+ printObject(originatorIdentity),
printArgs(args));
Log.e(TAG, message, ex);
appendMessage(message + " " + ex.toString());
@@ -429,7 +446,8 @@ public class SoundTriggerMiddlewareLogging implements ISoundTriggerMiddlewareInt
}
}
- @Override public void dump(PrintWriter pw) {
+ @Override
+ public void dump(PrintWriter pw) {
pw.println();
pw.println("=========================================");
pw.println("Last events");
diff --git a/services/core/java/com/android/server/soundtrigger_middleware/SoundTriggerMiddlewarePermission.java b/services/core/java/com/android/server/soundtrigger_middleware/SoundTriggerMiddlewarePermission.java
new file mode 100644
index 000000000000..7b6c6561ccf1
--- /dev/null
+++ b/services/core/java/com/android/server/soundtrigger_middleware/SoundTriggerMiddlewarePermission.java
@@ -0,0 +1,348 @@
+/*
+ * 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.soundtrigger_middleware;
+
+import static android.Manifest.permission.CAPTURE_AUDIO_HOTWORD;
+import static android.Manifest.permission.RECORD_AUDIO;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.content.Context;
+import android.content.PermissionChecker;
+import android.media.permission.Identity;
+import android.media.permission.IdentityContext;
+import android.media.permission.PermissionUtil;
+import android.media.soundtrigger_middleware.ISoundTriggerCallback;
+import android.media.soundtrigger_middleware.ISoundTriggerMiddlewareService;
+import android.media.soundtrigger_middleware.ISoundTriggerModule;
+import android.media.soundtrigger_middleware.ModelParameterRange;
+import android.media.soundtrigger_middleware.PhraseRecognitionEvent;
+import android.media.soundtrigger_middleware.PhraseSoundModel;
+import android.media.soundtrigger_middleware.RecognitionConfig;
+import android.media.soundtrigger_middleware.RecognitionEvent;
+import android.media.soundtrigger_middleware.SoundModel;
+import android.media.soundtrigger_middleware.SoundTriggerModuleDescriptor;
+import android.media.soundtrigger_middleware.Status;
+import android.os.IBinder;
+import android.os.RemoteException;
+import android.os.ServiceSpecificException;
+
+import java.io.PrintWriter;
+import java.util.Objects;
+
+/**
+ * This is a decorator of an {@link ISoundTriggerMiddlewareService}, which enforces permissions.
+ * <p>
+ * Every public method in this class, overriding an interface method, must follow a similar
+ * pattern:
+ * <code><pre>
+ * @Override public T method(S arg) {
+ * // Permission check.
+ * enforcePermissions*(...);
+ * return mDelegate.method(arg);
+ * }
+ * </pre></code>
+ *
+ * {@hide}
+ */
+public class SoundTriggerMiddlewarePermission implements ISoundTriggerMiddlewareInternal, Dumpable {
+ private static final String TAG = "SoundTriggerMiddlewarePermission";
+
+ private final @NonNull ISoundTriggerMiddlewareInternal mDelegate;
+ private final @NonNull Context mContext;
+
+ public SoundTriggerMiddlewarePermission(
+ @NonNull ISoundTriggerMiddlewareInternal delegate, @NonNull Context context) {
+ mDelegate = delegate;
+ mContext = context;
+ }
+
+ @Override
+ public @NonNull
+ SoundTriggerModuleDescriptor[] listModules() {
+ Identity identity = getIdentity();
+ enforcePermissionsForPreflight(identity);
+ return mDelegate.listModules();
+ }
+
+ @Override
+ public @NonNull
+ ISoundTriggerModule attach(int handle,
+ @NonNull ISoundTriggerCallback callback) {
+ Identity identity = getIdentity();
+ enforcePermissionsForPreflight(identity);
+ ModuleWrapper wrapper = new ModuleWrapper(identity, callback);
+ return wrapper.attach(mDelegate.attach(handle, wrapper.getCallbackWrapper()));
+ }
+
+ @Override
+ public void setCaptureState(boolean active) throws RemoteException {
+ // This is an internal call. No permissions needed.
+ mDelegate.setCaptureState(active);
+ }
+
+ // Override toString() in order to have the delegate's ID in it.
+ @Override
+ public String toString() {
+ return Objects.toString(mDelegate);
+ }
+
+ @Override
+ public IBinder asBinder() {
+ throw new UnsupportedOperationException(
+ "This implementation is not inteded to be used directly with Binder.");
+ }
+
+ /**
+ * Get the identity context, or throws an InternalServerError if it has not been established.
+ *
+ * @return The identity.
+ */
+ private static @NonNull
+ Identity getIdentity() {
+ return IdentityContext.getNonNull();
+ }
+
+ /**
+ * Throws a {@link SecurityException} if originator permanently doesn't have the given
+ * permission,
+ * or a {@link ServiceSpecificException} with a {@link Status#TEMPORARY_PERMISSION_DENIED} if
+ * originator temporarily doesn't have the right permissions to use this service.
+ */
+ private void enforcePermissionsForPreflight(@NonNull Identity identity) {
+ enforcePermissionForPreflight(mContext, identity, RECORD_AUDIO);
+ enforcePermissionForPreflight(mContext, identity, CAPTURE_AUDIO_HOTWORD);
+ }
+
+ /**
+ * Throws a {@link SecurityException} iff the originator has permission to receive data.
+ */
+ void enforcePermissionsForDataDelivery(@NonNull Identity identity, @NonNull String reason) {
+ enforcePermissionForDataDelivery(mContext, identity, RECORD_AUDIO, reason);
+ enforcePermissionForDataDelivery(mContext, identity, CAPTURE_AUDIO_HOTWORD,
+ reason);
+ }
+
+ /**
+ * Throws a {@link SecurityException} iff the given identity has given permission to receive
+ * data.
+ *
+ * @param context A {@link Context}, used for permission checks.
+ * @param identity The identity to check.
+ * @param permission The identifier of the permission we want to check.
+ * @param reason The reason why we're requesting the permission, for auditing purposes.
+ */
+ private static void enforcePermissionForDataDelivery(@NonNull Context context,
+ @NonNull Identity identity,
+ @NonNull String permission, @NonNull String reason) {
+ // TODO(ytai): We're temporarily ignoring proc state until we have a proper permission that
+ // represents being able to use the microphone in the background. Otherwise, some of our
+ // existing use-cases would break.
+ final int status = PermissionUtil.checkPermissionForDataDeliveryIgnoreProcState(context,
+ identity, permission, reason);
+ if (status != PermissionChecker.PERMISSION_GRANTED) {
+ throw new SecurityException(
+ String.format("Failed to obtain permission %s for identity %s", permission,
+ ObjectPrinter.print(identity, true, 16)));
+ }
+ }
+
+ /**
+ * Throws a {@link SecurityException} if originator permanently doesn't have the given
+ * permission, or a {@link ServiceSpecificException} with a {@link
+ * Status#TEMPORARY_PERMISSION_DENIED} if caller originator doesn't have the given permission.
+ *
+ * @param context A {@link Context}, used for permission checks.
+ * @param identity The identity to check.
+ * @param permission The identifier of the permission we want to check.
+ */
+ private static void enforcePermissionForPreflight(@NonNull Context context,
+ @NonNull Identity identity, @NonNull String permission) {
+ final int status = PermissionUtil.checkPermissionForPreflight(context, identity,
+ permission);
+ switch (status) {
+ case PermissionChecker.PERMISSION_GRANTED:
+ return;
+ case PermissionChecker.PERMISSION_HARD_DENIED:
+ throw new SecurityException(
+ String.format("Failed to obtain permission %s for identity %s", permission,
+ ObjectPrinter.print(identity, true, 16)));
+ case PermissionChecker.PERMISSION_SOFT_DENIED:
+ throw new ServiceSpecificException(Status.TEMPORARY_PERMISSION_DENIED,
+ String.format("Failed to obtain permission %s for identity %s", permission,
+ ObjectPrinter.print(identity, true, 16)));
+ default:
+ throw new RuntimeException("Unexpected perimission check result.");
+ }
+ }
+
+
+ @Override
+ public void dump(PrintWriter pw) {
+ if (mDelegate instanceof Dumpable) {
+ ((Dumpable) mDelegate).dump(pw);
+ }
+ }
+
+ /**
+ * A wrapper around an {@link ISoundTriggerModule} implementation, to address the same aspects
+ * mentioned in {@link SoundTriggerModule} above. This class follows the same conventions.
+ */
+ private class ModuleWrapper extends ISoundTriggerModule.Stub {
+ private ISoundTriggerModule mDelegate;
+ private final @NonNull Identity mOriginatorIdentity;
+ private final @NonNull CallbackWrapper mCallbackWrapper;
+
+ ModuleWrapper(@NonNull Identity originatorIdentity,
+ @NonNull ISoundTriggerCallback callback) {
+ mOriginatorIdentity = originatorIdentity;
+ mCallbackWrapper = new CallbackWrapper(callback);
+ }
+
+ ModuleWrapper attach(@NonNull ISoundTriggerModule delegate) {
+ mDelegate = delegate;
+ return this;
+ }
+
+ ISoundTriggerCallback getCallbackWrapper() {
+ return mCallbackWrapper;
+ }
+
+ @Override
+ public int loadModel(@NonNull SoundModel model) throws RemoteException {
+ enforcePermissions();
+ return mDelegate.loadModel(model);
+ }
+
+ @Override
+ public int loadPhraseModel(@NonNull PhraseSoundModel model) throws RemoteException {
+ enforcePermissions();
+ return mDelegate.loadPhraseModel(model);
+ }
+
+ @Override
+ public void unloadModel(int modelHandle) throws RemoteException {
+ enforcePermissions();
+ mDelegate.unloadModel(modelHandle);
+
+ }
+
+ @Override
+ public void startRecognition(int modelHandle, @NonNull RecognitionConfig config)
+ throws RemoteException {
+ enforcePermissions();
+ mDelegate.startRecognition(modelHandle, config);
+ }
+
+ @Override
+ public void stopRecognition(int modelHandle) throws RemoteException {
+ enforcePermissions();
+ mDelegate.stopRecognition(modelHandle);
+ }
+
+ @Override
+ public void forceRecognitionEvent(int modelHandle) throws RemoteException {
+ enforcePermissions();
+ mDelegate.forceRecognitionEvent(modelHandle);
+ }
+
+ @Override
+ public void setModelParameter(int modelHandle, int modelParam, int value)
+ throws RemoteException {
+ enforcePermissions();
+ mDelegate.setModelParameter(modelHandle, modelParam, value);
+ }
+
+ @Override
+ public int getModelParameter(int modelHandle, int modelParam) throws RemoteException {
+ enforcePermissions();
+ return mDelegate.getModelParameter(modelHandle, modelParam);
+ }
+
+ @Override
+ @Nullable
+ public ModelParameterRange queryModelParameterSupport(int modelHandle, int modelParam)
+ throws RemoteException {
+ enforcePermissions();
+ return mDelegate.queryModelParameterSupport(modelHandle,
+ modelParam);
+ }
+
+ @Override
+ public void detach() throws RemoteException {
+ enforcePermissions();
+ mDelegate.detach();
+ }
+
+ // Override toString() in order to have the delegate's ID in it.
+ @Override
+ public String toString() {
+ return Objects.toString(mDelegate);
+ }
+
+ private void enforcePermissions() {
+ enforcePermissionsForPreflight(mOriginatorIdentity);
+ }
+
+ private class CallbackWrapper implements ISoundTriggerCallback {
+ private final ISoundTriggerCallback mDelegate;
+
+ private CallbackWrapper(ISoundTriggerCallback delegate) {
+ mDelegate = delegate;
+ }
+
+ @Override
+ public void onRecognition(int modelHandle, RecognitionEvent event)
+ throws RemoteException {
+ enforcePermissions("Sound trigger recognition.");
+ mDelegate.onRecognition(modelHandle, event);
+ }
+
+ @Override
+ public void onPhraseRecognition(int modelHandle, PhraseRecognitionEvent event)
+ throws RemoteException {
+ enforcePermissions("Sound trigger phrase recognition.");
+ mDelegate.onPhraseRecognition(modelHandle, event);
+ }
+
+ @Override
+ public void onRecognitionAvailabilityChange(boolean available) throws RemoteException {
+ mDelegate.onRecognitionAvailabilityChange(available);
+ }
+
+ @Override
+ public void onModuleDied() throws RemoteException {
+ mDelegate.onModuleDied();
+ }
+
+ @Override
+ public IBinder asBinder() {
+ return mDelegate.asBinder();
+ }
+
+ // Override toString() in order to have the delegate's ID in it.
+ @Override
+ public String toString() {
+ return mDelegate.toString();
+ }
+
+ private void enforcePermissions(String reason) {
+ enforcePermissionsForDataDelivery(mOriginatorIdentity, reason);
+ }
+ }
+ }
+}
diff --git a/services/core/java/com/android/server/soundtrigger_middleware/SoundTriggerMiddlewareService.java b/services/core/java/com/android/server/soundtrigger_middleware/SoundTriggerMiddlewareService.java
index 4b464d2d3e04..db7a575b08e2 100644
--- a/services/core/java/com/android/server/soundtrigger_middleware/SoundTriggerMiddlewareService.java
+++ b/services/core/java/com/android/server/soundtrigger_middleware/SoundTriggerMiddlewareService.java
@@ -16,9 +16,15 @@
package com.android.server.soundtrigger_middleware;
+import static android.Manifest.permission.SOUNDTRIGGER_DELEGATE_IDENTITY;
+
import android.annotation.NonNull;
import android.content.Context;
import android.hardware.soundtrigger.V2_0.ISoundTriggerHw;
+import android.media.permission.ClearCallingIdentityContext;
+import android.media.permission.Identity;
+import android.media.permission.PermissionUtil;
+import android.media.permission.SafeCloseable;
import android.media.soundtrigger_middleware.ISoundTriggerCallback;
import android.media.soundtrigger_middleware.ISoundTriggerMiddlewareService;
import android.media.soundtrigger_middleware.ISoundTriggerModule;
@@ -62,15 +68,17 @@ import java.util.Objects;
public class SoundTriggerMiddlewareService extends ISoundTriggerMiddlewareService.Stub {
static private final String TAG = "SoundTriggerMiddlewareService";
- @NonNull
- private final ISoundTriggerMiddlewareInternal mDelegate;
+ private final @NonNull ISoundTriggerMiddlewareInternal mDelegate;
+ private final @NonNull Context mContext;
/**
* Constructor for internal use only. Could be exposed for testing purposes in the future.
* Users should access this class via {@link Lifecycle}.
*/
- private SoundTriggerMiddlewareService(@NonNull ISoundTriggerMiddlewareInternal delegate) {
+ private SoundTriggerMiddlewareService(@NonNull ISoundTriggerMiddlewareInternal delegate,
+ @NonNull Context context) {
mDelegate = Objects.requireNonNull(delegate);
+ mContext = context;
new ExternalCaptureStateTracker(active -> {
try {
mDelegate.setCaptureState(active);
@@ -81,24 +89,58 @@ public class SoundTriggerMiddlewareService extends ISoundTriggerMiddlewareServic
}
@Override
- public @NonNull
- SoundTriggerModuleDescriptor[] listModules() throws RemoteException {
- return mDelegate.listModules();
+ public SoundTriggerModuleDescriptor[] listModulesAsOriginator(Identity identity) {
+ try (SafeCloseable ignored = establishIdentityDirect(identity)) {
+ return mDelegate.listModules();
+ }
}
@Override
- public @NonNull
- ISoundTriggerModule attach(int handle, @NonNull ISoundTriggerCallback callback)
- throws RemoteException {
- return new ModuleService(mDelegate.attach(handle, callback));
+ public SoundTriggerModuleDescriptor[] listModulesAsMiddleman(Identity middlemanIdentity,
+ Identity originatorIdentity) {
+ try (SafeCloseable ignored = establishIdentityIndirect(middlemanIdentity,
+ originatorIdentity)) {
+ return mDelegate.listModules();
+ }
+ }
+
+ @Override
+ public ISoundTriggerModule attachAsOriginator(int handle, Identity identity,
+ ISoundTriggerCallback callback) {
+ try (SafeCloseable ignored = establishIdentityDirect(Objects.requireNonNull(identity))) {
+ return new ModuleService(mDelegate.attach(handle, callback));
+ }
}
- @Override protected void dump(FileDescriptor fd, PrintWriter fout, String[] args) {
+ @Override
+ public ISoundTriggerModule attachAsMiddleman(int handle, Identity middlemanIdentity,
+ Identity originatorIdentity, ISoundTriggerCallback callback) {
+ try (SafeCloseable ignored = establishIdentityIndirect(
+ Objects.requireNonNull(middlemanIdentity),
+ Objects.requireNonNull(originatorIdentity))) {
+ return new ModuleService(mDelegate.attach(handle, callback));
+ }
+ }
+
+ @Override
+ protected void dump(FileDescriptor fd, PrintWriter fout, String[] args) {
if (mDelegate instanceof Dumpable) {
((Dumpable) mDelegate).dump(fout);
}
}
+ private @NonNull
+ SafeCloseable establishIdentityIndirect(Identity middlemanIdentity,
+ Identity originatorIdentity) {
+ return PermissionUtil.establishIdentityIndirect(mContext, SOUNDTRIGGER_DELEGATE_IDENTITY,
+ middlemanIdentity, originatorIdentity);
+ }
+
+ private @NonNull
+ SafeCloseable establishIdentityDirect(Identity originatorIdentity) {
+ return PermissionUtil.establishIdentityDirect(originatorIdentity);
+ }
+
private final static class ModuleService extends ISoundTriggerModule.Stub {
private final ISoundTriggerModule mDelegate;
@@ -108,55 +150,75 @@ public class SoundTriggerMiddlewareService extends ISoundTriggerMiddlewareServic
@Override
public int loadModel(SoundModel model) throws RemoteException {
- return mDelegate.loadModel(model);
+ try (SafeCloseable ignored = ClearCallingIdentityContext.create()) {
+ return mDelegate.loadModel(model);
+ }
}
@Override
public int loadPhraseModel(PhraseSoundModel model) throws RemoteException {
- return mDelegate.loadPhraseModel(model);
+ try (SafeCloseable ignored = ClearCallingIdentityContext.create()) {
+ return mDelegate.loadPhraseModel(model);
+ }
}
@Override
public void unloadModel(int modelHandle) throws RemoteException {
- mDelegate.unloadModel(modelHandle);
+ try (SafeCloseable ignored = ClearCallingIdentityContext.create()) {
+ mDelegate.unloadModel(modelHandle);
+ }
}
@Override
public void startRecognition(int modelHandle, RecognitionConfig config)
throws RemoteException {
- mDelegate.startRecognition(modelHandle, config);
+ try (SafeCloseable ignored = ClearCallingIdentityContext.create()) {
+ mDelegate.startRecognition(modelHandle, config);
+ }
}
@Override
public void stopRecognition(int modelHandle) throws RemoteException {
- mDelegate.stopRecognition(modelHandle);
+ try (SafeCloseable ignored = ClearCallingIdentityContext.create()) {
+ mDelegate.stopRecognition(modelHandle);
+ }
}
@Override
public void forceRecognitionEvent(int modelHandle) throws RemoteException {
- mDelegate.forceRecognitionEvent(modelHandle);
+ try (SafeCloseable ignored = ClearCallingIdentityContext.create()) {
+ mDelegate.forceRecognitionEvent(modelHandle);
+ }
}
@Override
public void setModelParameter(int modelHandle, int modelParam, int value)
throws RemoteException {
- mDelegate.setModelParameter(modelHandle, modelParam, value);
+ try (SafeCloseable ignored = ClearCallingIdentityContext.create()) {
+ mDelegate.setModelParameter(modelHandle, modelParam, value);
+ }
}
@Override
public int getModelParameter(int modelHandle, int modelParam) throws RemoteException {
- return mDelegate.getModelParameter(modelHandle, modelParam);
+ try (SafeCloseable ignored = ClearCallingIdentityContext.create()) {
+ return mDelegate.getModelParameter(modelHandle, modelParam);
+ }
}
@Override
public ModelParameterRange queryModelParameterSupport(int modelHandle, int modelParam)
throws RemoteException {
- return mDelegate.queryModelParameterSupport(modelHandle, modelParam);
+ try (SafeCloseable ignored = ClearCallingIdentityContext.create()) {
+ return mDelegate.queryModelParameterSupport(modelHandle, modelParam);
+ }
}
@Override
public void detach() throws RemoteException {
- mDelegate.detach();
+ try (SafeCloseable ignored = ClearCallingIdentityContext.create()) {
+ mDelegate.detach();
+ }
}
}
@@ -182,10 +244,11 @@ public class SoundTriggerMiddlewareService extends ISoundTriggerMiddlewareServic
publishBinderService(Context.SOUND_TRIGGER_MIDDLEWARE_SERVICE,
new SoundTriggerMiddlewareService(
new SoundTriggerMiddlewareLogging(
- new SoundTriggerMiddlewareValidation(
- new SoundTriggerMiddlewareImpl(factories,
- new AudioSessionProviderImpl()),
- getContext()))));
+ new SoundTriggerMiddlewarePermission(
+ new SoundTriggerMiddlewareValidation(
+ new SoundTriggerMiddlewareImpl(factories,
+ new AudioSessionProviderImpl())),
+ getContext())), getContext()));
}
}
}
diff --git a/services/core/java/com/android/server/soundtrigger_middleware/SoundTriggerMiddlewareValidation.java b/services/core/java/com/android/server/soundtrigger_middleware/SoundTriggerMiddlewareValidation.java
index 5d25d2cb554d..95a30c7f0278 100644
--- a/services/core/java/com/android/server/soundtrigger_middleware/SoundTriggerMiddlewareValidation.java
+++ b/services/core/java/com/android/server/soundtrigger_middleware/SoundTriggerMiddlewareValidation.java
@@ -16,11 +16,10 @@
package com.android.server.soundtrigger_middleware;
-import android.Manifest;
import android.annotation.NonNull;
import android.annotation.Nullable;
-import android.content.Context;
-import android.content.PermissionChecker;
+import android.media.permission.Identity;
+import android.media.permission.IdentityContext;
import android.media.soundtrigger_middleware.ISoundTriggerCallback;
import android.media.soundtrigger_middleware.ISoundTriggerMiddlewareService;
import android.media.soundtrigger_middleware.ISoundTriggerModule;
@@ -53,9 +52,9 @@ import java.util.concurrent.atomic.AtomicInteger;
import java.util.concurrent.atomic.AtomicReference;
/**
- * This is a decorator of an {@link ISoundTriggerMiddlewareService}, which enforces permissions and
- * correct usage by the client, as well as makes sure that exceptions representing a server
- * malfunction do not get sent to the client.
+ * This is a decorator of an {@link ISoundTriggerMiddlewareService}, which enforces correct usage by
+ * the client, as well as makes sure that exceptions representing a server malfunction get sent to
+ * the client in a consistent manner, which cannot be confused with a client fault.
* <p>
* This is intended to extract the non-business logic out of the underlying implementation and thus
* make it easier to maintain each one of those separate aspects. A design trade-off is being made
@@ -70,8 +69,6 @@ import java.util.concurrent.atomic.AtomicReference;
* pattern:
* <code><pre>
* @Override public T method(S arg) {
- * // Permission check.
- * checkPermissions();
* // Input validation.
* ValidationUtil.validateS(arg);
* synchronized (this) {
@@ -95,9 +92,8 @@ import java.util.concurrent.atomic.AtomicReference;
* with client-server separation.
* <p>
* <b>Exception handling approach:</b><br>
- * We make sure all client faults (permissions, argument and state validation) happen first, and
- * would throw {@link SecurityException}, {@link IllegalArgumentException}/
- * {@link NullPointerException} or {@link
+ * We make sure all client faults (argument and state validation) happen first, and
+ * would throw {@link IllegalArgumentException}/{@link NullPointerException} or {@link
* IllegalStateException}, respectively. All those exceptions are treated specially by Binder and
* will get sent back to the client.<br>
* Once this is done, any subsequent fault is considered either a recoverable (expected) or
@@ -116,11 +112,11 @@ public class SoundTriggerMiddlewareValidation implements ISoundTriggerMiddleware
ALIVE,
DETACHED,
DEAD
- };
+ }
private class ModuleState {
final @NonNull SoundTriggerModuleProperties properties;
- Set<ModuleService> sessions = new HashSet<>();
+ Set<Session> sessions = new HashSet<>();
private ModuleState(@NonNull SoundTriggerModuleProperties properties) {
this.properties = properties;
@@ -130,13 +126,11 @@ public class SoundTriggerMiddlewareValidation implements ISoundTriggerMiddleware
private AtomicReference<Boolean> mCaptureState = new AtomicReference<>();
private final @NonNull ISoundTriggerMiddlewareInternal mDelegate;
- private final @NonNull Context mContext;
private Map<Integer, ModuleState> mModules;
public SoundTriggerMiddlewareValidation(
- @NonNull ISoundTriggerMiddlewareInternal delegate, @NonNull Context context) {
+ @NonNull ISoundTriggerMiddlewareInternal delegate) {
mDelegate = delegate;
- mContext = context;
}
/**
@@ -169,8 +163,6 @@ public class SoundTriggerMiddlewareValidation implements ISoundTriggerMiddleware
@Override
public @NonNull
SoundTriggerModuleDescriptor[] listModules() {
- // Permission check.
- checkPermissions();
// Input validation (always valid).
synchronized (this) {
@@ -179,9 +171,22 @@ public class SoundTriggerMiddlewareValidation implements ISoundTriggerMiddleware
// From here on, every exception isn't client's fault.
try {
SoundTriggerModuleDescriptor[] result = mDelegate.listModules();
- mModules = new HashMap<>(result.length);
- for (SoundTriggerModuleDescriptor desc : result) {
- mModules.put(desc.handle, new ModuleState(desc.properties));
+ if (mModules == null) {
+ mModules = new HashMap<>(result.length);
+ for (SoundTriggerModuleDescriptor desc : result) {
+ mModules.put(desc.handle, new ModuleState(desc.properties));
+ }
+ } else {
+ if (result.length != mModules.size()) {
+ throw new RuntimeException(
+ "listModules must always return the same result.");
+ }
+ for (SoundTriggerModuleDescriptor desc : result) {
+ if (!mModules.containsKey(desc.handle)) {
+ throw new RuntimeException(
+ "listModules must always return the same result.");
+ }
+ }
}
return result;
} catch (Exception e) {
@@ -193,8 +198,6 @@ public class SoundTriggerMiddlewareValidation implements ISoundTriggerMiddleware
@Override
public @NonNull ISoundTriggerModule attach(int handle,
@NonNull ISoundTriggerCallback callback) {
- // Permission check.
- checkPermissions();
// Input validation.
Objects.requireNonNull(callback);
Objects.requireNonNull(callback.asBinder());
@@ -211,10 +214,9 @@ public class SoundTriggerMiddlewareValidation implements ISoundTriggerMiddleware
// From here on, every exception isn't client's fault.
try {
- ModuleService moduleService =
- new ModuleService(handle, callback);
- moduleService.attach(mDelegate.attach(handle, moduleService));
- return moduleService;
+ Session session = new Session(handle, callback);
+ session.attach(mDelegate.attach(handle, session.getCallbackWrapper()));
+ return session;
} catch (Exception e) {
throw handleException(e);
}
@@ -244,40 +246,6 @@ public class SoundTriggerMiddlewareValidation implements ISoundTriggerMiddleware
return mDelegate.toString();
}
- /**
- * Throws a {@link SecurityException} if caller permanently doesn't have the given permission,
- * or a {@link ServiceSpecificException} with a {@link Status#TEMPORARY_PERMISSION_DENIED} if
- * caller temporarily doesn't have the right permissions to use this service.
- */
- void checkPermissions() {
- enforcePermission(Manifest.permission.RECORD_AUDIO);
- enforcePermission(Manifest.permission.CAPTURE_AUDIO_HOTWORD);
- }
-
- /**
- * Throws a {@link SecurityException} if caller permanently doesn't have the given permission,
- * or a {@link ServiceSpecificException} with a {@link Status#TEMPORARY_PERMISSION_DENIED} if
- * caller temporarily doesn't have the given permission.
- *
- * @param permission The permission to check.
- */
- void enforcePermission(String permission) {
- final int status = PermissionChecker.checkCallingOrSelfPermissionForPreflight(mContext,
- permission);
- switch (status) {
- case PermissionChecker.PERMISSION_GRANTED:
- return;
- case PermissionChecker.PERMISSION_HARD_DENIED:
- throw new SecurityException(
- String.format("Caller must have the %s permission.", permission));
- case PermissionChecker.PERMISSION_SOFT_DENIED:
- throw new ServiceSpecificException(Status.TEMPORARY_PERMISSION_DENIED,
- String.format("Caller must have the %s permission.", permission));
- default:
- throw new RuntimeException("Unexpected perimission check result.");
- }
- }
-
@Override
public IBinder asBinder() {
throw new UnsupportedOperationException(
@@ -297,7 +265,7 @@ public class SoundTriggerMiddlewareValidation implements ISoundTriggerMiddleware
pw.printf("Module %d\n%s\n", handle,
ObjectPrinter.print(module.properties, true, 16));
pw.println("=========================================");
- for (ModuleService session : module.sessions) {
+ for (Session session : module.sessions) {
session.dump(pw);
}
}
@@ -327,7 +295,14 @@ public class SoundTriggerMiddlewareValidation implements ISoundTriggerMiddleware
/** Model is loaded, recognition is inactive. */
LOADED,
/** Model is loaded, recognition is active. */
- ACTIVE
+ ACTIVE,
+ /**
+ * Model is active as far as the client is concerned, but loaded as far as the
+ * layers are concerned. This condition occurs when a recognition event that indicates
+ * the recognition for this model arrived from the underlying layer, but had not been
+ * delivered to the caller (most commonly, for permission reasons).
+ */
+ INTERCEPTED,
}
/** Activity state. */
@@ -400,9 +375,7 @@ public class SoundTriggerMiddlewareValidation implements ISoundTriggerMiddleware
* A wrapper around an {@link ISoundTriggerModule} implementation, to address the same aspects
* mentioned in {@link SoundTriggerModule} above. This class follows the same conventions.
*/
- private class ModuleService extends ISoundTriggerModule.Stub implements ISoundTriggerCallback,
- IBinder.DeathRecipient {
- private final ISoundTriggerCallback mCallback;
+ private class Session extends ISoundTriggerModule.Stub {
private ISoundTriggerModule mDelegate;
// While generally all the fields of this class must be changed under a lock, an exception
// is made for the specific case of changing a model state from ACTIVE to LOADED, which
@@ -413,15 +386,17 @@ public class SoundTriggerMiddlewareValidation implements ISoundTriggerMiddleware
ConcurrentMap<Integer, ModelState> mLoadedModels = new ConcurrentHashMap<>();
private final int mHandle;
private ModuleStatus mState = ModuleStatus.ALIVE;
+ private final CallbackWrapper mCallbackWrapper;
+ private final Identity mOriginatorIdentity;
- ModuleService(int handle, @NonNull ISoundTriggerCallback callback) {
- mCallback = callback;
+ Session(int handle, @NonNull ISoundTriggerCallback callback) {
+ mCallbackWrapper = new CallbackWrapper(callback);
mHandle = handle;
- try {
- mCallback.asBinder().linkToDeath(this, 0);
- } catch (RemoteException e) {
- throw e.rethrowAsRuntimeException();
- }
+ mOriginatorIdentity = IdentityContext.get();
+ }
+
+ ISoundTriggerCallback getCallbackWrapper() {
+ return mCallbackWrapper;
}
void attach(@NonNull ISoundTriggerModule delegate) {
@@ -431,8 +406,6 @@ public class SoundTriggerMiddlewareValidation implements ISoundTriggerMiddleware
@Override
public int loadModel(@NonNull SoundModel model) {
- // Permission check.
- checkPermissions();
// Input validation.
ValidationUtil.validateGenericModel(model);
@@ -455,8 +428,6 @@ public class SoundTriggerMiddlewareValidation implements ISoundTriggerMiddleware
@Override
public int loadPhraseModel(@NonNull PhraseSoundModel model) {
- // Permission check.
- checkPermissions();
// Input validation.
ValidationUtil.validatePhraseModel(model);
@@ -479,8 +450,6 @@ public class SoundTriggerMiddlewareValidation implements ISoundTriggerMiddleware
@Override
public void unloadModel(int modelHandle) {
- // Permission check.
- checkPermissions();
// Input validation (always valid).
synchronized (SoundTriggerMiddlewareValidation.this) {
@@ -495,7 +464,7 @@ public class SoundTriggerMiddlewareValidation implements ISoundTriggerMiddleware
}
if (modelState.getActivityState() != ModelState.Activity.LOADED) {
throw new IllegalStateException("Model with handle: " + modelHandle
- + " has invalid state for unloading: " + modelState.getActivityState());
+ + " has invalid state for unloading");
}
// From here on, every exception isn't client's fault.
@@ -510,8 +479,6 @@ public class SoundTriggerMiddlewareValidation implements ISoundTriggerMiddleware
@Override
public void startRecognition(int modelHandle, @NonNull RecognitionConfig config) {
- // Permission check.
- checkPermissions();
// Input validation.
ValidationUtil.validateRecognitionConfig(config);
@@ -527,8 +494,7 @@ public class SoundTriggerMiddlewareValidation implements ISoundTriggerMiddleware
}
if (modelState.getActivityState() != ModelState.Activity.LOADED) {
throw new IllegalStateException("Model with handle: " + modelHandle
- + " has invalid state for starting recognition: "
- + modelState.getActivityState());
+ + " has invalid state for starting recognition");
}
// From here on, every exception isn't client's fault.
@@ -547,8 +513,6 @@ public class SoundTriggerMiddlewareValidation implements ISoundTriggerMiddleware
@Override
public void stopRecognition(int modelHandle) {
- // Permission check.
- checkPermissions();
// Input validation (always valid).
synchronized (SoundTriggerMiddlewareValidation.this) {
@@ -565,7 +529,12 @@ public class SoundTriggerMiddlewareValidation implements ISoundTriggerMiddleware
// From here on, every exception isn't client's fault.
try {
- mDelegate.stopRecognition(modelHandle);
+ // If the activity state is LOADED or INTERCEPTED, we skip delegating the
+ // command, but still consider the call valid. In either case, the resulting
+ // state is LOADED.
+ if (modelState.getActivityState() == ModelState.Activity.ACTIVE) {
+ mDelegate.stopRecognition(modelHandle);
+ }
modelState.setActivityState(ModelState.Activity.LOADED);
} catch (Exception e) {
throw handleException(e);
@@ -575,8 +544,6 @@ public class SoundTriggerMiddlewareValidation implements ISoundTriggerMiddleware
@Override
public void forceRecognitionEvent(int modelHandle) {
- // Permission check.
- checkPermissions();
// Input validation (always valid).
synchronized (SoundTriggerMiddlewareValidation.this) {
@@ -593,7 +560,11 @@ public class SoundTriggerMiddlewareValidation implements ISoundTriggerMiddleware
// From here on, every exception isn't client's fault.
try {
- mDelegate.forceRecognitionEvent(modelHandle);
+ // If the activity state is LOADED or INTERCEPTED, we skip delegating the
+ // command, but still consider the call valid.
+ if (modelState.getActivityState() == ModelState.Activity.ACTIVE) {
+ mDelegate.forceRecognitionEvent(modelHandle);
+ }
} catch (Exception e) {
throw handleException(e);
}
@@ -602,8 +573,6 @@ public class SoundTriggerMiddlewareValidation implements ISoundTriggerMiddleware
@Override
public void setModelParameter(int modelHandle, int modelParam, int value) {
- // Permission check.
- checkPermissions();
// Input validation.
ValidationUtil.validateModelParameter(modelParam);
@@ -630,8 +599,6 @@ public class SoundTriggerMiddlewareValidation implements ISoundTriggerMiddleware
@Override
public int getModelParameter(int modelHandle, int modelParam) {
- // Permission check.
- checkPermissions();
// Input validation.
ValidationUtil.validateModelParameter(modelParam);
@@ -659,8 +626,6 @@ public class SoundTriggerMiddlewareValidation implements ISoundTriggerMiddleware
@Override
@Nullable
public ModelParameterRange queryModelParameterSupport(int modelHandle, int modelParam) {
- // Permission check.
- checkPermissions();
// Input validation.
ValidationUtil.validateModelParameter(modelParam);
@@ -689,8 +654,6 @@ public class SoundTriggerMiddlewareValidation implements ISoundTriggerMiddleware
@Override
public void detach() {
- // Permission check.
- checkPermissions();
// Input validation (always valid).
synchronized (SoundTriggerMiddlewareValidation.this) {
@@ -714,14 +677,14 @@ public class SoundTriggerMiddlewareValidation implements ISoundTriggerMiddleware
// Override toString() in order to have the delegate's ID in it.
@Override
public String toString() {
- return Objects.toString(mDelegate.toString());
+ return Objects.toString(mDelegate);
}
private void detachInternal() {
try {
mDelegate.detach();
mState = ModuleStatus.DETACHED;
- mCallback.asBinder().unlinkToDeath(this, 0);
+ mCallbackWrapper.detached();
mModules.get(mHandle).sessions.remove(this);
} catch (RemoteException e) {
throw e.rethrowAsRuntimeException();
@@ -730,7 +693,10 @@ public class SoundTriggerMiddlewareValidation implements ISoundTriggerMiddleware
void dump(PrintWriter pw) {
if (mState == ModuleStatus.ALIVE) {
- pw.printf("Loaded models for session %s (handle, active)", toString());
+ pw.println("-------------------------------");
+ pw.printf("Session %s, client: %s\n", toString(),
+ ObjectPrinter.print(mOriginatorIdentity, true, 16));
+ pw.printf("Loaded models (handle, active, description):", toString());
pw.println();
pw.println("-------------------------------");
for (Map.Entry<Integer, ModelState> entry : mLoadedModels.entrySet()) {
@@ -741,16 +707,31 @@ public class SoundTriggerMiddlewareValidation implements ISoundTriggerMiddleware
pw.print(entry.getValue().description);
pw.println();
}
+ pw.println();
} else {
pw.printf("Session %s is dead", toString());
pw.println();
}
}
- ////////////////////////////////////////////////////////////////////////////////////////////
- // Callbacks
+ class CallbackWrapper implements ISoundTriggerCallback,
+ IBinder.DeathRecipient {
+ private final ISoundTriggerCallback mCallback;
- @Override
+ CallbackWrapper(ISoundTriggerCallback callback) {
+ mCallback = callback;
+ try {
+ mCallback.asBinder().linkToDeath(this, 0);
+ } catch (RemoteException e) {
+ throw e.rethrowAsRuntimeException();
+ }
+ }
+
+ void detached() {
+ mCallback.asBinder().unlinkToDeath(this, 0);
+ }
+
+ @Override
public void onRecognition(int modelHandle, @NonNull RecognitionEvent event) {
// We cannot obtain a lock on SoundTriggerMiddlewareValidation.this, since this call
// might be coming from the audio server (via setCaptureState()) while it is holding
@@ -759,18 +740,21 @@ public class SoundTriggerMiddlewareValidation implements ISoundTriggerMiddleware
// To avoid this problem, we use an atomic model activity state. There is a risk of the
// model not being in the mLoadedModels map here, since it might have been stopped /
// unloaded while the event was in flight.
- if (event.status != RecognitionStatus.FORCED) {
- ModelState modelState = mLoadedModels.get(modelHandle);
- if (modelState != null) {
+ ModelState modelState = mLoadedModels.get(modelHandle);
+ if (modelState != null) {
+ if (event.status != RecognitionStatus.FORCED) {
modelState.setActivityState(ModelState.Activity.LOADED);
}
- }
- try {
- mCallback.onRecognition(modelHandle, event);
- } catch (RemoteException e) {
- // Dead client will be handled by binderDied() - no need to handle here.
- // In any case, client callbacks are considered best effort.
- Log.e(TAG, "Client callback exception.", e);
+ try {
+ mCallback.onRecognition(modelHandle, event);
+ } catch (Exception e) {
+ // Dead client will be handled by binderDied() - no need to handle here.
+ // In any case, client callbacks are considered best effort.
+ Log.e(TAG, "Client callback exception.", e);
+ if (event.status != RecognitionStatus.FORCED) {
+ modelState.setActivityState(ModelState.Activity.INTERCEPTED);
+ }
+ }
}
}
@@ -783,18 +767,21 @@ public class SoundTriggerMiddlewareValidation implements ISoundTriggerMiddleware
// To avoid this problem, we use an atomic model activity state. There is a risk of the
// model not being in the mLoadedModels map here, since it might have been stopped /
// unloaded while the event was in flight.
- if (event.common.status != RecognitionStatus.FORCED) {
- ModelState modelState = mLoadedModels.get(modelHandle);
- if (modelState != null) {
- modelState.setActivityState(ModelState.Activity.LOADED);
+ ModelState modelState = mLoadedModels.get(modelHandle);
+ if (modelState != null) {
+ if (event.common.status != RecognitionStatus.FORCED) {
+ modelState.setActivityState(ModelState.Activity.LOADED);
+ }
+ try {
+ mCallback.onPhraseRecognition(modelHandle, event);
+ } catch (Exception e) {
+ // Dead client will be handled by binderDied() - no need to handle here.
+ // In any case, client callbacks are considered best effort.
+ Log.e(TAG, "Client callback exception.", e);
+ if (event.common.status != RecognitionStatus.FORCED) {
+ modelState.setActivityState(ModelState.Activity.INTERCEPTED);
+ }
}
- }
- try {
- mCallback.onPhraseRecognition(modelHandle, event);
- } catch (RemoteException e) {
- // Dead client will be handled by binderDied() - no need to handle here.
- // In any case, client callbacks are considered best effort.
- Log.e(TAG, "Client callback exception.", e);
}
}
@@ -810,41 +797,53 @@ public class SoundTriggerMiddlewareValidation implements ISoundTriggerMiddleware
}
}
- @Override
- public void onModuleDied() {
- synchronized (SoundTriggerMiddlewareValidation.this) {
- mState = ModuleStatus.DEAD;
- }
- // Trigger the callback outside of the lock to avoid deadlocks.
- try {
- mCallback.onModuleDied();
- } catch (RemoteException e) {
- // Dead client will be handled by binderDied() - no need to handle here.
- // In any case, client callbacks are considered best effort.
- Log.e(TAG, "Client callback exception.", e);
+ @Override
+ public void onModuleDied() {
+ synchronized (SoundTriggerMiddlewareValidation.this) {
+ mState = ModuleStatus.DEAD;
+ }
+ // Trigger the callback outside of the lock to avoid deadlocks.
+ try {
+ mCallback.onModuleDied();
+ } catch (RemoteException e) {
+ // Dead client will be handled by binderDied() - no need to handle here.
+ // In any case, client callbacks are considered best effort.
+ Log.e(TAG, "Client callback exception.", e);
+ }
}
- }
-
- @Override
- public void binderDied() {
- // This is called whenever our client process dies.
- synchronized (SoundTriggerMiddlewareValidation.this) {
- try {
- // Gracefully stop all active recognitions and unload the models.
- for (Map.Entry<Integer, ModelState> entry :
- mLoadedModels.entrySet()) {
- // Idempotent call, no harm in calling even for models that are already
- // stopped.
- mDelegate.stopRecognition(entry.getKey());
- mDelegate.unloadModel(entry.getKey());
+ @Override
+ public void binderDied() {
+ // This is called whenever our client process dies.
+ synchronized (SoundTriggerMiddlewareValidation.this) {
+ try {
+ // Gracefully stop all active recognitions and unload the models.
+ for (Map.Entry<Integer, ModelState> entry :
+ mLoadedModels.entrySet()) {
+ if (entry.getValue().getActivityState()
+ == ModelState.Activity.ACTIVE) {
+ mDelegate.stopRecognition(entry.getKey());
+ }
+ mDelegate.unloadModel(entry.getKey());
+ }
+ // Detach.
+ detachInternal();
+ } catch (Exception e) {
+ throw handleException(e);
}
- // Detach.
- detachInternal();
- } catch (Exception e) {
- throw handleException(e);
}
}
+
+ @Override
+ public IBinder asBinder() {
+ return mCallback.asBinder();
+ }
+
+ // Override toString() in order to have the delegate's ID in it.
+ @Override
+ public String toString() {
+ return Objects.toString(mDelegate);
+ }
}
}
}
diff --git a/services/core/java/com/android/server/soundtrigger_middleware/SoundTriggerModule.java b/services/core/java/com/android/server/soundtrigger_middleware/SoundTriggerModule.java
index 5a587cc9764c..02d978dfdf99 100644
--- a/services/core/java/com/android/server/soundtrigger_middleware/SoundTriggerModule.java
+++ b/services/core/java/com/android/server/soundtrigger_middleware/SoundTriggerModule.java
@@ -184,11 +184,13 @@ class SoundTriggerModule implements IHwBinder.DeathRecipient {
@Override
public void serviceDied(long cookie) {
Log.w(TAG, String.format("Underlying HAL driver died."));
- List<ISoundTriggerCallback> callbacks = new ArrayList<>(mActiveSessions.size());
+ List<ISoundTriggerCallback> callbacks;
synchronized (this) {
+ callbacks = new ArrayList<>(mActiveSessions.size());
for (Session session : mActiveSessions) {
callbacks.add(session.moduleDied());
}
+ mActiveSessions.clear();
reset();
}
// Trigger the callbacks outside of the lock to avoid deadlocks.
@@ -431,7 +433,6 @@ class SoundTriggerModule implements IHwBinder.DeathRecipient {
*/
private ISoundTriggerCallback moduleDied() {
ISoundTriggerCallback callback = mCallback;
- removeSession(this);
mCallback = null;
return callback;
}
diff --git a/services/core/java/com/android/server/timezonedetector/TimeZoneDetectorService.java b/services/core/java/com/android/server/timezonedetector/TimeZoneDetectorService.java
index 7501d9fe6a7c..73322a6987df 100644
--- a/services/core/java/com/android/server/timezonedetector/TimeZoneDetectorService.java
+++ b/services/core/java/com/android/server/timezonedetector/TimeZoneDetectorService.java
@@ -57,8 +57,12 @@ public final class TimeZoneDetectorService extends ITimeZoneDetectorService.Stub
private static final String TAG = "TimeZoneDetectorService";
- /** A compile time switch for enabling / disabling geolocation-based time zone detection. */
- private static final boolean GEOLOCATION_TIME_ZONE_DETECTION_ENABLED = false;
+ /**
+ * A compile time constant "feature switch" for enabling / disabling location-based time zone
+ * detection on Android. If this is {@code false}, there should be few / little changes in
+ * behavior with previous releases and little overhead associated with geolocation components.
+ */
+ public static final boolean GEOLOCATION_TIME_ZONE_DETECTION_ENABLED = false;
/**
* Handles the service lifecycle for {@link TimeZoneDetectorService} and
diff --git a/services/core/java/com/android/server/vr/Vr2dDisplay.java b/services/core/java/com/android/server/vr/Vr2dDisplay.java
index 3f2b5c231dca..a713e5b13667 100644
--- a/services/core/java/com/android/server/vr/Vr2dDisplay.java
+++ b/services/core/java/com/android/server/vr/Vr2dDisplay.java
@@ -295,6 +295,7 @@ class Vr2dDisplay {
flags |= DisplayManager.VIRTUAL_DISPLAY_FLAG_OWN_CONTENT_ONLY;
flags |= DisplayManager.VIRTUAL_DISPLAY_FLAG_DESTROY_CONTENT_ON_REMOVAL;
flags |= DisplayManager.VIRTUAL_DISPLAY_FLAG_SECURE;
+ flags |= DisplayManager.VIRTUAL_DISPLAY_FLAG_TRUSTED;
final VirtualDisplayConfig.Builder builder = new VirtualDisplayConfig.Builder(
DISPLAY_NAME, mVirtualDisplayWidth, mVirtualDisplayHeight, mVirtualDisplayDpi);
diff --git a/services/core/java/com/android/server/wm/ActivityRecord.java b/services/core/java/com/android/server/wm/ActivityRecord.java
index 3e638502db56..67924307f009 100644
--- a/services/core/java/com/android/server/wm/ActivityRecord.java
+++ b/services/core/java/com/android/server/wm/ActivityRecord.java
@@ -2606,7 +2606,7 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A
Slog.v(TAG_USER_LEAVING, "finish() => pause with userLeaving=false");
}
stack.startPausingLocked(false /* userLeaving */, false /* uiSleeping */,
- null /* resuming */);
+ null /* resuming */, "finish");
}
if (endTask) {
@@ -4775,7 +4775,7 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A
if (deferHidingClient) {
getRootTask().startPausingLocked(
mStackSupervisor.mUserLeaving /* userLeaving */,
- false /* uiSleeping */, null /* resuming */);
+ false /* uiSleeping */, null /* resuming */, "makeInvisible");
break;
}
case INITIALIZING:
diff --git a/services/core/java/com/android/server/wm/ActivityStarter.java b/services/core/java/com/android/server/wm/ActivityStarter.java
index e8e4059af324..fa4373ff2f3a 100644
--- a/services/core/java/com/android/server/wm/ActivityStarter.java
+++ b/services/core/java/com/android/server/wm/ActivityStarter.java
@@ -700,11 +700,15 @@ class ActivityStarter {
mService.updateConfigurationLocked(mRequest.globalConfig, null, false);
}
+ // The original options may have additional info about metrics. The mOptions is not
+ // used here because it may be cleared in setTargetStackIfNeeded.
+ final ActivityOptions originalOptions = mRequest.activityOptions != null
+ ? mRequest.activityOptions.getOriginalOptions() : null;
// Notify ActivityMetricsLogger that the activity has launched.
// ActivityMetricsLogger will then wait for the windows to be drawn and populate
// WaitResult.
mSupervisor.getActivityMetricsLogger().notifyActivityLaunched(launchingState, res,
- mLastStartActivityRecord, mOptions);
+ mLastStartActivityRecord, originalOptions);
return getExternalResult(mRequest.waitResult == null ? res
: waitForResult(res, mLastStartActivityRecord));
}
@@ -1493,9 +1497,10 @@ class ActivityStarter {
// anyone interested in this piece of information.
final Task homeStack = targetTask.getDisplayArea().getRootHomeTask();
final boolean homeTaskVisible = homeStack != null && homeStack.shouldBeVisible(null);
+ final ActivityRecord top = targetTask.getTopNonFinishingActivity();
+ final boolean visible = top != null && top.isVisible();
mService.getTaskChangeNotificationController().notifyActivityRestartAttempt(
- targetTask.getTaskInfo(), homeTaskVisible, clearedTask,
- targetTask.getTopNonFinishingActivity().isVisible());
+ targetTask.getTaskInfo(), homeTaskVisible, clearedTask, visible);
}
}
diff --git a/services/core/java/com/android/server/wm/ActivityTaskManagerService.java b/services/core/java/com/android/server/wm/ActivityTaskManagerService.java
index ebfd0cd9fd83..15669cbe227a 100644
--- a/services/core/java/com/android/server/wm/ActivityTaskManagerService.java
+++ b/services/core/java/com/android/server/wm/ActivityTaskManagerService.java
@@ -123,7 +123,6 @@ import static com.android.server.wm.Task.ActivityState.DESTROYED;
import static com.android.server.wm.Task.ActivityState.DESTROYING;
import static com.android.server.wm.Task.LOCK_TASK_AUTH_DONT_LOCK;
import static com.android.server.wm.Task.REPARENT_KEEP_STACK_AT_FRONT;
-import static com.android.server.wm.Task.REPARENT_LEAVE_STACK_IN_PLACE;
import static com.android.server.wm.WindowContainer.POSITION_TOP;
import static com.android.server.wm.WindowManagerService.UPDATE_FOCUS_NORMAL;
@@ -3952,52 +3951,6 @@ public class ActivityTaskManagerService extends IActivityTaskManager.Stub {
}
}
- /**
- * Try to place task to provided position. The final position might be different depending on
- * current user and stacks state. The task will be moved to target stack if it's currently in
- * different stack.
- */
- @Override
- public void positionTaskInStack(int taskId, int stackId, int position) {
- mAmInternal.enforceCallingPermission(MANAGE_ACTIVITY_STACKS, "positionTaskInStack()");
- synchronized (mGlobalLock) {
- long ident = Binder.clearCallingIdentity();
- try {
- if (DEBUG_STACK) Slog.d(TAG_STACK, "positionTaskInStack: positioning task="
- + taskId + " in stackId=" + stackId + " at position=" + position);
- final Task task = mRootWindowContainer.anyTaskForId(taskId);
- if (task == null) {
- throw new IllegalArgumentException("positionTaskInStack: no task for id="
- + taskId);
- }
-
- final Task stack = mRootWindowContainer.getStack(stackId);
-
- if (stack == null) {
- throw new IllegalArgumentException("positionTaskInStack: no stack for id="
- + stackId);
- }
- if (!stack.isActivityTypeStandardOrUndefined()) {
- throw new IllegalArgumentException("positionTaskInStack: Attempt to change"
- + " the position of task " + taskId + " in/to non-standard stack");
- }
-
- // TODO: Have the callers of this API call a separate reparent method if that is
- // what they intended to do vs. having this method also do reparenting.
- if (task.getRootTask() == stack) {
- // Change position in current stack.
- stack.positionChildAt(task, position);
- } else {
- // Reparent to new stack.
- task.reparent(stack, position, REPARENT_LEAVE_STACK_IN_PLACE, !ANIMATE,
- !DEFER_RESUME, "positionTaskInStack");
- }
- } finally {
- Binder.restoreCallingIdentity(ident);
- }
- }
- }
-
@Override
public void reportSizeConfigurations(IBinder token, int[] horizontalSizeConfiguration,
int[] verticalSizeConfigurations, int[] smallestSizeConfigurations) {
diff --git a/services/core/java/com/android/server/wm/BLASTSyncEngine.java b/services/core/java/com/android/server/wm/BLASTSyncEngine.java
index 6305fda4924c..958a7a8f07f8 100644
--- a/services/core/java/com/android/server/wm/BLASTSyncEngine.java
+++ b/services/core/java/com/android/server/wm/BLASTSyncEngine.java
@@ -16,9 +16,13 @@
package com.android.server.wm;
+import static com.android.internal.protolog.ProtoLogGroup.WM_DEBUG_SYNC_ENGINE;
+
import android.util.ArrayMap;
import android.util.ArraySet;
+import com.android.internal.protolog.common.ProtoLog;
+
import java.util.Set;
/**
@@ -63,25 +67,38 @@ class BLASTSyncEngine {
private void tryFinish() {
if (mRemainingTransactions == 0 && mReady) {
+ ProtoLog.v(WM_DEBUG_SYNC_ENGINE, "SyncSet{%x:%d} Finished. Reporting %d "
+ + "containers to %s", BLASTSyncEngine.this.hashCode(), mSyncId,
+ mWindowContainersReady.size(), mListener);
mListener.onTransactionReady(mSyncId, mWindowContainersReady);
mPendingSyncs.remove(mSyncId);
}
}
- public void onTransactionReady(int mSyncId, Set<WindowContainer> windowContainersReady) {
+ public void onTransactionReady(int syncId, Set<WindowContainer> windowContainersReady) {
mRemainingTransactions--;
+ ProtoLog.v(WM_DEBUG_SYNC_ENGINE, "SyncSet{%x:%d} Child ready, now ready=%b"
+ + " and waiting on %d transactions", BLASTSyncEngine.this.hashCode(), mSyncId,
+ mReady, mRemainingTransactions);
mWindowContainersReady.addAll(windowContainersReady);
tryFinish();
}
void setReady() {
+ ProtoLog.v(WM_DEBUG_SYNC_ENGINE, "SyncSet{%x:%d} Set ready",
+ BLASTSyncEngine.this.hashCode(), mSyncId);
mReady = true;
tryFinish();
}
boolean addToSync(WindowContainer wc) {
+ ProtoLog.v(WM_DEBUG_SYNC_ENGINE, "SyncSet{%x:%d} Trying to add %s",
+ BLASTSyncEngine.this.hashCode(), mSyncId, wc);
if (wc.prepareForSync(this, mSyncId)) {
mRemainingTransactions++;
+ ProtoLog.v(WM_DEBUG_SYNC_ENGINE, "SyncSet{%x:%d} Added %s. now waiting "
+ + "on %d transactions", BLASTSyncEngine.this.hashCode(), mSyncId, wc,
+ mRemainingTransactions);
return true;
}
return false;
@@ -105,6 +122,7 @@ class BLASTSyncEngine {
final int id = mNextSyncId++;
final SyncState s = new SyncState(listener, id);
mPendingSyncs.put(id, s);
+ ProtoLog.v(WM_DEBUG_SYNC_ENGINE, "SyncSet{%x:%d} Start for %s", hashCode(), id, listener);
return id;
}
diff --git a/services/core/java/com/android/server/wm/BarController.java b/services/core/java/com/android/server/wm/BarController.java
index 8568d5fc1d64..4a90bbcc6623 100644
--- a/services/core/java/com/android/server/wm/BarController.java
+++ b/services/core/java/com/android/server/wm/BarController.java
@@ -16,88 +16,19 @@
package com.android.server.wm;
-import static com.android.server.wm.BarControllerProto.STATE;
-import static com.android.server.wm.BarControllerProto.TRANSIENT_STATE;
-
import android.annotation.NonNull;
-import android.app.StatusBarManager;
import android.graphics.Rect;
-import android.os.Handler;
-import android.os.Message;
-import android.os.SystemClock;
-import android.util.Slog;
-import android.util.proto.ProtoOutputStream;
-import android.view.View;
-import android.view.ViewRootImpl;
-import android.view.WindowManager;
-
-import com.android.server.LocalServices;
-import com.android.server.UiThread;
-import com.android.server.statusbar.StatusBarManagerInternal;
-
-import java.io.PrintWriter;
/**
* Controls state/behavior specific to a system bar window.
*/
public class BarController {
- private static final boolean DEBUG = false;
-
- private static final int TRANSIENT_BAR_NONE = 0;
- private static final int TRANSIENT_BAR_SHOW_REQUESTED = 1;
- private static final int TRANSIENT_BAR_SHOWING = 2;
- private static final int TRANSIENT_BAR_HIDING = 3;
-
- private static final int TRANSLUCENT_ANIMATION_DELAY_MS = 1000;
-
- private static final int MSG_NAV_BAR_VISIBILITY_CHANGED = 1;
-
- protected final String mTag;
- protected final int mDisplayId;
- private final int mTransientFlag;
- private final int mUnhideFlag;
- private final int mTranslucentFlag;
- private final int mTransparentFlag;
- private final int mStatusBarManagerId;
- private final int mTranslucentWmFlag;
private final int mWindowType;
- protected final Handler mHandler;
- private final Object mServiceAquireLock = new Object();
- private StatusBarManagerInternal mStatusBarInternal;
- protected WindowState mWin;
- private @StatusBarManager.WindowVisibleState int mState =
- StatusBarManager.WINDOW_STATE_SHOWING;
- private int mTransientBarState;
- private boolean mPendingShow;
- private long mLastTranslucent;
- private boolean mShowTransparent;
- private boolean mSetUnHideFlagWhenNextTransparent;
- private boolean mNoAnimationOnNextShow;
private final Rect mContentFrame = new Rect();
- private OnBarVisibilityChangedListener mVisibilityChangeListener;
-
- BarController(String tag, int displayId, int transientFlag, int unhideFlag, int translucentFlag,
- int statusBarManagerId, int windowType, int translucentWmFlag, int transparentFlag) {
- mTag = "BarController." + tag;
- mDisplayId = displayId;
- mTransientFlag = transientFlag;
- mUnhideFlag = unhideFlag;
- mTranslucentFlag = translucentFlag;
- mStatusBarManagerId = statusBarManagerId;
+ BarController(int windowType) {
mWindowType = windowType;
- mTranslucentWmFlag = translucentWmFlag;
- mTransparentFlag = transparentFlag;
- mHandler = new BarHandler();
- }
-
- void setWindow(WindowState win) {
- if (ViewRootImpl.sNewInsetsMode == ViewRootImpl.NEW_INSETS_MODE_FULL) {
- // BarController gets replaced with InsetsPolicy in the full insets mode.
- return;
- }
- mWin = win;
}
/**
@@ -109,67 +40,6 @@ public class BarController {
mContentFrame.set(frame);
}
- void setShowTransparent(boolean transparent) {
- if (transparent != mShowTransparent) {
- mShowTransparent = transparent;
- mSetUnHideFlagWhenNextTransparent = transparent;
- mNoAnimationOnNextShow = true;
- }
- }
-
- void showTransient() {
- if (mWin != null) {
- setTransientBarState(TRANSIENT_BAR_SHOW_REQUESTED);
- }
- }
-
- boolean isTransientShowing() {
- return mTransientBarState == TRANSIENT_BAR_SHOWING;
- }
-
- boolean isTransientShowRequested() {
- return mTransientBarState == TRANSIENT_BAR_SHOW_REQUESTED;
- }
-
- boolean wasRecentlyTranslucent() {
- return (SystemClock.uptimeMillis() - mLastTranslucent) < TRANSLUCENT_ANIMATION_DELAY_MS;
- }
-
- void adjustSystemUiVisibilityLw(int oldVis, int vis) {
- if (mWin != null && mTransientBarState == TRANSIENT_BAR_SHOWING
- && (vis & mTransientFlag) == 0) {
- // sysui requests hide
- setTransientBarState(TRANSIENT_BAR_HIDING);
- setBarShowingLw(false);
- } else if (mWin != null && (oldVis & mUnhideFlag) != 0 && (vis & mUnhideFlag) == 0) {
- // sysui ready to unhide
- setBarShowingLw(true);
- }
- }
-
- int applyTranslucentFlagLw(WindowState win, int vis, int oldVis) {
- if (mWin != null) {
- if (win != null) {
- int fl = PolicyControl.getWindowFlags(win, null);
- if ((fl & mTranslucentWmFlag) != 0) {
- vis |= mTranslucentFlag;
- } else {
- vis &= ~mTranslucentFlag;
- }
- if ((fl & WindowManager.LayoutParams.FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS) != 0
- && isTransparentAllowed(win)) {
- vis |= mTransparentFlag;
- } else {
- vis &= ~mTransparentFlag;
- }
- } else {
- vis = (vis & ~mTranslucentFlag) | (oldVis & mTranslucentFlag);
- vis = (vis & ~mTransparentFlag) | (oldVis & mTransparentFlag);
- }
- }
- return vis;
- }
-
private Rect getContentFrame(@NonNull WindowState win) {
final Rect rotatedContentFrame = win.mToken.getFixedRotationBarContentFrame(mWindowType);
return rotatedContentFrame != null ? rotatedContentFrame : mContentFrame;
@@ -188,212 +58,4 @@ public class BarController {
}
return win.letterboxNotIntersectsOrFullyContains(getContentFrame(win));
}
-
- boolean setBarShowingLw(final boolean show) {
- if (mWin == null) return false;
- if (show && mTransientBarState == TRANSIENT_BAR_HIDING) {
- mPendingShow = true;
- return false;
- }
- final boolean wasVis = mWin.isVisibleLw();
- final boolean wasAnim = mWin.isAnimatingLw();
- final boolean skipAnim = skipAnimation();
- final boolean change = show ? mWin.showLw(!mNoAnimationOnNextShow && !skipAnim)
- : mWin.hideLw(!mNoAnimationOnNextShow && !skipAnim);
- mNoAnimationOnNextShow = false;
- final int state = computeStateLw(wasVis, wasAnim, mWin, change);
- final boolean stateChanged = updateStateLw(state);
-
- if (change && (mVisibilityChangeListener != null)) {
- mHandler.obtainMessage(MSG_NAV_BAR_VISIBILITY_CHANGED, show ? 1 : 0, 0).sendToTarget();
- }
-
- return change || stateChanged;
- }
-
- void setOnBarVisibilityChangedListener(OnBarVisibilityChangedListener listener,
- boolean invokeWithState) {
- mVisibilityChangeListener = listener;
- if (invokeWithState) {
- // Optionally report the initial window state for initialization purposes
- mHandler.obtainMessage(MSG_NAV_BAR_VISIBILITY_CHANGED,
- (mState == StatusBarManager.WINDOW_STATE_SHOWING) ? 1 : 0, 0).sendToTarget();
- }
- }
-
- protected boolean skipAnimation() {
- return !mWin.isDrawn();
- }
-
- private @StatusBarManager.WindowVisibleState int computeStateLw(
- boolean wasVis, boolean wasAnim, WindowState win, boolean change) {
- if (win.isDrawn()) {
- final boolean vis = win.isVisibleLw();
- final boolean anim = win.isAnimatingLw();
- if (mState == StatusBarManager.WINDOW_STATE_HIDING && !change && !vis) {
- return StatusBarManager.WINDOW_STATE_HIDDEN;
- } else if (mState == StatusBarManager.WINDOW_STATE_HIDDEN && vis) {
- return StatusBarManager.WINDOW_STATE_SHOWING;
- } else if (change) {
- if (wasVis && vis && !wasAnim && anim) {
- return StatusBarManager.WINDOW_STATE_HIDING;
- } else {
- return StatusBarManager.WINDOW_STATE_SHOWING;
- }
- }
- }
- return mState;
- }
-
- private boolean updateStateLw(@StatusBarManager.WindowVisibleState final int state) {
- if (mWin != null && state != mState) {
- mState = state;
- if (DEBUG) Slog.d(mTag, "mState: " + StatusBarManager.windowStateToString(state));
- mHandler.post(new Runnable() {
- @Override
- public void run() {
- StatusBarManagerInternal statusbar = getStatusBarInternal();
- if (statusbar != null) {
- statusbar.setWindowState(mDisplayId, mStatusBarManagerId, state);
- }
- }
- });
- return true;
- }
- return false;
- }
-
- boolean checkHiddenLw() {
- if (mWin != null && mWin.isDrawn()) {
- if (!mWin.isVisibleLw() && !mWin.isAnimatingLw()) {
- updateStateLw(StatusBarManager.WINDOW_STATE_HIDDEN);
- }
- if (mTransientBarState == TRANSIENT_BAR_HIDING && !mWin.isVisibleLw()) {
- // Finished animating out, clean up and reset style
- setTransientBarState(TRANSIENT_BAR_NONE);
- if (mPendingShow) {
- setBarShowingLw(true);
- mPendingShow = false;
- }
- return true;
- }
- }
- return false;
- }
-
- boolean checkShowTransientBarLw() {
- if (mTransientBarState == TRANSIENT_BAR_SHOWING) {
- if (DEBUG) Slog.d(mTag, "Not showing transient bar, already shown");
- return false;
- } else if (mTransientBarState == TRANSIENT_BAR_SHOW_REQUESTED) {
- if (DEBUG) Slog.d(mTag, "Not showing transient bar, already requested");
- return false;
- } else if (mWin == null) {
- if (DEBUG) Slog.d(mTag, "Not showing transient bar, bar doesn't exist");
- return false;
- } else if (mWin.isDisplayed()) {
- if (DEBUG) Slog.d(mTag, "Not showing transient bar, bar already visible");
- return false;
- } else {
- return true;
- }
- }
-
- int updateVisibilityLw(boolean transientAllowed, int oldVis, int vis) {
- if (mWin == null) return vis;
- if (isTransientShowing() || isTransientShowRequested()) { // transient bar requested
- if (transientAllowed) {
- vis |= mTransientFlag;
- if ((oldVis & mTransientFlag) == 0) {
- vis |= mUnhideFlag; // tell sysui we're ready to unhide
- }
- setTransientBarState(TRANSIENT_BAR_SHOWING); // request accepted
- } else {
- setTransientBarState(TRANSIENT_BAR_NONE); // request denied
- }
- }
- if (mShowTransparent) {
- vis |= mTransparentFlag;
- if (mSetUnHideFlagWhenNextTransparent) {
- vis |= mUnhideFlag;
- mSetUnHideFlagWhenNextTransparent = false;
- }
- }
- if (mTransientBarState != TRANSIENT_BAR_NONE) {
- vis |= mTransientFlag; // ignore clear requests until transition completes
- vis &= ~View.SYSTEM_UI_FLAG_LOW_PROFILE; // never show transient bars in low profile
- }
- if ((vis & mTranslucentFlag) != 0 || (oldVis & mTranslucentFlag) != 0
- || ((vis | oldVis) & mTransparentFlag) != 0) {
- mLastTranslucent = SystemClock.uptimeMillis();
- }
- return vis;
- }
-
- private void setTransientBarState(int state) {
- if (mWin != null && state != mTransientBarState) {
- if (mTransientBarState == TRANSIENT_BAR_SHOWING || state == TRANSIENT_BAR_SHOWING) {
- mLastTranslucent = SystemClock.uptimeMillis();
- }
- mTransientBarState = state;
- if (DEBUG) Slog.d(mTag, "mTransientBarState: " + transientBarStateToString(state));
- }
- }
-
- protected StatusBarManagerInternal getStatusBarInternal() {
- synchronized (mServiceAquireLock) {
- if (mStatusBarInternal == null) {
- mStatusBarInternal = LocalServices.getService(StatusBarManagerInternal.class);
- }
- return mStatusBarInternal;
- }
- }
-
- private static String transientBarStateToString(int state) {
- if (state == TRANSIENT_BAR_HIDING) return "TRANSIENT_BAR_HIDING";
- if (state == TRANSIENT_BAR_SHOWING) return "TRANSIENT_BAR_SHOWING";
- if (state == TRANSIENT_BAR_SHOW_REQUESTED) return "TRANSIENT_BAR_SHOW_REQUESTED";
- if (state == TRANSIENT_BAR_NONE) return "TRANSIENT_BAR_NONE";
- throw new IllegalArgumentException("Unknown state " + state);
- }
-
- void dumpDebug(ProtoOutputStream proto, long fieldId) {
- final long token = proto.start(fieldId);
- proto.write(STATE, mState);
- proto.write(TRANSIENT_STATE, mTransientBarState);
- proto.end(token);
- }
-
- void dump(PrintWriter pw, String prefix) {
- if (mWin != null) {
- pw.print(prefix); pw.println(mTag);
- pw.print(prefix); pw.print(" "); pw.print("mState"); pw.print('=');
- pw.println(StatusBarManager.windowStateToString(mState));
- pw.print(prefix); pw.print(" "); pw.print("mTransientBar"); pw.print('=');
- pw.println(transientBarStateToString(mTransientBarState));
- pw.print(prefix); pw.print(" mContentFrame="); pw.println(mContentFrame);
- }
- }
-
- private class BarHandler extends Handler {
- BarHandler() {
- super(UiThread.getHandler().getLooper());
- }
-
- @Override
- public void handleMessage(Message msg) {
- switch (msg.what) {
- case MSG_NAV_BAR_VISIBILITY_CHANGED:
- final boolean visible = msg.arg1 != 0;
- if (mVisibilityChangeListener != null) {
- mVisibilityChangeListener.onBarVisibilityChanged(visible);
- }
- break;
- }
- }
- }
-
- interface OnBarVisibilityChangedListener {
- void onBarVisibilityChanged(boolean visible);
- }
}
diff --git a/services/core/java/com/android/server/wm/DisplayAreaOrganizerController.java b/services/core/java/com/android/server/wm/DisplayAreaOrganizerController.java
index 01c007e381b1..dd92f507a33d 100644
--- a/services/core/java/com/android/server/wm/DisplayAreaOrganizerController.java
+++ b/services/core/java/com/android/server/wm/DisplayAreaOrganizerController.java
@@ -52,10 +52,7 @@ public class DisplayAreaOrganizerController extends IDisplayAreaOrganizerControl
public void binderDied() {
synchronized (mGlobalLock) {
mOrganizersByFeatureIds.remove(mFeature);
- mService.mRootWindowContainer.forAllDisplayAreas((da) -> {
- if (da.mOrganizer != mOrganizer) return;
- da.setOrganizer(null);
- });
+ removeOrganizer(mOrganizer);
}
}
}
@@ -112,11 +109,7 @@ public class DisplayAreaOrganizerController extends IDisplayAreaOrganizerControl
organizer.asBinder(), uid);
mOrganizersByFeatureIds.entrySet().removeIf(
entry -> entry.getValue().asBinder() == organizer.asBinder());
-
- mService.mRootWindowContainer.forAllDisplayAreas((da) -> {
- if (da.mOrganizer != organizer) return;
- da.setOrganizer(null);
- });
+ removeOrganizer(organizer);
}
} finally {
Binder.restoreCallingIdentity(origId);
@@ -151,4 +144,13 @@ public class DisplayAreaOrganizerController extends IDisplayAreaOrganizerControl
// Oh well...
}
}
+
+ private void removeOrganizer(IDisplayAreaOrganizer organizer) {
+ IBinder organizerBinder = organizer.asBinder();
+ mService.mRootWindowContainer.forAllDisplayAreas((da) -> {
+ if (da.mOrganizer != null && da.mOrganizer.asBinder().equals(organizerBinder)) {
+ da.setOrganizer(null);
+ }
+ });
+ }
}
diff --git a/services/core/java/com/android/server/wm/DisplayContent.java b/services/core/java/com/android/server/wm/DisplayContent.java
index 2ca849de7f22..0be5cbe5199c 100644
--- a/services/core/java/com/android/server/wm/DisplayContent.java
+++ b/services/core/java/com/android/server/wm/DisplayContent.java
@@ -28,6 +28,8 @@ import static android.app.WindowConfiguration.WINDOWING_MODE_MULTI_WINDOW;
import static android.app.WindowConfiguration.WINDOWING_MODE_PINNED;
import static android.app.WindowConfiguration.WINDOWING_MODE_SPLIT_SCREEN_SECONDARY;
import static android.app.WindowConfiguration.WINDOWING_MODE_UNDEFINED;
+import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_BEHIND;
+import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_UNSET;
import static android.content.res.Configuration.ORIENTATION_LANDSCAPE;
import static android.content.res.Configuration.ORIENTATION_PORTRAIT;
import static android.content.res.Configuration.ORIENTATION_UNDEFINED;
@@ -192,7 +194,6 @@ import android.view.SurfaceControl;
import android.view.SurfaceControl.Transaction;
import android.view.SurfaceSession;
import android.view.View;
-import android.view.ViewRootImpl;
import android.view.WindowInsets;
import android.view.WindowManager;
import android.view.WindowManagerPolicyConstants.PointerEventListener;
@@ -465,6 +466,9 @@ class DisplayContent extends RootDisplayArea implements WindowManagerPolicy.Disp
*/
ActivityRecord mFocusedApp = null;
+ /** The last focused {@link TaskDisplayArea} on this display. */
+ private TaskDisplayArea mLastFocusedTaskDisplayArea = null;
+
/**
* The launching activity which is using fixed rotation transformation.
*
@@ -2328,7 +2332,21 @@ class DisplayContent extends RootDisplayArea implements WindowManagerPolicy.Disp
return getLastOrientation();
}
}
- return super.getOrientation();
+
+ final int orientation = super.getOrientation();
+ if (orientation != SCREEN_ORIENTATION_UNSET && orientation != SCREEN_ORIENTATION_BEHIND) {
+ ProtoLog.v(WM_DEBUG_ORIENTATION,
+ "App is requesting an orientation, return %d for display id=%d",
+ orientation, mDisplayId);
+ return orientation;
+ }
+
+ ProtoLog.v(WM_DEBUG_ORIENTATION,
+ "No app is requesting an orientation, return %d for display id=%d",
+ getLastOrientation(), mDisplayId);
+ // The next app has not been requested to be visible, so we keep the current orientation
+ // to prevent freezing/unfreezing the display too early.
+ return getLastOrientation();
}
void updateDisplayInfo() {
@@ -3203,6 +3221,10 @@ class DisplayContent extends RootDisplayArea implements WindowManagerPolicy.Disp
throw new IllegalStateException(newFocus + " is not on " + getName()
+ " but " + ((appDisplay != null) ? appDisplay.getName() : "none"));
}
+
+ // Called even if the focused app is not changed in case the app is moved to a different
+ // TaskDisplayArea.
+ setLastFocusedTaskDisplayArea(newFocus.getDisplayArea());
}
if (mFocusedApp == newFocus) {
return false;
@@ -3215,6 +3237,19 @@ class DisplayContent extends RootDisplayArea implements WindowManagerPolicy.Disp
return true;
}
+ /** Called when the focused {@link TaskDisplayArea} on this display may have changed. */
+ @VisibleForTesting
+ void setLastFocusedTaskDisplayArea(@Nullable TaskDisplayArea taskDisplayArea) {
+ if (taskDisplayArea != null) {
+ mLastFocusedTaskDisplayArea = taskDisplayArea;
+ }
+ }
+
+ /** Gets the last focused {@link TaskDisplayArea} on this display. */
+ TaskDisplayArea getLastFocusedTaskDisplayArea() {
+ return mLastFocusedTaskDisplayArea;
+ }
+
/** Updates the layer assignment of windows on this display. */
void assignWindowLayers(boolean setLayoutNeeded) {
Trace.traceBegin(TRACE_TAG_WINDOW_MANAGER, "assignWindowLayers");
@@ -3744,7 +3779,6 @@ class DisplayContent extends RootDisplayArea implements WindowManagerPolicy.Disp
void statusBarVisibilityChanged(int visibility) {
mLastStatusBarVisibility = visibility;
- visibility = getDisplayPolicy().adjustSystemUiVisibilityLw(visibility);
updateStatusBarVisibilityLocked(visibility);
}
@@ -3769,32 +3803,16 @@ class DisplayContent extends RootDisplayArea implements WindowManagerPolicy.Disp
void updateSystemUiVisibility(int visibility, int globalDiff) {
forAllWindows(w -> {
- try {
- final int curValue = w.mSystemUiVisibility;
- final int diff = (curValue ^ visibility) & globalDiff;
- final int newValue = (curValue & ~diff) | (visibility & diff);
- if (newValue != curValue) {
- w.mSeq++;
- w.mSystemUiVisibility = newValue;
- }
- if ((newValue != curValue || w.mAttrs.hasSystemUiListeners)
- && ViewRootImpl.sNewInsetsMode != ViewRootImpl.NEW_INSETS_MODE_FULL) {
- w.mClient.dispatchSystemUiVisibilityChanged(w.mSeq,
- visibility, newValue, diff);
- }
- } catch (RemoteException e) {
- // so sorry
+ final int curValue = w.mSystemUiVisibility;
+ final int diff = (curValue ^ visibility) & globalDiff;
+ final int newValue = (curValue & ~diff) | (visibility & diff);
+ if (newValue != curValue) {
+ w.mSeq++;
+ w.mSystemUiVisibility = newValue;
}
}, true /* traverseTopToBottom */);
}
- void reevaluateStatusBarVisibility() {
- int visibility = getDisplayPolicy().adjustSystemUiVisibilityLw(mLastStatusBarVisibility);
- if (updateStatusBarVisibilityLocked(visibility)) {
- mWmService.mWindowPlacerLocked.requestTraversal();
- }
- }
-
void onWindowFreezeTimeout() {
Slog.w(TAG_WM, "Window freeze timeout expired.");
mWmService.mWindowsFreezingScreen = WINDOWS_FREEZING_SCREENS_TIMEOUT;
diff --git a/services/core/java/com/android/server/wm/DisplayPolicy.java b/services/core/java/com/android/server/wm/DisplayPolicy.java
index 779f6b2d30cc..6fffde14cf7d 100644
--- a/services/core/java/com/android/server/wm/DisplayPolicy.java
+++ b/services/core/java/com/android/server/wm/DisplayPolicy.java
@@ -57,8 +57,6 @@ import static android.view.WindowManager.LayoutParams.FLAG_LAYOUT_INSET_DECOR;
import static android.view.WindowManager.LayoutParams.FLAG_LAYOUT_IN_SCREEN;
import static android.view.WindowManager.LayoutParams.FLAG_LAYOUT_NO_LIMITS;
import static android.view.WindowManager.LayoutParams.FLAG_NOT_TOUCHABLE;
-import static android.view.WindowManager.LayoutParams.FLAG_TRANSLUCENT_NAVIGATION;
-import static android.view.WindowManager.LayoutParams.FLAG_TRANSLUCENT_STATUS;
import static android.view.WindowManager.LayoutParams.LAYOUT_IN_DISPLAY_CUTOUT_MODE_ALWAYS;
import static android.view.WindowManager.LayoutParams.LAYOUT_IN_DISPLAY_CUTOUT_MODE_DEFAULT;
import static android.view.WindowManager.LayoutParams.LAYOUT_IN_DISPLAY_CUTOUT_MODE_SHORT_EDGES;
@@ -66,13 +64,11 @@ import static android.view.WindowManager.LayoutParams.PRIVATE_FLAG_FORCE_DRAW_BA
import static android.view.WindowManager.LayoutParams.PRIVATE_FLAG_FORCE_SHOW_STATUS_BAR;
import static android.view.WindowManager.LayoutParams.PRIVATE_FLAG_INSET_PARENT_FRAME_BY_IME;
import static android.view.WindowManager.LayoutParams.PRIVATE_FLAG_IS_SCREEN_DECOR;
-import static android.view.WindowManager.LayoutParams.PRIVATE_FLAG_STATUS_FORCE_SHOW_NAVIGATION;
import static android.view.WindowManager.LayoutParams.PRIVATE_FLAG_TRUSTED_OVERLAY;
import static android.view.WindowManager.LayoutParams.SOFT_INPUT_ADJUST_NOTHING;
import static android.view.WindowManager.LayoutParams.SOFT_INPUT_ADJUST_RESIZE;
import static android.view.WindowManager.LayoutParams.SOFT_INPUT_MASK_ADJUST;
import static android.view.WindowManager.LayoutParams.TYPE_BASE_APPLICATION;
-import static android.view.WindowManager.LayoutParams.TYPE_INPUT_CONSUMER;
import static android.view.WindowManager.LayoutParams.TYPE_INPUT_METHOD;
import static android.view.WindowManager.LayoutParams.TYPE_NAVIGATION_BAR;
import static android.view.WindowManager.LayoutParams.TYPE_NAVIGATION_BAR_PANEL;
@@ -134,14 +130,13 @@ import android.graphics.Region;
import android.hardware.input.InputManager;
import android.hardware.power.Boost;
import android.os.Handler;
+import android.os.IBinder;
import android.os.Looper;
import android.os.Message;
import android.os.SystemClock;
import android.os.SystemProperties;
import android.os.UserHandle;
import android.util.ArraySet;
-import android.util.IntArray;
-import android.util.Pair;
import android.util.PrintWriterPrinter;
import android.util.Slog;
import android.util.SparseArray;
@@ -199,7 +194,6 @@ import java.util.function.Consumer;
*/
public class DisplayPolicy {
private static final String TAG = TAG_WITH_CLASS_NAME ? "DisplayPolicy" : TAG_WM;
- private static final boolean DEBUG = false;
private static final boolean ALTERNATE_CAR_MODE_NAV_SIZE = false;
@@ -221,18 +215,6 @@ public class DisplayPolicy {
/** Use the transit animation in style resource (see {@link #selectAnimation}). */
static final int ANIMATION_STYLEABLE = 0;
- /**
- * These are the system UI flags that, when changing, can cause the layout
- * of the screen to change.
- */
- private static final int SYSTEM_UI_CHANGING_LAYOUT =
- View.SYSTEM_UI_FLAG_HIDE_NAVIGATION
- | View.SYSTEM_UI_FLAG_FULLSCREEN
- | View.STATUS_BAR_TRANSLUCENT
- | View.NAVIGATION_BAR_TRANSLUCENT
- | View.STATUS_BAR_TRANSPARENT
- | View.NAVIGATION_BAR_TRANSPARENT;
-
private static final int[] SHOW_TYPES_FOR_SWIPE = {ITYPE_NAVIGATION_BAR, ITYPE_STATUS_BAR};
private static final int[] SHOW_TYPES_FOR_PANIC = {ITYPE_NAVIGATION_BAR};
@@ -327,21 +309,10 @@ public class DisplayPolicy {
private boolean mLastImmersiveMode;
- private final StatusBarController mStatusBarController;
-
+ private StatusBarManagerInternal mStatusBarInternal;
+ private final BarController mStatusBarController;
private final BarController mNavigationBarController;
- private final BarController.OnBarVisibilityChangedListener mNavBarVisibilityListener =
- new BarController.OnBarVisibilityChangedListener() {
- @Override
- public void onBarVisibilityChanged(boolean visible) {
- if (mAccessibilityManager == null) {
- return;
- }
- mAccessibilityManager.notifyAccessibilityButtonVisibilityChanged(visible);
- }
- };
-
// The windows we were told about in focusChanged.
private WindowState mFocusedWindow;
private WindowState mLastFocusedWindow;
@@ -353,15 +324,8 @@ public class DisplayPolicy {
private boolean mLastNavVisible;
private boolean mLastNavTranslucent;
private boolean mLastNavAllowedHidden;
- private boolean mLastNotificationShadeForcesShowingNavigation;
-
- int mLastSystemUiFlags;
- // Bits that we are in the process of clearing, so we want to prevent
- // them from being set by applications until everything has been updated
- // to have them clear.
- private int mResettingSystemUiFlags = 0;
- // Bits that we are currently always keeping cleared.
- private int mForceClearedSystemUiFlags = 0;
+
+ private int mLastDisableFlags;
private int mLastAppearance;
private int mLastFullscreenAppearance;
private int mLastDockedAppearance;
@@ -430,6 +394,8 @@ public class DisplayPolicy {
private final GestureNavigationSettingsObserver mGestureNavigationSettingsObserver;
+ private final WindowManagerInternal.AppTransitionListener mAppTransitionListener;
+
private class PolicyHandler extends Handler {
PolicyHandler(Looper looper) {
@@ -472,16 +438,9 @@ public class DisplayPolicy {
mLock = service.getWindowManagerLock();
final int displayId = displayContent.getDisplayId();
- mStatusBarController = new StatusBarController(displayId);
- mNavigationBarController = new BarController("NavigationBar",
- displayId,
- View.NAVIGATION_BAR_TRANSIENT,
- View.NAVIGATION_BAR_UNHIDE,
- View.NAVIGATION_BAR_TRANSLUCENT,
- StatusBarManager.WINDOW_NAVIGATION_BAR,
- TYPE_NAVIGATION_BAR,
- FLAG_TRANSLUCENT_NAVIGATION,
- View.NAVIGATION_BAR_TRANSPARENT);
+
+ mStatusBarController = new BarController(TYPE_STATUS_BAR);
+ mNavigationBarController = new BarController(TYPE_NAVIGATION_BAR);
final Resources r = mContext.getResources();
mCarDockEnablesAccelerometer = r.getBoolean(R.bool.config_carDockEnablesAccelerometer);
@@ -612,8 +571,58 @@ public class DisplayPolicy {
}
});
displayContent.registerPointerEventListener(mSystemGestures);
- displayContent.mAppTransition.registerListenerLocked(
- mStatusBarController.getAppTransitionListener());
+ mAppTransitionListener = new WindowManagerInternal.AppTransitionListener() {
+
+ private Runnable mAppTransitionPending = () -> {
+ StatusBarManagerInternal statusBar = getStatusBarManagerInternal();
+ if (statusBar != null) {
+ statusBar.appTransitionPending(displayId);
+ }
+ };
+
+ private Runnable mAppTransitionCancelled = () -> {
+ StatusBarManagerInternal statusBar = getStatusBarManagerInternal();
+ if (statusBar != null) {
+ statusBar.appTransitionCancelled(displayId);
+ }
+ };
+
+ private Runnable mAppTransitionFinished = () -> {
+ StatusBarManagerInternal statusBar = getStatusBarManagerInternal();
+ if (statusBar != null) {
+ statusBar.appTransitionFinished(displayId);
+ }
+ };
+
+ @Override
+ public void onAppTransitionPendingLocked() {
+ mHandler.post(mAppTransitionPending);
+ }
+
+ @Override
+ public int onAppTransitionStartingLocked(int transit, long duration,
+ long statusBarAnimationStartTime, long statusBarAnimationDuration) {
+ mHandler.post(() -> {
+ StatusBarManagerInternal statusBar = getStatusBarManagerInternal();
+ if (statusBar != null) {
+ statusBar.appTransitionStarting(mContext.getDisplayId(),
+ statusBarAnimationStartTime, statusBarAnimationDuration);
+ }
+ });
+ return 0;
+ }
+
+ @Override
+ public void onAppTransitionCancelledLocked(int transit) {
+ mHandler.post(mAppTransitionCancelled);
+ }
+
+ @Override
+ public void onAppTransitionFinishedLocked(IBinder token) {
+ mHandler.post(mAppTransitionFinished);
+ }
+ };
+ displayContent.mAppTransition.registerListenerLocked(mAppTransitionListener);
mImmersiveModeConfirmation = new ImmersiveModeConfirmation(mContext, looper,
mService.mVrModeEnabled);
@@ -640,7 +649,7 @@ public class DisplayPolicy {
mRefreshRatePolicy = new RefreshRatePolicy(mService,
mDisplayContent.getDisplayInfo(),
- mService.mHighRefreshRateBlacklist);
+ mService.mHighRefreshRateDenylist);
mGestureNavigationSettingsObserver = new GestureNavigationSettingsObserver(mHandler,
mContext, () -> {
@@ -1066,7 +1075,6 @@ public class DisplayPolicy {
break;
case TYPE_STATUS_BAR:
mStatusBar = win;
- mStatusBarController.setWindow(win);
final TriConsumer<DisplayFrames, WindowState, Rect> frameProvider =
(displayFrames, windowState, rect) -> {
rect.bottom = rect.top + getStatusBarHeight(displayFrames);
@@ -1077,9 +1085,6 @@ public class DisplayPolicy {
break;
case TYPE_NAVIGATION_BAR:
mNavigationBar = win;
- mNavigationBarController.setWindow(win);
- mNavigationBarController.setOnBarVisibilityChangedListener(
- mNavBarVisibilityListener, true);
mDisplayContent.setInsetProvider(ITYPE_NAVIGATION_BAR, win,
(displayFrames, windowState, inOutFrame) -> {
@@ -1135,12 +1140,10 @@ public class DisplayPolicy {
switch (insetsType) {
case ITYPE_STATUS_BAR:
mStatusBarAlt = win;
- mStatusBarController.setWindow(mStatusBarAlt);
mStatusBarAltPosition = getAltBarPosition(attrs);
break;
case ITYPE_NAVIGATION_BAR:
mNavigationBarAlt = win;
- mNavigationBarController.setWindow(mNavigationBarAlt);
mNavigationBarAltPosition = getAltBarPosition(attrs);
break;
}
@@ -1210,12 +1213,10 @@ public class DisplayPolicy {
if (mStatusBar == win || mStatusBarAlt == win) {
mStatusBar = null;
mStatusBarAlt = null;
- mStatusBarController.setWindow(null);
mDisplayContent.setInsetProvider(ITYPE_STATUS_BAR, null, null);
} else if (mNavigationBar == win || mNavigationBarAlt == win) {
mNavigationBar = null;
mNavigationBarAlt = null;
- mNavigationBarController.setWindow(null);
mDisplayContent.setInsetProvider(ITYPE_NAVIGATION_BAR, null, null);
} else if (mNotificationShade == win) {
mNotificationShade = null;
@@ -1235,7 +1236,7 @@ public class DisplayPolicy {
}
@VisibleForTesting
- StatusBarController getStatusBarController() {
+ BarController getStatusBarController() {
return mStatusBarController;
}
@@ -1368,25 +1369,6 @@ public class DisplayPolicy {
}
/**
- * Called when a new system UI visibility is being reported, allowing
- * the policy to adjust what is actually reported.
- * @param visibility The raw visibility reported by the status bar.
- * @return The new desired visibility.
- */
- public int adjustSystemUiVisibilityLw(int visibility) {
- mStatusBarController.adjustSystemUiVisibilityLw(mLastSystemUiFlags, visibility);
- mNavigationBarController.adjustSystemUiVisibilityLw(mLastSystemUiFlags, visibility);
-
- // Reset any bits in mForceClearingStatusBarVisibility that
- // are now clear.
- mResettingSystemUiFlags &= visibility;
- // Clear any bits in the new visibility that are currently being
- // force cleared, before reporting it.
- return visibility & ~mResettingSystemUiFlags
- & ~mForceClearedSystemUiFlags;
- }
-
- /**
* @return true if the system bars are forced to stay visible
*/
public boolean areSystemBarsForcedShownLw(WindowState windowState) {
@@ -1481,16 +1463,6 @@ public class DisplayPolicy {
return mForceShowSystemBars;
}
- private final Runnable mClearHideNavigationFlag = new Runnable() {
- @Override
- public void run() {
- synchronized (mLock) {
- mForceClearedSystemUiFlags &= ~View.SYSTEM_UI_FLAG_HIDE_NAVIGATION;
- mDisplayContent.reevaluateStatusBarVisibility();
- }
- }
- };
-
/**
* Input handler used while nav bar is hidden. Captures any touch on the screen,
* to determine when the nav bar should be shown and prevent applications from
@@ -1515,32 +1487,6 @@ public class DisplayPolicy {
return;
}
showSystemBars();
- // Any user activity always causes us to show the
- // navigation controls, if they had been hidden.
- // We also clear the low profile and only content
- // flags so that tapping on the screen will atomically
- // restore all currently hidden screen decorations.
- int newVal = mResettingSystemUiFlags
- | View.SYSTEM_UI_FLAG_HIDE_NAVIGATION
- | View.SYSTEM_UI_FLAG_LOW_PROFILE
- | View.SYSTEM_UI_FLAG_FULLSCREEN;
- if (mResettingSystemUiFlags != newVal) {
- mResettingSystemUiFlags = newVal;
- changed = true;
- }
- // We don't allow the system's nav bar to be hidden
- // again for 1 second, to prevent applications from
- // spamming us and keeping it from being shown.
- newVal = mForceClearedSystemUiFlags
- | View.SYSTEM_UI_FLAG_HIDE_NAVIGATION;
- if (mForceClearedSystemUiFlags != newVal) {
- mForceClearedSystemUiFlags = newVal;
- changed = true;
- mHandler.postDelayed(mClearHideNavigationFlag, 1000);
- }
- if (changed) {
- mDisplayContent.reevaluateStatusBarVisibility();
- }
}
}
}
@@ -1594,12 +1540,12 @@ public class DisplayPolicy {
contentFrame -> layoutNavigationBar(displayFrames,
mDisplayContent.getConfiguration().uiMode, mLastNavVisible,
mLastNavTranslucent, mLastNavAllowedHidden,
- mLastNotificationShadeForcesShowingNavigation, contentFrame));
+ contentFrame));
}
if (mStatusBar != null) {
simulateLayoutDecorWindow(mStatusBar, displayFrames, insetsState,
simulatedWindowFrames, barContentFrames,
- contentFrame -> layoutStatusBar(displayFrames, mLastSystemUiFlags,
+ contentFrame -> layoutStatusBar(displayFrames, mLastAppearance,
contentFrame));
}
layoutScreenDecorWindows(displayFrames, simulatedWindowFrames);
@@ -1621,25 +1567,17 @@ public class DisplayPolicy {
// For purposes of putting out fake window up to steal focus, we will
// drive nav being hidden only by whether it is requested.
- final int sysui = mLastSystemUiFlags;
+ final int appearance = mLastAppearance;
final int behavior = mLastBehavior;
final InsetsSourceProvider provider =
mDisplayContent.getInsetsStateController().peekSourceProvider(ITYPE_NAVIGATION_BAR);
boolean navVisible = provider != null ? provider.isClientVisible()
: InsetsState.getDefaultVisibility(ITYPE_NAVIGATION_BAR);
- boolean navTranslucent = (sysui
- & (View.NAVIGATION_BAR_TRANSLUCENT | View.NAVIGATION_BAR_TRANSPARENT)) != 0;
- boolean immersive = (sysui & View.SYSTEM_UI_FLAG_IMMERSIVE) != 0
- || (behavior & BEHAVIOR_SHOW_BARS_BY_SWIPE) != 0;
- boolean immersiveSticky = (sysui & View.SYSTEM_UI_FLAG_IMMERSIVE_STICKY) != 0
- || (behavior & BEHAVIOR_SHOW_TRANSIENT_BARS_BY_SWIPE) != 0;
+ boolean navTranslucent = (appearance & APPEARANCE_OPAQUE_NAVIGATION_BARS) == 0;
+ boolean immersive = (behavior & BEHAVIOR_SHOW_BARS_BY_SWIPE) != 0;
+ boolean immersiveSticky = (behavior & BEHAVIOR_SHOW_TRANSIENT_BARS_BY_SWIPE) != 0;
boolean navAllowedHidden = immersive || immersiveSticky;
navTranslucent &= !immersiveSticky; // transient trumps translucent
- boolean isKeyguardShowing = isKeyguardShowing() && !isKeyguardOccluded();
- boolean notificationShadeForcesShowingNavigation =
- !isKeyguardShowing && mNotificationShade != null
- && (mNotificationShade.getAttrs().privateFlags
- & PRIVATE_FLAG_STATUS_FORCE_SHOW_NAVIGATION) != 0;
updateHideNavInputEventReceiver();
@@ -1647,21 +1585,15 @@ public class DisplayPolicy {
// be hidden (because of the screen aspect ratio), then take that into account.
navVisible |= !canHideNavigationBar();
- boolean updateSysUiVisibility = layoutNavigationBar(displayFrames, uiMode, navVisible,
- navTranslucent, navAllowedHidden, notificationShadeForcesShowingNavigation,
- null /* simulatedContentFrame */);
+ layoutNavigationBar(displayFrames, uiMode, navVisible,
+ navTranslucent, navAllowedHidden, null /* simulatedContentFrame */);
if (DEBUG_LAYOUT) Slog.i(TAG, "mDock rect:" + displayFrames.mDock);
- updateSysUiVisibility |= layoutStatusBar(displayFrames, sysui,
- null /* simulatedContentFrame */);
- if (updateSysUiVisibility) {
- updateSystemUiVisibilityLw();
- }
+ layoutStatusBar(displayFrames, appearance, null /* simulatedContentFrame */);
layoutScreenDecorWindows(displayFrames, null /* simulatedFrames */);
postAdjustDisplayFrames(displayFrames);
mLastNavVisible = navVisible;
mLastNavTranslucent = navTranslucent;
mLastNavAllowedHidden = navAllowedHidden;
- mLastNotificationShadeForcesShowingNavigation = notificationShadeForcesShowingNavigation;
}
void updateHideNavInputEventReceiver() {
@@ -1825,11 +1757,11 @@ public class DisplayPolicy {
displayFrames.mContent.set(dockFrame);
}
- private boolean layoutStatusBar(DisplayFrames displayFrames, int sysui,
+ private void layoutStatusBar(DisplayFrames displayFrames, int appearance,
Rect simulatedContentFrame) {
// decide where the status bar goes ahead of time
if (mStatusBar == null) {
- return false;
+ return;
}
// apply any status bar insets
getRotatedWindowBounds(displayFrames, mStatusBar, sTmpStatusFrame);
@@ -1860,10 +1792,9 @@ public class DisplayPolicy {
mStatusBarController.setContentFrame(sTmpRect);
}
- boolean statusBarTransient = (sysui & View.STATUS_BAR_TRANSIENT) != 0
- || mDisplayContent.getInsetsPolicy().isTransient(ITYPE_STATUS_BAR);
- boolean statusBarTranslucent = (sysui
- & (View.STATUS_BAR_TRANSLUCENT | View.STATUS_BAR_TRANSPARENT)) != 0;
+ boolean statusBarTransient =
+ mDisplayContent.getInsetsPolicy().isTransient(ITYPE_STATUS_BAR);
+ boolean statusBarTranslucent = (appearance & APPEARANCE_OPAQUE_STATUS_BARS) == 0;
// If the status bar is hidden, we don't want to cause windows behind it to scroll.
if (mStatusBar.isVisibleLw() && !statusBarTransient) {
@@ -1879,8 +1810,7 @@ public class DisplayPolicy {
"dock=%s content=%s cur=%s", dockFrame.toString(),
displayFrames.mContent.toString(), displayFrames.mCurrent.toString()));
- if (!statusBarTranslucent && !mStatusBarController.wasRecentlyTranslucent()
- && !mStatusBar.isAnimatingLw()) {
+ if (!statusBarTranslucent && !mStatusBar.isAnimatingLw()) {
// If the opaque status bar is currently requested to be visible, and not in the
// process of animating on or off, then we can tell the app that it is covered by
@@ -1888,18 +1818,17 @@ public class DisplayPolicy {
displayFrames.mSystem.top = displayFrames.mStable.top;
}
}
- return mStatusBarController.checkHiddenLw();
}
- private boolean layoutNavigationBar(DisplayFrames displayFrames, int uiMode, boolean navVisible,
- boolean navTranslucent, boolean navAllowedHidden,
- boolean statusBarForcesShowingNavigation, Rect simulatedContentFrame) {
+ private void layoutNavigationBar(DisplayFrames displayFrames, int uiMode, boolean navVisible,
+ boolean navTranslucent, boolean navAllowedHidden, Rect simulatedContentFrame) {
if (mNavigationBar == null) {
- return false;
+ return;
}
final Rect navigationFrame = sTmpNavFrame;
- boolean transientNavBarShowing = mNavigationBarController.isTransientShowing();
+ boolean navBarTransient =
+ mDisplayContent.getInsetsPolicy().isTransient(ITYPE_NAVIGATION_BAR);
// Force the navigation bar to its appropriate place and size. We need to do this directly,
// instead of relying on it to bubble up from the nav bar, because this needs to change
// atomically with screen rotations.
@@ -1924,18 +1853,11 @@ public class DisplayPolicy {
- getNavigationBarHeight(rotation, uiMode);
navigationFrame.top = topNavBar;
displayFrames.mStable.bottom = displayFrames.mStableFullscreen.bottom = top;
- if (transientNavBarShowing) {
- mNavigationBarController.setBarShowingLw(true);
- } else if (navVisible) {
- mNavigationBarController.setBarShowingLw(true);
+ if (navVisible && !navBarTransient) {
dockFrame.bottom = displayFrames.mRestricted.bottom = top;
- } else {
- // We currently want to hide the navigation UI - unless we expanded the status bar.
- mNavigationBarController.setBarShowingLw(statusBarForcesShowingNavigation);
}
if (navVisible && !navTranslucent && !navAllowedHidden
- && !mNavigationBar.isAnimatingLw()
- && !mNavigationBarController.wasRecentlyTranslucent()) {
+ && !mNavigationBar.isAnimatingLw()) {
// If the opaque nav bar is currently requested to be visible and not in the process
// of animating on or off, then we can tell the app that it is covered by it.
displayFrames.mSystem.bottom = top;
@@ -1946,18 +1868,11 @@ public class DisplayPolicy {
- getNavigationBarWidth(rotation, uiMode);
navigationFrame.left = left;
displayFrames.mStable.right = displayFrames.mStableFullscreen.right = left;
- if (transientNavBarShowing) {
- mNavigationBarController.setBarShowingLw(true);
- } else if (navVisible) {
- mNavigationBarController.setBarShowingLw(true);
+ if (navVisible && !navBarTransient) {
dockFrame.right = displayFrames.mRestricted.right = left;
- } else {
- // We currently want to hide the navigation UI - unless we expanded the status bar.
- mNavigationBarController.setBarShowingLw(statusBarForcesShowingNavigation);
}
if (navVisible && !navTranslucent && !navAllowedHidden
- && !mNavigationBar.isAnimatingLw()
- && !mNavigationBarController.wasRecentlyTranslucent()) {
+ && !mNavigationBar.isAnimatingLw()) {
// If the nav bar is currently requested to be visible, and not in the process of
// animating on or off, then we can tell the app that it is covered by it.
displayFrames.mSystem.right = left;
@@ -1968,18 +1883,11 @@ public class DisplayPolicy {
+ getNavigationBarWidth(rotation, uiMode);
navigationFrame.right = right;
displayFrames.mStable.left = displayFrames.mStableFullscreen.left = right;
- if (transientNavBarShowing) {
- mNavigationBarController.setBarShowingLw(true);
- } else if (navVisible) {
- mNavigationBarController.setBarShowingLw(true);
+ if (navVisible && !navBarTransient) {
dockFrame.left = displayFrames.mRestricted.left = right;
- } else {
- // We currently want to hide the navigation UI - unless we expanded the status bar.
- mNavigationBarController.setBarShowingLw(statusBarForcesShowingNavigation);
}
if (navVisible && !navTranslucent && !navAllowedHidden
- && !mNavigationBar.isAnimatingLw()
- && !mNavigationBarController.wasRecentlyTranslucent()) {
+ && !mNavigationBar.isAnimatingLw()) {
// If the nav bar is currently requested to be visible, and not in the process of
// animating on or off, then we can tell the app that it is covered by it.
displayFrames.mSystem.left = right;
@@ -2008,7 +1916,6 @@ public class DisplayPolicy {
}
if (DEBUG_LAYOUT) Slog.i(TAG, "mNavigationBar frame: " + navigationFrame);
- return mNavigationBarController.checkHiddenLw();
}
private boolean canReceiveInput(WindowState win) {
@@ -2059,9 +1966,6 @@ public class DisplayPolicy {
dcf.setEmpty();
windowFrames.setParentFrameWasClippedByDisplayCutout(false);
- final boolean hasNavBar = hasNavigationBar() && mNavigationBar != null
- && mNavigationBar.isVisibleLw();
-
final int adjust = sim & SOFT_INPUT_MASK_ADJUST;
final boolean layoutInScreen = (fl & FLAG_LAYOUT_IN_SCREEN) == FLAG_LAYOUT_IN_SCREEN;
@@ -2407,53 +2311,27 @@ public class DisplayPolicy {
+ " top=" + mTopFullscreenOpaqueWindowState);
final boolean forceShowStatusBar = (getStatusBar().getAttrs().privateFlags
& PRIVATE_FLAG_FORCE_SHOW_STATUS_BAR) != 0;
- final boolean notificationShadeForcesShowingNavigation =
- mNotificationShade != null
- && (mNotificationShade.getAttrs().privateFlags
- & PRIVATE_FLAG_STATUS_FORCE_SHOW_NAVIGATION) != 0;
boolean topAppHidesStatusBar = topAppHidesStatusBar();
if (mForceStatusBar || forceShowStatusBar) {
if (DEBUG_LAYOUT) Slog.v(TAG, "Showing status bar: forced");
- if (mStatusBarController.setBarShowingLw(true)) {
- changes |= FINISH_LAYOUT_REDO_LAYOUT;
- }
// Maintain fullscreen layout until incoming animation is complete.
topIsFullscreen = mTopIsFullscreen && mStatusBar.isAnimatingLw();
- // Transient status bar is not allowed if notification shade is expecting the
- // navigation keys from the user.
- if (notificationShadeForcesShowingNavigation
- && mStatusBarController.isTransientShowing()) {
- mStatusBarController.updateVisibilityLw(false /*transientAllowed*/,
- mLastSystemUiFlags, mLastSystemUiFlags);
- }
} else if (mTopFullscreenOpaqueWindowState != null) {
topIsFullscreen = topAppHidesStatusBar;
// The subtle difference between the window for mTopFullscreenOpaqueWindowState
// and mTopIsFullscreen is that mTopIsFullscreen is set only if the window
// has the FLAG_FULLSCREEN set. Not sure if there is another way that to be the
// case though.
- if (mStatusBarController.isTransientShowing()) {
- if (mStatusBarController.setBarShowingLw(true)) {
- changes |= FINISH_LAYOUT_REDO_LAYOUT;
- }
- } else if (topIsFullscreen && !mDisplayContent.getDefaultTaskDisplayArea()
+ if (!topIsFullscreen || mDisplayContent.getDefaultTaskDisplayArea()
.isStackVisible(WINDOWING_MODE_SPLIT_SCREEN_PRIMARY)) {
- if (DEBUG_LAYOUT) Slog.v(TAG, "** HIDING status bar");
- if (mStatusBarController.setBarShowingLw(false)) {
- changes |= FINISH_LAYOUT_REDO_LAYOUT;
- } else {
- if (DEBUG_LAYOUT) Slog.v(TAG, "Status bar already hiding");
- }
- } else {
- if (DEBUG_LAYOUT) Slog.v(TAG, "** SHOWING status bar: top is not fullscreen");
- if (mStatusBarController.setBarShowingLw(true)) {
- changes |= FINISH_LAYOUT_REDO_LAYOUT;
- }
topAppHidesStatusBar = false;
}
}
- mStatusBarController.setTopAppHidesStatusBar(topAppHidesStatusBar);
+ StatusBarManagerInternal statusBar = getStatusBarManagerInternal();
+ if (statusBar != null) {
+ statusBar.setTopAppHidesStatusBar(topAppHidesStatusBar);
+ }
}
if (mTopIsFullscreen != topIsFullscreen) {
@@ -2464,7 +2342,7 @@ public class DisplayPolicy {
mTopIsFullscreen = topIsFullscreen;
}
- if ((updateSystemUiVisibilityLw() & SYSTEM_UI_CHANGING_LAYOUT) != 0) {
+ if (updateSystemUiVisibilityLw()) {
// If the navigation bar has been hidden or shown, we need to do another
// layout pass to update that window.
changes |= FINISH_LAYOUT_REDO_LAYOUT;
@@ -2497,7 +2375,6 @@ public class DisplayPolicy {
Slog.d(TAG, "attr: " + attrs + " request: " + request);
}
return (fl & LayoutParams.FLAG_FULLSCREEN) != 0
- || (sysui & View.SYSTEM_UI_FLAG_FULLSCREEN) != 0
|| (request != null && !request.isVisible());
}
@@ -2910,7 +2787,7 @@ public class DisplayPolicy {
if (mDisplayContent.isDefaultDisplay) {
mService.mPolicy.onDefaultDisplayFocusChangedLw(newFocus);
}
- if ((updateSystemUiVisibilityLw() & SYSTEM_UI_CHANGING_LAYOUT) != 0) {
+ if (updateSystemUiVisibilityLw()) {
// If the navigation bar has been hidden or shown, we need to do another
// layout pass to update that window.
return FINISH_LAYOUT_REDO_LAYOUT;
@@ -2977,17 +2854,20 @@ public class DisplayPolicy {
}
void resetSystemUiVisibilityLw() {
- mLastSystemUiFlags = 0;
+ mLastDisableFlags = 0;
updateSystemUiVisibilityLw();
}
- int updateSystemUiVisibilityLw() {
+ /**
+ * @return {@code true} if the update may affect the layout.
+ */
+ boolean updateSystemUiVisibilityLw() {
// If there is no window focused, there will be nobody to handle the events
// anyway, so just hang on in whatever state we're in until things settle down.
WindowState winCandidate = mFocusedWindow != null ? mFocusedWindow
: mTopFullscreenOpaqueWindowState;
if (winCandidate == null) {
- return 0;
+ return false;
}
// The immersive mode confirmation should never affect the system bar visibility, otherwise
@@ -3003,7 +2883,7 @@ public class DisplayPolicy {
: lastFocusCanReceiveKeys ? mLastFocusedWindow
: mTopFullscreenOpaqueWindowState;
if (winCandidate == null) {
- return 0;
+ return false;
}
}
final WindowState win = winCandidate;
@@ -3011,17 +2891,9 @@ public class DisplayPolicy {
mDisplayContent.getInsetsPolicy().updateBarControlTarget(win);
- int tmpVisibility = PolicyControl.getSystemUiVisibility(win, null)
- & ~mResettingSystemUiFlags
- & ~mForceClearedSystemUiFlags;
- if (mForcingShowNavBar && win.getSurfaceLayer() < mForcingShowNavBarLayer) {
- tmpVisibility
- &= ~PolicyControl.adjustClearableFlags(win, View.SYSTEM_UI_CLEARABLE_FLAGS);
- }
-
- final int fullscreenAppearance = updateLightStatusBarAppearanceLw(0 /* vis */,
+ final int fullscreenAppearance = updateLightStatusBarLw(0 /* vis */,
mTopFullscreenOpaqueWindowState, mTopFullscreenOpaqueOrDimmingWindowState);
- final int dockedAppearance = updateLightStatusBarAppearanceLw(0 /* vis */,
+ final int dockedAppearance = updateLightStatusBarLw(0 /* vis */,
mTopDockedOpaqueWindowState, mTopDockedOpaqueOrDimmingWindowState);
final boolean inSplitScreen =
mService.mRoot.getDefaultTaskDisplayArea().isSplitScreenModeActivated();
@@ -3034,32 +2906,24 @@ public class DisplayPolicy {
mService.getStackBounds(inSplitScreen ? WINDOWING_MODE_SPLIT_SCREEN_SECONDARY
: WINDOWING_MODE_FULLSCREEN,
ACTIVITY_TYPE_UNDEFINED, mNonDockedStackBounds);
- final Pair<Integer, WindowState> result =
- updateSystemBarsLw(win, mLastSystemUiFlags, tmpVisibility);
- final int visibility = result.first;
- final WindowState navColorWin = result.second;
+ final int disableFlags = win.getSystemUiVisibility() & StatusBarManager.DISABLE_MASK;
+ final int opaqueAppearance = updateSystemBarsLw(win, disableFlags);
+ final WindowState navColorWin = chooseNavigationColorWindowLw(
+ mTopFullscreenOpaqueWindowState, mTopFullscreenOpaqueOrDimmingWindowState,
+ mDisplayContent.mInputMethodWindow, mNavigationBarPosition);
final boolean isNavbarColorManagedByIme =
navColorWin != null && navColorWin == mDisplayContent.mInputMethodWindow;
- final int opaqueAppearance = InsetsFlags.getAppearance(visibility)
- & (APPEARANCE_OPAQUE_STATUS_BARS | APPEARANCE_OPAQUE_NAVIGATION_BARS);
- final int appearance = updateLightNavigationBarAppearanceLw(
+ final int appearance = updateLightNavigationBarLw(
win.mAttrs.insetsFlags.appearance, mTopFullscreenOpaqueWindowState,
mTopFullscreenOpaqueOrDimmingWindowState,
mDisplayContent.mInputMethodWindow, navColorWin) | opaqueAppearance;
- final int diff = visibility ^ mLastSystemUiFlags;
- final InsetsPolicy insetsPolicy = getInsetsPolicy();
- final boolean isFullscreen = (visibility & (View.SYSTEM_UI_FLAG_FULLSCREEN
- | View.SYSTEM_UI_FLAG_HIDE_NAVIGATION)) != 0
- || (PolicyControl.getWindowFlags(win, win.mAttrs) & FLAG_FULLSCREEN) != 0
- || (getStatusBar() != null && insetsPolicy.isHidden(ITYPE_STATUS_BAR))
- || (getNavigationBar() != null && insetsPolicy.isHidden(
- ITYPE_NAVIGATION_BAR));
+ final InsetsState requestedInsets = win.getRequestedInsetsState();
final int behavior = win.mAttrs.insetsFlags.behavior;
- final boolean isImmersive = (visibility & (View.SYSTEM_UI_FLAG_IMMERSIVE
- | View.SYSTEM_UI_FLAG_IMMERSIVE_STICKY)) != 0
- || behavior == BEHAVIOR_SHOW_BARS_BY_SWIPE
+ final boolean isImmersive = behavior == BEHAVIOR_SHOW_BARS_BY_SWIPE
|| behavior == BEHAVIOR_SHOW_TRANSIENT_BARS_BY_SWIPE;
- if (diff == 0
+ final boolean isFullscreen = !requestedInsets.getSourceOrDefaultVisibility(ITYPE_STATUS_BAR)
+ || !requestedInsets.getSourceOrDefaultVisibility(ITYPE_NAVIGATION_BAR);
+ if (mLastDisableFlags == disableFlags
&& mLastAppearance == appearance
&& mLastFullscreenAppearance == fullscreenAppearance
&& mLastDockedAppearance == dockedAppearance
@@ -3068,14 +2932,10 @@ public class DisplayPolicy {
&& mLastFocusIsImmersive == isImmersive
&& mLastNonDockedStackBounds.equals(mNonDockedStackBounds)
&& mLastDockedStackBounds.equals(mDockedStackBounds)) {
- return 0;
+ return false;
}
- // Obtains which types should show transient and which types should abort transient.
- // If there is no transient state change, this pair will contain two empty arrays.
- final Pair<int[], int[]> transientState = getTransientState(visibility, mLastSystemUiFlags);
-
- mLastSystemUiFlags = visibility;
+ mLastDisableFlags = disableFlags;
mLastAppearance = appearance;
mLastFullscreenAppearance = fullscreenAppearance;
mLastDockedAppearance = dockedAppearance;
@@ -3097,50 +2957,17 @@ public class DisplayPolicy {
StatusBarManagerInternal statusBar = getStatusBarManagerInternal();
if (statusBar != null) {
final int displayId = getDisplayId();
- statusBar.setDisableFlags(displayId, visibility & StatusBarManager.DISABLE_MASK,
- cause);
- if (transientState.first.length > 0) {
- statusBar.showTransient(displayId, transientState.first);
- }
- if (transientState.second.length > 0) {
- statusBar.abortTransient(displayId, transientState.second);
- }
+ statusBar.setDisableFlags(displayId, disableFlags, cause);
statusBar.onSystemBarAppearanceChanged(displayId, appearance,
appearanceRegions, isNavbarColorManagedByIme);
statusBar.topAppWindowChanged(displayId, isFullscreen, isImmersive);
- // TODO(b/118118435): Remove this after removing system UI visibilities.
- synchronized (mLock) {
- mDisplayContent.statusBarVisibilityChanged(
- visibility & ~(View.STATUS_BAR_UNHIDE | View.NAVIGATION_BAR_UNHIDE));
- }
}
});
- return diff;
- }
-
- private static Pair<int[], int[]> getTransientState(int vis, int oldVis) {
- final IntArray typesToShow = new IntArray(0);
- final IntArray typesToAbort = new IntArray(0);
- updateTransientState(vis, oldVis, View.STATUS_BAR_TRANSIENT, ITYPE_STATUS_BAR, typesToShow,
- typesToAbort);
- updateTransientState(vis, oldVis, View.NAVIGATION_BAR_TRANSIENT,
- ITYPE_NAVIGATION_BAR, typesToShow, typesToAbort);
- return Pair.create(typesToShow.toArray(), typesToAbort.toArray());
- }
-
- private static void updateTransientState(int vis, int oldVis, int transientFlag,
- @InternalInsetsType int type, IntArray typesToShow, IntArray typesToAbort) {
- final boolean wasTransient = (oldVis & transientFlag) != 0;
- final boolean isTransient = (vis & transientFlag) != 0;
- if (!wasTransient && isTransient) {
- typesToShow.add(type);
- } else if (wasTransient && !isTransient) {
- typesToAbort.add(type);
- }
+ return true;
}
- private int updateLightStatusBarAppearanceLw(@Appearance int appearance, WindowState opaque,
+ private int updateLightStatusBarLw(@Appearance int appearance, WindowState opaque,
WindowState opaqueOrDimming) {
final boolean onKeyguard = isKeyguardShowing() && !isKeyguardOccluded();
final WindowState statusColorWin = onKeyguard ? mNotificationShade : opaqueOrDimming;
@@ -3207,24 +3034,7 @@ public class DisplayPolicy {
}
@VisibleForTesting
- static int updateLightNavigationBarLw(int vis, WindowState opaque, WindowState opaqueOrDimming,
- WindowState imeWindow, WindowState navColorWin) {
-
- if (navColorWin != null) {
- if (navColorWin == imeWindow || navColorWin == opaque) {
- // Respect the light flag.
- vis &= ~View.SYSTEM_UI_FLAG_LIGHT_NAVIGATION_BAR;
- vis |= PolicyControl.getSystemUiVisibility(navColorWin, null)
- & View.SYSTEM_UI_FLAG_LIGHT_NAVIGATION_BAR;
- } else if (navColorWin == opaqueOrDimming && navColorWin.isDimming()) {
- // Clear the light flag for dimming window.
- vis &= ~View.SYSTEM_UI_FLAG_LIGHT_NAVIGATION_BAR;
- }
- }
- return vis;
- }
-
- private int updateLightNavigationBarAppearanceLw(int appearance, WindowState opaque,
+ int updateLightNavigationBarLw(int appearance, WindowState opaque,
WindowState opaqueOrDimming, WindowState imeWindow, WindowState navColorWin) {
if (navColorWin != null) {
@@ -3244,7 +3054,7 @@ public class DisplayPolicy {
return appearance;
}
- private Pair<Integer, WindowState> updateSystemBarsLw(WindowState win, int oldVis, int vis) {
+ private int updateSystemBarsLw(WindowState win, int disableFlags) {
final boolean dockedStackVisible = mDisplayContent.getDefaultTaskDisplayArea()
.isStackVisible(WINDOWING_MODE_SPLIT_SCREEN_PRIMARY);
final boolean freeformStackVisible = mDisplayContent.getDefaultTaskDisplayArea()
@@ -3257,115 +3067,46 @@ public class DisplayPolicy {
mForceShowSystemBars = dockedStackVisible || win.inFreeformWindowingMode() || resizing;
final boolean forceOpaqueStatusBar = mForceShowSystemBars && !isKeyguardShowing();
- // apply translucent bar vis flags
- WindowState fullscreenTransWin = isKeyguardShowing() && !isKeyguardOccluded()
- ? mNotificationShade
- : mTopFullscreenOpaqueWindowState;
- vis = mStatusBarController.applyTranslucentFlagLw(fullscreenTransWin, vis, oldVis);
- vis = mNavigationBarController.applyTranslucentFlagLw(fullscreenTransWin, vis, oldVis);
- int dockedVis = mStatusBarController.applyTranslucentFlagLw(
- mTopDockedOpaqueWindowState, 0, 0);
- dockedVis = mNavigationBarController.applyTranslucentFlagLw(
- mTopDockedOpaqueWindowState, dockedVis, 0);
-
final boolean fullscreenDrawsStatusBarBackground =
- drawsStatusBarBackground(vis, mTopFullscreenOpaqueWindowState);
+ drawsStatusBarBackground(mTopFullscreenOpaqueWindowState);
final boolean dockedDrawsStatusBarBackground =
- drawsStatusBarBackground(dockedVis, mTopDockedOpaqueWindowState);
+ drawsStatusBarBackground(mTopDockedOpaqueWindowState);
final boolean fullscreenDrawsNavBarBackground =
- drawsNavigationBarBackground(vis, mTopFullscreenOpaqueWindowState);
+ drawsNavigationBarBackground(mTopFullscreenOpaqueWindowState);
final boolean dockedDrawsNavigationBarBackground =
- drawsNavigationBarBackground(dockedVis, mTopDockedOpaqueWindowState);
+ drawsNavigationBarBackground(mTopDockedOpaqueWindowState);
- // prevent status bar interaction from clearing certain flags
- int type = win.getAttrs().type;
- boolean notificationShadeHasFocus = type == TYPE_NOTIFICATION_SHADE;
- if (notificationShadeHasFocus && !isKeyguardShowing()) {
- int flags = View.SYSTEM_UI_FLAG_FULLSCREEN
- | View.SYSTEM_UI_FLAG_HIDE_NAVIGATION
- | View.SYSTEM_UI_FLAG_IMMERSIVE
- | View.SYSTEM_UI_FLAG_IMMERSIVE_STICKY
- | View.SYSTEM_UI_FLAG_LIGHT_STATUS_BAR;
- if (isKeyguardOccluded()) {
- flags |= View.STATUS_BAR_TRANSLUCENT | View.NAVIGATION_BAR_TRANSLUCENT;
- }
- vis = (vis & ~flags) | (oldVis & flags);
- }
+ int appearance = APPEARANCE_OPAQUE_NAVIGATION_BARS | APPEARANCE_OPAQUE_STATUS_BARS;
if (fullscreenDrawsStatusBarBackground && dockedDrawsStatusBarBackground) {
- vis |= View.STATUS_BAR_TRANSPARENT;
- vis &= ~View.STATUS_BAR_TRANSLUCENT;
- } else if (forceOpaqueStatusBar) {
- vis &= ~(View.STATUS_BAR_TRANSLUCENT | View.STATUS_BAR_TRANSPARENT);
- }
-
- vis = configureNavBarOpacity(vis, dockedStackVisible, freeformStackVisible, resizing,
- fullscreenDrawsNavBarBackground, dockedDrawsNavigationBarBackground);
-
- // update status bar
- boolean immersiveSticky =
- (vis & View.SYSTEM_UI_FLAG_IMMERSIVE_STICKY) != 0;
- final boolean hideStatusBarWM =
- mTopFullscreenOpaqueWindowState != null
- && (PolicyControl.getWindowFlags(mTopFullscreenOpaqueWindowState, null)
- & WindowManager.LayoutParams.FLAG_FULLSCREEN) != 0;
- final boolean hideStatusBarSysui =
- (vis & View.SYSTEM_UI_FLAG_FULLSCREEN) != 0;
- final boolean hideNavBarSysui = (vis & View.SYSTEM_UI_FLAG_HIDE_NAVIGATION) != 0
- // We shouldn't rely on the system UI visibilities anymore because the window can
- // use the new API (e.g., WindowInsetsController.hide) to hide navigation bar.
- // TODO(b/149813814): clean up the system UI flag usages in this function.
- || !win.getRequestedInsetsState().getSourceOrDefaultVisibility(
- ITYPE_NAVIGATION_BAR);
+ appearance &= ~APPEARANCE_OPAQUE_STATUS_BARS;
+ }
- final boolean transientStatusBarAllowed = getStatusBar() != null
- && (notificationShadeHasFocus || (!mForceShowSystemBars
- && (hideStatusBarWM || (hideStatusBarSysui && immersiveSticky))));
+ appearance = configureNavBarOpacity(appearance, dockedStackVisible,
+ freeformStackVisible, resizing, fullscreenDrawsNavBarBackground,
+ dockedDrawsNavigationBarBackground);
- final boolean transientNavBarAllowed = mNavigationBar != null
- && !mForceShowSystemBars && hideNavBarSysui && immersiveSticky;
+ final InsetsState requestedInsetsState = win.getRequestedInsetsState();
+ final boolean requestHideNavBar = !requestedInsetsState.getSourceOrDefaultVisibility(
+ ITYPE_NAVIGATION_BAR);
final long now = SystemClock.uptimeMillis();
final boolean pendingPanic = mPendingPanicGestureUptime != 0
&& now - mPendingPanicGestureUptime <= PANIC_GESTURE_EXPIRATION;
final DisplayPolicy defaultDisplayPolicy =
mService.getDefaultDisplayContentLocked().getDisplayPolicy();
- if (pendingPanic && hideNavBarSysui && win != mNotificationShade
+ if (pendingPanic && requestHideNavBar && win != mNotificationShade
&& getInsetsPolicy().isHidden(ITYPE_NAVIGATION_BAR)
// TODO (b/111955725): Show keyguard presentation on all external displays
&& defaultDisplayPolicy.isKeyguardDrawComplete()) {
// The user performed the panic gesture recently, we're about to hide the bars,
// we're no longer on the Keyguard and the screen is ready. We can now request the bars.
mPendingPanicGestureUptime = 0;
- if (!isNavBarEmpty(vis)) {
+ if (!isNavBarEmpty(disableFlags)) {
mDisplayContent.getInsetsPolicy().showTransient(SHOW_TYPES_FOR_PANIC);
}
}
- final boolean denyTransientStatus = mStatusBarController.isTransientShowRequested()
- && !transientStatusBarAllowed && hideStatusBarSysui;
- final boolean denyTransientNav = mNavigationBarController.isTransientShowRequested()
- && !transientNavBarAllowed;
- if (denyTransientStatus || denyTransientNav || mForceShowSystemBars) {
- // clear the clearable flags instead
- clearClearableFlagsLw();
- vis &= ~View.SYSTEM_UI_CLEARABLE_FLAGS;
- }
-
- final boolean immersive = (vis & View.SYSTEM_UI_FLAG_IMMERSIVE) != 0;
- immersiveSticky = (vis & View.SYSTEM_UI_FLAG_IMMERSIVE_STICKY) != 0;
- final boolean navAllowedHidden = immersive || immersiveSticky;
-
- if (hideNavBarSysui && !navAllowedHidden
- && mService.mPolicy.getWindowLayerLw(win)
- > mService.mPolicy.getWindowLayerFromTypeLw(TYPE_INPUT_CONSUMER)) {
- // We can't hide the navbar from this window otherwise the input consumer would not get
- // the input events.
- vis = (vis & ~View.SYSTEM_UI_FLAG_HIDE_NAVIGATION);
- }
-
- vis = mStatusBarController.updateVisibilityLw(transientStatusBarAllowed, oldVis, vis);
-
// update navigation bar
boolean oldImmersiveMode = mLastImmersiveMode;
boolean newImmersiveMode = isImmersiveMode(win);
@@ -3374,23 +3115,13 @@ public class DisplayPolicy {
final String pkg = win.getOwningPackage();
mImmersiveModeConfirmation.immersiveModeChangedLw(pkg, newImmersiveMode,
mService.mPolicy.isUserSetupComplete(),
- isNavBarEmpty(win.getSystemUiVisibility()));
+ isNavBarEmpty(disableFlags));
}
- vis = mNavigationBarController.updateVisibilityLw(transientNavBarAllowed, oldVis, vis);
-
- final WindowState navColorWin = chooseNavigationColorWindowLw(
- mTopFullscreenOpaqueWindowState, mTopFullscreenOpaqueOrDimmingWindowState,
- mDisplayContent.mInputMethodWindow, mNavigationBarPosition);
- vis = updateLightNavigationBarLw(vis, mTopFullscreenOpaqueWindowState,
- mTopFullscreenOpaqueOrDimmingWindowState,
- mDisplayContent.mInputMethodWindow, navColorWin);
-
- return Pair.create(vis, navColorWin);
+ return appearance;
}
- private boolean drawsBarBackground(int vis, WindowState win, BarController controller,
- int translucentFlag) {
+ private boolean drawsBarBackground(WindowState win, BarController controller) {
if (!controller.isTransparentAllowed(win)) {
return false;
}
@@ -3403,73 +3134,59 @@ public class DisplayPolicy {
final boolean forceDrawsSystemBars =
(win.getAttrs().privateFlags & PRIVATE_FLAG_FORCE_DRAW_BAR_BACKGROUNDS) != 0;
- return forceDrawsSystemBars || drawsSystemBars && (vis & translucentFlag) == 0;
+ return forceDrawsSystemBars || drawsSystemBars;
}
- private boolean drawsStatusBarBackground(int vis, WindowState win) {
- return drawsBarBackground(vis, win, mStatusBarController, FLAG_TRANSLUCENT_STATUS);
+ private boolean drawsStatusBarBackground(WindowState win) {
+ return drawsBarBackground(win, mStatusBarController);
}
- private boolean drawsNavigationBarBackground(int vis, WindowState win) {
- return drawsBarBackground(vis, win, mNavigationBarController, FLAG_TRANSLUCENT_NAVIGATION);
+ private boolean drawsNavigationBarBackground(WindowState win) {
+ return drawsBarBackground(win, mNavigationBarController);
}
/**
* @return the current visibility flags with the nav-bar opacity related flags toggled based
* on the nav bar opacity rules chosen by {@link #mNavBarOpacityMode}.
*/
- private int configureNavBarOpacity(int visibility, boolean dockedStackVisible,
+ private int configureNavBarOpacity(int appearance, boolean dockedStackVisible,
boolean freeformStackVisible, boolean isDockedDividerResizing,
boolean fullscreenDrawsBackground, boolean dockedDrawsNavigationBarBackground) {
if (mNavBarOpacityMode == NAV_BAR_FORCE_TRANSPARENT) {
if (fullscreenDrawsBackground && dockedDrawsNavigationBarBackground) {
- visibility = setNavBarTransparentFlag(visibility);
+ appearance = clearNavBarOpaqueFlag(appearance);
} else if (dockedStackVisible) {
- visibility = setNavBarOpaqueFlag(visibility);
+ appearance = setNavBarOpaqueFlag(appearance);
}
} else if (mNavBarOpacityMode == NAV_BAR_OPAQUE_WHEN_FREEFORM_OR_DOCKED) {
if (dockedStackVisible || freeformStackVisible || isDockedDividerResizing) {
if (mIsFreeformWindowOverlappingWithNavBar) {
- visibility = setNavBarTranslucentFlag(visibility);
+ appearance = clearNavBarOpaqueFlag(appearance);
} else {
- visibility = setNavBarOpaqueFlag(visibility);
+ appearance = setNavBarOpaqueFlag(appearance);
}
} else if (fullscreenDrawsBackground) {
- visibility = setNavBarTransparentFlag(visibility);
+ appearance = clearNavBarOpaqueFlag(appearance);
}
} else if (mNavBarOpacityMode == NAV_BAR_TRANSLUCENT_WHEN_FREEFORM_OPAQUE_OTHERWISE) {
if (isDockedDividerResizing) {
- visibility = setNavBarOpaqueFlag(visibility);
+ appearance = setNavBarOpaqueFlag(appearance);
} else if (freeformStackVisible) {
- visibility = setNavBarTranslucentFlag(visibility);
+ appearance = clearNavBarOpaqueFlag(appearance);
} else {
- visibility = setNavBarOpaqueFlag(visibility);
+ appearance = setNavBarOpaqueFlag(appearance);
}
}
- return visibility;
- }
-
- private int setNavBarOpaqueFlag(int visibility) {
- return visibility & ~(View.NAVIGATION_BAR_TRANSLUCENT | View.NAVIGATION_BAR_TRANSPARENT);
- }
-
- private int setNavBarTranslucentFlag(int visibility) {
- visibility &= ~View.NAVIGATION_BAR_TRANSPARENT;
- return visibility | View.NAVIGATION_BAR_TRANSLUCENT;
+ return appearance;
}
- private int setNavBarTransparentFlag(int visibility) {
- visibility &= ~View.NAVIGATION_BAR_TRANSLUCENT;
- return visibility | View.NAVIGATION_BAR_TRANSPARENT;
+ private int setNavBarOpaqueFlag(int appearance) {
+ return appearance | APPEARANCE_OPAQUE_NAVIGATION_BARS;
}
- private void clearClearableFlagsLw() {
- int newVal = mResettingSystemUiFlags | View.SYSTEM_UI_CLEARABLE_FLAGS;
- if (newVal != mResettingSystemUiFlags) {
- mResettingSystemUiFlags = newVal;
- mDisplayContent.reevaluateStatusBarVisibility();
- }
+ private int clearNavBarOpaqueFlag(int appearance) {
+ return appearance & ~APPEARANCE_OPAQUE_NAVIGATION_BARS;
}
private boolean isImmersiveMode(WindowState win) {
@@ -3520,7 +3237,7 @@ public class DisplayPolicy {
// taken over the whole screen.
boolean panic = mImmersiveModeConfirmation.onPowerKeyDown(isScreenOn,
SystemClock.elapsedRealtime(), isImmersiveMode(mSystemUiControllingWindow),
- isNavBarEmpty(mLastSystemUiFlags));
+ isNavBarEmpty(mLastDisableFlags));
if (panic) {
mHandler.post(mHiddenNavPanic);
}
@@ -3579,14 +3296,9 @@ public class DisplayPolicy {
pw.print(prefix); pw.print("mKeyguardDrawComplete="); pw.print(mKeyguardDrawComplete);
pw.print(" mWindowManagerDrawComplete="); pw.println(mWindowManagerDrawComplete);
pw.print(prefix); pw.print("mHdmiPlugged="); pw.println(mHdmiPlugged);
- if (mLastSystemUiFlags != 0 || mResettingSystemUiFlags != 0
- || mForceClearedSystemUiFlags != 0) {
- pw.print(prefix); pw.print("mLastSystemUiFlags=0x");
- pw.print(Integer.toHexString(mLastSystemUiFlags));
- pw.print(" mResettingSystemUiFlags=0x");
- pw.print(Integer.toHexString(mResettingSystemUiFlags));
- pw.print(" mForceClearedSystemUiFlags=0x");
- pw.println(Integer.toHexString(mForceClearedSystemUiFlags));
+ if (mLastDisableFlags != 0) {
+ pw.print(prefix); pw.print("mLastDisableFlags=0x");
+ pw.print(Integer.toHexString(mLastDisableFlags));
}
pw.print(prefix); pw.print("mShowingDream="); pw.print(mShowingDream);
pw.print(" mDreamingLockscreen="); pw.print(mDreamingLockscreen);
@@ -3635,8 +3347,6 @@ public class DisplayPolicy {
pw.print(prefix); pw.print("mRemoteInsetsControllerControlsSystemBars");
pw.print(mDisplayContent.getInsetsPolicy().getRemoteInsetsControllerControlsSystemBars());
pw.print(" mAllowLockscreenWhenOn="); pw.println(mAllowLockscreenWhenOn);
- mStatusBarController.dump(pw, prefix);
- mNavigationBarController.dump(pw, prefix);
pw.print(prefix); pw.println("Looper state:");
mHandler.getLooper().dump(new PrintWriterPrinter(pw), prefix + " ");
diff --git a/services/core/java/com/android/server/wm/DragState.java b/services/core/java/com/android/server/wm/DragState.java
index 6e32d0eddaaf..b80ed6be2256 100644
--- a/services/core/java/com/android/server/wm/DragState.java
+++ b/services/core/java/com/android/server/wm/DragState.java
@@ -258,16 +258,13 @@ class DragState {
}
class InputInterceptor {
- InputChannel mServerChannel, mClientChannel;
+ InputChannel mClientChannel;
DragInputEventReceiver mInputEventReceiver;
InputApplicationHandle mDragApplicationHandle;
InputWindowHandle mDragWindowHandle;
InputInterceptor(Display display) {
- InputChannel[] channels = InputChannel.openInputChannelPair("drag");
- mServerChannel = channels[0];
- mClientChannel = channels[1];
- mService.mInputManager.registerInputChannel(mServerChannel);
+ mClientChannel = mService.mInputManager.createInputChannel("drag");
mInputEventReceiver = new DragInputEventReceiver(mClientChannel,
mService.mH.getLooper(), mDragDropController);
@@ -278,7 +275,7 @@ class DragState {
mDragWindowHandle = new InputWindowHandle(mDragApplicationHandle,
display.getDisplayId());
mDragWindowHandle.name = "drag";
- mDragWindowHandle.token = mServerChannel.getToken();
+ mDragWindowHandle.token = mClientChannel.getToken();
mDragWindowHandle.layoutParamsFlags = 0;
mDragWindowHandle.layoutParamsType = WindowManager.LayoutParams.TYPE_DRAG;
mDragWindowHandle.dispatchingTimeoutMillis = DEFAULT_DISPATCHING_TIMEOUT_MILLIS;
@@ -308,13 +305,11 @@ class DragState {
}
void tearDown() {
- mService.mInputManager.unregisterInputChannel(mServerChannel.getToken());
+ mService.mInputManager.removeInputChannel(mClientChannel.getToken());
mInputEventReceiver.dispose();
mInputEventReceiver = null;
mClientChannel.dispose();
- mServerChannel.dispose();
mClientChannel = null;
- mServerChannel = null;
mDragWindowHandle = null;
mDragApplicationHandle = null;
@@ -326,7 +321,7 @@ class DragState {
}
InputChannel getInputChannel() {
- return mInputInterceptor == null ? null : mInputInterceptor.mServerChannel;
+ return mInputInterceptor == null ? null : mInputInterceptor.mClientChannel;
}
InputWindowHandle getInputWindowHandle() {
diff --git a/services/core/java/com/android/server/wm/EmbeddedWindowController.java b/services/core/java/com/android/server/wm/EmbeddedWindowController.java
index 2a5bf16e09d0..3b89a24184f0 100644
--- a/services/core/java/com/android/server/wm/EmbeddedWindowController.java
+++ b/services/core/java/com/android/server/wm/EmbeddedWindowController.java
@@ -184,23 +184,13 @@ class EmbeddedWindowController {
InputChannel openInputChannel() {
final String name = getName();
-
- final InputChannel[] inputChannels = InputChannel.openInputChannelPair(name);
- mInputChannel = inputChannels[0];
- final InputChannel clientChannel = inputChannels[1];
- mWmService.mInputManager.registerInputChannel(mInputChannel);
-
- if (mInputChannel.getToken() != clientChannel.getToken()) {
- throw new IllegalStateException("Client and Server tokens are expected to"
- + "be the same");
- }
-
- return clientChannel;
+ mInputChannel = mWmService.mInputManager.createInputChannel(name);
+ return mInputChannel;
}
void onRemoved() {
if (mInputChannel != null) {
- mWmService.mInputManager.unregisterInputChannel(mInputChannel.getToken());
+ mWmService.mInputManager.removeInputChannel(mInputChannel.getToken());
mInputChannel.dispose();
mInputChannel = null;
}
diff --git a/services/core/java/com/android/server/wm/EventLogTags.logtags b/services/core/java/com/android/server/wm/EventLogTags.logtags
index aab901ebcdb6..6c346090578f 100644
--- a/services/core/java/com/android/server/wm/EventLogTags.logtags
+++ b/services/core/java/com/android/server/wm/EventLogTags.logtags
@@ -26,7 +26,7 @@ option java_package com.android.server.wm
# The Activity Manager failed to pause the given activity.
30012 wm_failed_to_pause (User|1|5),(Token|1|5),(Wanting to pause|3),(Currently pausing|3)
# Attempting to pause the current activity
-30013 wm_pause_activity (User|1|5),(Token|1|5),(Component Name|3),(User Leaving|3)
+30013 wm_pause_activity (User|1|5),(Token|1|5),(Component Name|3),(User Leaving|3),(Reason|3)
# Application process has been started
# An activity is being destroyed:
diff --git a/services/core/java/com/android/server/wm/HighRefreshRateBlacklist.java b/services/core/java/com/android/server/wm/HighRefreshRateDenylist.java
index aac6b2544c4f..cdc14cd11228 100644
--- a/services/core/java/com/android/server/wm/HighRefreshRateBlacklist.java
+++ b/services/core/java/com/android/server/wm/HighRefreshRateDenylist.java
@@ -34,62 +34,62 @@ import java.io.PrintWriter;
/**
* A Denylist for packages that should force the display out of high refresh rate.
*/
-class HighRefreshRateBlacklist {
+class HighRefreshRateDenylist {
- private final ArraySet<String> mBlacklistedPackages = new ArraySet<>();
+ private final ArraySet<String> mDenylistedPackages = new ArraySet<>();
@NonNull
- private final String[] mDefaultBlacklist;
+ private final String[] mDefaultDenylist;
private final Object mLock = new Object();
private DeviceConfigInterface mDeviceConfig;
private OnPropertiesChangedListener mListener = new OnPropertiesChangedListener();
- static HighRefreshRateBlacklist create(@NonNull Resources r) {
- return new HighRefreshRateBlacklist(r, DeviceConfigInterface.REAL);
+ static HighRefreshRateDenylist create(@NonNull Resources r) {
+ return new HighRefreshRateDenylist(r, DeviceConfigInterface.REAL);
}
@VisibleForTesting
- HighRefreshRateBlacklist(Resources r, DeviceConfigInterface deviceConfig) {
- mDefaultBlacklist = r.getStringArray(R.array.config_highRefreshRateBlacklist);
+ HighRefreshRateDenylist(Resources r, DeviceConfigInterface deviceConfig) {
+ mDefaultDenylist = r.getStringArray(R.array.config_highRefreshRateBlacklist);
mDeviceConfig = deviceConfig;
mDeviceConfig.addOnPropertiesChangedListener(DeviceConfig.NAMESPACE_DISPLAY_MANAGER,
BackgroundThread.getExecutor(), mListener);
final String property = mDeviceConfig.getProperty(DeviceConfig.NAMESPACE_DISPLAY_MANAGER,
KEY_HIGH_REFRESH_RATE_BLACKLIST);
- updateBlacklist(property);
+ updateDenylist(property);
}
- private void updateBlacklist(@Nullable String property) {
+ private void updateDenylist(@Nullable String property) {
synchronized (mLock) {
- mBlacklistedPackages.clear();
+ mDenylistedPackages.clear();
if (property != null) {
String[] packages = property.split(",");
for (String pkg : packages) {
String pkgName = pkg.trim();
if (!pkgName.isEmpty()) {
- mBlacklistedPackages.add(pkgName);
+ mDenylistedPackages.add(pkgName);
}
}
} else {
// If there's no config, or the config has been deleted, fallback to the device's
// default denylist
- for (String pkg : mDefaultBlacklist) {
- mBlacklistedPackages.add(pkg);
+ for (String pkg : mDefaultDenylist) {
+ mDenylistedPackages.add(pkg);
}
}
}
}
- boolean isBlacklisted(String packageName) {
+ boolean isDenylisted(String packageName) {
synchronized (mLock) {
- return mBlacklistedPackages.contains(packageName);
+ return mDenylistedPackages.contains(packageName);
}
}
void dump(PrintWriter pw) {
- pw.println("High Refresh Rate Blacklist");
+ pw.println("High Refresh Rate Denylist");
pw.println(" Packages:");
synchronized (mLock) {
- for (String pkg : mBlacklistedPackages) {
+ for (String pkg : mDenylistedPackages) {
pw.println(" " + pkg);
}
}
@@ -100,13 +100,13 @@ class HighRefreshRateBlacklist {
void dispose() {
mDeviceConfig.removeOnPropertiesChangedListener(mListener);
mDeviceConfig = null;
- mBlacklistedPackages.clear();
+ mDenylistedPackages.clear();
}
private class OnPropertiesChangedListener implements DeviceConfig.OnPropertiesChangedListener {
public void onPropertiesChanged(@NonNull DeviceConfig.Properties properties) {
if (properties.getKeyset().contains(KEY_HIGH_REFRESH_RATE_BLACKLIST)) {
- updateBlacklist(
+ updateDenylist(
properties.getString(KEY_HIGH_REFRESH_RATE_BLACKLIST, null /*default*/));
}
}
diff --git a/services/core/java/com/android/server/wm/InputConsumerImpl.java b/services/core/java/com/android/server/wm/InputConsumerImpl.java
index 1d1a2663823c..edb5e853af4f 100644
--- a/services/core/java/com/android/server/wm/InputConsumerImpl.java
+++ b/services/core/java/com/android/server/wm/InputConsumerImpl.java
@@ -35,7 +35,7 @@ import java.io.PrintWriter;
class InputConsumerImpl implements IBinder.DeathRecipient {
final WindowManagerService mService;
- final InputChannel mServerChannel, mClientChannel;
+ final InputChannel mClientChannel;
final InputApplicationHandle mApplicationHandle;
final InputWindowHandle mWindowHandle;
@@ -58,16 +58,10 @@ class InputConsumerImpl implements IBinder.DeathRecipient {
mClientPid = clientPid;
mClientUser = clientUser;
- InputChannel[] channels = InputChannel.openInputChannelPair(name);
- mServerChannel = channels[0];
+ mClientChannel = mService.mInputManager.createInputChannel(name);
if (inputChannel != null) {
- channels[1].transferTo(inputChannel);
- channels[1].dispose();
- mClientChannel = inputChannel;
- } else {
- mClientChannel = channels[1];
+ mClientChannel.copyTo(inputChannel);
}
- mService.mInputManager.registerInputChannel(mServerChannel);
mApplicationHandle = new InputApplicationHandle(new Binder());
mApplicationHandle.name = name;
@@ -75,7 +69,7 @@ class InputConsumerImpl implements IBinder.DeathRecipient {
mWindowHandle = new InputWindowHandle(mApplicationHandle, displayId);
mWindowHandle.name = name;
- mWindowHandle.token = mServerChannel.getToken();
+ mWindowHandle.token = mClientChannel.getToken();
mWindowHandle.layoutParamsType = WindowManager.LayoutParams.TYPE_INPUT_CONSUMER;
mWindowHandle.layoutParamsFlags = 0;
mWindowHandle.dispatchingTimeoutMillis = DEFAULT_DISPATCHING_TIMEOUT_MILLIS;
@@ -156,9 +150,8 @@ class InputConsumerImpl implements IBinder.DeathRecipient {
}
void disposeChannelsLw(SurfaceControl.Transaction t) {
- mService.mInputManager.unregisterInputChannel(mServerChannel.getToken());
+ mService.mInputManager.removeInputChannel(mClientChannel.getToken());
mClientChannel.dispose();
- mServerChannel.dispose();
t.remove(mInputSurface);
unlinkFromDeathRecipient();
}
diff --git a/services/core/java/com/android/server/wm/InsetsPolicy.java b/services/core/java/com/android/server/wm/InsetsPolicy.java
index 18a25033b1e6..361788497e11 100644
--- a/services/core/java/com/android/server/wm/InsetsPolicy.java
+++ b/services/core/java/com/android/server/wm/InsetsPolicy.java
@@ -36,12 +36,12 @@ import android.view.InsetsAnimationControlCallbacks;
import android.view.InsetsAnimationControlImpl;
import android.view.InsetsAnimationControlRunner;
import android.view.InsetsController;
+import android.view.InsetsSource;
import android.view.InsetsSourceControl;
import android.view.InsetsState;
import android.view.InsetsState.InternalInsetsType;
import android.view.SurfaceControl;
import android.view.SyncRtSurfaceTransactionApplier;
-import android.view.ViewRootImpl;
import android.view.WindowInsetsAnimation;
import android.view.WindowInsetsAnimation.Bounds;
import android.view.WindowInsetsAnimationControlListener;
@@ -139,9 +139,6 @@ class InsetsPolicy {
getFakeControlTarget(focusedWin, statusControlTarget),
navControlTarget,
getFakeControlTarget(focusedWin, navControlTarget));
- if (ViewRootImpl.sNewInsetsMode != ViewRootImpl.NEW_INSETS_MODE_FULL) {
- return;
- }
mStatusBar.updateVisibility(statusControlTarget, ITYPE_STATUS_BAR);
mNavBar.updateVisibility(navControlTarget, ITYPE_NAVIGATION_BAR);
mPolicy.updateHideNavInputEventReceiver();
@@ -211,11 +208,21 @@ class InsetsPolicy {
* @see InsetsStateController#getInsetsForDispatch
*/
InsetsState getInsetsForDispatch(WindowState target) {
- InsetsState originalState = mStateController.getInsetsForDispatch(target);
+ final InsetsState originalState = mStateController.getInsetsForDispatch(target);
InsetsState state = originalState;
for (int i = mShowingTransientTypes.size() - 1; i >= 0; i--) {
- state = new InsetsState(state);
- state.setSourceVisible(mShowingTransientTypes.get(i), false);
+ final int type = mShowingTransientTypes.get(i);
+ final InsetsSource originalSource = state.peekSource(type);
+ if (originalSource != null && originalSource.isVisible()) {
+ if (state == originalState) {
+ // The source will be modified, create a non-deep copy to store the new one.
+ state = new InsetsState(originalState);
+ }
+ // Replace the source with a copy in invisible state.
+ final InsetsSource source = new InsetsSource(originalSource);
+ source.setVisible(false);
+ state.addSource(source);
+ }
}
return state;
}
@@ -251,11 +258,14 @@ class InsetsPolicy {
}
}
+ /**
+ * If the caller is not {@link #updateBarControlTarget}, it should call
+ * updateBarControlTarget(mFocusedWin) after this invocation.
+ */
private void abortTransient() {
mPolicy.getStatusBarManagerInternal().abortTransient(mDisplayContent.getDisplayId(),
mShowingTransientTypes.toArray());
mShowingTransientTypes.clear();
- updateBarControlTarget(mFocusedWin);
}
private @Nullable InsetsControlTarget getFakeControlTarget(@Nullable WindowState focused,
@@ -289,7 +299,7 @@ class InsetsPolicy {
// fake control to the client, so that it can re-show the bar during this scenario.
return mDummyControlTarget;
}
- if (mPolicy.topAppHidesStatusBar()) {
+ if (!canBeTopFullscreenOpaqueWindow(focusedWin) && mPolicy.topAppHidesStatusBar()) {
// Non-fullscreen focused window should not break the state that the top-fullscreen-app
// window hides status bar.
return mPolicy.getTopFullscreenOpaqueWindow();
@@ -297,6 +307,16 @@ class InsetsPolicy {
return focusedWin;
}
+ private static boolean canBeTopFullscreenOpaqueWindow(@Nullable WindowState win) {
+ // The condition doesn't use WindowState#canAffectSystemUiFlags because the window may
+ // haven't drawn or committed the visibility.
+ final boolean nonAttachedAppWindow = win != null
+ && win.mAttrs.type >= WindowManager.LayoutParams.FIRST_APPLICATION_WINDOW
+ && win.mAttrs.type <= WindowManager.LayoutParams.LAST_APPLICATION_WINDOW;
+ return nonAttachedAppWindow && win.mAttrs.isFullscreen() && !win.isFullyTransparent()
+ && !win.inMultiWindowMode();
+ }
+
private @Nullable InsetsControlTarget getNavControlTarget(@Nullable WindowState focusedWin,
boolean forceShowsSystemBarsForWindowingMode) {
if (mShowingTransientTypes.indexOf(ITYPE_NAVIGATION_BAR) != -1) {
diff --git a/services/core/java/com/android/server/wm/InsetsSourceProvider.java b/services/core/java/com/android/server/wm/InsetsSourceProvider.java
index e00c9e7ac38b..a7f32c09b83b 100644
--- a/services/core/java/com/android/server/wm/InsetsSourceProvider.java
+++ b/services/core/java/com/android/server/wm/InsetsSourceProvider.java
@@ -19,10 +19,6 @@ package com.android.server.wm;
import static android.view.InsetsState.ITYPE_IME;
import static android.view.InsetsState.ITYPE_NAVIGATION_BAR;
import static android.view.InsetsState.ITYPE_STATUS_BAR;
-import static android.view.ViewRootImpl.NEW_INSETS_MODE_FULL;
-import static android.view.ViewRootImpl.NEW_INSETS_MODE_IME;
-import static android.view.ViewRootImpl.NEW_INSETS_MODE_NONE;
-import static android.view.ViewRootImpl.sNewInsetsMode;
import static com.android.internal.protolog.ProtoLogGroup.WM_DEBUG_IME;
import static com.android.server.wm.SurfaceAnimator.ANIMATION_TYPE_INSETS_CONTROL;
@@ -93,13 +89,14 @@ class InsetsSourceProvider {
mFakeControl = new InsetsSourceControl(source.getType(), null /* leash */,
new Point());
- final int type = source.getType();
- if (type == ITYPE_STATUS_BAR || type == ITYPE_NAVIGATION_BAR) {
- mControllable = sNewInsetsMode == NEW_INSETS_MODE_FULL;
- } else if (type == ITYPE_IME) {
- mControllable = sNewInsetsMode >= NEW_INSETS_MODE_IME;
- } else {
- mControllable = false;
+ switch (source.getType()) {
+ case ITYPE_STATUS_BAR:
+ case ITYPE_NAVIGATION_BAR:
+ case ITYPE_IME:
+ mControllable = true;
+ break;
+ default:
+ mControllable = false;
}
}
@@ -395,7 +392,7 @@ class InsetsSourceProvider {
}
boolean isClientVisible() {
- return sNewInsetsMode == NEW_INSETS_MODE_NONE || mClientVisible;
+ return mClientVisible;
}
/**
diff --git a/services/core/java/com/android/server/wm/InsetsStateController.java b/services/core/java/com/android/server/wm/InsetsStateController.java
index 5520ad37e8ed..c0bdbff6b761 100644
--- a/services/core/java/com/android/server/wm/InsetsStateController.java
+++ b/services/core/java/com/android/server/wm/InsetsStateController.java
@@ -24,8 +24,6 @@ import static android.view.InsetsState.ITYPE_IME;
import static android.view.InsetsState.ITYPE_INVALID;
import static android.view.InsetsState.ITYPE_NAVIGATION_BAR;
import static android.view.InsetsState.ITYPE_STATUS_BAR;
-import static android.view.ViewRootImpl.NEW_INSETS_MODE_FULL;
-import static android.view.ViewRootImpl.sNewInsetsMode;
import static android.view.WindowManager.LayoutParams.TYPE_INPUT_METHOD;
import static android.view.WindowManager.LayoutParams.TYPE_NAVIGATION_BAR;
import static android.view.WindowManager.LayoutParams.TYPE_STATUS_BAR;
@@ -382,9 +380,6 @@ class InsetsStateController {
*/
void onControlFakeTargetChanged(@InternalInsetsType int type,
@Nullable InsetsControlTarget fakeTarget) {
- if (sNewInsetsMode != NEW_INSETS_MODE_FULL) {
- return;
- }
final InsetsControlTarget previous = mTypeFakeControlTargetMap.get(type);
if (fakeTarget == previous) {
return;
diff --git a/services/core/java/com/android/server/wm/Letterbox.java b/services/core/java/com/android/server/wm/Letterbox.java
index 4fe678dc1974..44ce4de529b0 100644
--- a/services/core/java/com/android/server/wm/Letterbox.java
+++ b/services/core/java/com/android/server/wm/Letterbox.java
@@ -191,7 +191,6 @@ public class Letterbox {
}
private static class InputInterceptor {
- final InputChannel mServerChannel;
final InputChannel mClientChannel;
final InputWindowHandle mWindowHandle;
final InputEventReceiver mInputEventReceiver;
@@ -201,13 +200,10 @@ public class Letterbox {
InputInterceptor(String namePrefix, WindowState win) {
mWmService = win.mWmService;
final String name = namePrefix + (win.mActivityRecord != null ? win.mActivityRecord : win);
- final InputChannel[] channels = InputChannel.openInputChannelPair(name);
- mServerChannel = channels[0];
- mClientChannel = channels[1];
+ mClientChannel = mWmService.mInputManager.createInputChannel(name);
mInputEventReceiver = new SimpleInputReceiver(mClientChannel);
- mWmService.mInputManager.registerInputChannel(mServerChannel);
- mToken = mServerChannel.getToken();
+ mToken = mClientChannel.getToken();
mWindowHandle = new InputWindowHandle(null /* inputApplicationHandle */,
win.getDisplayId());
@@ -239,9 +235,8 @@ public class Letterbox {
}
void dispose() {
- mWmService.mInputManager.unregisterInputChannel(mServerChannel.getToken());
+ mWmService.mInputManager.removeInputChannel(mToken);
mInputEventReceiver.dispose();
- mServerChannel.dispose();
mClientChannel.dispose();
}
diff --git a/services/core/java/com/android/server/wm/RefreshRatePolicy.java b/services/core/java/com/android/server/wm/RefreshRatePolicy.java
index 072116f04aac..91014aa69831 100644
--- a/services/core/java/com/android/server/wm/RefreshRatePolicy.java
+++ b/services/core/java/com/android/server/wm/RefreshRatePolicy.java
@@ -30,7 +30,7 @@ class RefreshRatePolicy {
private final int mLowRefreshRateId;
private final ArraySet<String> mNonHighRefreshRatePackages = new ArraySet<>();
- private final HighRefreshRateBlacklist mHighRefreshRateBlacklist;
+ private final HighRefreshRateDenylist mHighRefreshRateDenylist;
private final WindowManagerService mWmService;
/**
@@ -55,9 +55,9 @@ class RefreshRatePolicy {
static final int LAYER_PRIORITY_NOT_FOCUSED_WITH_MODE = 2;
RefreshRatePolicy(WindowManagerService wmService, DisplayInfo displayInfo,
- HighRefreshRateBlacklist blacklist) {
+ HighRefreshRateDenylist denylist) {
mLowRefreshRateId = findLowRefreshRateModeId(displayInfo);
- mHighRefreshRateBlacklist = blacklist;
+ mHighRefreshRateDenylist = denylist;
mWmService = wmService;
}
@@ -108,7 +108,7 @@ class RefreshRatePolicy {
}
// If app is denylisted using higher refresh rate, return default (lower) refresh rate
- if (mHighRefreshRateBlacklist.isBlacklisted(packageName)) {
+ if (mHighRefreshRateDenylist.isDenylisted(packageName)) {
return mLowRefreshRateId;
}
return 0;
diff --git a/services/core/java/com/android/server/wm/SafeActivityOptions.java b/services/core/java/com/android/server/wm/SafeActivityOptions.java
index ede6708d5f8f..9205401ed2ee 100644
--- a/services/core/java/com/android/server/wm/SafeActivityOptions.java
+++ b/services/core/java/com/android/server/wm/SafeActivityOptions.java
@@ -150,6 +150,14 @@ public class SafeActivityOptions {
}
/**
+ * Gets the original options passed in. It should only be used for logging. DO NOT use it as a
+ * condition in the logic of activity launch.
+ */
+ ActivityOptions getOriginalOptions() {
+ return mOriginalOptions;
+ }
+
+ /**
* @see ActivityOptions#popAppVerificationBundle
*/
Bundle popAppVerificationBundle() {
diff --git a/services/core/java/com/android/server/wm/StatusBarController.java b/services/core/java/com/android/server/wm/StatusBarController.java
deleted file mode 100644
index 3564e0bce5f5..000000000000
--- a/services/core/java/com/android/server/wm/StatusBarController.java
+++ /dev/null
@@ -1,109 +0,0 @@
-/*
- * Copyright (C) 2018 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.server.wm;
-
-import static android.view.WindowManager.LayoutParams.FLAG_TRANSLUCENT_STATUS;
-import static android.view.WindowManager.LayoutParams.TYPE_STATUS_BAR;
-
-import static com.android.server.wm.WindowManagerInternal.AppTransitionListener;
-
-import android.app.StatusBarManager;
-import android.os.IBinder;
-import android.view.View;
-
-import com.android.server.statusbar.StatusBarManagerInternal;
-
-/**
- * Implements status bar specific behavior.
- */
-public class StatusBarController extends BarController {
-
- private final AppTransitionListener mAppTransitionListener = new AppTransitionListener() {
-
- private Runnable mAppTransitionPending = () -> {
- StatusBarManagerInternal statusBar = getStatusBarInternal();
- if (statusBar != null) {
- statusBar.appTransitionPending(mDisplayId);
- }
- };
-
- private Runnable mAppTransitionCancelled = () -> {
- StatusBarManagerInternal statusBar = getStatusBarInternal();
- if (statusBar != null) {
- statusBar.appTransitionCancelled(mDisplayId);
- }
- };
-
- private Runnable mAppTransitionFinished = () -> {
- StatusBarManagerInternal statusBar = getStatusBarInternal();
- if (statusBar != null) {
- statusBar.appTransitionFinished(mDisplayId);
- }
- };
-
- @Override
- public void onAppTransitionPendingLocked() {
- mHandler.post(mAppTransitionPending);
- }
-
- @Override
- public int onAppTransitionStartingLocked(int transit, long duration,
- long statusBarAnimationStartTime, long statusBarAnimationDuration) {
- mHandler.post(() -> {
- StatusBarManagerInternal statusBar = getStatusBarInternal();
- if (statusBar != null) {
- statusBar.appTransitionStarting(mDisplayId,
- statusBarAnimationStartTime, statusBarAnimationDuration);
- }
- });
- return 0;
- }
-
- @Override
- public void onAppTransitionCancelledLocked(int transit) {
- mHandler.post(mAppTransitionCancelled);
- }
-
- @Override
- public void onAppTransitionFinishedLocked(IBinder token) {
- mHandler.post(mAppTransitionFinished);
- }
- };
-
- StatusBarController(int displayId) {
- super("StatusBar",
- displayId,
- View.STATUS_BAR_TRANSIENT,
- View.STATUS_BAR_UNHIDE,
- View.STATUS_BAR_TRANSLUCENT,
- StatusBarManager.WINDOW_STATUS_BAR,
- TYPE_STATUS_BAR,
- FLAG_TRANSLUCENT_STATUS,
- View.STATUS_BAR_TRANSPARENT);
- }
-
- void setTopAppHidesStatusBar(boolean hidesStatusBar) {
- StatusBarManagerInternal statusBar = getStatusBarInternal();
- if (statusBar != null) {
- statusBar.setTopAppHidesStatusBar(hidesStatusBar);
- }
- }
-
- AppTransitionListener getAppTransitionListener() {
- return mAppTransitionListener;
- }
-}
diff --git a/services/core/java/com/android/server/wm/Task.java b/services/core/java/com/android/server/wm/Task.java
index b7a5d05486c4..4a1151bc13fd 100644
--- a/services/core/java/com/android/server/wm/Task.java
+++ b/services/core/java/com/android/server/wm/Task.java
@@ -1102,7 +1102,7 @@ class Task extends WindowContainer<WindowContainer> {
&& toStack.topRunningActivity() != null) {
// Pause the resumed activity on the target stack while re-parenting task on top of it.
toStack.startPausingLocked(false /* userLeaving */, false /* uiSleeping */,
- null /* resuming */);
+ null /* resuming */, "reparent");
}
final int toStackWindowingMode = toStack.getWindowingMode();
@@ -5340,7 +5340,8 @@ class Task extends WindowContainer<WindowContainer> {
if (DEBUG_USER_LEAVING) Slog.v(TAG_USER_LEAVING,
"Sleep => pause with userLeaving=false");
- startPausingLocked(false /* userLeaving */, true /* uiSleeping */, null /* resuming */);
+ startPausingLocked(false /* userLeaving */, true /* uiSleeping */, null /* resuming */,
+ "sleep");
shouldSleep = false ;
} else if (mPausingActivity != null) {
// Still waiting for something to pause; can't sleep yet.
@@ -5400,11 +5401,12 @@ class Task extends WindowContainer<WindowContainer> {
* @param resuming The activity we are currently trying to resume or null if this is not being
* called as part of resuming the top activity, so we shouldn't try to instigate
* a resume here if not null.
+ * @param reason The reason of pausing the activity.
* @return Returns true if an activity now is in the PAUSING state, and we are waiting for
* it to tell us when it is done.
*/
final boolean startPausingLocked(boolean userLeaving, boolean uiSleeping,
- ActivityRecord resuming) {
+ ActivityRecord resuming, String reason) {
if (mPausingActivity != null) {
Slog.wtf(TAG, "Going to pause when pause is already pending for " + mPausingActivity
+ " state=" + mPausingActivity.getState());
@@ -5471,7 +5473,7 @@ class Task extends WindowContainer<WindowContainer> {
if (DEBUG_PAUSE) Slog.v(TAG_PAUSE, "Enqueueing pending pause: " + prev);
try {
EventLogTags.writeWmPauseActivity(prev.mUserId, System.identityHashCode(prev),
- prev.shortComponentName, "userLeaving=" + userLeaving);
+ prev.shortComponentName, "userLeaving=" + userLeaving, reason);
mAtmService.getLifecycleManager().scheduleTransaction(prev.app.getThread(),
prev.appToken, PauseActivityItem.obtain(prev.finishing, userLeaving,
@@ -5995,7 +5997,8 @@ class Task extends WindowContainer<WindowContainer> {
if (mResumedActivity != null) {
if (DEBUG_STATES) Slog.d(TAG_STATES,
"resumeTopActivityLocked: Pausing " + mResumedActivity);
- pausing |= startPausingLocked(userLeaving, false /* uiSleeping */, next);
+ pausing |= startPausingLocked(userLeaving, false /* uiSleeping */, next,
+ "resumeTopActivityInnerLocked");
}
if (pausing) {
if (DEBUG_SWITCH || DEBUG_STATES) Slog.v(TAG_STATES,
@@ -6036,8 +6039,9 @@ class Task extends WindowContainer<WindowContainer> {
// If the most recent activity was noHistory but was only stopped rather
// than stopped+finished because the device went to sleep, we need to make
// sure to finish it as we're making a new activity topmost.
- if (shouldSleepActivities() && mLastNoHistoryActivity != null &&
- !mLastNoHistoryActivity.finishing) {
+ if (shouldSleepActivities() && mLastNoHistoryActivity != null
+ && !mLastNoHistoryActivity.finishing
+ && mLastNoHistoryActivity != next) {
if (DEBUG_STATES) Slog.d(TAG_STATES,
"no-history finish of " + mLastNoHistoryActivity + " on new resume");
mLastNoHistoryActivity.finishIfPossible("resume-no-history", false /* oomAdj */);
diff --git a/services/core/java/com/android/server/wm/TaskDisplayArea.java b/services/core/java/com/android/server/wm/TaskDisplayArea.java
index 890f56e50bac..5239b1f87bea 100644
--- a/services/core/java/com/android/server/wm/TaskDisplayArea.java
+++ b/services/core/java/com/android/server/wm/TaskDisplayArea.java
@@ -29,12 +29,10 @@ import static android.app.WindowConfiguration.WINDOWING_MODE_SPLIT_SCREEN_PRIMAR
import static android.app.WindowConfiguration.WINDOWING_MODE_SPLIT_SCREEN_SECONDARY;
import static android.app.WindowConfiguration.WINDOWING_MODE_UNDEFINED;
import static android.app.WindowConfiguration.isSplitScreenWindowingMode;
-import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_BEHIND;
import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_UNSET;
import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED;
import static com.android.internal.protolog.ProtoLogGroup.WM_DEBUG_ADD_REMOVE;
-import static com.android.internal.protolog.ProtoLogGroup.WM_DEBUG_ORIENTATION;
import static com.android.server.wm.ActivityStackSupervisor.TAG_TASKS;
import static com.android.server.wm.ActivityTaskManagerDebugConfig.DEBUG_STATES;
import static com.android.server.wm.ActivityTaskManagerDebugConfig.DEBUG_TASKS;
@@ -153,6 +151,12 @@ final class TaskDisplayArea extends DisplayArea<Task> {
*/
private boolean mRemoved;
+ /**
+ * Whether the task display area should ignore fixed-orientation request. If {@code true}, it
+ * can never specify orientation, but show the fixed-orientation apps in the letterbox;
+ * otherwise, it rotates based on the fixed-orientation request when it has the focus.
+ */
+ private boolean mIgnoreOrientationRequest;
/**
* The id of a leaf task that most recently being moved to front.
@@ -643,8 +647,33 @@ final class TaskDisplayArea extends DisplayArea<Task> {
}
}
+ /**
+ * Sets whether the task display area should ignore fixed-orientation request from apps.
+ * @return Whether the display orientation changed
+ */
+ boolean setIgnoreOrientationRequest(boolean ignoreOrientationRequest) {
+ if (mIgnoreOrientationRequest == ignoreOrientationRequest) {
+ return false;
+ }
+
+ mIgnoreOrientationRequest = ignoreOrientationRequest;
+ if (isLastFocused()) {
+ // Update orientation if this TDA is the last focused, otherwise it shouldn't affect
+ // the display.
+ return mDisplayContent.updateOrientation();
+ }
+
+ return false;
+ }
+
@Override
int getOrientation(int candidate) {
+ // Only allow to specify orientation if this TDA is not set to ignore orientation request,
+ // and it has the focus.
+ if (mIgnoreOrientationRequest || !isLastFocused()) {
+ return SCREEN_ORIENTATION_UNSET;
+ }
+
if (isStackVisible(WINDOWING_MODE_SPLIT_SCREEN_PRIMARY)) {
// Apps and their containers are not allowed to specify an orientation while using
// root tasks...except for the home stack if it is not resizable and currently
@@ -671,21 +700,7 @@ final class TaskDisplayArea extends DisplayArea<Task> {
return SCREEN_ORIENTATION_UNSPECIFIED;
}
- final int orientation = super.getOrientation(candidate);
- if (orientation != SCREEN_ORIENTATION_UNSET
- && orientation != SCREEN_ORIENTATION_BEHIND) {
- ProtoLog.v(WM_DEBUG_ORIENTATION,
- "App is requesting an orientation, return %d for display id=%d",
- orientation, mDisplayContent.mDisplayId);
- return orientation;
- }
-
- ProtoLog.v(WM_DEBUG_ORIENTATION,
- "No app is requesting an orientation, return %d for display id=%d",
- mDisplayContent.getLastOrientation(), mDisplayContent.mDisplayId);
- // The next app has not been requested to be visible, so we keep the current orientation
- // to prevent freezing/unfreezing the display too early.
- return mDisplayContent.getLastOrientation();
+ return super.getOrientation(candidate);
}
@Override
@@ -1219,7 +1234,7 @@ final class TaskDisplayArea extends DisplayArea<Task> {
+ " mResumedActivity=" + resumedActivity);
}
someActivityPaused |= stack.startPausingLocked(userLeaving, false /* uiSleeping*/,
- resuming);
+ resuming, "pauseBackStacks");
}
}
return someActivityPaused;
@@ -1880,6 +1895,12 @@ final class TaskDisplayArea extends DisplayArea<Task> {
return lastReparentedStack;
}
+ /** Whether this task display area is the last focused one on this logical display. */
+ @VisibleForTesting
+ boolean isLastFocused() {
+ return mDisplayContent.getLastFocusedTaskDisplayArea() == this;
+ }
+
@Override
protected boolean isTaskDisplayArea() {
return true;
diff --git a/services/core/java/com/android/server/wm/TaskPositioner.java b/services/core/java/com/android/server/wm/TaskPositioner.java
index abe632941b97..a6f0f464c8b3 100644
--- a/services/core/java/com/android/server/wm/TaskPositioner.java
+++ b/services/core/java/com/android/server/wm/TaskPositioner.java
@@ -96,7 +96,6 @@ class TaskPositioner implements IBinder.DeathRecipient {
boolean mDragEnded;
IBinder mClientCallback;
- InputChannel mServerChannel;
InputChannel mClientChannel;
InputApplicationHandle mDragApplicationHandle;
InputWindowHandle mDragWindowHandle;
@@ -220,10 +219,7 @@ class TaskPositioner implements IBinder.DeathRecipient {
}
mDisplayContent = displayContent;
- final InputChannel[] channels = InputChannel.openInputChannelPair(TAG);
- mServerChannel = channels[0];
- mClientChannel = channels[1];
- mService.mInputManager.registerInputChannel(mServerChannel);
+ mClientChannel = mService.mInputManager.createInputChannel(TAG);
mInputEventReceiver = new WindowPositionerEventReceiver(
mClientChannel, mService.mAnimationHandler.getLooper(),
@@ -237,7 +233,7 @@ class TaskPositioner implements IBinder.DeathRecipient {
mDragWindowHandle = new InputWindowHandle(mDragApplicationHandle,
displayContent.getDisplayId());
mDragWindowHandle.name = TAG;
- mDragWindowHandle.token = mServerChannel.getToken();
+ mDragWindowHandle.token = mClientChannel.getToken();
mDragWindowHandle.layoutParamsFlags = 0;
mDragWindowHandle.layoutParamsType = WindowManager.LayoutParams.TYPE_DRAG;
mDragWindowHandle.dispatchingTimeoutMillis = DEFAULT_DISPATCHING_TIMEOUT_MILLIS;
@@ -299,14 +295,12 @@ class TaskPositioner implements IBinder.DeathRecipient {
}
mService.mTaskPositioningController.hideInputSurface(mDisplayContent.getDisplayId());
- mService.mInputManager.unregisterInputChannel(mServerChannel.getToken());
+ mService.mInputManager.removeInputChannel(mClientChannel.getToken());
mInputEventReceiver.dispose();
mInputEventReceiver = null;
mClientChannel.dispose();
- mServerChannel.dispose();
mClientChannel = null;
- mServerChannel = null;
mDragWindowHandle = null;
mDragApplicationHandle = null;
diff --git a/services/core/java/com/android/server/wm/TaskPositioningController.java b/services/core/java/com/android/server/wm/TaskPositioningController.java
index b9a449f558f0..9d35c25fc546 100644
--- a/services/core/java/com/android/server/wm/TaskPositioningController.java
+++ b/services/core/java/com/android/server/wm/TaskPositioningController.java
@@ -188,7 +188,7 @@ class TaskPositioningController {
transferFocusFromWin = displayContent.mCurrentFocus;
}
if (!mInputManager.transferTouchFocus(
- transferFocusFromWin.mInputChannel, mTaskPositioner.mServerChannel)) {
+ transferFocusFromWin.mInputChannel, mTaskPositioner.mClientChannel)) {
Slog.e(TAG_WM, "startPositioningLocked: Unable to transfer touch focus");
cleanUpTaskPositioner();
return false;
diff --git a/services/core/java/com/android/server/wm/TaskSnapshotController.java b/services/core/java/com/android/server/wm/TaskSnapshotController.java
index f39fa1b7fbc8..bc4f75276a95 100644
--- a/services/core/java/com/android/server/wm/TaskSnapshotController.java
+++ b/services/core/java/com/android/server/wm/TaskSnapshotController.java
@@ -481,13 +481,11 @@ class TaskSnapshotController {
final int color = ColorUtils.setAlphaComponent(
task.getTaskDescription().getBackgroundColor(), 255);
final LayoutParams attrs = mainWindow.getAttrs();
- final InsetsPolicy insetsPolicy = mainWindow.getDisplayContent().getInsetsPolicy();
- final InsetsState insetsState =
- new InsetsState(insetsPolicy.getInsetsForDispatch(mainWindow));
+ final InsetsState insetsState = new InsetsState(mainWindow.getInsetsState());
mergeInsetsSources(insetsState, mainWindow.getRequestedInsetsState());
final Rect systemBarInsets = getSystemBarInsets(mainWindow.getFrame(), insetsState);
final SystemBarBackgroundPainter decorPainter = new SystemBarBackgroundPainter(attrs.flags,
- attrs.privateFlags, attrs.systemUiVisibility, task.getTaskDescription(),
+ attrs.privateFlags, attrs.insetsFlags.appearance, task.getTaskDescription(),
mHighResTaskSnapshotScale, insetsState);
final int taskWidth = task.getBounds().width();
final int taskHeight = task.getBounds().height();
diff --git a/services/core/java/com/android/server/wm/TaskSnapshotSurface.java b/services/core/java/com/android/server/wm/TaskSnapshotSurface.java
index 100f1bff12a7..cccda3a4f4b8 100644
--- a/services/core/java/com/android/server/wm/TaskSnapshotSurface.java
+++ b/services/core/java/com/android/server/wm/TaskSnapshotSurface.java
@@ -19,8 +19,8 @@ package com.android.server.wm;
import static android.app.WindowConfiguration.ACTIVITY_TYPE_HOME;
import static android.graphics.Color.WHITE;
import static android.graphics.Color.alpha;
-import static android.view.View.SYSTEM_UI_FLAG_LIGHT_NAVIGATION_BAR;
-import static android.view.View.SYSTEM_UI_FLAG_LIGHT_STATUS_BAR;
+import static android.view.WindowInsetsController.APPEARANCE_LIGHT_NAVIGATION_BARS;
+import static android.view.WindowInsetsController.APPEARANCE_LIGHT_STATUS_BARS;
import static android.view.WindowManager.LayoutParams.FLAG_ALT_FOCUSABLE_IM;
import static android.view.WindowManager.LayoutParams.FLAG_HARDWARE_ACCELERATED;
import static android.view.WindowManager.LayoutParams.FLAG_IGNORE_CHEEK_PRESSES;
@@ -75,7 +75,6 @@ import android.view.SurfaceControl;
import android.view.SurfaceSession;
import android.view.View;
import android.view.ViewGroup.LayoutParams;
-import android.view.ViewRootImpl;
import android.view.WindowManager;
import android.view.WindowManagerGlobal;
import android.window.ClientWindowFrames;
@@ -167,7 +166,7 @@ class TaskSnapshotSurface implements StartingSurface {
final TaskDescription taskDescription = new TaskDescription();
taskDescription.setBackgroundColor(WHITE);
final WindowState topFullscreenOpaqueWindow;
- final int sysUiVis;
+ final int appearance;
final int windowFlags;
final int windowPrivateFlags;
final int currentOrientation;
@@ -204,8 +203,8 @@ class TaskSnapshotSurface implements StartingSurface {
topFullscreenActivity, false /* checkOpening */);
}
- sysUiVis = topFullscreenOpaqueWindow.getSystemUiVisibility();
WindowManager.LayoutParams attrs = topFullscreenOpaqueWindow.mAttrs;
+ appearance = attrs.insetsFlags.appearance;
windowFlags = attrs.flags;
windowPrivateFlags = attrs.privateFlags;
@@ -221,11 +220,8 @@ class TaskSnapshotSurface implements StartingSurface {
layoutParams.token = activity.token;
layoutParams.width = LayoutParams.MATCH_PARENT;
layoutParams.height = LayoutParams.MATCH_PARENT;
- layoutParams.systemUiVisibility = sysUiVis;
- layoutParams.insetsFlags.behavior
- = topFullscreenOpaqueWindow.mAttrs.insetsFlags.behavior;
- layoutParams.insetsFlags.appearance
- = topFullscreenOpaqueWindow.mAttrs.insetsFlags.appearance;
+ layoutParams.insetsFlags.appearance = appearance;
+ layoutParams.insetsFlags.behavior = attrs.insetsFlags.behavior;
layoutParams.layoutInDisplayCutoutMode = attrs.layoutInDisplayCutoutMode;
layoutParams.setFitInsetsTypes(attrs.getFitInsetsTypes());
layoutParams.setFitInsetsSides(attrs.getFitInsetsSides());
@@ -241,11 +237,7 @@ class TaskSnapshotSurface implements StartingSurface {
task.getBounds(taskBounds);
currentOrientation = topFullscreenOpaqueWindow.getConfiguration().orientation;
activityType = activity.getActivityType();
-
- final InsetsPolicy insetsPolicy = topFullscreenOpaqueWindow.getDisplayContent()
- .getInsetsPolicy();
- insetsState =
- new InsetsState(insetsPolicy.getInsetsForDispatch(topFullscreenOpaqueWindow));
+ insetsState = new InsetsState(topFullscreenOpaqueWindow.getInsetsState());
mergeInsetsSources(insetsState, topFullscreenOpaqueWindow.getRequestedInsetsState());
}
try {
@@ -261,7 +253,7 @@ class TaskSnapshotSurface implements StartingSurface {
// Local call.
}
final TaskSnapshotSurface snapshotSurface = new TaskSnapshotSurface(service, window,
- surfaceControl, snapshot, layoutParams.getTitle(), taskDescription, sysUiVis,
+ surfaceControl, snapshot, layoutParams.getTitle(), taskDescription, appearance,
windowFlags, windowPrivateFlags, taskBounds, currentOrientation, activityType,
insetsState);
window.setOuter(snapshotSurface);
@@ -282,7 +274,7 @@ class TaskSnapshotSurface implements StartingSurface {
@VisibleForTesting
TaskSnapshotSurface(WindowManagerService service, Window window, SurfaceControl surfaceControl,
TaskSnapshot snapshot, CharSequence title, TaskDescription taskDescription,
- int sysUiVis, int windowFlags, int windowPrivateFlags, Rect taskBounds,
+ int appearance, int windowFlags, int windowPrivateFlags, Rect taskBounds,
int currentOrientation, int activityType, InsetsState insetsState) {
mService = service;
mSurface = service.mSurfaceFactory.get();
@@ -296,7 +288,7 @@ class TaskSnapshotSurface implements StartingSurface {
mBackgroundPaint.setColor(backgroundColor != 0 ? backgroundColor : WHITE);
mTaskBounds = taskBounds;
mSystemBarBackgroundPainter = new SystemBarBackgroundPainter(windowFlags,
- windowPrivateFlags, sysUiVis, taskDescription, 1f, insetsState);
+ windowPrivateFlags, appearance, taskDescription, 1f, insetsState);
mStatusBarColor = taskDescription.getStatusBarColor();
mOrientationOnCreation = currentOrientation;
mActivityType = activityType;
@@ -554,28 +546,26 @@ class TaskSnapshotSurface implements StartingSurface {
private final int mNavigationBarColor;
private final int mWindowFlags;
private final int mWindowPrivateFlags;
- private final int mSysUiVis;
private final float mScale;
private final InsetsState mInsetsState;
private final Rect mSystemBarInsets = new Rect();
- SystemBarBackgroundPainter(int windowFlags, int windowPrivateFlags, int sysUiVis,
+ SystemBarBackgroundPainter(int windowFlags, int windowPrivateFlags, int appearance,
TaskDescription taskDescription, float scale, InsetsState insetsState) {
mWindowFlags = windowFlags;
mWindowPrivateFlags = windowPrivateFlags;
- mSysUiVis = sysUiVis;
mScale = scale;
final Context context = ActivityThread.currentActivityThread().getSystemUiContext();
final int semiTransparent = context.getColor(
R.color.system_bar_background_semi_transparent);
mStatusBarColor = DecorView.calculateBarColor(windowFlags, FLAG_TRANSLUCENT_STATUS,
- semiTransparent, taskDescription.getStatusBarColor(), sysUiVis,
- SYSTEM_UI_FLAG_LIGHT_STATUS_BAR,
+ semiTransparent, taskDescription.getStatusBarColor(), appearance,
+ APPEARANCE_LIGHT_STATUS_BARS,
taskDescription.getEnsureStatusBarContrastWhenTransparent());
mNavigationBarColor = DecorView.calculateBarColor(windowFlags,
FLAG_TRANSLUCENT_NAVIGATION, semiTransparent,
- taskDescription.getNavigationBarColor(), sysUiVis,
- SYSTEM_UI_FLAG_LIGHT_NAVIGATION_BAR,
+ taskDescription.getNavigationBarColor(), appearance,
+ APPEARANCE_LIGHT_NAVIGATION_BARS,
taskDescription.getEnsureNavigationBarContrastWhenTransparent()
&& context.getResources().getBoolean(R.bool.config_navBarNeedsScrim));
mStatusBarPaint.setColor(mStatusBarColor);
@@ -590,11 +580,8 @@ class TaskSnapshotSurface implements StartingSurface {
int getStatusBarColorViewHeight() {
final boolean forceBarBackground =
(mWindowPrivateFlags & PRIVATE_FLAG_FORCE_DRAW_BAR_BACKGROUNDS) != 0;
- if (ViewRootImpl.sNewInsetsMode != ViewRootImpl.NEW_INSETS_MODE_FULL
- ? STATUS_BAR_COLOR_VIEW_ATTRIBUTES.isVisible(
- mSysUiVis, mStatusBarColor, mWindowFlags, forceBarBackground)
- : STATUS_BAR_COLOR_VIEW_ATTRIBUTES.isVisible(
- mInsetsState, mStatusBarColor, mWindowFlags, forceBarBackground)) {
+ if (STATUS_BAR_COLOR_VIEW_ATTRIBUTES.isVisible(
+ mInsetsState, mStatusBarColor, mWindowFlags, forceBarBackground)) {
return (int) (mSystemBarInsets.top * mScale);
} else {
return 0;
@@ -604,11 +591,8 @@ class TaskSnapshotSurface implements StartingSurface {
private boolean isNavigationBarColorViewVisible() {
final boolean forceBarBackground =
(mWindowPrivateFlags & PRIVATE_FLAG_FORCE_DRAW_BAR_BACKGROUNDS) != 0;
- return ViewRootImpl.sNewInsetsMode != ViewRootImpl.NEW_INSETS_MODE_FULL
- ? NAVIGATION_BAR_COLOR_VIEW_ATTRIBUTES.isVisible(
- mSysUiVis, mNavigationBarColor, mWindowFlags, forceBarBackground)
- : NAVIGATION_BAR_COLOR_VIEW_ATTRIBUTES.isVisible(
- mInsetsState, mNavigationBarColor, mWindowFlags, forceBarBackground);
+ return NAVIGATION_BAR_COLOR_VIEW_ATTRIBUTES.isVisible(
+ mInsetsState, mNavigationBarColor, mWindowFlags, forceBarBackground);
}
void drawDecors(Canvas c, @Nullable Rect alreadyDrawnFrame) {
diff --git a/services/core/java/com/android/server/wm/WindowContainer.java b/services/core/java/com/android/server/wm/WindowContainer.java
index ae8f7a556ffd..2b93080a8dad 100644
--- a/services/core/java/com/android/server/wm/WindowContainer.java
+++ b/services/core/java/com/android/server/wm/WindowContainer.java
@@ -32,6 +32,7 @@ import static android.view.SurfaceControl.Transaction;
import static com.android.internal.protolog.ProtoLogGroup.WM_DEBUG_APP_TRANSITIONS;
import static com.android.internal.protolog.ProtoLogGroup.WM_DEBUG_APP_TRANSITIONS_ANIM;
import static com.android.internal.protolog.ProtoLogGroup.WM_DEBUG_ORIENTATION;
+import static com.android.internal.protolog.ProtoLogGroup.WM_DEBUG_SYNC_ENGINE;
import static com.android.server.policy.WindowManagerPolicy.FINISH_LAYOUT_REDO_WALLPAPER;
import static com.android.server.wm.AppTransition.MAX_APP_TRANSITION_DURATION;
import static com.android.server.wm.IdentifierProto.HASH_CODE;
@@ -2886,6 +2887,8 @@ class WindowContainer<E extends WindowContainer> extends ConfigurationContainer<
// If we are invisible, no need to sync, likewise if we are already engaged in a sync,
// we can't support overlapping syncs on a single container yet.
if (!isVisible() || mWaitingListener != null) {
+ ProtoLog.v(WM_DEBUG_SYNC_ENGINE, "- NOT adding to sync: visible=%b "
+ + "hasListener=%b", isVisible(), mWaitingListener != null);
return false;
}
mUsingBLASTSyncTransaction = true;
diff --git a/services/core/java/com/android/server/wm/WindowManagerService.java b/services/core/java/com/android/server/wm/WindowManagerService.java
index 3a2b67085e35..d9c574ccc64c 100644
--- a/services/core/java/com/android/server/wm/WindowManagerService.java
+++ b/services/core/java/com/android/server/wm/WindowManagerService.java
@@ -1005,7 +1005,7 @@ public class WindowManagerService extends IWindowManager.Stub
final Configuration mTempConfiguration = new Configuration();
- final HighRefreshRateBlacklist mHighRefreshRateBlacklist;
+ final HighRefreshRateDenylist mHighRefreshRateDenylist;
// If true, only the core apps and services are being launched because the device
// is in a special boot mode, such as being encrypted or waiting for a decryption password.
@@ -1302,7 +1302,7 @@ public class WindowManagerService extends IWindowManager.Stub
this, mInputManager, mActivityTaskManager, mH.getLooper());
mDragDropController = new DragDropController(this, mH.getLooper());
- mHighRefreshRateBlacklist = HighRefreshRateBlacklist.create(context.getResources());
+ mHighRefreshRateDenylist = HighRefreshRateDenylist.create(context.getResources());
mConstants = new WindowManagerConstants(this, DeviceConfigInterface.REAL);
mConstants.start(new HandlerExecutor(mH));
@@ -3069,6 +3069,9 @@ public class WindowManagerService extends IWindowManager.Stub
if (!checkCallingPermission(permission.CONTROL_KEYGUARD, "dismissKeyguard")) {
throw new SecurityException("Requires CONTROL_KEYGUARD permission");
}
+ if (mAtmInternal.isDreaming()) {
+ mAtmService.mStackSupervisor.wakeUp("dismissKeyguard");
+ }
synchronized (mGlobalLock) {
mPolicy.dismissKeyguardLw(callback, message);
}
@@ -5936,7 +5939,7 @@ public class WindowManagerService extends IWindowManager.Stub
private void dumpHighRefreshRateBlacklist(PrintWriter pw) {
pw.println("WINDOW MANAGER HIGH REFRESH RATE BLACKLIST (dumpsys window refresh)");
- mHighRefreshRateBlacklist.dump(pw);
+ mHighRefreshRateDenylist.dump(pw);
}
private void dumpTraceStatus(PrintWriter pw) {
@@ -7994,8 +7997,7 @@ public class WindowManagerService extends IWindowManager.Stub
updateInputChannel(clientChannel.getToken(), callingUid, callingPid, displayId, surface,
name, applicationHandle, flags, privateFlags, type, null /* region */);
- clientChannel.transferTo(outInputChannel);
- clientChannel.dispose();
+ clientChannel.copyTo(outInputChannel);
}
private void updateInputChannel(IBinder channelToken, int callingUid, int callingPid,
diff --git a/services/core/java/com/android/server/wm/WindowOrganizerController.java b/services/core/java/com/android/server/wm/WindowOrganizerController.java
index c7cad2f76486..999181dc486c 100644
--- a/services/core/java/com/android/server/wm/WindowOrganizerController.java
+++ b/services/core/java/com/android/server/wm/WindowOrganizerController.java
@@ -306,6 +306,19 @@ class WindowOrganizerController extends IWindowOrganizerController.Stub
return effects;
}
+ private int applyTaskDisplayAreaChanges(TaskDisplayArea taskDisplayArea,
+ WindowContainerTransaction.Change c) {
+ int effects = applyDisplayAreaChanges(taskDisplayArea, c);
+ if ((c.getChangeMask()
+ & WindowContainerTransaction.Change.CHANGE_IGNORE_ORIENTATION_REQUEST) != 0) {
+ if (taskDisplayArea.setIgnoreOrientationRequest(c.getIgnoreOrientationRequest())) {
+ effects |= TRANSACT_EFFECTS_LIFECYCLE;
+ }
+ }
+
+ return effects;
+ }
+
private int applyDisplayAreaChanges(WindowContainer container,
WindowContainerTransaction.Change c) {
final int[] effects = new int[1];
@@ -388,7 +401,9 @@ class WindowOrganizerController extends IWindowOrganizerController.Stub
int effects = applyChanges(wc, c);
- if (wc instanceof DisplayArea) {
+ if (wc instanceof TaskDisplayArea) {
+ effects |= applyTaskDisplayAreaChanges((TaskDisplayArea) wc, c);
+ } else if (wc instanceof DisplayArea) {
effects |= applyDisplayAreaChanges(wc, c);
} else if (wc instanceof Task) {
effects |= applyTaskChanges(wc.asTask(), c);
diff --git a/services/core/java/com/android/server/wm/WindowProcessController.java b/services/core/java/com/android/server/wm/WindowProcessController.java
index 8bf0820c7dad..59f209aca6ba 100644
--- a/services/core/java/com/android/server/wm/WindowProcessController.java
+++ b/services/core/java/com/android/server/wm/WindowProcessController.java
@@ -41,7 +41,6 @@ import static com.android.server.wm.Task.ActivityState.RESUMED;
import static com.android.server.wm.Task.ActivityState.STARTED;
import static com.android.server.wm.Task.ActivityState.STOPPING;
-
import android.Manifest;
import android.annotation.NonNull;
import android.annotation.Nullable;
@@ -465,8 +464,10 @@ public class WindowProcessController extends ConfigurationContainer<Configuratio
* Allows background activity starts using token {@code entity}. Optionally, you can provide
* {@code originatingToken} if you have one such originating token, this is useful for tracing
* back the grant in the case of the notification token.
+ *
+ * If {@code entity} is already added, this method will update its {@code originatingToken}.
*/
- public void addAllowBackgroundActivityStartsToken(Binder entity,
+ public void addOrUpdateAllowBackgroundActivityStartsToken(Binder entity,
@Nullable IBinder originatingToken) {
synchronized (mAtm.mGlobalLock) {
mBackgroundActivityStartTokens.put(entity, originatingToken);
@@ -475,7 +476,7 @@ public class WindowProcessController extends ConfigurationContainer<Configuratio
/**
* Removes token {@code entity} that allowed background activity starts added via {@link
- * #addAllowBackgroundActivityStartsToken(Binder, IBinder)}.
+ * #addOrUpdateAllowBackgroundActivityStartsToken(Binder, IBinder)}.
*/
public void removeAllowBackgroundActivityStartsToken(Binder entity) {
synchronized (mAtm.mGlobalLock) {
@@ -485,7 +486,7 @@ public class WindowProcessController extends ConfigurationContainer<Configuratio
/**
* Returns true if background activity starts are allowed by any token added via {@link
- * #addAllowBackgroundActivityStartsToken(Binder, IBinder)}.
+ * #addOrUpdateAllowBackgroundActivityStartsToken(Binder, IBinder)}.
*/
public boolean areBackgroundActivityStartsAllowedByToken() {
synchronized (mAtm.mGlobalLock) {
@@ -768,7 +769,7 @@ public class WindowProcessController extends ConfigurationContainer<Configuratio
final Task stack = mPreQTopResumedActivity.getRootTask();
if (stack != null) {
stack.startPausingLocked(false /* userLeaving */, false /* uiSleeping */,
- activity);
+ activity, "top-resumed-changed");
}
}
mPreQTopResumedActivity = activity;
@@ -1575,10 +1576,14 @@ public class WindowProcessController extends ConfigurationContainer<Configuratio
pw.print(prefix); pw.print("mVrThreadTid="); pw.println(mVrThreadTid);
}
if (mBackgroundActivityStartTokens.size() > 0) {
- pw.print(prefix); pw.println("Background activity start tokens:");
+ pw.print(prefix);
+ pw.println("Background activity start tokens (token: originating token):");
for (int i = 0; i < mBackgroundActivityStartTokens.size(); i++) {
pw.print(prefix); pw.print(" - ");
- pw.println(mBackgroundActivityStartTokens.keyAt(i));
+ pw.print(mBackgroundActivityStartTokens.keyAt(i));
+ pw.print(": ");
+ pw.println(mBackgroundActivityStartTokens.valueAt(i));
+
}
}
}
diff --git a/services/core/java/com/android/server/wm/WindowState.java b/services/core/java/com/android/server/wm/WindowState.java
index f2f3cefae8a5..1f7457c088c5 100644
--- a/services/core/java/com/android/server/wm/WindowState.java
+++ b/services/core/java/com/android/server/wm/WindowState.java
@@ -556,7 +556,6 @@ class WindowState extends WindowContainer<WindowState> implements WindowManagerP
// Input channel and input window handle used by the input dispatcher.
final InputWindowHandle mInputWindowHandle;
InputChannel mInputChannel;
- private InputChannel mClientChannel;
// Used to improve performance of toString()
private String mStringNameCache;
@@ -1531,6 +1530,10 @@ class WindowState extends WindowContainer<WindowState> implements WindowManagerP
return getDisplayContent().getDisplayInfo();
}
+ /**
+ * Returns the insets state for the client. Its sources may be the copies with visibility
+ * modification according to the state of transient bars.
+ */
InsetsState getInsetsState() {
return getDisplayContent().getInsetsPolicy().getInsetsForDispatch(this);
}
@@ -2474,20 +2477,15 @@ class WindowState extends WindowContainer<WindowState> implements WindowManagerP
throw new IllegalStateException("Window already has an input channel.");
}
String name = getName();
- InputChannel[] inputChannels = InputChannel.openInputChannelPair(name);
- mInputChannel = inputChannels[0];
- mClientChannel = inputChannels[1];
- mWmService.mInputManager.registerInputChannel(mInputChannel);
+ mInputChannel = mWmService.mInputManager.createInputChannel(name);
mInputWindowHandle.token = mInputChannel.getToken();
if (outInputChannel != null) {
- mClientChannel.transferTo(outInputChannel);
- mClientChannel.dispose();
- mClientChannel = null;
+ mInputChannel.copyTo(outInputChannel);
} else {
// If the window died visible, we setup a fake input channel, so that taps
// can still detected by input monitor channel, and we can relaunch the app.
// Create fake event receiver that simply reports all events as handled.
- mDeadWindowEventReceiver = new DeadWindowEventReceiver(mClientChannel);
+ mDeadWindowEventReceiver = new DeadWindowEventReceiver(mInputChannel);
}
mWmService.mInputToWindowMap.put(mInputWindowHandle.token, this);
}
@@ -2500,15 +2498,11 @@ class WindowState extends WindowContainer<WindowState> implements WindowManagerP
// unregister server channel first otherwise it complains about broken channel
if (mInputChannel != null) {
- mWmService.mInputManager.unregisterInputChannel(mInputChannel.getToken());
+ mWmService.mInputManager.removeInputChannel(mInputChannel.getToken());
mInputChannel.dispose();
mInputChannel = null;
}
- if (mClientChannel != null) {
- mClientChannel.dispose();
- mClientChannel = null;
- }
mWmService.mKeyInterceptionInfoForToken.remove(mInputWindowHandle.token);
mWmService.mInputToWindowMap.remove(mInputWindowHandle.token);
mInputWindowHandle.token = null;
diff --git a/services/core/java/com/android/server/wm/WindowStateAnimator.java b/services/core/java/com/android/server/wm/WindowStateAnimator.java
index 5e814005a5e2..029c158814b3 100644
--- a/services/core/java/com/android/server/wm/WindowStateAnimator.java
+++ b/services/core/java/com/android/server/wm/WindowStateAnimator.java
@@ -441,10 +441,6 @@ class WindowStateAnimator {
return mSurfaceController;
}
- if ((mWin.mAttrs.privateFlags & PRIVATE_FLAG_IS_ROUNDED_CORNERS_OVERLAY) != 0) {
- windowType = SurfaceControl.WINDOW_TYPE_DONT_SCREENSHOT;
- }
-
w.setHasSurface(false);
if (DEBUG_ANIM) {
@@ -462,6 +458,10 @@ class WindowStateAnimator {
flags |= SurfaceControl.SECURE;
}
+ if ((mWin.mAttrs.privateFlags & PRIVATE_FLAG_IS_ROUNDED_CORNERS_OVERLAY) != 0) {
+ flags |= SurfaceControl.SKIP_SCREENSHOT;
+ }
+
calculateSurfaceBounds(w, attrs, mTmpSize);
final int width = mTmpSize.width();
final int height = mTmpSize.height();
diff --git a/services/core/jni/com_android_server_am_CachedAppOptimizer.cpp b/services/core/jni/com_android_server_am_CachedAppOptimizer.cpp
index 95d4ba7c199a..678308af34ea 100644
--- a/services/core/jni/com_android_server_am_CachedAppOptimizer.cpp
+++ b/services/core/jni/com_android_server_am_CachedAppOptimizer.cpp
@@ -36,6 +36,9 @@
using android::base::StringPrintf;
using android::base::WriteStringToFile;
+#define SYNC_RECEIVED_WHILE_FROZEN (1)
+#define ASYNC_RECEIVED_WHILE_FROZEN (2)
+
namespace android {
// This performs per-process reclaim on all processes belonging to non-app UIDs.
@@ -99,12 +102,37 @@ static void com_android_server_am_CachedAppOptimizer_freezeBinder(
}
}
+static jint com_android_server_am_CachedAppOptimizer_getBinderFreezeInfo(JNIEnv *env,
+ jobject clazz, jint pid) {
+ bool syncReceived = false, asyncReceived = false;
+
+ int error = IPCThreadState::getProcessFreezeInfo(pid, &syncReceived, &asyncReceived);
+
+ if (error < 0) {
+ jniThrowException(env, "java/lang/RuntimeException", strerror(error));
+ }
+
+ jint retVal = 0;
+
+ if(syncReceived) {
+ retVal |= SYNC_RECEIVED_WHILE_FROZEN;;
+ }
+
+ if(asyncReceived) {
+ retVal |= ASYNC_RECEIVED_WHILE_FROZEN;
+ }
+
+ return retVal;
+}
+
static const JNINativeMethod sMethods[] = {
/* name, signature, funcPtr */
{"compactSystem", "()V", (void*)com_android_server_am_CachedAppOptimizer_compactSystem},
{"enableFreezerInternal", "(Z)V",
(void*)com_android_server_am_CachedAppOptimizer_enableFreezerInternal},
- {"freezeBinder", "(IZ)V", (void*)com_android_server_am_CachedAppOptimizer_freezeBinder}
+ {"freezeBinder", "(IZ)V", (void*)com_android_server_am_CachedAppOptimizer_freezeBinder},
+ {"getBinderFreezeInfo", "(I)I",
+ (void*)com_android_server_am_CachedAppOptimizer_getBinderFreezeInfo}
};
int register_android_server_am_CachedAppOptimizer(JNIEnv* env)
diff --git a/services/core/jni/com_android_server_input_InputManagerService.cpp b/services/core/jni/com_android_server_input_InputManagerService.cpp
index 46136ca0647d..17a05f3c7d1a 100644
--- a/services/core/jni/com_android_server_input_InputManagerService.cpp
+++ b/services/core/jni/com_android_server_input_InputManagerService.cpp
@@ -206,10 +206,12 @@ public:
void setDisplayViewports(JNIEnv* env, jobjectArray viewportObjArray);
- status_t registerInputChannel(JNIEnv* env, const std::shared_ptr<InputChannel>& inputChannel);
- status_t registerInputMonitor(JNIEnv* env, const std::shared_ptr<InputChannel>& inputChannel,
- int32_t displayId, bool isGestureMonitor);
- status_t unregisterInputChannel(JNIEnv* env, const sp<IBinder>& connectionToken);
+ base::Result<std::unique_ptr<InputChannel>> createInputChannel(JNIEnv* env,
+ const std::string& name);
+ base::Result<std::unique_ptr<InputChannel>> createInputMonitor(JNIEnv* env, int32_t displayId,
+ bool isGestureMonitor,
+ const std::string& name);
+ status_t removeInputChannel(JNIEnv* env, const sp<IBinder>& connectionToken);
status_t pilferPointers(const sp<IBinder>& token);
void displayRemoved(JNIEnv* env, int32_t displayId);
@@ -432,24 +434,22 @@ void NativeInputManager::setDisplayViewports(JNIEnv* env, jobjectArray viewportO
InputReaderConfiguration::CHANGE_DISPLAY_INFO);
}
-status_t NativeInputManager::registerInputChannel(
- JNIEnv* /* env */, const std::shared_ptr<InputChannel>& inputChannel) {
+base::Result<std::unique_ptr<InputChannel>> NativeInputManager::createInputChannel(
+ JNIEnv* /* env */, const std::string& name) {
ATRACE_CALL();
- return mInputManager->getDispatcher()->registerInputChannel(inputChannel);
+ return mInputManager->getDispatcher()->createInputChannel(name);
}
-status_t NativeInputManager::registerInputMonitor(JNIEnv* /* env */,
- const std::shared_ptr<InputChannel>& inputChannel,
- int32_t displayId, bool isGestureMonitor) {
+base::Result<std::unique_ptr<InputChannel>> NativeInputManager::createInputMonitor(
+ JNIEnv* /* env */, int32_t displayId, bool isGestureMonitor, const std::string& name) {
ATRACE_CALL();
- return mInputManager->getDispatcher()->registerInputMonitor(
- inputChannel, displayId, isGestureMonitor);
+ return mInputManager->getDispatcher()->createInputMonitor(displayId, isGestureMonitor, name);
}
-status_t NativeInputManager::unregisterInputChannel(JNIEnv* /* env */,
- const sp<IBinder>& connectionToken) {
+status_t NativeInputManager::removeInputChannel(JNIEnv* /* env */,
+ const sp<IBinder>& connectionToken) {
ATRACE_CALL();
- return mInputManager->getDispatcher()->unregisterInputChannel(connectionToken);
+ return mInputManager->getDispatcher()->removeInputChannel(connectionToken);
}
status_t NativeInputManager::pilferPointers(const sp<IBinder>& token) {
@@ -1352,80 +1352,83 @@ static jboolean nativeHasKeys(JNIEnv* env, jclass /* clazz */,
return result;
}
-static void throwInputChannelNotInitialized(JNIEnv* env) {
- jniThrowException(env, "java/lang/IllegalStateException",
- "inputChannel is not initialized");
-}
-
static void handleInputChannelDisposed(JNIEnv* env, jobject /* inputChannelObj */,
const std::shared_ptr<InputChannel>& inputChannel,
void* data) {
NativeInputManager* im = static_cast<NativeInputManager*>(data);
- ALOGW("Input channel object '%s' was disposed without first being unregistered with "
- "the input manager!", inputChannel->getName().c_str());
- im->unregisterInputChannel(env, inputChannel->getConnectionToken());
+ ALOGW("Input channel object '%s' was disposed without first being removed with "
+ "the input manager!",
+ inputChannel->getName().c_str());
+ im->removeInputChannel(env, inputChannel->getConnectionToken());
}
-static void nativeRegisterInputChannel(JNIEnv* env, jclass /* clazz */,
- jlong ptr, jobject inputChannelObj) {
+static jobject nativeCreateInputChannel(JNIEnv* env, jclass /* clazz */, jlong ptr,
+ jstring nameObj) {
NativeInputManager* im = reinterpret_cast<NativeInputManager*>(ptr);
- std::shared_ptr<InputChannel> inputChannel =
- android_view_InputChannel_getInputChannel(env, inputChannelObj);
- if (inputChannel == nullptr) {
- throwInputChannelNotInitialized(env);
- return;
- }
+ ScopedUtfChars nameChars(env, nameObj);
+ std::string name = nameChars.c_str();
- status_t status = im->registerInputChannel(env, inputChannel);
+ base::Result<std::unique_ptr<InputChannel>> inputChannel = im->createInputChannel(env, name);
- if (status) {
- std::string message;
- message += StringPrintf("Failed to register input channel. status=%d", status);
+ if (!inputChannel) {
+ std::string message = inputChannel.error().message();
+ message += StringPrintf(" Status=%d", inputChannel.error().code());
jniThrowRuntimeException(env, message.c_str());
- return;
+ return nullptr;
+ }
+
+ jobject inputChannelObj =
+ android_view_InputChannel_createJavaObject(env, std::move(*inputChannel));
+ if (!inputChannelObj) {
+ return nullptr;
}
android_view_InputChannel_setDisposeCallback(env, inputChannelObj,
handleInputChannelDisposed, im);
+ return inputChannelObj;
}
-static void nativeRegisterInputMonitor(JNIEnv* env, jclass /* clazz */,
- jlong ptr, jobject inputChannelObj, jint displayId, jboolean isGestureMonitor) {
+static jobject nativeCreateInputMonitor(JNIEnv* env, jclass /* clazz */, jlong ptr, jint displayId,
+ jboolean isGestureMonitor, jstring nameObj) {
NativeInputManager* im = reinterpret_cast<NativeInputManager*>(ptr);
- std::shared_ptr<InputChannel> inputChannel =
- android_view_InputChannel_getInputChannel(env, inputChannelObj);
- if (inputChannel == nullptr) {
- throwInputChannelNotInitialized(env);
- return;
- }
-
if (displayId == ADISPLAY_ID_NONE) {
std::string message = "InputChannel used as a monitor must be associated with a display";
jniThrowRuntimeException(env, message.c_str());
- return;
+ return nullptr;
}
- status_t status = im->registerInputMonitor(env, inputChannel, displayId, isGestureMonitor);
+ ScopedUtfChars nameChars(env, nameObj);
+ std::string name = nameChars.c_str();
+
+ base::Result<std::unique_ptr<InputChannel>> inputChannel =
+ im->createInputMonitor(env, displayId, isGestureMonitor, name);
- if (status) {
- std::string message = StringPrintf("Failed to register input channel. status=%d", status);
+ if (!inputChannel) {
+ std::string message = inputChannel.error().message();
+ message += StringPrintf(" Status=%d", inputChannel.error().code());
jniThrowRuntimeException(env, message.c_str());
- return;
+ return nullptr;
+ }
+
+ jobject inputChannelObj =
+ android_view_InputChannel_createJavaObject(env, std::move(*inputChannel));
+ if (!inputChannelObj) {
+ return nullptr;
}
+ return inputChannelObj;
}
-static void nativeUnregisterInputChannel(JNIEnv* env, jclass /* clazz */, jlong ptr,
- jobject tokenObj) {
+static void nativeRemoveInputChannel(JNIEnv* env, jclass /* clazz */, jlong ptr, jobject tokenObj) {
NativeInputManager* im = reinterpret_cast<NativeInputManager*>(ptr);
sp<IBinder> token = ibinderForJavaObject(env, tokenObj);
- status_t status = im->unregisterInputChannel(env, token);
- if (status && status != BAD_VALUE) { // ignore already unregistered channel
+ status_t status = im->removeInputChannel(env, token);
+ if (status && status != BAD_VALUE) { // ignore already removed channel
std::string message;
- message += StringPrintf("Failed to unregister input channel. status=%d", status);
+ message += StringPrintf("Failed to remove input channel. status=%d", status);
jniThrowRuntimeException(env, message.c_str());
}
}
@@ -1780,12 +1783,11 @@ static const JNINativeMethod gInputManagerMethods[] = {
{"nativeGetKeyCodeState", "(JIII)I", (void*)nativeGetKeyCodeState},
{"nativeGetSwitchState", "(JIII)I", (void*)nativeGetSwitchState},
{"nativeHasKeys", "(JII[I[Z)Z", (void*)nativeHasKeys},
- {"nativeRegisterInputChannel", "(JLandroid/view/InputChannel;)V",
- (void*)nativeRegisterInputChannel},
- {"nativeRegisterInputMonitor", "(JLandroid/view/InputChannel;IZ)V",
- (void*)nativeRegisterInputMonitor},
- {"nativeUnregisterInputChannel", "(JLandroid/os/IBinder;)V",
- (void*)nativeUnregisterInputChannel},
+ {"nativeCreateInputChannel", "(JLjava/lang/String;)Landroid/view/InputChannel;",
+ (void*)nativeCreateInputChannel},
+ {"nativeCreateInputMonitor", "(JIZLjava/lang/String;)Landroid/view/InputChannel;",
+ (void*)nativeCreateInputMonitor},
+ {"nativeRemoveInputChannel", "(JLandroid/os/IBinder;)V", (void*)nativeRemoveInputChannel},
{"nativePilferPointers", "(JLandroid/os/IBinder;)V", (void*)nativePilferPointers},
{"nativeSetInputFilterEnabled", "(JZ)V", (void*)nativeSetInputFilterEnabled},
{"nativeSetInTouchMode", "(JZ)V", (void*)nativeSetInTouchMode},
diff --git a/services/core/jni/com_android_server_pm_PackageManagerShellCommandDataLoader.cpp b/services/core/jni/com_android_server_pm_PackageManagerShellCommandDataLoader.cpp
index 9e2bb45ab341..631e185fffce 100644
--- a/services/core/jni/com_android_server_pm_PackageManagerShellCommandDataLoader.cpp
+++ b/services/core/jni/com_android_server_pm_PackageManagerShellCommandDataLoader.cpp
@@ -225,6 +225,20 @@ std::optional<T> read(IncFsSpan& data) {
return res;
}
+static inline unique_fd openLocalFile(JNIEnv* env, const JniIds& jni, jobject shellCommand,
+ const std::string& path) {
+ if (shellCommand) {
+ return unique_fd{env->CallStaticIntMethod(jni.packageManagerShellCommandDataLoader,
+ jni.pmscdGetLocalFile, shellCommand,
+ env->NewStringUTF(path.c_str()))};
+ }
+ auto fd = unique_fd(::open(path.c_str(), O_RDONLY | O_CLOEXEC));
+ if (!fd.ok()) {
+ PLOG(ERROR) << "Failed to open file: " << path << ", error code: " << fd.get();
+ }
+ return fd;
+}
+
static inline InputDescs openLocalFile(JNIEnv* env, const JniIds& jni, jobject shellCommand,
IncFsSize size, const std::string& filePath) {
InputDescs result;
@@ -232,9 +246,7 @@ static inline InputDescs openLocalFile(JNIEnv* env, const JniIds& jni, jobject s
const std::string idsigPath = filePath + ".idsig";
- unique_fd idsigFd{env->CallStaticIntMethod(jni.packageManagerShellCommandDataLoader,
- jni.pmscdGetLocalFile, shellCommand,
- env->NewStringUTF(idsigPath.c_str()))};
+ unique_fd idsigFd = openLocalFile(env, jni, shellCommand, idsigPath);
if (idsigFd.ok()) {
auto treeSize = verityTreeSizeForFile(size);
auto actualTreeSize = skipIdSigHeaders(idsigFd);
@@ -250,9 +262,7 @@ static inline InputDescs openLocalFile(JNIEnv* env, const JniIds& jni, jobject s
});
}
- unique_fd fileFd{env->CallStaticIntMethod(jni.packageManagerShellCommandDataLoader,
- jni.pmscdGetLocalFile, shellCommand,
- env->NewStringUTF(filePath.c_str()))};
+ unique_fd fileFd = openLocalFile(env, jni, shellCommand, filePath);
if (fileFd.ok()) {
result.push_back(InputDesc{
.fd = std::move(fileFd),
@@ -272,6 +282,11 @@ static inline InputDescs openInputs(JNIEnv* env, const JniIds& jni, jobject shel
std::string(metadata.data, metadata.size));
}
+ if (!shellCommand) {
+ ALOGE("Missing shell command.");
+ return {};
+ }
+
unique_fd fd{env->CallStaticIntMethod(jni.packageManagerShellCommandDataLoader,
jni.pmscdGetStdIn, shellCommand)};
if (!fd.ok()) {
@@ -396,10 +411,6 @@ private:
jobject shellCommand = env->CallStaticObjectMethod(jni.packageManagerShellCommandDataLoader,
jni.pmscdLookupShellCommand,
env->NewStringUTF(mArgs.c_str()));
- if (!shellCommand) {
- ALOGE("Missing shell command.");
- return false;
- }
std::vector<char> buffer;
buffer.reserve(BUFFER_SIZE);
diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/CallerIdentity.java b/services/devicepolicy/java/com/android/server/devicepolicy/CallerIdentity.java
index 5193fa85d238..b6b4d8a04cb6 100644
--- a/services/devicepolicy/java/com/android/server/devicepolicy/CallerIdentity.java
+++ b/services/devicepolicy/java/com/android/server/devicepolicy/CallerIdentity.java
@@ -58,4 +58,12 @@ class CallerIdentity {
@Nullable public ComponentName getComponentName() {
return mComponentName;
}
+
+ public boolean hasAdminComponent() {
+ return mComponentName != null;
+ }
+
+ public boolean hasPackage() {
+ return mPackageName != null;
+ }
}
diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
index f8d457e877fa..8f1fb127ec17 100644
--- a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
+++ b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
@@ -1567,6 +1567,54 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager {
}
/**
+ * Creates a new {@link CallerIdentity} object to represent the caller's identity. If no
+ * component name is provided, look up the component name and fill it in for the caller.
+ */
+ private CallerIdentity getCallerIdentityOptionalAdmin(@Nullable ComponentName adminComponent) {
+ if (adminComponent == null) {
+ ActiveAdmin admin = getActiveAdminOfCaller();
+ if (admin != null) {
+ return getCallerIdentity(admin.info.getComponent());
+ }
+ throw new SecurityException("Caller is not an active admin");
+ } else {
+ return getCallerIdentity(adminComponent);
+ }
+ }
+
+ /**
+ * Creates a new {@link CallerIdentity} object to represent the caller's identity. If no
+ * package name is provided, look up the package name and fill it in for the caller.
+ */
+ private CallerIdentity getCallerIdentityOptionalPackage(@Nullable String callerPackage) {
+ if (callerPackage == null) {
+ ActiveAdmin admin = getActiveAdminOfCaller();
+ if (admin != null) {
+ return getCallerIdentity(admin.info.getPackageName());
+ }
+ throw new SecurityException("Caller is not an active admin");
+ } else {
+ return getCallerIdentity(callerPackage);
+ }
+ }
+
+ /**
+ * Retrieves the active admin of the caller. This method should not be called directly and
+ * should only be called by {@link #getCallerIdentityOptionalAdmin} or
+ * {@link #getCallerIdentityOptionalPackage}.
+ */
+ private ActiveAdmin getActiveAdminOfCaller() {
+ final int callerUid = mInjector.binderGetCallingUid();
+ final DevicePolicyData policy = getUserData(UserHandle.getUserId(callerUid));
+ for (ActiveAdmin admin : policy.mAdminList) {
+ if (admin.getUid() == callerUid) {
+ return admin;
+ }
+ }
+ return null;
+ }
+
+ /**
* Checks if the device is in COMP mode, and if so migrates it to managed profile on a
* corporate owned device.
*/
@@ -2081,9 +2129,7 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager {
ActiveAdmin getActiveAdminUncheckedLocked(ComponentName who, int userHandle, boolean parent) {
ensureLocked();
if (parent) {
- Preconditions.checkCallAuthorization(isManagedProfile(userHandle), String.format(
- "You can not call APIs on the parent profile outside a managed profile, "
- + "userId = %d", userHandle));
+ enforceManagedProfile(userHandle, "call APIs on the parent profile");
}
ActiveAdmin admin = getActiveAdminUncheckedLocked(who, userHandle);
if (admin != null && parent) {
@@ -2259,7 +2305,8 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager {
@Nullable String permission) throws SecurityException {
ensureLocked();
if (parent) {
- Preconditions.checkCallingUser(isManagedProfile(getCallerIdentity(who).getUserId()));
+ enforceManagedProfile(mInjector.userHandleGetCallingUserId(),
+ "call APIs on the parent profile");
}
ActiveAdmin admin = getActiveAdminOrCheckPermissionForCallerLocked(
who, reqPolicy, permission);
@@ -2853,7 +2900,7 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager {
if (!mHasFeature) {
return;
}
- Preconditions.checkCallAuthorization(canManageUsers(getCallerIdentity()));
+ enforceManageUsers();
synchronized (getLockObject()) {
final DevicePolicyData policy = getUserData(userHandle.getIdentifier());
@@ -3072,7 +3119,7 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager {
}
Preconditions.checkArgumentNonnegative(userHandle, "Invalid userId");
- final CallerIdentity caller = getCallerIdentity(adminReceiver);
+ final CallerIdentity caller = getCallerIdentity();
Preconditions.checkCallAuthorization(hasFullCrossUsersPermission(caller, userHandle));
synchronized (getLockObject()) {
@@ -3631,6 +3678,8 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager {
@Override
public boolean addCrossProfileWidgetProvider(ComponentName admin, String packageName) {
+ Objects.requireNonNull(admin, "ComponentName is null");
+
final CallerIdentity caller = getCallerIdentity(admin);
Preconditions.checkCallAuthorization(isProfileOwner(caller) || isDeviceOwner(caller));
List<String> changedProviders = null;
@@ -3664,6 +3713,8 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager {
@Override
public boolean removeCrossProfileWidgetProvider(ComponentName admin, String packageName) {
+ Objects.requireNonNull(admin, "ComponentName is null");
+
final CallerIdentity caller = getCallerIdentity(admin);
Preconditions.checkCallAuthorization(isProfileOwner(caller) || isDeviceOwner(caller));
List<String> changedProviders = null;
@@ -3697,6 +3748,8 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager {
@Override
public List<String> getCrossProfileWidgetProviders(ComponentName admin) {
+ Objects.requireNonNull(admin, "ComponentName is null");
+
final CallerIdentity caller = getCallerIdentity(admin);
Preconditions.checkCallAuthorization(isProfileOwner(caller) || isDeviceOwner(caller));
@@ -4043,13 +4096,10 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager {
if (!mHasFeature) {
return true;
}
- Objects.requireNonNull(admin, "ComponentName is null");
-
- final CallerIdentity caller = getCallerIdentity(admin);
- Preconditions.checkCallAuthorization(isDeviceOwner(caller) || isProfileOwner(caller));
- Preconditions.checkCallingUser(isManagedProfile(caller.getUserId()));
-
- return !isSeparateProfileChallengeEnabled(caller.getUserId());
+ final int userId = mInjector.userHandleGetCallingUserId();
+ enforceProfileOrDeviceOwner(admin);
+ enforceManagedProfile(userId, "query unified challenge status");
+ return !isSeparateProfileChallengeEnabled(userId);
}
@Override
@@ -4061,9 +4111,7 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager {
final CallerIdentity caller = getCallerIdentity();
Preconditions.checkCallAuthorization(hasFullCrossUsersPermission(caller, userHandle));
- Preconditions.checkCallAuthorization(isManagedProfile(userHandle), String.format(
- "can not call APIs refering to the parent profile outside a managed profile, "
- + "userId = %d", userHandle));
+ enforceManagedProfile(userHandle, "call APIs refering to the parent profile");
synchronized (getLockObject()) {
final int targetUser = getProfileParentId(userHandle);
@@ -4085,9 +4133,7 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager {
final CallerIdentity caller = getCallerIdentity();
Preconditions.checkCallAuthorization(hasFullCrossUsersPermission(caller, userHandle));
- Preconditions.checkCallAuthorization(!isManagedProfile(userHandle), String.format(
- "You can not check password sufficiency for a managed profile, userId = %d",
- userHandle));
+ enforceNotManagedProfile(userHandle, "check password sufficiency");
enforceUserUnlocked(userHandle);
synchronized (getLockObject()) {
@@ -4643,9 +4689,8 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager {
if (!mHasFeature && !hasCallingPermission(permission.LOCK_DEVICE)) {
return;
}
- final CallerIdentity caller = getCallerIdentity();
- final int callingUserId = caller.getUserId();
+ final int callingUserId = mInjector.userHandleGetCallingUserId();
ComponentName adminComponent = null;
synchronized (getLockObject()) {
// Make sure the caller has any active admin with the right policy or
@@ -4662,13 +4707,16 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager {
// For Profile Owners only, callers with only permission not allowed.
if ((flags & DevicePolicyManager.FLAG_EVICT_CREDENTIAL_ENCRYPTION_KEY) != 0) {
// Evict key
- Preconditions.checkCallingUser(isManagedProfile(callingUserId));
- Preconditions.checkArgument(!parent,
- "Cannot set FLAG_EVICT_CREDENTIAL_ENCRYPTION_KEY for the parent");
+ enforceManagedProfile(
+ callingUserId, "set FLAG_EVICT_CREDENTIAL_ENCRYPTION_KEY");
if (!isProfileOwner(adminComponent, callingUserId)) {
throw new SecurityException("Only profile owner admins can set "
+ "FLAG_EVICT_CREDENTIAL_ENCRYPTION_KEY");
}
+ if (parent) {
+ throw new IllegalArgumentException(
+ "Cannot set FLAG_EVICT_CREDENTIAL_ENCRYPTION_KEY for the parent");
+ }
if (!mInjector.storageManagerIsFileBasedEncryptionEnabled()) {
throw new UnsupportedOperationException(
"FLAG_EVICT_CREDENTIAL_ENCRYPTION_KEY only applies to FBE devices");
@@ -4713,19 +4761,32 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager {
@Override
public void enforceCanManageCaCerts(ComponentName who, String callerPackage) {
- final CallerIdentity caller = getCallerIdentity(who, callerPackage);
- Preconditions.checkCallAuthorization(canManageCaCerts(caller));
+ if (who == null) {
+ if (!isCallerDelegate(callerPackage, mInjector.binderGetCallingUid(),
+ DELEGATION_CERT_INSTALL)) {
+ mContext.enforceCallingOrSelfPermission(MANAGE_CA_CERTIFICATES, null);
+ }
+ } else {
+ enforceProfileOrDeviceOwner(who);
+ }
+ }
+
+ private void enforceProfileOrDeviceOwner(ComponentName who) {
+ synchronized (getLockObject()) {
+ getActiveAdminForCallerLocked(who, DeviceAdminInfo.USES_POLICY_PROFILE_OWNER);
+ }
}
- private boolean canManageCaCerts(CallerIdentity caller) {
- return isDeviceOwner(caller) || isProfileOwner(caller) || isCallerDelegate(caller,
- DELEGATION_CERT_INSTALL) || hasCallingOrSelfPermission(MANAGE_CA_CERTIFICATES);
+ private void enforceNetworkStackOrProfileOrDeviceOwner(ComponentName who) {
+ if (hasCallingPermission(PERMISSION_MAINLINE_NETWORK_STACK)) {
+ return;
+ }
+ enforceProfileOrDeviceOwner(who);
}
@Override
public boolean approveCaCert(String alias, int userId, boolean approval) {
- Preconditions.checkCallAuthorization(canManageUsers(getCallerIdentity()));
-
+ enforceManageUsers();
synchronized (getLockObject()) {
Set<String> certs = getUserData(userId).mAcceptedCaCertificates;
boolean changed = (approval ? certs.add(alias) : certs.remove(alias));
@@ -4740,8 +4801,7 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager {
@Override
public boolean isCaCertApproved(String alias, int userId) {
- Preconditions.checkCallAuthorization(canManageUsers(getCallerIdentity()));
-
+ enforceManageUsers();
synchronized (getLockObject()) {
return getUserData(userId).mAcceptedCaCertificates.contains(alias);
}
@@ -4766,20 +4826,21 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager {
}
@Override
- public boolean installCaCert(ComponentName admin, String callerPackage, byte[] certBuffer) {
+ public boolean installCaCert(ComponentName admin, String callerPackage, byte[] certBuffer)
+ throws RemoteException {
if (!mHasFeature) {
return false;
}
- final CallerIdentity caller = getCallerIdentity(admin, callerPackage);
- Preconditions.checkCallAuthorization(canManageCaCerts(caller));
+ enforceCanManageCaCerts(admin, callerPackage);
+ final UserHandle userHandle = mInjector.binderGetCallingUserHandle();
final String alias = mInjector.binderWithCleanCallingIdentity(() -> {
- String installedAlias = mCertificateMonitor.installCaCert(
- caller.getUserHandle(), certBuffer);
+ String installedAlias = mCertificateMonitor.installCaCert(userHandle, certBuffer);
+ final boolean isDelegate = (admin == null);
DevicePolicyEventLogger
.createEvent(DevicePolicyEnums.INSTALL_CA_CERT)
- .setAdmin(caller.getPackageName())
- .setBoolean(/* isDelegate */ admin == null)
+ .setAdmin(callerPackage)
+ .setBoolean(isDelegate)
.write();
return installedAlias;
});
@@ -4790,8 +4851,8 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager {
}
synchronized (getLockObject()) {
- getUserData(caller.getUserId()).mOwnerInstalledCaCerts.add(alias);
- saveSettingsLocked(caller.getUserId());
+ getUserData(userHandle.getIdentifier()).mOwnerInstalledCaCerts.add(alias);
+ saveSettingsLocked(userHandle.getIdentifier());
}
return true;
}
@@ -4801,22 +4862,22 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager {
if (!mHasFeature) {
return;
}
- final CallerIdentity caller = getCallerIdentity(admin, callerPackage);
- Preconditions.checkCallAuthorization(canManageCaCerts(caller));
+ enforceCanManageCaCerts(admin, callerPackage);
+ final int userId = mInjector.userHandleGetCallingUserId();
mInjector.binderWithCleanCallingIdentity(() -> {
- mCertificateMonitor.uninstallCaCerts(caller.getUserHandle(), aliases);
+ mCertificateMonitor.uninstallCaCerts(UserHandle.of(userId), aliases);
+ final boolean isDelegate = (admin == null);
DevicePolicyEventLogger
.createEvent(DevicePolicyEnums.UNINSTALL_CA_CERTS)
- .setAdmin(caller.getPackageName())
- .setBoolean(/* isDelegate */ admin == null)
+ .setAdmin(callerPackage)
+ .setBoolean(isDelegate)
.write();
});
synchronized (getLockObject()) {
- if (getUserData(caller.getUserId()).mOwnerInstalledCaCerts.removeAll(
- Arrays.asList(aliases))) {
- saveSettingsLocked(caller.getUserId());
+ if (getUserData(userId).mOwnerInstalledCaCerts.removeAll(Arrays.asList(aliases))) {
+ saveSettingsLocked(userId);
}
}
}
@@ -4826,8 +4887,9 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager {
byte[] cert, byte[] chain, String alias, boolean requestAccess,
boolean isUserSelectable) {
final CallerIdentity caller = getCallerIdentity(who, callerPackage);
- Preconditions.checkCallAuthorization(isDeviceOwner(caller)
- || isProfileOwner(caller) || isCallerDelegate(caller, DELEGATION_CERT_INSTALL));
+ Preconditions.checkCallAuthorization((caller.hasAdminComponent()
+ && (isProfileOwner(caller) || isDeviceOwner(caller)))
+ || (caller.hasPackage() && isCallerDelegate(caller, DELEGATION_CERT_INSTALL)));
final long id = mInjector.binderClearCallingIdentity();
try {
@@ -4865,8 +4927,9 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager {
@Override
public boolean removeKeyPair(ComponentName who, String callerPackage, String alias) {
final CallerIdentity caller = getCallerIdentity(who, callerPackage);
- Preconditions.checkCallAuthorization(isDeviceOwner(caller)
- || isProfileOwner(caller) || isCallerDelegate(caller, DELEGATION_CERT_INSTALL));
+ Preconditions.checkCallAuthorization((caller.hasAdminComponent()
+ && (isProfileOwner(caller) || isDeviceOwner(caller)))
+ || (caller.hasPackage() && isCallerDelegate(caller, DELEGATION_CERT_INSTALL)));
final long id = Binder.clearCallingIdentity();
try {
@@ -4901,8 +4964,9 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager {
Preconditions.checkStringNotEmpty(packageName, "Package to grant to cannot be empty");
final CallerIdentity caller = getCallerIdentity(who, callerPackage);
- Preconditions.checkCallAuthorization(isDeviceOwner(caller)
- || isProfileOwner(caller) || isCallerDelegate(caller, DELEGATION_CERT_INSTALL));
+ Preconditions.checkCallAuthorization((caller.hasAdminComponent()
+ && (isProfileOwner(caller) || isDeviceOwner(caller)))
+ || (caller.hasPackage() && isCallerDelegate(caller, DELEGATION_CERT_INSTALL)));
final int granteeUid;
try {
@@ -5045,8 +5109,9 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager {
enforceCallerCanRequestDeviceIdAttestation(caller);
enforceIndividualAttestationSupportedIfRequested(attestationUtilsFlags);
} else {
- Preconditions.checkCallAuthorization(isDeviceOwner(caller) || isProfileOwner(caller)
- || isCallerDelegate(caller, DELEGATION_CERT_INSTALL));
+ Preconditions.checkCallAuthorization((caller.hasAdminComponent()
+ && (isProfileOwner(caller) || isDeviceOwner(caller)))
+ || (caller.hasPackage() && isCallerDelegate(caller, DELEGATION_CERT_INSTALL)));
}
// As the caller will be granted access to the key, ensure no UID was specified, as
@@ -5140,8 +5205,9 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager {
public boolean setKeyPairCertificate(ComponentName who, String callerPackage, String alias,
byte[] cert, byte[] chain, boolean isUserSelectable) {
final CallerIdentity caller = getCallerIdentity(who, callerPackage);
- Preconditions.checkCallAuthorization(isDeviceOwner(caller)
- || isProfileOwner(caller) || isCallerDelegate(caller, DELEGATION_CERT_INSTALL));
+ Preconditions.checkCallAuthorization((caller.hasAdminComponent()
+ && (isProfileOwner(caller) || isDeviceOwner(caller)))
+ || (caller.hasPackage() && isCallerDelegate(caller, DELEGATION_CERT_INSTALL)));
final long id = mInjector.binderClearCallingIdentity();
try (final KeyChainConnection keyChainConnection =
@@ -5608,8 +5674,8 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager {
throws SecurityException {
Objects.requireNonNull(who, "ComponentName is null");
+ enforceProfileOrDeviceOwner(who);
final CallerIdentity caller = getCallerIdentity(who);
- Preconditions.checkCallAuthorization(isDeviceOwner(caller) || isProfileOwner(caller));
final int userId = caller.getUserId();
mInjector.binderWithCleanCallingIdentity(() -> {
@@ -5635,7 +5701,7 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager {
}
DevicePolicyEventLogger
.createEvent(DevicePolicyEnums.SET_ALWAYS_ON_VPN_PACKAGE)
- .setAdmin(caller.getComponentName())
+ .setAdmin(who)
.setStrings(vpnPackage)
.setBoolean(lockdown)
.setInt(lockdownWhitelist != null ? lockdownWhitelist.size() : 0)
@@ -5655,14 +5721,11 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager {
@Override
public String getAlwaysOnVpnPackage(ComponentName admin) throws SecurityException {
- Objects.requireNonNull(admin, "ComponentName is null");
-
- final CallerIdentity caller = getCallerIdentity(admin);
- Preconditions.checkCallAuthorization(isDeviceOwner(caller) || isProfileOwner(caller));
+ enforceProfileOrDeviceOwner(admin);
+ final int userId = mInjector.userHandleGetCallingUserId();
return mInjector.binderWithCleanCallingIdentity(
- () -> mInjector.getConnectivityManager().getAlwaysOnVpnPackageForUser(
- caller.getUserId()));
+ () -> mInjector.getConnectivityManager().getAlwaysOnVpnPackageForUser(userId));
}
@Override
@@ -5676,14 +5739,11 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager {
@Override
public boolean isAlwaysOnVpnLockdownEnabled(ComponentName admin) throws SecurityException {
- Objects.requireNonNull(admin, "ComponentName is null");
-
- final CallerIdentity caller = getCallerIdentity(admin);
- Preconditions.checkCallAuthorization(isDeviceOwner(caller) || isProfileOwner(caller)
- || hasCallingPermission(PERMISSION_MAINLINE_NETWORK_STACK));
+ enforceNetworkStackOrProfileOrDeviceOwner(admin);
+ final int userId = mInjector.userHandleGetCallingUserId();
return mInjector.binderWithCleanCallingIdentity(
- () -> mInjector.getConnectivityManager().isVpnLockdownEnabled(caller.getUserId()));
+ () -> mInjector.getConnectivityManager().isVpnLockdownEnabled(userId));
}
@Override
@@ -5698,14 +5758,11 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager {
@Override
public List<String> getAlwaysOnVpnLockdownWhitelist(ComponentName admin)
throws SecurityException {
- Objects.requireNonNull(admin, "ComponentName is null");
-
- final CallerIdentity caller = getCallerIdentity(admin);
- Preconditions.checkCallAuthorization(isDeviceOwner(caller) || isProfileOwner(caller));
+ enforceProfileOrDeviceOwner(admin);
+ final int userId = mInjector.userHandleGetCallingUserId();
return mInjector.binderWithCleanCallingIdentity(
- () -> mInjector.getConnectivityManager().getVpnLockdownWhitelist(
- caller.getUserId()));
+ () -> mInjector.getConnectivityManager().getVpnLockdownWhitelist(userId));
}
private void forceWipeDeviceNoLock(boolean wipeExtRequested, String reason, boolean wipeEuicc) {
@@ -5756,16 +5813,16 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager {
}
final CallerIdentity caller = getCallerIdentity();
boolean calledByProfileOwnerOnOrgOwnedDevice =
- isProfileOwnerOfOrganizationOwnedDevice(caller);
+ isProfileOwnerOfOrganizationOwnedDevice(caller.getUserId());
if (calledOnParentInstance) {
Preconditions.checkCallAuthorization(calledByProfileOwnerOnOrgOwnedDevice,
"Wiping the entire device can only be done by a profile owner on "
+ "organization-owned device.");
}
if ((flags & WIPE_RESET_PROTECTION_DATA) != 0) {
- Preconditions.checkCallAuthorization(
- isDeviceOwner(caller) || calledByProfileOwnerOnOrgOwnedDevice,
- "Only device owners or proflie owners of organization-owned device can set "
+ Preconditions.checkCallAuthorization(isCallerDeviceOwner(caller.getUid())
+ || calledByProfileOwnerOnOrgOwnedDevice,
+ "Only device owners or profile owners of organization-owned device can set "
+ "WIPE_RESET_PROTECTION_DATA");
}
@@ -5964,9 +6021,7 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager {
}
Preconditions.checkArgumentNonnegative(userHandle, "Invalid userId");
- final CallerIdentity caller = comp != null
- ? getCallerIdentity(comp)
- : getCallerIdentity();
+ final CallerIdentity caller = getCallerIdentityOptionalAdmin(comp);
Preconditions.checkCallAuthorization(hasFullCrossUsersPermission(caller, userHandle));
Preconditions.checkCallAuthorization(hasCallingOrSelfPermission(BIND_DEVICE_ADMIN));
@@ -5994,13 +6049,11 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager {
if (!mHasFeature || !mLockPatternUtils.hasSecureLockScreen()) {
return;
}
+ enforceSystemCaller("report password change");
- final CallerIdentity caller = getCallerIdentity();
- Preconditions.checkCallAuthorization(isSystemUid(caller));
// Managed Profile password can only be changed when it has a separate challenge.
if (!isSeparateProfileChallengeEnabled(userId)) {
- Preconditions.checkCallAuthorization(!isManagedProfile(userId), String.format("You can "
- + "not set the active password for a managed profile, userId = %d", userId));
+ enforceNotManagedProfile(userId, "set the active password");
}
DevicePolicyData policy = getUserData(userId);
@@ -6053,9 +6106,8 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager {
Preconditions.checkCallAuthorization(hasFullCrossUsersPermission(caller, userHandle));
Preconditions.checkCallAuthorization(hasCallingOrSelfPermission(BIND_DEVICE_ADMIN));
if (!isSeparateProfileChallengeEnabled(userHandle)) {
- Preconditions.checkCallAuthorization(!isManagedProfile(userHandle), String.format(
- "You can not report failed password attempt if separate profile challenge is "
- + "not in place for a managed profile, userId = %d", userHandle));
+ enforceNotManagedProfile(userHandle,
+ "report failed password attempt if separate profile challenge is not in place");
}
boolean wipeData = false;
@@ -6406,9 +6458,7 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager {
}
Preconditions.checkArgumentNonnegative(userHandle, "Invalid userId");
- final CallerIdentity caller = who != null
- ? getCallerIdentity(who)
- : getCallerIdentity();
+ final CallerIdentity caller = getCallerIdentityOptionalAdmin(who);
Preconditions.checkCallAuthorization(hasFullCrossUsersPermission(caller, userHandle));
synchronized (getLockObject()) {
@@ -6442,9 +6492,7 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager {
}
Preconditions.checkArgumentNonnegative(userHandle, "Invalid userId");
- final CallerIdentity caller = callerPackage != null
- ? getCallerIdentity(callerPackage)
- : getCallerIdentity();
+ final CallerIdentity caller = getCallerIdentityOptionalPackage(callerPackage);
Preconditions.checkCallAuthorization(hasFullCrossUsersPermission(caller, userHandle));
// It's not critical here, but let's make sure the package name is correct, in case
@@ -6553,9 +6601,8 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager {
return false;
}
if (parent) {
- Objects.requireNonNull(who, "ComponentName is null");
Preconditions.checkCallAuthorization(
- isProfileOwnerOfOrganizationOwnedDevice(getCallerIdentity(who)));
+ isProfileOwnerOfOrganizationOwnedDevice(getCallerIdentity().getUserId()));
}
synchronized (getLockObject()) {
@@ -6941,9 +6988,8 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager {
return false;
}
if (parent) {
- Objects.requireNonNull(who, "ComponentName is null");
Preconditions.checkCallAuthorization(
- isProfileOwnerOfOrganizationOwnedDevice(getCallerIdentity(who)));
+ isProfileOwnerOfOrganizationOwnedDevice(getCallerIdentity().getUserId()));
}
synchronized (getLockObject()) {
@@ -7077,8 +7123,9 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager {
}
Objects.requireNonNull(packageList, "packageList is null");
final CallerIdentity caller = getCallerIdentity(who, callerPackage);
- Preconditions.checkCallAuthorization(isDeviceOwner(caller)
- || isCallerDelegate(caller, DELEGATION_KEEP_UNINSTALLED_PACKAGES));
+ Preconditions.checkCallAuthorization((caller.hasAdminComponent() && isDeviceOwner(caller))
+ || (caller.hasPackage()
+ && isCallerDelegate(caller, DELEGATION_KEEP_UNINSTALLED_PACKAGES)));
synchronized (getLockObject()) {
// Get the device owner
@@ -7104,8 +7151,9 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager {
return null;
}
final CallerIdentity caller = getCallerIdentity(who, callerPackage);
- Preconditions.checkCallAuthorization(isDeviceOwner(caller)
- || isCallerDelegate(caller, DELEGATION_KEEP_UNINSTALLED_PACKAGES));
+ Preconditions.checkCallAuthorization((caller.hasAdminComponent() && isDeviceOwner(caller))
+ || (caller.hasPackage()
+ && isCallerDelegate(caller, DELEGATION_KEEP_UNINSTALLED_PACKAGES)));
// TODO In split system user mode, allow apps on user 0 to query the list
synchronized (getLockObject()) {
@@ -7284,7 +7332,7 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager {
return null;
}
if (!callingUserOnly) {
- Preconditions.checkCallAuthorization(canManageUsers(getCallerIdentity()));
+ enforceManageUsers();
}
synchronized (getLockObject()) {
if (!mOwners.hasDeviceOwner()) {
@@ -7303,8 +7351,7 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager {
if (!mHasFeature) {
return UserHandle.USER_NULL;
}
- Preconditions.checkCallAuthorization(canManageUsers(getCallerIdentity()));
-
+ enforceManageUsers();
synchronized (getLockObject()) {
return mOwners.hasDeviceOwner() ? mOwners.getDeviceOwnerUserId() : UserHandle.USER_NULL;
}
@@ -7319,8 +7366,7 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager {
if (!mHasFeature) {
return null;
}
- Preconditions.checkCallAuthorization(canManageUsers(getCallerIdentity()));
-
+ enforceManageUsers();
synchronized (getLockObject()) {
if (!mOwners.hasDeviceOwner()) {
return null;
@@ -7545,10 +7591,8 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager {
}
Objects.requireNonNull(who, "ComponentName is null");
- final CallerIdentity caller = getCallerIdentity(who);
- Preconditions.checkCallingUser(!isManagedProfile(caller.getUserId()));
-
- final int userId = caller.getUserId();
+ final int userId = mInjector.userHandleGetCallingUserId();
+ enforceNotManagedProfile(userId, "clear profile owner");
enforceUserUnlocked(userId);
synchronized (getLockObject()) {
// Check if this is the profile owner who is calling
@@ -7665,10 +7709,9 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager {
if (!mHasFeature) {
return DevicePolicyManager.STATE_USER_UNMANAGED;
}
- final CallerIdentity caller = getCallerIdentity();
- Preconditions.checkCallAuthorization(canManageUsers(caller));
-
- return getUserProvisioningState(caller.getUserId());
+ enforceManageUsers();
+ int userHandle = mInjector.userHandleGetCallingUserId();
+ return getUserProvisioningState(userHandle);
}
private int getUserProvisioningState(int userHandle) {
@@ -7706,8 +7749,8 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager {
}
transitionCheckNeeded = false;
} else {
- Preconditions.checkCallAuthorization(
- hasCallingOrSelfPermission(permission.MANAGE_PROFILE_AND_DEVICE_OWNERS));
+ // For all other cases, caller must have MANAGE_PROFILE_AND_DEVICE_OWNERS.
+ enforceCanManageProfileAndDeviceOwners();
}
final DevicePolicyData policyData = getUserData(userHandle);
@@ -7762,13 +7805,11 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager {
return;
}
Objects.requireNonNull(who, "ComponentName is null");
-
- final CallerIdentity caller = getCallerIdentity(who);
- Preconditions.checkCallAuthorization(isProfileOwner(caller));
- Preconditions.checkCallingUser(isManagedProfile(caller.getUserId()));
-
synchronized (getLockObject()) {
- final int userId = caller.getUserId();
+ // Check if this is the profile owner who is calling
+ getActiveAdminForCallerLocked(who, DeviceAdminInfo.USES_POLICY_PROFILE_OWNER);
+ final int userId = UserHandle.getCallingUserId();
+ enforceManagedProfile(userId, "enable the profile");
// Check if the profile is already enabled.
UserInfo managedProfile = getUserInfo(userId);
if (managedProfile.isEnabled()) {
@@ -7794,15 +7835,14 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager {
@Override
public void setProfileName(ComponentName who, String profileName) {
Objects.requireNonNull(who, "ComponentName is null");
+ enforceProfileOrDeviceOwner(who);
- final CallerIdentity caller = getCallerIdentity(who);
- Preconditions.checkCallAuthorization(isDeviceOwner(caller) || isProfileOwner(caller));
-
+ final int userId = UserHandle.getCallingUserId();
mInjector.binderWithCleanCallingIdentity(() -> {
- mUserManager.setUserName(caller.getUserId(), profileName);
+ mUserManager.setUserName(userId, profileName);
DevicePolicyEventLogger
.createEvent(DevicePolicyEnums.SET_PROFILE_NAME)
- .setAdmin(caller.getComponentName())
+ .setAdmin(who)
.write();
});
}
@@ -7911,8 +7951,7 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager {
if (!mHasFeature) {
return null;
}
- Preconditions.checkCallAuthorization(canManageUsers(getCallerIdentity()));
-
+ enforceManageUsers();
ComponentName profileOwner = getProfileOwner(userHandle);
if (profileOwner == null) {
return null;
@@ -8084,8 +8123,7 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager {
}
return;
}
- Preconditions.checkCallAuthorization(
- hasCallingOrSelfPermission(permission.MANAGE_PROFILE_AND_DEVICE_OWNERS));
+ enforceCanManageProfileAndDeviceOwners();
if ((mIsWatch || hasUserSetupCompleted(userHandle))) {
if (!isCallerWithSystemUid()) {
@@ -8121,8 +8159,7 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager {
@UserIdInt int userId,
boolean hasIncompatibleAccountsOrNonAdb) {
if (!isAdb()) {
- Preconditions.checkCallAuthorization(
- hasCallingOrSelfPermission(permission.MANAGE_PROFILE_AND_DEVICE_OWNERS));
+ enforceCanManageProfileAndDeviceOwners();
}
final int code = checkDeviceOwnerProvisioningPreConditionLocked(
@@ -8176,9 +8213,11 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager {
}
}
- private boolean canManageUsers(CallerIdentity caller) {
- return isSystemUid(caller) || isRootUid(caller)
- || hasCallingOrSelfPermission(permission.MANAGE_USERS);
+ private void enforceManageUsers() {
+ final int callingUid = mInjector.binderGetCallingUid();
+ if (!(isCallerWithSystemUid() || callingUid == Process.ROOT_UID)) {
+ mContext.enforceCallingOrSelfPermission(android.Manifest.permission.MANAGE_USERS, null);
+ }
}
private boolean hasCallingPermission(String permission) {
@@ -8208,6 +8247,20 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager {
|| hasCallingOrSelfPermission(permission.INTERACT_ACROSS_USERS);
}
+ private void enforceManagedProfile(int userId, String message) {
+ if (!isManagedProfile(userId)) {
+ throw new SecurityException(String.format(
+ "You can not %s outside a managed profile, userId = %d", message, userId));
+ }
+ }
+
+ private void enforceNotManagedProfile(int userId, String message) {
+ if (isManagedProfile(userId)) {
+ throw new SecurityException(String.format(
+ "You can not %s for a managed profile, userId = %d", message, userId));
+ }
+ }
+
private void enforceDeviceOwnerOrManageUsers() {
synchronized (getLockObject()) {
if (getActiveAdminWithPolicyForUidLocked(null, DeviceAdminInfo.USES_POLICY_DEVICE_OWNER,
@@ -8215,7 +8268,7 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager {
return;
}
}
- Preconditions.checkCallAuthorization(canManageUsers(getCallerIdentity()));
+ enforceManageUsers();
}
private void enforceProfileOwnerOrSystemUser() {
@@ -8519,8 +8572,9 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager {
public void setApplicationRestrictions(ComponentName who, String callerPackage,
String packageName, Bundle settings) {
final CallerIdentity caller = getCallerIdentity(who, callerPackage);
- Preconditions.checkCallAuthorization(isDeviceOwner(caller) || isProfileOwner(caller)
- || isCallerDelegate(caller, DELEGATION_APP_RESTRICTIONS));
+ Preconditions.checkCallAuthorization((caller.hasAdminComponent()
+ && (isProfileOwner(caller) || isDeviceOwner(caller)))
+ || (caller.hasPackage() && isCallerDelegate(caller, DELEGATION_APP_RESTRICTIONS)));
mInjector.binderWithCleanCallingIdentity(() -> {
mUserManager.setApplicationRestrictions(packageName, settings,
@@ -8560,9 +8614,7 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager {
Objects.requireNonNull(agent, "agent null");
Preconditions.checkArgumentNonnegative(userHandle, "Invalid userId");
- final CallerIdentity caller = admin != null
- ? getCallerIdentity(admin)
- : getCallerIdentity();
+ final CallerIdentity caller = getCallerIdentityOptionalAdmin(admin);
Preconditions.checkCallAuthorization(hasFullCrossUsersPermission(caller, userHandle));
synchronized (getLockObject()) {
@@ -8816,7 +8868,6 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager {
return null;
}
Objects.requireNonNull(who, "ComponentName is null");
-
final CallerIdentity caller = getCallerIdentity(who);
Preconditions.checkCallAuthorization(isDeviceOwner(caller) || isProfileOwner(caller));
@@ -8831,8 +8882,7 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager {
if (!mHasFeature) {
return null;
}
- Preconditions.checkCallAuthorization(canManageUsers(getCallerIdentity()));
-
+ enforceManageUsers();
synchronized (getLockObject()) {
List<String> result = null;
// If we have multiple profiles we return the intersection of the
@@ -8917,7 +8967,6 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager {
return false;
}
Objects.requireNonNull(who, "ComponentName is null");
-
final CallerIdentity caller = getCallerIdentity(who);
Preconditions.checkCallAuthorization(isDeviceOwner(caller) || isProfileOwner(caller));
@@ -8959,7 +9008,6 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager {
return null;
}
Objects.requireNonNull(who, "ComponentName is null");
-
final CallerIdentity caller = getCallerIdentity(who);
Preconditions.checkCallAuthorization(isDeviceOwner(caller) || isProfileOwner(caller));
@@ -8971,13 +9019,13 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager {
@Override
public List getPermittedInputMethodsForCurrentUser() {
- final CallerIdentity caller = getCallerIdentity();
- Preconditions.checkCallAuthorization(canManageUsers(caller));
+ enforceManageUsers();
+ final int callingUserId = mInjector.userHandleGetCallingUserId();
synchronized (getLockObject()) {
List<String> result = null;
// Only device or profile owners can have permitted lists set.
- DevicePolicyData policy = getUserDataUnchecked(caller.getUserId());
+ DevicePolicyData policy = getUserDataUnchecked(callingUserId);
for (int i = 0; i < policy.mAdminList.size(); i++) {
ActiveAdmin admin = policy.mAdminList.get(i);
List<String> fromAdmin = admin.permittedInputMethods;
@@ -8992,8 +9040,8 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager {
// If we have a permitted list add all system input methods.
if (result != null) {
- List<InputMethodInfo> imes = InputMethodManagerInternal
- .get().getInputMethodListAsUser(caller.getUserId());
+ List<InputMethodInfo> imes =
+ InputMethodManagerInternal.get().getInputMethodListAsUser(callingUserId);
if (imes != null) {
for (InputMethodInfo ime : imes) {
ServiceInfo serviceInfo = ime.getServiceInfo();
@@ -9437,20 +9485,20 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager {
@Override
public boolean isEphemeralUser(ComponentName who) {
Objects.requireNonNull(who, "ComponentName is null");
+ enforceProfileOrDeviceOwner(who);
- final CallerIdentity caller = getCallerIdentity(who);
- Preconditions.checkCallAuthorization(isDeviceOwner(caller) || isProfileOwner(caller));
-
+ final int callingUserId = mInjector.userHandleGetCallingUserId();
return mInjector.binderWithCleanCallingIdentity(
- () -> mInjector.getUserManager().isUserEphemeral(caller.getUserId()));
+ () -> mInjector.getUserManager().isUserEphemeral(callingUserId));
}
@Override
public Bundle getApplicationRestrictions(ComponentName who, String callerPackage,
String packageName) {
final CallerIdentity caller = getCallerIdentity(who, callerPackage);
- Preconditions.checkCallAuthorization(isDeviceOwner(caller) || isProfileOwner(caller)
- || isCallerDelegate(caller, DELEGATION_APP_RESTRICTIONS));
+ Preconditions.checkCallAuthorization((caller.hasAdminComponent()
+ && (isProfileOwner(caller) || isDeviceOwner(caller)))
+ || (caller.hasPackage() && isCallerDelegate(caller, DELEGATION_APP_RESTRICTIONS)));
return mInjector.binderWithCleanCallingIdentity(() -> {
Bundle bundle = mUserManager.getApplicationRestrictions(packageName,
@@ -9465,8 +9513,9 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager {
public String[] setPackagesSuspended(ComponentName who, String callerPackage,
String[] packageNames, boolean suspended) {
final CallerIdentity caller = getCallerIdentity(who, callerPackage);
- Preconditions.checkCallAuthorization(isDeviceOwner(caller) || isProfileOwner(caller)
- || isCallerDelegate(caller, DELEGATION_PACKAGE_ACCESS));
+ Preconditions.checkCallAuthorization((caller.hasAdminComponent()
+ && (isProfileOwner(caller) || isDeviceOwner(caller)))
+ || (caller.hasPackage() && isCallerDelegate(caller, DELEGATION_PACKAGE_ACCESS)));
String[] result = null;
synchronized (getLockObject()) {
@@ -9496,8 +9545,9 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager {
@Override
public boolean isPackageSuspended(ComponentName who, String callerPackage, String packageName) {
final CallerIdentity caller = getCallerIdentity(who, callerPackage);
- Preconditions.checkCallAuthorization(isDeviceOwner(caller) || isProfileOwner(caller)
- || isCallerDelegate(caller, DELEGATION_PACKAGE_ACCESS));
+ Preconditions.checkCallAuthorization((caller.hasAdminComponent()
+ && (isProfileOwner(caller) || isDeviceOwner(caller)))
+ || (caller.hasPackage() && isCallerDelegate(caller, DELEGATION_PACKAGE_ACCESS)));
synchronized (getLockObject()) {
long id = mInjector.binderClearCallingIdentity();
@@ -9657,8 +9707,9 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager {
public boolean setApplicationHidden(ComponentName who, String callerPackage, String packageName,
boolean hidden, boolean parent) {
final CallerIdentity caller = getCallerIdentity(who, callerPackage);
- Preconditions.checkCallAuthorization(isDeviceOwner(caller) || isProfileOwner(caller)
- || isCallerDelegate(caller, DELEGATION_PACKAGE_ACCESS));
+ Preconditions.checkCallAuthorization((caller.hasAdminComponent()
+ && (isProfileOwner(caller) || isDeviceOwner(caller)))
+ || (caller.hasPackage() && isCallerDelegate(caller, DELEGATION_PACKAGE_ACCESS)));
final int userId = parent ? getProfileParentId(caller.getUserId()) : caller.getUserId();
boolean result;
@@ -9689,8 +9740,9 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager {
public boolean isApplicationHidden(ComponentName who, String callerPackage,
String packageName, boolean parent) {
final CallerIdentity caller = getCallerIdentity(who, callerPackage);
- Preconditions.checkCallAuthorization(isDeviceOwner(caller) || isProfileOwner(caller)
- || isCallerDelegate(caller, DELEGATION_PACKAGE_ACCESS));
+ Preconditions.checkCallAuthorization((caller.hasAdminComponent()
+ && (isProfileOwner(caller) || isDeviceOwner(caller)))
+ || (caller.hasPackage() && isCallerDelegate(caller, DELEGATION_PACKAGE_ACCESS)));
final int userId = parent ? getProfileParentId(caller.getUserId()) : caller.getUserId();
synchronized (getLockObject()) {
@@ -9723,8 +9775,9 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager {
@Override
public void enableSystemApp(ComponentName who, String callerPackage, String packageName) {
final CallerIdentity caller = getCallerIdentity(who, callerPackage);
- Preconditions.checkCallAuthorization(isDeviceOwner(caller) || isProfileOwner(caller)
- || isCallerDelegate(caller, DELEGATION_ENABLE_SYSTEM_APP));
+ Preconditions.checkCallAuthorization((caller.hasAdminComponent()
+ && (isProfileOwner(caller) || isDeviceOwner(caller)))
+ || (caller.hasPackage() && isCallerDelegate(caller, DELEGATION_ENABLE_SYSTEM_APP)));
synchronized (getLockObject()) {
final boolean isDemo = isCurrentUserDemo();
@@ -9766,8 +9819,9 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager {
@Override
public int enableSystemAppWithIntent(ComponentName who, String callerPackage, Intent intent) {
final CallerIdentity caller = getCallerIdentity(who, callerPackage);
- Preconditions.checkCallAuthorization(isDeviceOwner(caller) || isProfileOwner(caller)
- || isCallerDelegate(caller, DELEGATION_ENABLE_SYSTEM_APP));
+ Preconditions.checkCallAuthorization((caller.hasAdminComponent()
+ && (isProfileOwner(caller) || isDeviceOwner(caller)))
+ || (caller.hasPackage() && isCallerDelegate(caller, DELEGATION_ENABLE_SYSTEM_APP)));
int numberOfAppsInstalled = 0;
synchronized (getLockObject()) {
@@ -9834,8 +9888,10 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager {
public boolean installExistingPackage(ComponentName who, String callerPackage,
String packageName) {
final CallerIdentity caller = getCallerIdentity(who, callerPackage);
- Preconditions.checkCallAuthorization(isDeviceOwner(caller) || isProfileOwner(caller)
- || isCallerDelegate(caller, DELEGATION_INSTALL_EXISTING_PACKAGE));
+ Preconditions.checkCallAuthorization((caller.hasAdminComponent()
+ && (isProfileOwner(caller) || isDeviceOwner(caller)))
+ || (caller.hasPackage()
+ && isCallerDelegate(caller, DELEGATION_INSTALL_EXISTING_PACKAGE)));
boolean result;
synchronized (getLockObject()) {
@@ -9948,8 +10004,9 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager {
public void setUninstallBlocked(ComponentName who, String callerPackage, String packageName,
boolean uninstallBlocked) {
final CallerIdentity caller = getCallerIdentity(who, callerPackage);
- Preconditions.checkCallAuthorization(isDeviceOwner(caller) || isProfileOwner(caller)
- || isCallerDelegate(caller, DELEGATION_BLOCK_UNINSTALL));
+ Preconditions.checkCallAuthorization((caller.hasAdminComponent()
+ && (isProfileOwner(caller) || isDeviceOwner(caller)))
+ || (caller.hasPackage() && isCallerDelegate(caller, DELEGATION_BLOCK_UNINSTALL)));
final int userId = caller.getUserId();
synchronized (getLockObject()) {
@@ -10170,7 +10227,6 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager {
return;
}
Objects.requireNonNull(who, "ComponentName is null");
-
final CallerIdentity caller = getCallerIdentity(who);
Preconditions.checkCallAuthorization(isDeviceOwner(caller) || isProfileOwner(caller));
@@ -10194,7 +10250,6 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager {
return false;
}
Objects.requireNonNull(who, "ComponentName is null");
-
final CallerIdentity caller = getCallerIdentity(who);
Preconditions.checkCallAuthorization(isDeviceOwner(caller) || isProfileOwner(caller));
@@ -10217,23 +10272,12 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager {
@Override
public void setSecondaryLockscreenEnabled(ComponentName who, boolean enabled) {
- Objects.requireNonNull(who, "ComponentName is null");
-
- // Check can set secondary lockscreen enabled
- final CallerIdentity caller = getCallerIdentity(who);
- Preconditions.checkCallAuthorization(isDeviceOwner(caller) || isProfileOwner(caller));
- Preconditions.checkCallAuthorization(!isManagedProfile(caller.getUserId()),
- String.format("User %d is not allowed to call setSecondaryLockscreenEnabled",
- caller.getUserId()));
- // Allow testOnly admins to bypass supervision config requirement.
- Preconditions.checkCallAuthorization(isAdminTestOnlyLocked(who, caller.getUserId())
- || isDefaultSupervisor(caller), String.format("Admin %s is not the "
- + "default supervision component", caller.getComponentName()));
-
+ enforceCanSetSecondaryLockscreenEnabled(who);
synchronized (getLockObject()) {
- DevicePolicyData policy = getUserData(caller.getUserId());
+ final int userId = mInjector.userHandleGetCallingUserId();
+ DevicePolicyData policy = getUserData(userId);
policy.mSecondaryLockscreenEnabled = enabled;
- saveSettingsLocked(caller.getUserId());
+ saveSettingsLocked(userId);
}
}
@@ -10244,14 +10288,31 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager {
}
}
- private boolean isDefaultSupervisor(CallerIdentity caller) {
+ private void enforceCanSetSecondaryLockscreenEnabled(ComponentName who) {
+ enforceProfileOrDeviceOwner(who);
+ final int userId = mInjector.userHandleGetCallingUserId();
+ if (isManagedProfile(userId)) {
+ throw new SecurityException(
+ "User " + userId + " is not allowed to call setSecondaryLockscreenEnabled");
+ }
+ synchronized (getLockObject()) {
+ if (isAdminTestOnlyLocked(who, userId)) {
+ // Allow testOnly admins to bypass supervision config requirement.
+ return;
+ }
+ }
+ // Only the default supervision app can use this API.
final String supervisor = mContext.getResources().getString(
com.android.internal.R.string.config_defaultSupervisionProfileOwnerComponent);
if (supervisor == null) {
- return false;
+ throw new SecurityException("Unable to set secondary lockscreen setting, no "
+ + "default supervision component defined");
}
final ComponentName supervisorComponent = ComponentName.unflattenFromString(supervisor);
- return caller.getComponentName().equals(supervisorComponent);
+ if (!who.equals(supervisorComponent)) {
+ throw new SecurityException(
+ "Admin " + who + " is not the default supervision component");
+ }
}
@Override
@@ -10492,6 +10553,8 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager {
@Override
public void setLocationEnabled(ComponentName who, boolean locationEnabled) {
+ Preconditions.checkNotNull(who, "ComponentName is null");
+
final CallerIdentity caller = getCallerIdentity(who);
Preconditions.checkCallAuthorization(isDeviceOwner(caller));
@@ -11591,9 +11654,7 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager {
@Override
public SystemUpdateInfo getPendingSystemUpdate(ComponentName admin) {
Objects.requireNonNull(admin, "ComponentName is null");
-
- final CallerIdentity caller = getCallerIdentity(admin);
- Preconditions.checkCallAuthorization(isDeviceOwner(caller) || isProfileOwner(caller));
+ enforceProfileOrDeviceOwner(admin);
return mOwners.getSystemUpdateInfo();
}
@@ -11601,8 +11662,9 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager {
@Override
public void setPermissionPolicy(ComponentName admin, String callerPackage, int policy) {
final CallerIdentity caller = getCallerIdentity(admin, callerPackage);
- Preconditions.checkCallAuthorization(isDeviceOwner(caller) || isProfileOwner(caller)
- || isCallerDelegate(caller, DELEGATION_PERMISSION_GRANT));
+ Preconditions.checkCallAuthorization((caller.hasAdminComponent()
+ && (isProfileOwner(caller) || isDeviceOwner(caller)))
+ || (caller.hasPackage() && isCallerDelegate(caller, DELEGATION_PERMISSION_GRANT)));
synchronized (getLockObject()) {
DevicePolicyData userPolicy = getUserData(caller.getUserId());
@@ -11635,8 +11697,9 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager {
Objects.requireNonNull(callback);
final CallerIdentity caller = getCallerIdentity(admin, callerPackage);
- Preconditions.checkCallAuthorization(isDeviceOwner(caller) || isProfileOwner(caller)
- || isCallerDelegate(caller, DELEGATION_PERMISSION_GRANT));
+ Preconditions.checkCallAuthorization((caller.hasAdminComponent()
+ && (isProfileOwner(caller) || isDeviceOwner(caller)))
+ || (caller.hasPackage() && isCallerDelegate(caller, DELEGATION_PERMISSION_GRANT)));
synchronized (getLockObject()) {
long ident = mInjector.binderClearCallingIdentity();
@@ -11699,9 +11762,9 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager {
public int getPermissionGrantState(ComponentName admin, String callerPackage,
String packageName, String permission) throws RemoteException {
final CallerIdentity caller = getCallerIdentity(admin, callerPackage);
- Preconditions.checkCallAuthorization(
- isSystemUid(caller) || isDeviceOwner(caller) || isProfileOwner(caller)
- || isCallerDelegate(caller, DELEGATION_PERMISSION_GRANT));
+ Preconditions.checkCallAuthorization(isSystemUid(caller) || (caller.hasAdminComponent()
+ && (isProfileOwner(caller) || isDeviceOwner(caller)))
+ || (caller.hasPackage() && isCallerDelegate(caller, DELEGATION_PERMISSION_GRANT)));
synchronized (getLockObject()) {
return mInjector.binderWithCleanCallingIdentity(() -> {
@@ -11784,11 +11847,8 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager {
@Override
public int checkProvisioningPreCondition(String action, String packageName) {
- Objects.requireNonNull(packageName, "packageName is null");
-
- Preconditions.checkCallAuthorization(
- hasCallingOrSelfPermission(permission.MANAGE_PROFILE_AND_DEVICE_OWNERS));
-
+ Objects.requireNonNull(packageName);
+ enforceCanManageProfileAndDeviceOwners();
return checkProvisioningPreConditionSkipPermission(action, packageName);
}
@@ -12049,12 +12109,8 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager {
@Override
public boolean isManagedProfile(ComponentName admin) {
- Objects.requireNonNull(admin, "ComponentName is null");
-
- final CallerIdentity caller = getCallerIdentity(admin);
- Preconditions.checkCallAuthorization(isDeviceOwner(caller) || isProfileOwner(caller));
-
- return isManagedProfile(caller.getUserId());
+ enforceProfileOrDeviceOwner(admin);
+ return isManagedProfile(mInjector.userHandleGetCallingUserId());
}
@Override
@@ -12180,10 +12236,8 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager {
return;
}
Objects.requireNonNull(who, "ComponentName is null");
-
final CallerIdentity caller = getCallerIdentity(who);
- Preconditions.checkCallingUser(isManagedProfile(caller.getUserId()));
-
+ enforceManagedProfile(caller.getUserId(), "set organization color");
synchronized (getLockObject()) {
ActiveAdmin admin = getProfileOwnerOrDeviceOwnerLocked(caller);
admin.organizationColor = color;
@@ -12191,7 +12245,7 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager {
}
DevicePolicyEventLogger
.createEvent(DevicePolicyEnums.SET_ORGANIZATION_COLOR)
- .setAdmin(caller.getComponentName())
+ .setAdmin(who)
.write();
}
@@ -12204,10 +12258,9 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager {
final CallerIdentity caller = getCallerIdentity();
Preconditions.checkCallAuthorization(hasFullCrossUsersPermission(caller, userId));
- Preconditions.checkCallAuthorization(canManageUsers(caller));
- Preconditions.checkCallAuthorization(isManagedProfile(userId), String.format("You can not "
- + "set organization color outside a managed profile, userId = %d", userId));
+ enforceManageUsers();
+ enforceManagedProfile(userId, "set organization color");
synchronized (getLockObject()) {
ActiveAdmin admin = getProfileOwnerAdminLocked(userId);
admin.organizationColor = color;
@@ -12221,10 +12274,8 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager {
return ActiveAdmin.DEF_ORGANIZATION_COLOR;
}
Objects.requireNonNull(who, "ComponentName is null");
-
final CallerIdentity caller = getCallerIdentity(who);
- Preconditions.checkCallingUser(isManagedProfile(caller.getUserId()));
-
+ enforceManagedProfile(caller.getUserId(), "get organization color");
synchronized (getLockObject()) {
ActiveAdmin admin = getProfileOwnerOrDeviceOwnerLocked(caller);
return admin.organizationColor;
@@ -12240,9 +12291,8 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager {
final CallerIdentity caller = getCallerIdentity();
Preconditions.checkCallAuthorization(hasFullCrossUsersPermission(caller, userHandle));
- Preconditions.checkCallAuthorization(isManagedProfile(userHandle), String.format("You can "
- + "not get organization color outside a managed profile, userId = %d", userHandle));
+ enforceManagedProfile(userHandle, "get organization color");
synchronized (getLockObject()) {
ActiveAdmin profileOwner = getProfileOwnerAdminLocked(userHandle);
return (profileOwner != null)
@@ -12275,10 +12325,8 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager {
return null;
}
Objects.requireNonNull(who, "ComponentName is null");
-
final CallerIdentity caller = getCallerIdentity(who);
- Preconditions.checkCallingUser(isManagedProfile(caller.getUserId()));
-
+ enforceManagedProfile(caller.getUserId(), "get organization name");
synchronized (getLockObject()) {
ActiveAdmin admin = getProfileOwnerOrDeviceOwnerLocked(caller);
return admin.organizationName;
@@ -12306,10 +12354,8 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager {
final CallerIdentity caller = getCallerIdentity();
Preconditions.checkCallAuthorization(hasFullCrossUsersPermission(caller, userHandle));
- Preconditions.checkCallAuthorization(isManagedProfile(userHandle), String.format(
- "You can not get organization name outside a managed profile, userId = %d",
- userHandle));
+ enforceManagedProfile(userHandle, "get organization name");
synchronized (getLockObject()) {
ActiveAdmin profileOwner = getProfileOwnerAdminLocked(userHandle);
return (profileOwner != null)
@@ -12744,6 +12790,16 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager {
return mSecurityLogMonitor.forceLogs();
}
+ private void enforceCanManageDeviceAdmin() {
+ mContext.enforceCallingOrSelfPermission(android.Manifest.permission.MANAGE_DEVICE_ADMINS,
+ null);
+ }
+
+ private void enforceCanManageProfileAndDeviceOwners() {
+ mContext.enforceCallingOrSelfPermission(
+ android.Manifest.permission.MANAGE_PROFILE_AND_DEVICE_OWNERS, null);
+ }
+
private void enforceCallerSystemUserHandle() {
final int callingUid = mInjector.binderGetCallingUid();
final int userId = UserHandle.getUserId(callingUid);
@@ -12754,11 +12810,9 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager {
@Override
public boolean isUninstallInQueue(final String packageName) {
- final CallerIdentity caller = getCallerIdentity();
- Preconditions.checkCallAuthorization(
- hasCallingOrSelfPermission(permission.MANAGE_DEVICE_ADMINS));
-
- Pair<String, Integer> packageUserPair = new Pair<>(packageName, caller.getUserId());
+ enforceCanManageDeviceAdmin();
+ final int userId = mInjector.userHandleGetCallingUserId();
+ Pair<String, Integer> packageUserPair = new Pair<>(packageName, userId);
synchronized (getLockObject()) {
return mPackagesToRemove.contains(packageUserPair);
}
@@ -12766,13 +12820,11 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager {
@Override
public void uninstallPackageWithActiveAdmins(final String packageName) {
+ enforceCanManageDeviceAdmin();
Preconditions.checkArgument(!TextUtils.isEmpty(packageName));
- final CallerIdentity caller = getCallerIdentity();
- Preconditions.checkCallAuthorization(
- hasCallingOrSelfPermission(permission.MANAGE_DEVICE_ADMINS));
+ final int userId = mInjector.userHandleGetCallingUserId();
- final int userId = caller.getUserId();
enforceUserUnlocked(userId);
final ComponentName profileOwner = getProfileOwner(userId);
@@ -12821,9 +12873,7 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager {
@Override
public boolean isDeviceProvisioned() {
- final CallerIdentity caller = getCallerIdentity();
- Preconditions.checkCallAuthorization(canManageUsers(caller));
-
+ enforceManageUsers();
synchronized (getLockObject()) {
return getUserDataUnchecked(UserHandle.USER_SYSTEM).mUserSetupComplete;
}
@@ -12907,8 +12957,7 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager {
@Override
public void setDeviceProvisioningConfigApplied() {
- Preconditions.checkCallAuthorization(canManageUsers(getCallerIdentity()));
-
+ enforceManageUsers();
synchronized (getLockObject()) {
DevicePolicyData policy = getUserData(UserHandle.USER_SYSTEM);
policy.mDeviceProvisioningConfigApplied = true;
@@ -12918,8 +12967,7 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager {
@Override
public boolean isDeviceProvisioningConfigApplied() {
- Preconditions.checkCallAuthorization(canManageUsers(getCallerIdentity()));
-
+ enforceManageUsers();
synchronized (getLockObject()) {
final DevicePolicyData policy = getUserData(UserHandle.USER_SYSTEM);
return policy.mDeviceProvisioningConfigApplied;
@@ -12935,10 +12983,8 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager {
*/
@Override
public void forceUpdateUserSetupComplete() {
- Preconditions.checkCallAuthorization(
- hasCallingOrSelfPermission(permission.MANAGE_PROFILE_AND_DEVICE_OWNERS));
+ enforceCanManageProfileAndDeviceOwners();
enforceCallerSystemUserHandle();
-
// no effect if it's called from user build
if (!mInjector.isBuildDebuggable()) {
return;
@@ -12959,28 +13005,25 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager {
if (!mHasFeature) {
return;
}
- Objects.requireNonNull(admin, "ComponentName is null");
-
- final CallerIdentity caller = getCallerIdentity(admin);
- Preconditions.checkCallAuthorization(isDeviceOwner(caller) || isProfileOwner(caller));
-
- toggleBackupServiceActive(caller.getUserId(), enabled);
+ Objects.requireNonNull(admin);
+ enforceProfileOrDeviceOwner(admin);
+ int userId = mInjector.userHandleGetCallingUserId();
+ toggleBackupServiceActive(userId, enabled);
}
@Override
public boolean isBackupServiceEnabled(ComponentName admin) {
+ Objects.requireNonNull(admin);
if (!mHasFeature) {
return true;
}
- Objects.requireNonNull(admin, "ComponentName is null");
-
- final CallerIdentity caller = getCallerIdentity(admin);
- Preconditions.checkCallAuthorization(isDeviceOwner(caller) || isProfileOwner(caller));
+ enforceProfileOrDeviceOwner(admin);
synchronized (getLockObject()) {
try {
IBackupManager ibm = mInjector.getIBackupManager();
- return ibm != null && ibm.isBackupServiceActive(caller.getUserId());
+ return ibm != null && ibm.isBackupServiceActive(
+ mInjector.userHandleGetCallingUserId());
} catch (RemoteException e) {
throw new IllegalStateException("Failed requesting backup service state.", e);
}
@@ -13174,8 +13217,8 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager {
return;
}
final CallerIdentity caller = getCallerIdentity(admin, packageName);
- Preconditions.checkCallAuthorization(isDeviceOwner(caller)
- || isCallerDelegate(caller, DELEGATION_NETWORK_LOGGING));
+ Preconditions.checkCallAuthorization((caller.hasAdminComponent() && isDeviceOwner(caller))
+ || (caller.hasPackage() && isCallerDelegate(caller, DELEGATION_NETWORK_LOGGING)));
synchronized (getLockObject()) {
if (enabled == isNetworkLoggingEnabledInternalLocked()) {
@@ -13292,8 +13335,8 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager {
return false;
}
final CallerIdentity caller = getCallerIdentity(admin, packageName);
- Preconditions.checkCallAuthorization(isDeviceOwner(caller)
- || isCallerDelegate(caller, DELEGATION_NETWORK_LOGGING)
+ Preconditions.checkCallAuthorization((caller.hasAdminComponent() && isDeviceOwner(caller))
+ || (caller.hasPackage() && isCallerDelegate(caller, DELEGATION_NETWORK_LOGGING))
|| hasCallingOrSelfPermission(permission.MANAGE_USERS));
synchronized (getLockObject()) {
@@ -13320,8 +13363,8 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager {
return null;
}
final CallerIdentity caller = getCallerIdentity(admin, packageName);
- Preconditions.checkCallAuthorization(isDeviceOwner(caller)
- || isCallerDelegate(caller, DELEGATION_NETWORK_LOGGING));
+ Preconditions.checkCallAuthorization((caller.hasAdminComponent() && isDeviceOwner(caller))
+ || (caller.hasPackage() && isCallerDelegate(caller, DELEGATION_NETWORK_LOGGING)));
Preconditions.checkCallAuthorization(areAllUsersAffiliatedWithDeviceLocked());
synchronized (getLockObject()) {
@@ -13562,14 +13605,13 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager {
Objects.requireNonNull(admin, "ComponentName is null");
Objects.requireNonNull(packageName, "packageName is null");
Objects.requireNonNull(callback, "callback is null");
-
- final CallerIdentity caller = getCallerIdentity(admin);
- Preconditions.checkCallAuthorization(isDeviceOwner(caller) || isProfileOwner(caller));
+ enforceProfileOrDeviceOwner(admin);
+ final int userId = UserHandle.getCallingUserId();
long ident = mInjector.binderClearCallingIdentity();
try {
ActivityManager.getService().clearApplicationUserData(packageName, false, callback,
- caller.getUserId());
+ userId);
} catch(RemoteException re) {
// Same process, should not happen.
} catch (SecurityException se) {
@@ -13622,9 +13664,7 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager {
@Override
public List<String> getDisallowedSystemApps(ComponentName admin, int userId,
String provisioningAction) throws RemoteException {
- Preconditions.checkCallAuthorization(
- hasCallingOrSelfPermission(permission.MANAGE_PROFILE_AND_DEVICE_OWNERS));
-
+ enforceCanManageProfileAndDeviceOwners();
return new ArrayList<>(
mOverlayPackagesProvider.getNonRequiredApps(admin, userId, provisioningAction));
}
@@ -13635,23 +13675,31 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager {
if (!mHasFeature) {
return;
}
- Objects.requireNonNull(admin, "ComponentName is null");
+
+ Objects.requireNonNull(admin, "Admin cannot be null.");
Objects.requireNonNull(target, "Target cannot be null.");
- Preconditions.checkArgument(!admin.equals(target),
- "Provided administrator and target are the same object.");
- Preconditions.checkArgument(!admin.getPackageName().equals(target.getPackageName()),
- "Provided administrator and target have the same package name.");
- final CallerIdentity caller = getCallerIdentity(admin);
- Preconditions.checkCallAuthorization(isDeviceOwner(caller) || isProfileOwner(caller));
+ enforceProfileOrDeviceOwner(admin);
- final int callingUserId = caller.getUserId();
+ if (admin.equals(target)) {
+ throw new IllegalArgumentException("Provided administrator and target are "
+ + "the same object.");
+ }
+
+ if (admin.getPackageName().equals(target.getPackageName())) {
+ throw new IllegalArgumentException("Provided administrator and target have "
+ + "the same package name.");
+ }
+
+ final int callingUserId = mInjector.userHandleGetCallingUserId();
final DevicePolicyData policy = getUserData(callingUserId);
final DeviceAdminInfo incomingDeviceInfo = findAdmin(target, callingUserId,
/* throwForMissingPermission= */ true);
checkActiveAdminPrecondition(target, incomingDeviceInfo, policy);
- Preconditions.checkArgument(incomingDeviceInfo.supportsTransferOwnership(),
- "Provided target does not support ownership transfer.");
+ if (!incomingDeviceInfo.supportsTransferOwnership()) {
+ throw new IllegalArgumentException("Provided target does not support "
+ + "ownership transfer.");
+ }
final long id = mInjector.binderClearCallingIdentity();
String ownerType = null;
@@ -13674,7 +13722,7 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager {
if (bundle == null) {
bundle = new PersistableBundle();
}
- if (isProfileOwner(caller)) {
+ if (isProfileOwner(admin, callingUserId)) {
ownerType = ADMIN_TYPE_PROFILE_OWNER;
prepareTransfer(admin, target, bundle, callingUserId,
ADMIN_TYPE_PROFILE_OWNER);
@@ -13685,7 +13733,7 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager {
if (isUserAffiliatedWithDeviceLocked(callingUserId)) {
notifyAffiliatedProfileTransferOwnershipComplete(callingUserId);
}
- } else if (isDeviceOwner(caller)) {
+ } else if (isDeviceOwner(admin, callingUserId)) {
ownerType = ADMIN_TYPE_DEVICE_OWNER;
prepareTransfer(admin, target, bundle, callingUserId,
ADMIN_TYPE_DEVICE_OWNER);
@@ -14355,8 +14403,7 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager {
if (!mHasFeature) {
return false;
}
- Preconditions.checkCallAuthorization(canManageUsers(getCallerIdentity()));
-
+ enforceManageUsers();
long id = mInjector.binderClearCallingIdentity();
try {
return isManagedKioskInternal();
@@ -14381,8 +14428,7 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager {
if (!mHasFeature) {
return false;
}
- Preconditions.checkCallAuthorization(canManageUsers(getCallerIdentity()));
-
+ enforceManageUsers();
return mInjector.binderWithCleanCallingIdentity(() -> isUnattendedManagedKioskUnchecked());
}
@@ -14516,6 +14562,8 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager {
@Override
public List<String> getUserControlDisabledPackages(ComponentName who) {
+ Objects.requireNonNull(who, "ComponentName is null");
+
final CallerIdentity caller = getCallerIdentity(who);
Preconditions.checkCallAuthorization(isDeviceOwner(caller));
diff --git a/services/incremental/BinderIncrementalService.cpp b/services/incremental/BinderIncrementalService.cpp
index bf3a8964465b..2f8825b064ce 100644
--- a/services/incremental/BinderIncrementalService.cpp
+++ b/services/incremental/BinderIncrementalService.cpp
@@ -200,16 +200,24 @@ static std::tuple<int, incfs::FileId, incfs::NewFileParams> toMakeFileParams(
return {0, id, nfp};
}
+static std::span<const uint8_t> toSpan(const ::std::optional<::std::vector<uint8_t>>& content) {
+ if (!content) {
+ return {};
+ }
+ return {content->data(), (int)content->size()};
+}
+
binder::Status BinderIncrementalService::makeFile(
int32_t storageId, const std::string& path,
- const ::android::os::incremental::IncrementalNewFileParams& params, int32_t* _aidl_return) {
+ const ::android::os::incremental::IncrementalNewFileParams& params,
+ const ::std::optional<::std::vector<uint8_t>>& content, int32_t* _aidl_return) {
auto [err, fileId, nfp] = toMakeFileParams(params);
if (err) {
*_aidl_return = err;
return ok();
}
- *_aidl_return = mImpl.makeFile(storageId, path, 0777, fileId, nfp);
+ *_aidl_return = mImpl.makeFile(storageId, path, 0777, fileId, nfp, toSpan(content));
return ok();
}
binder::Status BinderIncrementalService::makeFileFromRange(int32_t storageId,
diff --git a/services/incremental/BinderIncrementalService.h b/services/incremental/BinderIncrementalService.h
index 123849876095..0a89166f4868 100644
--- a/services/incremental/BinderIncrementalService.h
+++ b/services/incremental/BinderIncrementalService.h
@@ -58,7 +58,9 @@ public:
binder::Status makeDirectories(int32_t storageId, const std::string& path,
int32_t* _aidl_return) final;
binder::Status makeFile(int32_t storageId, const std::string& path,
- const IncrementalNewFileParams& params, int32_t* _aidl_return) final;
+ const IncrementalNewFileParams& params,
+ const ::std::optional<::std::vector<uint8_t>>& content,
+ int32_t* _aidl_return) final;
binder::Status makeFileFromRange(int32_t storageId, const std::string& targetPath,
const std::string& sourcePath, int64_t start, int64_t end,
int32_t* _aidl_return) final;
diff --git a/services/incremental/IncrementalService.cpp b/services/incremental/IncrementalService.cpp
index 10a508b06086..e8bf468f032e 100644
--- a/services/incremental/IncrementalService.cpp
+++ b/services/incremental/IncrementalService.cpp
@@ -308,6 +308,7 @@ IncrementalService::~IncrementalService() {
}
mJobCondition.notify_all();
mJobProcessor.join();
+ mLooper->wake();
mCmdLooperThread.join();
mTimedQueue->stop();
mProgressUpdateJobQueue->stop();
@@ -859,7 +860,7 @@ std::string IncrementalService::normalizePathToStorage(const IncFsMount& ifs, St
}
int IncrementalService::makeFile(StorageId storage, std::string_view path, int mode, FileId id,
- incfs::NewFileParams params) {
+ incfs::NewFileParams params, std::span<const uint8_t> data) {
if (auto ifs = getIfs(storage)) {
std::string normPath = normalizePathToStorage(*ifs, storage, path);
if (normPath.empty()) {
@@ -867,11 +868,15 @@ int IncrementalService::makeFile(StorageId storage, std::string_view path, int m
<< " failed to normalize: " << path;
return -EINVAL;
}
- auto err = mIncFs->makeFile(ifs->control, normPath, mode, id, params);
- if (err) {
+ if (auto err = mIncFs->makeFile(ifs->control, normPath, mode, id, params); err) {
LOG(ERROR) << "Internal error: storageId " << storage << " failed to makeFile: " << err;
return err;
}
+ if (!data.empty()) {
+ if (auto err = setFileContent(ifs, id, path, data); err) {
+ return err;
+ }
+ }
return 0;
}
return -EINVAL;
@@ -1382,7 +1387,7 @@ bool IncrementalService::mountExistingImage(std::string_view root) {
}
void IncrementalService::runCmdLooper() {
- constexpr auto kTimeoutMsecs = 1000;
+ constexpr auto kTimeoutMsecs = -1;
while (mRunning.load(std::memory_order_relaxed)) {
mLooper->pollAll(kTimeoutMsecs);
}
@@ -1584,67 +1589,38 @@ bool IncrementalService::configureNativeBinaries(StorageId storage, std::string_
void IncrementalService::extractZipFile(const IfsMountPtr& ifs, ZipArchiveHandle zipFile,
ZipEntry& entry, const incfs::FileId& libFileId,
- std::string_view targetLibPath,
+ std::string_view debugLibPath,
Clock::time_point scheduledTs) {
if (!ifs) {
- LOG(INFO) << "Skipping zip file " << targetLibPath << " extraction for an expired mount";
+ LOG(INFO) << "Skipping zip file " << debugLibPath << " extraction for an expired mount";
return;
}
- auto libName = path::basename(targetLibPath);
auto startedTs = Clock::now();
// Write extracted data to new file
// NOTE: don't zero-initialize memory, it may take a while for nothing
auto libData = std::unique_ptr<uint8_t[]>(new uint8_t[entry.uncompressed_length]);
if (ExtractToMemory(zipFile, &entry, libData.get(), entry.uncompressed_length)) {
- LOG(ERROR) << "Failed to extract native lib zip entry: " << libName;
+ LOG(ERROR) << "Failed to extract native lib zip entry: " << path::basename(debugLibPath);
return;
}
auto extractFileTs = Clock::now();
- const auto writeFd = mIncFs->openForSpecialOps(ifs->control, libFileId);
- if (!writeFd.ok()) {
- LOG(ERROR) << "Failed to open write fd for: " << targetLibPath
- << " errno: " << writeFd.get();
- return;
- }
-
- auto openFileTs = Clock::now();
- const int numBlocks =
- (entry.uncompressed_length + constants().blockSize - 1) / constants().blockSize;
- std::vector<IncFsDataBlock> instructions(numBlocks);
- auto remainingData = std::span(libData.get(), entry.uncompressed_length);
- for (int i = 0; i < numBlocks; i++) {
- const auto blockSize = std::min<long>(constants().blockSize, remainingData.size());
- instructions[i] = IncFsDataBlock{
- .fileFd = writeFd.get(),
- .pageIndex = static_cast<IncFsBlockIndex>(i),
- .compression = INCFS_COMPRESSION_KIND_NONE,
- .kind = INCFS_BLOCK_KIND_DATA,
- .dataSize = static_cast<uint32_t>(blockSize),
- .data = reinterpret_cast<const char*>(remainingData.data()),
- };
- remainingData = remainingData.subspan(blockSize);
- }
- auto prepareInstsTs = Clock::now();
-
- size_t res = mIncFs->writeBlocks(instructions);
- if (res != instructions.size()) {
- LOG(ERROR) << "Failed to write data into: " << targetLibPath;
+ if (setFileContent(ifs, libFileId, debugLibPath,
+ std::span(libData.get(), entry.uncompressed_length))) {
return;
}
if (perfLoggingEnabled()) {
auto endFileTs = Clock::now();
- LOG(INFO) << "incfs: Extracted " << libName << "(" << entry.compressed_length << " -> "
- << entry.uncompressed_length << " bytes): " << elapsedMcs(startedTs, endFileTs)
+ LOG(INFO) << "incfs: Extracted " << path::basename(debugLibPath) << "("
+ << entry.compressed_length << " -> " << entry.uncompressed_length
+ << " bytes): " << elapsedMcs(startedTs, endFileTs)
<< "mcs, scheduling delay: " << elapsedMcs(scheduledTs, startedTs)
<< " extract: " << elapsedMcs(startedTs, extractFileTs)
- << " open: " << elapsedMcs(extractFileTs, openFileTs)
- << " prepare: " << elapsedMcs(openFileTs, prepareInstsTs)
- << " write: " << elapsedMcs(prepareInstsTs, endFileTs);
+ << " open/prepare/write: " << elapsedMcs(extractFileTs, endFileTs);
}
}
@@ -1677,6 +1653,55 @@ bool IncrementalService::waitForNativeBinariesExtraction(StorageId storage) {
return mRunning;
}
+int IncrementalService::setFileContent(const IfsMountPtr& ifs, const incfs::FileId& fileId,
+ std::string_view debugFilePath,
+ std::span<const uint8_t> data) const {
+ auto startTs = Clock::now();
+
+ const auto writeFd = mIncFs->openForSpecialOps(ifs->control, fileId);
+ if (!writeFd.ok()) {
+ LOG(ERROR) << "Failed to open write fd for: " << debugFilePath
+ << " errno: " << writeFd.get();
+ return writeFd.get();
+ }
+
+ const auto dataLength = data.size();
+
+ auto openFileTs = Clock::now();
+ const int numBlocks = (data.size() + constants().blockSize - 1) / constants().blockSize;
+ std::vector<IncFsDataBlock> instructions(numBlocks);
+ for (int i = 0; i < numBlocks; i++) {
+ const auto blockSize = std::min<long>(constants().blockSize, data.size());
+ instructions[i] = IncFsDataBlock{
+ .fileFd = writeFd.get(),
+ .pageIndex = static_cast<IncFsBlockIndex>(i),
+ .compression = INCFS_COMPRESSION_KIND_NONE,
+ .kind = INCFS_BLOCK_KIND_DATA,
+ .dataSize = static_cast<uint32_t>(blockSize),
+ .data = reinterpret_cast<const char*>(data.data()),
+ };
+ data = data.subspan(blockSize);
+ }
+ auto prepareInstsTs = Clock::now();
+
+ size_t res = mIncFs->writeBlocks(instructions);
+ if (res != instructions.size()) {
+ LOG(ERROR) << "Failed to write data into: " << debugFilePath;
+ return res;
+ }
+
+ if (perfLoggingEnabled()) {
+ auto endTs = Clock::now();
+ LOG(INFO) << "incfs: Set file content " << debugFilePath << "(" << dataLength
+ << " bytes): " << elapsedMcs(startTs, endTs)
+ << "mcs, open: " << elapsedMcs(startTs, openFileTs)
+ << " prepare: " << elapsedMcs(openFileTs, prepareInstsTs)
+ << " write: " << elapsedMcs(prepareInstsTs, endTs);
+ }
+
+ return 0;
+}
+
int IncrementalService::isFileFullyLoaded(StorageId storage, const std::string& path) const {
std::unique_lock l(mLock);
const auto ifs = getIfsLocked(storage);
diff --git a/services/incremental/IncrementalService.h b/services/incremental/IncrementalService.h
index a49e0f36c9bc..d820417e73ed 100644
--- a/services/incremental/IncrementalService.h
+++ b/services/incremental/IncrementalService.h
@@ -127,7 +127,7 @@ public:
int setStorageParams(StorageId storage, bool enableReadLogs);
int makeFile(StorageId storage, std::string_view path, int mode, FileId id,
- incfs::NewFileParams params);
+ incfs::NewFileParams params, std::span<const uint8_t> data);
int makeDir(StorageId storage, std::string_view path, int mode = 0755);
int makeDirs(StorageId storage, std::string_view path, int mode = 0755);
@@ -349,13 +349,16 @@ private:
int isFileFullyLoadedFromPath(const IncFsMount& ifs, std::string_view filePath) const;
float getLoadingProgressFromPath(const IncFsMount& ifs, std::string_view path) const;
+ int setFileContent(const IfsMountPtr& ifs, const incfs::FileId& fileId,
+ std::string_view debugFilePath, std::span<const uint8_t> data) const;
+
void registerAppOpsCallback(const std::string& packageName);
bool unregisterAppOpsCallback(const std::string& packageName);
void onAppOpChanged(const std::string& packageName);
void runJobProcessing();
void extractZipFile(const IfsMountPtr& ifs, ZipArchiveHandle zipFile, ZipEntry& entry,
- const incfs::FileId& libFileId, std::string_view targetLibPath,
+ const incfs::FileId& libFileId, std::string_view debugLibPath,
Clock::time_point scheduledTs);
void runCmdLooper();
diff --git a/services/java/com/android/server/SystemServer.java b/services/java/com/android/server/SystemServer.java
index ddd23778884a..eca9f1556d43 100644
--- a/services/java/com/android/server/SystemServer.java
+++ b/services/java/com/android/server/SystemServer.java
@@ -293,6 +293,8 @@ public final class SystemServer {
"com.android.server.timedetector.TimeDetectorService$Lifecycle";
private static final String TIME_ZONE_DETECTOR_SERVICE_CLASS =
"com.android.server.timezonedetector.TimeZoneDetectorService$Lifecycle";
+ private static final String LOCATION_TIME_ZONE_MANAGER_SERVICE_CLASS =
+ "com.android.server.location.timezone.LocationTimeZoneManagerService$Lifecycle";
private static final String ACCESSIBILITY_MANAGER_SERVICE_CLASS =
"com.android.server.accessibility.AccessibilityManagerService$Lifecycle";
private static final String ADB_SERVICE_CLASS =
@@ -1621,7 +1623,7 @@ public final class SystemServer {
try {
mSystemServiceManager.startService(TIME_DETECTOR_SERVICE_CLASS);
} catch (Throwable e) {
- reportWtf("starting StartTimeDetectorService service", e);
+ reportWtf("starting TimeDetectorService service", e);
}
t.traceEnd();
@@ -1629,7 +1631,15 @@ public final class SystemServer {
try {
mSystemServiceManager.startService(TIME_ZONE_DETECTOR_SERVICE_CLASS);
} catch (Throwable e) {
- reportWtf("starting StartTimeZoneDetectorService service", e);
+ reportWtf("starting TimeZoneDetectorService service", e);
+ }
+ t.traceEnd();
+
+ t.traceBegin("StartLocationTimeZoneManagerService");
+ try {
+ mSystemServiceManager.startService(LOCATION_TIME_ZONE_MANAGER_SERVICE_CLASS);
+ } catch (Throwable e) {
+ reportWtf("starting LocationTimeZoneManagerService service", e);
}
t.traceEnd();
diff --git a/services/tests/mockingservicestests/src/com/android/server/alarm/AlarmManagerServiceTest.java b/services/tests/mockingservicestests/src/com/android/server/alarm/AlarmManagerServiceTest.java
index 0789d680a80c..d61783ef59d2 100644
--- a/services/tests/mockingservicestests/src/com/android/server/alarm/AlarmManagerServiceTest.java
+++ b/services/tests/mockingservicestests/src/com/android/server/alarm/AlarmManagerServiceTest.java
@@ -25,6 +25,7 @@ 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.doAnswer;
import static com.android.dx.mockito.inline.extended.ExtendedMockito.doCallRealMethod;
import static com.android.dx.mockito.inline.extended.ExtendedMockito.doReturn;
import static com.android.dx.mockito.inline.extended.ExtendedMockito.doThrow;
@@ -54,6 +55,7 @@ import static org.junit.Assert.assertNull;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.anyInt;
import static org.mockito.ArgumentMatchers.anyLong;
+import static org.mockito.ArgumentMatchers.anyString;
import static org.mockito.ArgumentMatchers.argThat;
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.ArgumentMatchers.isNull;
@@ -67,7 +69,6 @@ import android.app.IAlarmCompleteListener;
import android.app.IAlarmListener;
import android.app.PendingIntent;
import android.app.usage.UsageStatsManagerInternal;
-import android.content.ContentResolver;
import android.content.Context;
import android.content.Intent;
import android.os.BatteryManager;
@@ -79,6 +80,7 @@ import android.os.RemoteException;
import android.os.ServiceManager;
import android.os.UserHandle;
import android.platform.test.annotations.Presubmit;
+import android.provider.DeviceConfig;
import android.provider.Settings;
import android.util.Log;
import android.util.SparseArray;
@@ -100,11 +102,15 @@ import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.Answers;
import org.mockito.ArgumentCaptor;
+import org.mockito.ArgumentMatchers;
import org.mockito.Mock;
import org.mockito.MockitoSession;
import org.mockito.quality.Strictness;
+import org.mockito.stubbing.Answer;
import java.util.ArrayList;
+import java.util.HashSet;
+import java.util.concurrent.Executor;
@Presubmit
@RunWith(AndroidJUnit4.class)
@@ -120,8 +126,6 @@ public class AlarmManagerServiceTest {
private AppStandbyInternal.AppIdleStateChangeListener mAppStandbyListener;
private AlarmManagerService.ChargingReceiver mChargingReceiver;
@Mock
- private ContentResolver mMockResolver;
- @Mock
private Context mMockContext;
@Mock
private IActivityManager mIActivityManager;
@@ -137,6 +141,9 @@ public class AlarmManagerServiceTest {
private AlarmManagerService.ClockReceiver mClockReceiver;
@Mock
private PowerManager.WakeLock mWakeLock;
+ @Mock
+ DeviceConfig.Properties mDeviceConfigProperties;
+ HashSet<String> mDeviceConfigKeys = new HashSet<>();
private MockitoSession mMockingSession;
private Injector mInjector;
@@ -253,6 +260,14 @@ public class AlarmManagerServiceTest {
PowerManager.WakeLock getAlarmWakeLock() {
return mWakeLock;
}
+
+ @Override
+ void registerDeviceConfigListener(DeviceConfig.OnPropertiesChangedListener listener) {
+ // Do nothing.
+ // The tests become flaky with an error message of
+ // "IllegalStateException: Querying activity state off main thread is not allowed."
+ // when AlarmManager calls DeviceConfig.addOnPropertiesChangedListener().
+ }
}
@Before
@@ -260,6 +275,7 @@ public class AlarmManagerServiceTest {
mMockingSession = mockitoSession()
.initMocks(this)
.spyStatic(ActivityManager.class)
+ .spyStatic(DeviceConfig.class)
.mockStatic(LocalServices.class)
.spyStatic(Looper.class)
.mockStatic(Settings.Global.class)
@@ -283,9 +299,24 @@ public class AlarmManagerServiceTest {
eq(TEST_CALLING_USER), anyLong())).thenReturn(STANDBY_BUCKET_ACTIVE);
doReturn(Looper.getMainLooper()).when(Looper::myLooper);
- when(mMockContext.getContentResolver()).thenReturn(mMockResolver);
- doReturn("min_futurity=0,min_interval=0").when(() ->
- Settings.Global.getString(mMockResolver, Settings.Global.ALARM_MANAGER_CONSTANTS));
+ doReturn(mDeviceConfigKeys).when(mDeviceConfigProperties).getKeyset();
+ when(mDeviceConfigProperties.getLong(anyString(), anyLong()))
+ .thenAnswer((Answer<Long>) invocationOnMock -> {
+ Object[] args = invocationOnMock.getArguments();
+ return (Long) args[1];
+ });
+ when(mDeviceConfigProperties.getInt(anyString(), anyInt()))
+ .thenAnswer((Answer<Integer>) invocationOnMock -> {
+ Object[] args = invocationOnMock.getArguments();
+ return (Integer) args[1];
+ });
+ doAnswer((Answer<Void>) invocationOnMock -> null)
+ .when(() -> DeviceConfig.addOnPropertiesChangedListener(
+ anyString(), any(Executor.class),
+ any(DeviceConfig.OnPropertiesChangedListener.class)));
+ doReturn(mDeviceConfigProperties).when(
+ () -> DeviceConfig.getProperties(
+ eq(DeviceConfig.NAMESPACE_ALARM_MANAGER), ArgumentMatchers.<String>any()));
mInjector = new Injector(mMockContext);
mService = new AlarmManagerService(mMockContext, mInjector);
@@ -302,8 +333,6 @@ public class AlarmManagerServiceTest {
// Other boot phases don't matter
mService.onBootPhase(SystemService.PHASE_SYSTEM_SERVICES_READY);
- assertEquals(0, mService.mConstants.MIN_FUTURITY);
- assertEquals(0, mService.mConstants.MIN_INTERVAL);
mAppStandbyWindow = mService.mConstants.APP_STANDBY_WINDOW;
ArgumentCaptor<AppStandbyInternal.AppIdleStateChangeListener> captor =
ArgumentCaptor.forClass(AppStandbyInternal.AppIdleStateChangeListener.class);
@@ -354,22 +383,27 @@ public class AlarmManagerServiceTest {
return mockPi;
}
+ private void setDeviceConfigLong(String key, long val) {
+ mDeviceConfigKeys.add(key);
+ doReturn(val).when(mDeviceConfigProperties).getLong(eq(key), anyLong());
+ mService.mConstants.onPropertiesChanged(mDeviceConfigProperties);
+ }
+
/**
* Lowers quotas to make testing feasible. Careful while calling as this will replace any
* existing settings for the calling test.
*/
private void setTestableQuotas() {
- final StringBuilder constantsBuilder = new StringBuilder();
- constantsBuilder.append(KEY_MIN_FUTURITY);
- constantsBuilder.append("=0,");
- // Capping active and working quotas to make testing feasible.
- constantsBuilder.append(mService.mConstants.KEYS_APP_STANDBY_QUOTAS[ACTIVE_INDEX]);
- constantsBuilder.append("=8,");
- constantsBuilder.append(mService.mConstants.KEYS_APP_STANDBY_QUOTAS[WORKING_INDEX]);
- constantsBuilder.append("=5,");
- doReturn(constantsBuilder.toString()).when(() -> Settings.Global.getString(mMockResolver,
- Settings.Global.ALARM_MANAGER_CONSTANTS));
- mService.mConstants.onChange(false, null);
+ setDeviceConfigLong(KEY_MIN_FUTURITY, 0);
+ setDeviceConfigLong(KEY_MIN_INTERVAL, 0);
+ mDeviceConfigKeys.add(mService.mConstants.KEYS_APP_STANDBY_QUOTAS[ACTIVE_INDEX]);
+ mDeviceConfigKeys.add(mService.mConstants.KEYS_APP_STANDBY_QUOTAS[WORKING_INDEX]);
+ doReturn(8).when(mDeviceConfigProperties)
+ .getInt(eq(mService.mConstants.KEYS_APP_STANDBY_QUOTAS[ACTIVE_INDEX]), anyInt());
+ doReturn(5).when(mDeviceConfigProperties)
+ .getInt(eq(mService.mConstants.KEYS_APP_STANDBY_QUOTAS[WORKING_INDEX]), anyInt());
+
+ mService.mConstants.onPropertiesChanged(mDeviceConfigProperties);
}
@Test
@@ -427,25 +461,13 @@ public class AlarmManagerServiceTest {
@Test
public void testUpdateConstants() {
- final StringBuilder constantsBuilder = new StringBuilder();
- constantsBuilder.append(KEY_MIN_FUTURITY);
- constantsBuilder.append("=5,");
- constantsBuilder.append(KEY_MIN_INTERVAL);
- constantsBuilder.append("=10,");
- constantsBuilder.append(KEY_MAX_INTERVAL);
- constantsBuilder.append("=15,");
- constantsBuilder.append(KEY_ALLOW_WHILE_IDLE_SHORT_TIME);
- constantsBuilder.append("=20,");
- constantsBuilder.append(KEY_ALLOW_WHILE_IDLE_LONG_TIME);
- constantsBuilder.append("=25,");
- constantsBuilder.append(KEY_ALLOW_WHILE_IDLE_WHITELIST_DURATION);
- constantsBuilder.append("=30,");
- constantsBuilder.append(KEY_LISTENER_TIMEOUT);
- constantsBuilder.append("=35,");
-
- doReturn(constantsBuilder.toString()).when(() -> Settings.Global.getString(mMockResolver,
- Settings.Global.ALARM_MANAGER_CONSTANTS));
- mService.mConstants.onChange(false, null);
+ setDeviceConfigLong(KEY_MIN_FUTURITY, 5);
+ setDeviceConfigLong(KEY_MIN_INTERVAL, 10);
+ setDeviceConfigLong(KEY_MAX_INTERVAL, 15);
+ setDeviceConfigLong(KEY_ALLOW_WHILE_IDLE_SHORT_TIME, 20);
+ setDeviceConfigLong(KEY_ALLOW_WHILE_IDLE_LONG_TIME, 25);
+ setDeviceConfigLong(KEY_ALLOW_WHILE_IDLE_WHITELIST_DURATION, 30);
+ setDeviceConfigLong(KEY_LISTENER_TIMEOUT, 35);
assertEquals(5, mService.mConstants.MIN_FUTURITY);
assertEquals(10, mService.mConstants.MIN_INTERVAL);
assertEquals(15, mService.mConstants.MAX_INTERVAL);
@@ -457,9 +479,7 @@ public class AlarmManagerServiceTest {
@Test
public void testMinFuturity() {
- doReturn("min_futurity=10").when(() ->
- Settings.Global.getString(mMockResolver, Settings.Global.ALARM_MANAGER_CONSTANTS));
- mService.mConstants.onChange(false, null);
+ setDeviceConfigLong(KEY_MIN_FUTURITY, 10L);
assertEquals(10, mService.mConstants.MIN_FUTURITY);
final long triggerTime = mNowElapsedTest + 1;
final long expectedTriggerTime = mNowElapsedTest + mService.mConstants.MIN_FUTURITY;
@@ -469,9 +489,7 @@ public class AlarmManagerServiceTest {
@Test
public void testMinFuturityCoreUid() {
- doReturn("min_futurity=10").when(() ->
- Settings.Global.getString(mMockResolver, Settings.Global.ALARM_MANAGER_CONSTANTS));
- mService.mConstants.onChange(false, null);
+ setDeviceConfigLong(KEY_MIN_FUTURITY, 10L);
assertEquals(10, mService.mConstants.MIN_FUTURITY);
final long triggerTime = mNowElapsedTest + 1;
doReturn(true).when(() -> UserHandle.isCore(TEST_CALLING_UID));
@@ -769,7 +787,7 @@ public class AlarmManagerServiceTest {
}
@Test
- public void testAlarmRestrictedInBatterSaver() throws Exception {
+ public void testAlarmRestrictedInBatterySaver() throws Exception {
final ArgumentCaptor<AppStateTrackerImpl.Listener> listenerArgumentCaptor =
ArgumentCaptor.forClass(AppStateTrackerImpl.Listener.class);
verify(mAppStateTracker).addListener(listenerArgumentCaptor.capture());
@@ -1081,6 +1099,49 @@ public class AlarmManagerServiceTest {
}
}
+ @Test
+ public void nonWakeupAlarmsDeferred() throws Exception {
+ final int numAlarms = 10;
+ final PendingIntent[] pis = new PendingIntent[numAlarms];
+ for (int i = 0; i < numAlarms; i++) {
+ pis[i] = getNewMockPendingIntent();
+ setTestAlarm(ELAPSED_REALTIME, mNowElapsedTest + i + 5, pis[i]);
+ }
+ doReturn(true).when(mService).checkAllowNonWakeupDelayLocked(anyLong());
+ // Advance time past all expirations.
+ mNowElapsedTest += numAlarms + 5;
+ mTestTimer.expire();
+ assertEquals(numAlarms, mService.mPendingNonWakeupAlarms.size());
+
+ // These alarms should be sent on interactive state change to true
+ mService.interactiveStateChangedLocked(false);
+ mService.interactiveStateChangedLocked(true);
+
+ for (int i = 0; i < numAlarms; i++) {
+ verify(pis[i]).send(eq(mMockContext), eq(0), any(Intent.class), any(),
+ any(Handler.class), isNull(), any());
+ }
+ }
+
+ @Test
+ public void alarmCountOnPendingNonWakeupAlarmsRemoved() throws Exception {
+ final int numAlarms = 10;
+ final PendingIntent[] pis = new PendingIntent[numAlarms];
+ for (int i = 0; i < numAlarms; i++) {
+ pis[i] = getNewMockPendingIntent();
+ setTestAlarm(ELAPSED_REALTIME, mNowElapsedTest + i + 5, pis[i]);
+ }
+ doReturn(true).when(mService).checkAllowNonWakeupDelayLocked(anyLong());
+ // Advance time past all expirations.
+ mNowElapsedTest += numAlarms + 5;
+ mTestTimer.expire();
+ assertEquals(numAlarms, mService.mPendingNonWakeupAlarms.size());
+ for (int i = 0; i < numAlarms; i++) {
+ mService.removeLocked(pis[i], null);
+ assertEquals(numAlarms - i - 1, mService.mAlarmsPerUid.get(TEST_CALLING_UID, 0));
+ }
+ }
+
@After
public void tearDown() {
if (mMockingSession != null) {
diff --git a/services/tests/mockingservicestests/src/com/android/server/job/controllers/JobStatusTest.java b/services/tests/mockingservicestests/src/com/android/server/job/controllers/JobStatusTest.java
index 1bf9c2a0822e..6d40034c6000 100644
--- a/services/tests/mockingservicestests/src/com/android/server/job/controllers/JobStatusTest.java
+++ b/services/tests/mockingservicestests/src/com/android/server/job/controllers/JobStatusTest.java
@@ -46,6 +46,7 @@ import android.app.job.JobInfo;
import android.app.usage.UsageStatsManagerInternal;
import android.content.ComponentName;
import android.content.pm.PackageManagerInternal;
+import android.content.pm.ServiceInfo;
import android.net.Uri;
import android.os.SystemClock;
import android.provider.MediaStore;
@@ -685,6 +686,8 @@ public class JobStatusTest {
}
private static JobStatus createJobStatus(JobInfo job) {
- return JobStatus.createFromJobInfo(job, 0, null, -1, "JobStatusTest");
+ JobStatus jobStatus = JobStatus.createFromJobInfo(job, 0, null, -1, "JobStatusTest");
+ jobStatus.serviceInfo = mock(ServiceInfo.class);
+ return jobStatus;
}
}
diff --git a/services/tests/mockingservicestests/src/com/android/server/job/controllers/QuotaControllerTest.java b/services/tests/mockingservicestests/src/com/android/server/job/controllers/QuotaControllerTest.java
index 1cf133a2bbf2..18bd6b17f340 100644
--- a/services/tests/mockingservicestests/src/com/android/server/job/controllers/QuotaControllerTest.java
+++ b/services/tests/mockingservicestests/src/com/android/server/job/controllers/QuotaControllerTest.java
@@ -61,6 +61,7 @@ import android.content.Context;
import android.content.Intent;
import android.content.pm.IPackageManager;
import android.content.pm.PackageManagerInternal;
+import android.content.pm.ServiceInfo;
import android.os.BatteryManager;
import android.os.BatteryManagerInternal;
import android.os.Handler;
@@ -299,6 +300,7 @@ public class QuotaControllerTest {
JobInfo jobInfo) {
JobStatus js = JobStatus.createFromJobInfo(
jobInfo, callingUid, packageName, SOURCE_USER_ID, testTag);
+ js.serviceInfo = mock(ServiceInfo.class);
// Make sure tests aren't passing just because the default bucket is likely ACTIVE.
js.setStandbyBucket(FREQUENT_INDEX);
// Make sure Doze and background-not-restricted don't affect tests.
diff --git a/services/tests/mockingservicestests/src/com/android/server/location/LocationProviderManagerTest.java b/services/tests/mockingservicestests/src/com/android/server/location/LocationProviderManagerTest.java
index 149148649cf6..b4e8825c3902 100644
--- a/services/tests/mockingservicestests/src/com/android/server/location/LocationProviderManagerTest.java
+++ b/services/tests/mockingservicestests/src/com/android/server/location/LocationProviderManagerTest.java
@@ -66,7 +66,6 @@ import android.location.LocationManagerInternal.ProviderEnabledListener;
import android.location.LocationRequest;
import android.location.util.identity.CallerIdentity;
import android.os.Bundle;
-import android.os.CancellationSignal;
import android.os.Handler;
import android.os.ICancellationSignal;
import android.os.IRemoteCallback;
@@ -616,9 +615,7 @@ public class LocationProviderManagerTest {
ILocationCallback listener = createMockGetCurrentLocationListener();
LocationRequest locationRequest = new LocationRequest.Builder(0).build();
- ICancellationSignal cancellationSignal = CancellationSignal.createTransport();
- mManager.getCurrentLocation(locationRequest, IDENTITY,
- PERMISSION_FINE, cancellationSignal, listener);
+ mManager.getCurrentLocation(locationRequest, IDENTITY, PERMISSION_FINE, listener);
Location loc = createLocation(NAME, mRandom);
mProvider.setProviderLocation(loc);
@@ -632,9 +629,8 @@ public class LocationProviderManagerTest {
public void testGetCurrentLocation_Cancel() throws Exception {
ILocationCallback listener = createMockGetCurrentLocationListener();
LocationRequest locationRequest = new LocationRequest.Builder(0).build();
- ICancellationSignal cancellationSignal = CancellationSignal.createTransport();
- mManager.getCurrentLocation(locationRequest, IDENTITY,
- PERMISSION_FINE, cancellationSignal, listener);
+ ICancellationSignal cancellationSignal = mManager.getCurrentLocation(locationRequest,
+ IDENTITY, PERMISSION_FINE, listener);
cancellationSignal.cancel();
mProvider.setProviderLocation(createLocation(NAME, mRandom));
@@ -646,9 +642,7 @@ public class LocationProviderManagerTest {
public void testGetCurrentLocation_ProviderDisabled() throws Exception {
ILocationCallback listener = createMockGetCurrentLocationListener();
LocationRequest locationRequest = new LocationRequest.Builder(0).build();
- ICancellationSignal cancellationSignal = CancellationSignal.createTransport();
- mManager.getCurrentLocation(locationRequest, IDENTITY,
- PERMISSION_FINE, cancellationSignal, listener);
+ mManager.getCurrentLocation(locationRequest, IDENTITY, PERMISSION_FINE, listener);
mProvider.setProviderAllowed(false);
mProvider.setProviderAllowed(true);
@@ -662,9 +656,7 @@ public class LocationProviderManagerTest {
ILocationCallback listener = createMockGetCurrentLocationListener();
LocationRequest locationRequest = new LocationRequest.Builder(0).build();
- ICancellationSignal cancellationSignal = CancellationSignal.createTransport();
- mManager.getCurrentLocation(locationRequest, IDENTITY,
- PERMISSION_FINE, cancellationSignal, listener);
+ mManager.getCurrentLocation(locationRequest, IDENTITY, PERMISSION_FINE, listener);
mProvider.setProviderAllowed(true);
mProvider.setProviderLocation(createLocation(NAME, mRandom));
@@ -680,9 +672,7 @@ public class LocationProviderManagerTest {
ILocationCallback listener = createMockGetCurrentLocationListener();
LocationRequest locationRequest = new LocationRequest.Builder(0).build();
- ICancellationSignal cancellationSignal = CancellationSignal.createTransport();
- mManager.getCurrentLocation(locationRequest, IDENTITY,
- PERMISSION_FINE, cancellationSignal, listener);
+ mManager.getCurrentLocation(locationRequest, IDENTITY, PERMISSION_FINE, listener);
verify(listener, times(1)).onLocation(locationCaptor.capture());
assertThat(locationCaptor.getValue()).isEqualTo(loc);
@@ -692,9 +682,7 @@ public class LocationProviderManagerTest {
public void testGetCurrentLocation_Timeout() throws Exception {
ILocationCallback listener = createMockGetCurrentLocationListener();
LocationRequest locationRequest = new LocationRequest.Builder(0).build();
- ICancellationSignal cancellationSignal = CancellationSignal.createTransport();
- mManager.getCurrentLocation(locationRequest, IDENTITY,
- PERMISSION_FINE, cancellationSignal, listener);
+ mManager.getCurrentLocation(locationRequest, IDENTITY, PERMISSION_FINE, listener);
ArgumentCaptor<OnAlarmListener> listenerCapture = ArgumentCaptor.forClass(
OnAlarmListener.class);
diff --git a/services/tests/mockingservicestests/src/com/android/server/location/listeners/ListenerMultiplexerTest.java b/services/tests/mockingservicestests/src/com/android/server/location/listeners/ListenerMultiplexerTest.java
index 0b5a6998cd0d..29d3f29ad7e1 100644
--- a/services/tests/mockingservicestests/src/com/android/server/location/listeners/ListenerMultiplexerTest.java
+++ b/services/tests/mockingservicestests/src/com/android/server/location/listeners/ListenerMultiplexerTest.java
@@ -34,6 +34,7 @@ import android.platform.test.annotations.Presubmit;
import androidx.test.filters.SmallTest;
import androidx.test.runner.AndroidJUnit4;
+import com.android.internal.listeners.ListenerExecutor.ListenerOperation;
import com.android.server.location.listeners.ListenerMultiplexer.UpdateServiceLock;
import org.junit.Before;
@@ -318,7 +319,8 @@ public class ListenerMultiplexerTest {
}
private static class TestListenerRegistration extends
- ListenerRegistration<Integer, Consumer<TestListenerRegistration>> {
+ RequestListenerRegistration<Integer, Consumer<TestListenerRegistration>,
+ ListenerOperation<Consumer<TestListenerRegistration>>> {
boolean mActive = true;
@@ -329,8 +331,10 @@ public class ListenerMultiplexerTest {
}
private static class TestMultiplexer extends
- ListenerMultiplexer<Consumer<TestListenerRegistration>, Integer,
- Consumer<TestListenerRegistration>, TestListenerRegistration, Integer> {
+ ListenerMultiplexer<Consumer<TestListenerRegistration>,
+ Consumer<TestListenerRegistration>,
+ ListenerOperation<Consumer<TestListenerRegistration>>, TestListenerRegistration,
+ Integer> {
boolean mRegistered;
int mMergedRequest;
@@ -341,6 +345,11 @@ public class ListenerMultiplexerTest {
mCallbacks = callbacks;
}
+ @Override
+ public String getTag() {
+ return "TestMultiplexer";
+ }
+
public void addListener(Integer request, Consumer<TestListenerRegistration> consumer) {
addRegistration(consumer, new TestListenerRegistration(request, consumer));
}
@@ -369,7 +378,8 @@ public class ListenerMultiplexerTest {
}
@Override
- protected boolean registerWithService(Integer mergedRequest) {
+ protected boolean registerWithService(Integer mergedRequest,
+ Collection<TestListenerRegistration> registrations) {
mRegistered = true;
mMergedRequest = mergedRequest;
return true;
@@ -418,7 +428,8 @@ public class ListenerMultiplexerTest {
}
@Override
- protected Integer mergeRequests(Collection<TestListenerRegistration> testRegistrations) {
+ protected Integer mergeRegistrations(
+ Collection<TestListenerRegistration> testRegistrations) {
int max = Integer.MIN_VALUE;
for (TestListenerRegistration registration : testRegistrations) {
if (registration.getRequest() > max) {
diff --git a/services/tests/servicestests/src/com/android/server/am/BatteryStatsServiceTest.java b/services/tests/servicestests/src/com/android/server/am/BatteryStatsServiceTest.java
new file mode 100644
index 000000000000..488e5cdf33b9
--- /dev/null
+++ b/services/tests/servicestests/src/com/android/server/am/BatteryStatsServiceTest.java
@@ -0,0 +1,122 @@
+/*
+ * 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.am;
+
+import static org.junit.Assert.assertTrue;
+
+import android.content.Context;
+import android.os.Handler;
+import android.os.HandlerThread;
+import android.os.Process;
+
+import androidx.test.InstrumentationRegistry;
+import androidx.test.runner.AndroidJUnit4;
+
+import com.android.internal.os.BatteryStatsImpl;
+
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.TimeUnit;
+import java.util.concurrent.atomic.AtomicBoolean;
+
+@RunWith(AndroidJUnit4.class)
+public final class BatteryStatsServiceTest {
+
+ private BatteryStatsService mBatteryStatsService;
+ private HandlerThread mBgThread;
+
+ @Before
+ public void setUp() {
+ final Context context = InstrumentationRegistry.getContext();
+ mBgThread = new HandlerThread("bg thread");
+ mBgThread.start();
+ mBatteryStatsService = new BatteryStatsService(context,
+ context.getCacheDir(), new Handler(mBgThread.getLooper()));
+ }
+
+ @After
+ public void tearDown() {
+ mBatteryStatsService.shutdown();
+ mBgThread.quitSafely();
+ }
+
+ @Test
+ public void testAwaitCompletion() throws Exception {
+ final CountDownLatch readyLatch = new CountDownLatch(2);
+ final CountDownLatch startLatch = new CountDownLatch(1);
+ final CountDownLatch testLatch = new CountDownLatch(1);
+ final AtomicBoolean quiting = new AtomicBoolean(false);
+ final AtomicBoolean finished = new AtomicBoolean(false);
+ final int uid = Process.myUid();
+ final Thread noteThread = new Thread(() -> {
+ final int maxIterations = 1000;
+ final int eventCode = 12345;
+ final String eventName = "placeholder";
+ final BatteryStatsImpl stats = mBatteryStatsService.getActiveStatistics();
+
+ readyLatch.countDown();
+ try {
+ startLatch.await();
+ } catch (InterruptedException e) {
+ }
+
+ for (int i = 0; i < maxIterations && !quiting.get(); i++) {
+ synchronized (stats) {
+ mBatteryStatsService.noteEvent(eventCode, eventName, uid);
+ }
+ }
+ finished.set(true);
+ });
+ final Thread waitThread = new Thread(() -> {
+ readyLatch.countDown();
+ try {
+ startLatch.await();
+ } catch (InterruptedException e) {
+ }
+
+ do {
+ mBatteryStatsService.takeUidSnapshot(uid);
+ } while (!finished.get() && !quiting.get());
+
+ if (!quiting.get()) {
+ // do one more to ensure we've cleared the queue
+ mBatteryStatsService.takeUidSnapshot(uid);
+ }
+
+ testLatch.countDown();
+ });
+ noteThread.start();
+ waitThread.start();
+ readyLatch.await();
+ startLatch.countDown();
+
+ try {
+ assertTrue("Timed out in waiting for the completion of battery event handling",
+ testLatch.await(10 * 1000, TimeUnit.MILLISECONDS));
+ } finally {
+ quiting.set(true);
+ noteThread.interrupt();
+ noteThread.join(1000);
+ waitThread.interrupt();
+ waitThread.join(1000);
+ }
+ }
+}
diff --git a/services/tests/servicestests/src/com/android/server/biometrics/AuthServiceTest.java b/services/tests/servicestests/src/com/android/server/biometrics/AuthServiceTest.java
index cc1fdab3d22a..e011c797777e 100644
--- a/services/tests/servicestests/src/com/android/server/biometrics/AuthServiceTest.java
+++ b/services/tests/servicestests/src/com/android/server/biometrics/AuthServiceTest.java
@@ -123,8 +123,7 @@ public class AuthServiceTest {
final String[] config = {
"0:2:15", // ID0:Fingerprint:Strong
- "1:4:255", // ID1:Iris:Weak
- "2:8:4095", // ID2:Face:Convenience
+ "1:8:4095", // ID2:Face:Convenience
};
when(mInjector.getConfiguration(any())).thenReturn(config);
@@ -133,12 +132,14 @@ public class AuthServiceTest {
mAuthService.onStart();
final int fingerprintId = 0;
- final int irisId = 1;
- final int faceId = 2;
+ final int faceId = 1;
- verify(mFingerprintService).initializeConfiguration(eq(fingerprintId));
- verify(mIrisService).initializeConfiguration(eq(irisId));
- verify(mFaceService).initializeConfiguration(eq(faceId));
+ final int fingerprintStrength = 15;
+ final int faceStrength = 4095;
+
+ verify(mFingerprintService).initializeConfiguration(eq(fingerprintId),
+ eq(fingerprintStrength));
+ verify(mFaceService).initializeConfiguration(eq(faceId), eq(faceStrength));
}
diff --git a/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerTest.java b/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerTest.java
index 631b4d48e9f2..4ce6411dc306 100644
--- a/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerTest.java
+++ b/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerTest.java
@@ -4407,7 +4407,7 @@ public class DevicePolicyManagerTest extends DpmTestBase {
// Caller is Profile Owner, but no supervision app is configured.
setAsProfileOwner(admin1);
- assertExpectException(SecurityException.class, "is not the default supervision component",
+ assertExpectException(SecurityException.class, "no default supervision component defined",
() -> dpm.setSecondaryLockscreenEnabled(admin1, true));
assertFalse(dpm.isSecondaryLockscreenEnabled(UserHandle.of(CALLER_USER_HANDLE)));
diff --git a/services/tests/servicestests/src/com/android/server/display/DisplayModeDirectorTest.java b/services/tests/servicestests/src/com/android/server/display/DisplayModeDirectorTest.java
index 43a396d8e5d7..b8dbd6267bc5 100644
--- a/services/tests/servicestests/src/com/android/server/display/DisplayModeDirectorTest.java
+++ b/services/tests/servicestests/src/com/android/server/display/DisplayModeDirectorTest.java
@@ -302,4 +302,42 @@ public class DisplayModeDirectorTest {
verifyBrightnessObserverCall(director, 90, 90, 0, 90, 90);
verifyBrightnessObserverCall(director, 120, 90, 0, 120, 90);
}
+
+ @Test
+ public void testVotingWithAlwaysRespectAppRequest() {
+ final int displayId = 0;
+ DisplayModeDirector director = createDirectorFromFpsRange(50, 90);
+ SparseArray<Vote> votes = new SparseArray<>();
+ SparseArray<SparseArray<Vote>> votesByDisplay = new SparseArray<>();
+ votesByDisplay.put(displayId, votes);
+ votes.put(Vote.PRIORITY_LOW_BRIGHTNESS, Vote.forRefreshRates(0, 60));
+ votes.put(Vote.PRIORITY_USER_SETTING_MIN_REFRESH_RATE, Vote.forRefreshRates(60, 90));
+ votes.put(Vote.PRIORITY_APP_REQUEST_REFRESH_RATE, Vote.forRefreshRates(90, 90));
+ votes.put(Vote.PRIORITY_USER_SETTING_PEAK_REFRESH_RATE, Vote.forRefreshRates(60, 60));
+ votes.put(Vote.PRIORITY_LOW_POWER_MODE, Vote.forRefreshRates(0, 60));
+
+
+ director.injectVotesByDisplay(votesByDisplay);
+ Truth.assertThat(director.shouldAlwaysRespectAppRequestedMode()).isFalse();
+ DesiredDisplayModeSpecs desiredSpecs = director.getDesiredDisplayModeSpecs(displayId);
+
+ Truth.assertThat(desiredSpecs.primaryRefreshRateRange.min).isWithin(FLOAT_TOLERANCE).of(60);
+ Truth.assertThat(desiredSpecs.primaryRefreshRateRange.max).isWithin(FLOAT_TOLERANCE).of(60);
+ Truth.assertThat(desiredSpecs.baseModeId).isEqualTo(60);
+
+ director.setShouldAlwaysRespectAppRequestedMode(true);
+ Truth.assertThat(director.shouldAlwaysRespectAppRequestedMode()).isTrue();
+ desiredSpecs = director.getDesiredDisplayModeSpecs(displayId);
+ Truth.assertThat(desiredSpecs.primaryRefreshRateRange.min).isWithin(FLOAT_TOLERANCE).of(90);
+ Truth.assertThat(desiredSpecs.primaryRefreshRateRange.max).isWithin(FLOAT_TOLERANCE).of(90);
+ Truth.assertThat(desiredSpecs.baseModeId).isEqualTo(90);
+
+ director.setShouldAlwaysRespectAppRequestedMode(false);
+ Truth.assertThat(director.shouldAlwaysRespectAppRequestedMode()).isFalse();
+
+ desiredSpecs = director.getDesiredDisplayModeSpecs(displayId);
+ Truth.assertThat(desiredSpecs.primaryRefreshRateRange.min).isWithin(FLOAT_TOLERANCE).of(60);
+ Truth.assertThat(desiredSpecs.primaryRefreshRateRange.max).isWithin(FLOAT_TOLERANCE).of(60);
+ Truth.assertThat(desiredSpecs.baseModeId).isEqualTo(60);
+ }
}
diff --git a/services/tests/servicestests/src/com/android/server/display/color/GlobalSaturationTintControllerTest.java b/services/tests/servicestests/src/com/android/server/display/color/GlobalSaturationTintControllerTest.java
index 7b88a0e012de..4f0cb324f17f 100644
--- a/services/tests/servicestests/src/com/android/server/display/color/GlobalSaturationTintControllerTest.java
+++ b/services/tests/servicestests/src/com/android/server/display/color/GlobalSaturationTintControllerTest.java
@@ -32,9 +32,11 @@ public class GlobalSaturationTintControllerTest {
public void setAndGetMatrix() {
final GlobalSaturationTintController tintController = new GlobalSaturationTintController();
tintController.setMatrix(50);
- assertThat(tintController.getMatrix()).hasValuesWithin(0.00001f)
- .of(new float[]{0.6155f, 0.1155f, 0.1155f, 0.0f, 0.3575f, 0.85749996f, 0.3575f,
- 0.0f, 0.036f, 0.036f, 0.536f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f});
+ assertThat(tintController.getMatrix()).usingTolerance(0.00001f)
+ .containsExactly(
+ 0.6155f, 0.1155f, 0.1155f, 0.0f, 0.3575f, 0.85749996f, 0.3575f,
+ 0.0f, 0.036f, 0.036f, 0.536f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f)
+ .inOrder();
}
@Test
@@ -43,6 +45,7 @@ public class GlobalSaturationTintControllerTest {
tintController.setMatrix(100);
final float[] matrix = new float[16];
Matrix.setIdentityM(matrix, 0);
- assertThat(tintController.getMatrix()).hasValuesWithin(0.00001f).of(matrix);
+ assertThat(tintController.getMatrix()).usingTolerance(0.00001f)
+ .containsExactly(matrix).inOrder();
}
}
diff --git a/services/tests/servicestests/src/com/android/server/hdmi/ActiveSourceActionTest.java b/services/tests/servicestests/src/com/android/server/hdmi/ActiveSourceActionTest.java
index 870a27417cd2..63ad53bcab4a 100644
--- a/services/tests/servicestests/src/com/android/server/hdmi/ActiveSourceActionTest.java
+++ b/services/tests/servicestests/src/com/android/server/hdmi/ActiveSourceActionTest.java
@@ -110,7 +110,7 @@ public class ActiveSourceActionTest {
mHdmiControlService.setIoLooper(looper);
mNativeWrapper = new FakeNativeWrapper();
HdmiCecController hdmiCecController = HdmiCecController.createWithNativeWrapper(
- this.mHdmiControlService, mNativeWrapper);
+ this.mHdmiControlService, mNativeWrapper, mHdmiControlService.getAtomWriter());
mHdmiControlService.setCecController(hdmiCecController);
mHdmiControlService.setHdmiMhlController(HdmiMhlControllerStub.create(mHdmiControlService));
mHdmiControlService.setMessageValidator(new HdmiCecMessageValidator(mHdmiControlService));
diff --git a/services/tests/servicestests/src/com/android/server/hdmi/ArcInitiationActionFromAvrTest.java b/services/tests/servicestests/src/com/android/server/hdmi/ArcInitiationActionFromAvrTest.java
index 83df40629085..08f558e6d99c 100644
--- a/services/tests/servicestests/src/com/android/server/hdmi/ArcInitiationActionFromAvrTest.java
+++ b/services/tests/servicestests/src/com/android/server/hdmi/ArcInitiationActionFromAvrTest.java
@@ -19,11 +19,18 @@ import static com.android.server.hdmi.HdmiControlService.INITIATED_BY_ENABLE_CEC
import static com.google.common.truth.Truth.assertThat;
-import android.annotation.Nullable;
-import android.app.Instrumentation;
-import android.hardware.hdmi.HdmiDeviceInfo;
+import static org.mockito.Mockito.spy;
+import static org.mockito.Mockito.when;
+
+import android.content.Context;
+import android.content.ContextWrapper;
import android.hardware.tv.cec.V1_0.SendMessageResult;
+import android.media.AudioManager;
+import android.os.Handler;
+import android.os.IPowerManager;
+import android.os.IThermalService;
import android.os.Looper;
+import android.os.PowerManager;
import android.os.test.TestLooper;
import android.platform.test.annotations.Presubmit;
@@ -31,10 +38,11 @@ import androidx.test.InstrumentationRegistry;
import androidx.test.filters.SmallTest;
import org.junit.Before;
-import org.junit.Ignore;
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;
@@ -44,7 +52,7 @@ import java.util.ArrayList;
@RunWith(JUnit4.class)
public class ArcInitiationActionFromAvrTest {
- private HdmiDeviceInfo mDeviceInfoForTests;
+ private Context mContextSpy;
private HdmiCecLocalDeviceAudioSystem mHdmiCecLocalDeviceAudioSystem;
private HdmiCecController mHdmiCecController;
private HdmiControlService mHdmiControlService;
@@ -52,93 +60,68 @@ public class ArcInitiationActionFromAvrTest {
private ArcInitiationActionFromAvr mAction;
private TestLooper mTestLooper = new TestLooper();
- private boolean mSendCecCommandSuccess;
- private boolean mShouldDispatchARCInitiated;
- private boolean mArcInitSent;
- private boolean mRequestActiveSourceSent;
- private Instrumentation mInstrumentation;
private ArrayList<HdmiCecLocalDevice> mLocalDevices = new ArrayList<>();
+ @Mock private IPowerManager mIPowerManagerMock;
+ @Mock private IThermalService mIThermalServiceMock;
+ @Mock private AudioManager mAudioManager;
+
@Before
- public void setUp() {
- mDeviceInfoForTests = new HdmiDeviceInfo(1000, 1);
+ public void setUp() throws Exception {
+ MockitoAnnotations.initMocks(this);
- mInstrumentation = InstrumentationRegistry.getInstrumentation();
+ mContextSpy = spy(new ContextWrapper(InstrumentationRegistry.getTargetContext()));
- mHdmiControlService =
- new HdmiControlService(mInstrumentation.getTargetContext()) {
- @Override
- void sendCecCommand(
- HdmiCecMessage command, @Nullable SendMessageCallback callback) {
- switch (command.getOpcode()) {
- case Constants.MESSAGE_REQUEST_ACTIVE_SOURCE:
- if (callback != null) {
- callback.onSendCompleted(
- mSendCecCommandSuccess
- ? SendMessageResult.SUCCESS
- : SendMessageResult.NACK);
- }
- mRequestActiveSourceSent = true;
- break;
- case Constants.MESSAGE_INITIATE_ARC:
- if (callback != null) {
- callback.onSendCompleted(
- mSendCecCommandSuccess
- ? SendMessageResult.SUCCESS
- : SendMessageResult.NACK);
- }
- mArcInitSent = true;
- if (mShouldDispatchARCInitiated) {
- mHdmiCecLocalDeviceAudioSystem.dispatchMessage(
- HdmiCecMessageBuilder.buildReportArcInitiated(
- Constants.ADDR_TV,
- Constants.ADDR_AUDIO_SYSTEM));
- }
- break;
- default:
- }
- }
+ PowerManager powerManager = new PowerManager(mContextSpy, mIPowerManagerMock,
+ mIThermalServiceMock, new Handler(mTestLooper.getLooper()));
+ when(mContextSpy.getSystemService(Context.POWER_SERVICE)).thenReturn(powerManager);
+ when(mContextSpy.getSystemService(PowerManager.class)).thenReturn(powerManager);
+ when(mIPowerManagerMock.isInteractive()).thenReturn(true);
+ mHdmiControlService =
+ new HdmiControlService(mContextSpy) {
@Override
boolean isPowerStandby() {
return false;
}
@Override
- boolean isAddressAllocated() {
- return true;
+ void wakeUp() {
}
@Override
- Looper getServiceLooper() {
- return mTestLooper.getLooper();
+ PowerManager getPowerManager() {
+ return powerManager;
}
- };
- mHdmiCecLocalDeviceAudioSystem =
- new HdmiCecLocalDeviceAudioSystem(mHdmiControlService) {
@Override
- HdmiDeviceInfo getDeviceInfo() {
- return mDeviceInfoForTests;
+ AudioManager getAudioManager() {
+ return mAudioManager;
}
@Override
- void setArcStatus(boolean enabled) {
- // do nothing
+ boolean isAddressAllocated() {
+ return true;
}
@Override
- protected boolean isSystemAudioActivated() {
- return true;
+ Looper getServiceLooper() {
+ return mTestLooper.getLooper();
}
};
+ mHdmiCecLocalDeviceAudioSystem = new HdmiCecLocalDeviceAudioSystem(mHdmiControlService) {
+ @Override
+ protected void setPreferredAddress(int addr) {
+ }
+ };
+
mHdmiCecLocalDeviceAudioSystem.init();
Looper looper = mTestLooper.getLooper();
mHdmiControlService.setIoLooper(looper);
mNativeWrapper = new FakeNativeWrapper();
- mHdmiCecController =
- HdmiCecController.createWithNativeWrapper(this.mHdmiControlService, mNativeWrapper);
+ mHdmiCecController = HdmiCecController.createWithNativeWrapper(
+ this.mHdmiControlService, mNativeWrapper, mHdmiControlService.getAtomWriter());
mHdmiControlService.setCecController(mHdmiCecController);
mHdmiControlService.setHdmiMhlController(HdmiMhlControllerStub.create(mHdmiControlService));
mHdmiControlService.setMessageValidator(new HdmiCecMessageValidator(mHdmiControlService));
@@ -150,18 +133,88 @@ public class ArcInitiationActionFromAvrTest {
mTestLooper.dispatchAll();
}
- @Ignore("b/120845532")
@Test
- public void arcInitiation_requestActiveSource() {
- mSendCecCommandSuccess = true;
- mShouldDispatchARCInitiated = true;
- mRequestActiveSourceSent = false;
- mArcInitSent = false;
+ public void arcInitiation_initiated() {
+ mHdmiCecLocalDeviceAudioSystem.addAndStartAction(mAction);
+ mTestLooper.dispatchAll();
+ HdmiCecMessage initiateArc = HdmiCecMessageBuilder.buildInitiateArc(
+ Constants.ADDR_AUDIO_SYSTEM, Constants.ADDR_TV);
+
+ assertThat(mNativeWrapper.getResultMessages()).contains(initiateArc);
+
+ mHdmiControlService.sendCecCommand(
+ HdmiCecMessageBuilder.buildReportArcInitiated(
+ Constants.ADDR_TV,
+ Constants.ADDR_AUDIO_SYSTEM));
+ mTestLooper.dispatchAll();
+
+ assertThat(mHdmiCecLocalDeviceAudioSystem.isArcEnabled()).isTrue();
+ }
+ @Test
+ public void arcInitiation_sendFailed() {
+ mNativeWrapper.setMessageSendResult(Constants.MESSAGE_INITIATE_ARC, SendMessageResult.NACK);
mHdmiCecLocalDeviceAudioSystem.addAndStartAction(mAction);
mTestLooper.dispatchAll();
+ HdmiCecMessage initiateArc = HdmiCecMessageBuilder.buildInitiateArc(
+ Constants.ADDR_AUDIO_SYSTEM, Constants.ADDR_TV);
+
+ assertThat(mNativeWrapper.getResultMessages()).contains(initiateArc);
+
+ assertThat(mHdmiCecLocalDeviceAudioSystem.isArcEnabled()).isFalse();
+ }
- assertThat(mArcInitSent).isTrue();
- assertThat(mRequestActiveSourceSent).isTrue();
+ @Test
+ public void arcInitiation_terminated() {
+ mHdmiCecLocalDeviceAudioSystem.addAndStartAction(mAction);
+ mTestLooper.dispatchAll();
+
+ HdmiCecMessage initiateArc = HdmiCecMessageBuilder.buildInitiateArc(
+ Constants.ADDR_AUDIO_SYSTEM, Constants.ADDR_TV);
+
+ assertThat(mNativeWrapper.getResultMessages()).contains(initiateArc);
+
+ mHdmiControlService.handleCecCommand(HdmiCecMessageBuilder.buildReportArcTerminated(
+ Constants.ADDR_TV,
+ Constants.ADDR_AUDIO_SYSTEM));
+ mTestLooper.dispatchAll();
+
+ assertThat(mHdmiCecLocalDeviceAudioSystem.isArcEnabled()).isFalse();
+ }
+
+ @Test
+ public void arcInitiation_abort() {
+ mHdmiCecLocalDeviceAudioSystem.addAndStartAction(mAction);
+ mTestLooper.dispatchAll();
+
+ HdmiCecMessage initiateArc = HdmiCecMessageBuilder.buildInitiateArc(
+ Constants.ADDR_AUDIO_SYSTEM, Constants.ADDR_TV);
+
+ assertThat(mNativeWrapper.getResultMessages()).contains(initiateArc);
+
+ mHdmiControlService.handleCecCommand(
+ HdmiCecMessageBuilder.buildFeatureAbortCommand(
+ Constants.ADDR_TV,
+ Constants.ADDR_AUDIO_SYSTEM, Constants.MESSAGE_INITIATE_ARC,
+ Constants.ABORT_REFUSED));
+ mTestLooper.dispatchAll();
+
+ assertThat(mHdmiCecLocalDeviceAudioSystem.isArcEnabled()).isFalse();
+ }
+
+ //Fail
+ @Test
+ public void arcInitiation_timeout() {
+ mHdmiCecLocalDeviceAudioSystem.addAndStartAction(mAction);
+ mTestLooper.dispatchAll();
+
+ HdmiCecMessage initiateArc = HdmiCecMessageBuilder.buildInitiateArc(
+ Constants.ADDR_AUDIO_SYSTEM, Constants.ADDR_TV);
+
+ assertThat(mNativeWrapper.getResultMessages()).contains(initiateArc);
+
+ mTestLooper.moveTimeForward(1001);
+ mTestLooper.dispatchAll();
+ assertThat(mHdmiCecLocalDeviceAudioSystem.isArcEnabled()).isTrue();
}
}
diff --git a/services/tests/servicestests/src/com/android/server/hdmi/ArcTerminationActionFromAvrTest.java b/services/tests/servicestests/src/com/android/server/hdmi/ArcTerminationActionFromAvrTest.java
index dc326eefbc9b..4afbbf741102 100644
--- a/services/tests/servicestests/src/com/android/server/hdmi/ArcTerminationActionFromAvrTest.java
+++ b/services/tests/servicestests/src/com/android/server/hdmi/ArcTerminationActionFromAvrTest.java
@@ -15,13 +15,22 @@
*/
package com.android.server.hdmi;
+import static com.android.server.hdmi.HdmiControlService.INITIATED_BY_ENABLE_CEC;
+
import static com.google.common.truth.Truth.assertThat;
-import android.annotation.Nullable;
-import android.app.Instrumentation;
-import android.hardware.hdmi.HdmiDeviceInfo;
+import static org.mockito.Mockito.spy;
+import static org.mockito.Mockito.when;
+
+import android.content.Context;
+import android.content.ContextWrapper;
import android.hardware.tv.cec.V1_0.SendMessageResult;
+import android.media.AudioManager;
+import android.os.Handler;
+import android.os.IPowerManager;
+import android.os.IThermalService;
import android.os.Looper;
+import android.os.PowerManager;
import android.os.test.TestLooper;
import android.platform.test.annotations.Presubmit;
@@ -29,10 +38,13 @@ import androidx.test.InstrumentationRegistry;
import androidx.test.filters.SmallTest;
import org.junit.Before;
-import org.junit.Ignore;
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;
/** Tests for {@link ArcTerminationActionFromAvr} */
@SmallTest
@@ -40,45 +52,47 @@ import org.junit.runners.JUnit4;
@RunWith(JUnit4.class)
public class ArcTerminationActionFromAvrTest {
- private HdmiDeviceInfo mDeviceInfoForTests;
+ private Context mContextSpy;
private HdmiCecLocalDeviceAudioSystem mHdmiCecLocalDeviceAudioSystem;
private ArcTerminationActionFromAvr mAction;
+ private HdmiCecController mHdmiCecController;
+ private HdmiControlService mHdmiControlService;
+ private FakeNativeWrapper mNativeWrapper;
+
private TestLooper mTestLooper = new TestLooper();
- private boolean mSendCecCommandSuccess;
- private boolean mShouldDispatchReportArcTerminated;
- private Instrumentation mInstrumentation;
- @Nullable private Boolean mArcEnabled = null;
+ private ArrayList<HdmiCecLocalDevice> mLocalDevices = new ArrayList<>();
+
+ @Mock private IPowerManager mIPowerManagerMock;
+ @Mock private IThermalService mIThermalServiceMock;
+ @Mock private AudioManager mAudioManager;
@Before
- public void setUp() {
- mDeviceInfoForTests = new HdmiDeviceInfo(1000, 1);
+ public void setUp() throws Exception {
+ MockitoAnnotations.initMocks(this);
- mInstrumentation = InstrumentationRegistry.getInstrumentation();
+ mContextSpy = spy(new ContextWrapper(InstrumentationRegistry.getTargetContext()));
+
+ PowerManager powerManager = new PowerManager(mContextSpy, mIPowerManagerMock,
+ mIThermalServiceMock, new Handler(mTestLooper.getLooper()));
+ when(mContextSpy.getSystemService(Context.POWER_SERVICE)).thenReturn(powerManager);
+ when(mContextSpy.getSystemService(PowerManager.class)).thenReturn(powerManager);
+ when(mIPowerManagerMock.isInteractive()).thenReturn(true);
+
+ mHdmiControlService =
+ new HdmiControlService(mContextSpy) {
+ @Override
+ void wakeUp() {
+ }
- HdmiControlService hdmiControlService =
- new HdmiControlService(mInstrumentation.getTargetContext()) {
@Override
- void sendCecCommand(
- HdmiCecMessage command, @Nullable SendMessageCallback callback) {
- switch (command.getOpcode()) {
- case Constants.MESSAGE_TERMINATE_ARC:
- if (callback != null) {
- callback.onSendCompleted(
- mSendCecCommandSuccess
- ? SendMessageResult.SUCCESS
- : SendMessageResult.NACK);
- }
- if (mShouldDispatchReportArcTerminated) {
- mHdmiCecLocalDeviceAudioSystem.dispatchMessage(
- HdmiCecMessageBuilder.buildReportArcTerminated(
- Constants.ADDR_TV,
- mHdmiCecLocalDeviceAudioSystem.mAddress));
- }
- break;
- default:
- throw new IllegalArgumentException("Unexpected message");
- }
+ PowerManager getPowerManager() {
+ return powerManager;
+ }
+
+ @Override
+ AudioManager getAudioManager() {
+ return mAudioManager;
}
@Override
@@ -97,55 +111,71 @@ public class ArcTerminationActionFromAvrTest {
}
};
- mHdmiCecLocalDeviceAudioSystem =
- new HdmiCecLocalDeviceAudioSystem(hdmiControlService) {
- @Override
- HdmiDeviceInfo getDeviceInfo() {
- return mDeviceInfoForTests;
- }
-
- @Override
- void setArcStatus(boolean enabled) {
- mArcEnabled = enabled;
- }
- };
- mHdmiCecLocalDeviceAudioSystem.init();
Looper looper = mTestLooper.getLooper();
- hdmiControlService.setIoLooper(looper);
-
+ mHdmiControlService.setIoLooper(looper);
+ mNativeWrapper = new FakeNativeWrapper();
+ mHdmiCecController = HdmiCecController.createWithNativeWrapper(
+ this.mHdmiControlService, mNativeWrapper, mHdmiControlService.getAtomWriter());
+ mHdmiControlService.setCecController(mHdmiCecController);
+ mHdmiControlService.setHdmiMhlController(HdmiMhlControllerStub.create(mHdmiControlService));
+ mHdmiControlService.setMessageValidator(new HdmiCecMessageValidator(mHdmiControlService));
+ mHdmiControlService.initPortInfo();
+
+ mHdmiCecLocalDeviceAudioSystem = new HdmiCecLocalDeviceAudioSystem(mHdmiControlService) {
+ @Override
+ protected void setPreferredAddress(int addr) {
+ }
+ };
+ mHdmiCecLocalDeviceAudioSystem.init();
mAction = new ArcTerminationActionFromAvr(mHdmiCecLocalDeviceAudioSystem);
+
+ mLocalDevices.add(mHdmiCecLocalDeviceAudioSystem);
+ mHdmiControlService.allocateLogicalAddress(mLocalDevices, INITIATED_BY_ENABLE_CEC);
+ mHdmiCecLocalDeviceAudioSystem.setArcStatus(true);
+ mTestLooper.dispatchAll();
}
@Test
- @Ignore("b/120845532")
- public void testSendMessage_notSuccess() {
- mSendCecCommandSuccess = false;
- mShouldDispatchReportArcTerminated = false;
+ public void testSendMessage_sendFailed() {
+ mNativeWrapper.setMessageSendResult(Constants.MESSAGE_TERMINATE_ARC,
+ SendMessageResult.NACK);
mHdmiCecLocalDeviceAudioSystem.addAndStartAction(mAction);
-
mTestLooper.dispatchAll();
- assertThat(mArcEnabled).isNull();
+ HdmiCecMessage terminateArc = HdmiCecMessageBuilder.buildTerminateArc(
+ Constants.ADDR_AUDIO_SYSTEM, Constants.ADDR_TV);
+
+ assertThat(mNativeWrapper.getResultMessages()).contains(terminateArc);
+
+ assertThat(mHdmiCecLocalDeviceAudioSystem.isArcEnabled()).isFalse();
}
@Test
- public void testReportArcTerminated_notReceived() {
- mSendCecCommandSuccess = true;
- mShouldDispatchReportArcTerminated = false;
+ public void testReportArcTerminated_timeout() {
mHdmiCecLocalDeviceAudioSystem.addAndStartAction(mAction);
-
- mTestLooper.moveTimeForward(1000);
mTestLooper.dispatchAll();
- assertThat(mArcEnabled).isNull();
+ HdmiCecMessage terminateArc = HdmiCecMessageBuilder.buildTerminateArc(
+ Constants.ADDR_AUDIO_SYSTEM, Constants.ADDR_TV);
+
+ assertThat(mNativeWrapper.getResultMessages()).contains(terminateArc);
+
+ assertThat(mHdmiCecLocalDeviceAudioSystem.isArcEnabled()).isTrue();
}
@Test
public void testReportArcTerminated_received() {
- mSendCecCommandSuccess = true;
- mShouldDispatchReportArcTerminated = true;
mHdmiCecLocalDeviceAudioSystem.addAndStartAction(mAction);
+ mTestLooper.dispatchAll();
+ HdmiCecMessage terminateArc = HdmiCecMessageBuilder.buildTerminateArc(
+ Constants.ADDR_AUDIO_SYSTEM, Constants.ADDR_TV);
+
+ assertThat(mNativeWrapper.getResultMessages()).contains(terminateArc);
- mTestLooper.moveTimeForward(1000);
+ HdmiCecMessage arcTerminatedResponse = HdmiCecMessageBuilder.buildReportArcTerminated(
+ Constants.ADDR_TV, Constants.ADDR_AUDIO_SYSTEM);
+
+ mHdmiControlService.handleCecCommand(arcTerminatedResponse);
mTestLooper.dispatchAll();
- assertThat(mArcEnabled).isFalse();
+
+ assertThat(mHdmiCecLocalDeviceAudioSystem.isArcEnabled()).isFalse();
}
}
diff --git a/services/tests/servicestests/src/com/android/server/hdmi/FakeNativeWrapper.java b/services/tests/servicestests/src/com/android/server/hdmi/FakeNativeWrapper.java
index 7538468fbe31..01f0a3d398df 100644
--- a/services/tests/servicestests/src/com/android/server/hdmi/FakeNativeWrapper.java
+++ b/services/tests/servicestests/src/com/android/server/hdmi/FakeNativeWrapper.java
@@ -24,6 +24,7 @@ import com.android.server.hdmi.HdmiCecController.NativeWrapper;
import com.google.common.collect.Iterables;
import java.util.ArrayList;
+import java.util.HashMap;
import java.util.List;
/** Fake {@link NativeWrapper} useful for testing. */
@@ -48,6 +49,7 @@ final class FakeNativeWrapper implements NativeWrapper {
};
private final List<HdmiCecMessage> mResultMessages = new ArrayList<>();
+ private final HashMap<Integer, Integer> mMessageSendResult = new HashMap<>();
private int mMyPhysicalAddress = 0;
private HdmiPortInfo[] mHdmiPortInfo = null;
@@ -65,9 +67,10 @@ final class FakeNativeWrapper implements NativeWrapper {
if (body.length == 0) {
return mPollAddressResponse[dstAddress];
} else {
- mResultMessages.add(HdmiCecMessageBuilder.of(srcAddress, dstAddress, body));
+ HdmiCecMessage message = HdmiCecMessageBuilder.of(srcAddress, dstAddress, body);
+ mResultMessages.add(message);
+ return mMessageSendResult.getOrDefault(message.getOpcode(), SendMessageResult.SUCCESS);
}
- return SendMessageResult.SUCCESS;
}
@Override
@@ -132,6 +135,10 @@ final class FakeNativeWrapper implements NativeWrapper {
mPollAddressResponse[logicalAddress] = response;
}
+ public void setMessageSendResult(int opcode, int result) {
+ mMessageSendResult.put(opcode, result);
+ }
+
@VisibleForTesting
protected void setPhysicalAddress(int physicalAddress) {
mMyPhysicalAddress = physicalAddress;
diff --git a/services/tests/servicestests/src/com/android/server/hdmi/HdmiCecControllerTest.java b/services/tests/servicestests/src/com/android/server/hdmi/HdmiCecControllerTest.java
index 55c925f7f7aa..c60d5fb95846 100644
--- a/services/tests/servicestests/src/com/android/server/hdmi/HdmiCecControllerTest.java
+++ b/services/tests/servicestests/src/com/android/server/hdmi/HdmiCecControllerTest.java
@@ -89,20 +89,20 @@ public class HdmiCecControllerTest {
mMyLooper = mTestLooper.getLooper();
mHdmiControlService = new MyHdmiControlService(InstrumentationRegistry.getTargetContext());
mNativeWrapper = new FakeNativeWrapper();
- mHdmiCecController =
- HdmiCecController.createWithNativeWrapper(mHdmiControlService, mNativeWrapper);
+ mHdmiCecController = HdmiCecController.createWithNativeWrapper(
+ mHdmiControlService, mNativeWrapper, mHdmiControlService.getAtomWriter());
}
/** Tests for {@link HdmiCecController#allocateLogicalAddress} */
@Test
- public void testAllocatLogicalAddress_TvDevicePreferredNotOcupied() {
+ public void testAllocateLogicalAddress_TvDevicePreferredNotOccupied() {
mHdmiCecController.allocateLogicalAddress(DEVICE_TV, ADDR_TV, mCallback);
mTestLooper.dispatchAll();
assertEquals(ADDR_TV, mLogicalAddress);
}
@Test
- public void testAllocatLogicalAddress_TvDeviceNonPreferredNotOcupied() {
+ public void testAllocateLogicalAddress_TvDeviceNonPreferredNotOccupied() {
mHdmiCecController.allocateLogicalAddress(DEVICE_TV, ADDR_UNREGISTERED, mCallback);
mTestLooper.dispatchAll();
@@ -110,7 +110,7 @@ public class HdmiCecControllerTest {
}
@Test
- public void testAllocatLogicalAddress_TvDeviceNonPreferredFirstOcupied() {
+ public void testAllocateLogicalAddress_TvDeviceNonPreferredFirstOccupied() {
mNativeWrapper.setPollAddressResponse(ADDR_TV, SendMessageResult.SUCCESS);
mHdmiCecController.allocateLogicalAddress(DEVICE_TV, ADDR_UNREGISTERED, mCallback);
mTestLooper.dispatchAll();
@@ -118,7 +118,7 @@ public class HdmiCecControllerTest {
}
@Test
- public void testAllocatLogicalAddress_TvDeviceNonPreferredAllOcupied() {
+ public void testAllocateLogicalAddress_TvDeviceNonPreferredAllOccupied() {
mNativeWrapper.setPollAddressResponse(ADDR_TV, SendMessageResult.SUCCESS);
mNativeWrapper.setPollAddressResponse(ADDR_SPECIFIC_USE, SendMessageResult.SUCCESS);
mHdmiCecController.allocateLogicalAddress(DEVICE_TV, ADDR_UNREGISTERED, mCallback);
@@ -127,7 +127,7 @@ public class HdmiCecControllerTest {
}
@Test
- public void testAllocatLogicalAddress_AudioSystemNonPreferredNotOcupied() {
+ public void testAllocateLogicalAddress_AudioSystemNonPreferredNotOccupied() {
mHdmiCecController.allocateLogicalAddress(
DEVICE_AUDIO_SYSTEM, ADDR_UNREGISTERED, mCallback);
mTestLooper.dispatchAll();
@@ -135,7 +135,7 @@ public class HdmiCecControllerTest {
}
@Test
- public void testAllocatLogicalAddress_AudioSystemNonPreferredAllOcupied() {
+ public void testAllocateLogicalAddress_AudioSystemNonPreferredAllOccupied() {
mNativeWrapper.setPollAddressResponse(ADDR_AUDIO_SYSTEM, SendMessageResult.SUCCESS);
mHdmiCecController.allocateLogicalAddress(
DEVICE_AUDIO_SYSTEM, ADDR_UNREGISTERED, mCallback);
@@ -144,14 +144,14 @@ public class HdmiCecControllerTest {
}
@Test
- public void testAllocatLogicalAddress_PlaybackPreferredNotOccupied() {
+ public void testAllocateLogicalAddress_PlaybackPreferredNotOccupied() {
mHdmiCecController.allocateLogicalAddress(DEVICE_PLAYBACK, ADDR_PLAYBACK_1, mCallback);
mTestLooper.dispatchAll();
assertEquals(ADDR_PLAYBACK_1, mLogicalAddress);
}
@Test
- public void testAllocatLogicalAddress_PlaybackPreferredOcuppied() {
+ public void testAllocateLogicalAddress_PlaybackPreferredOccupied() {
mNativeWrapper.setPollAddressResponse(ADDR_PLAYBACK_1, SendMessageResult.SUCCESS);
mHdmiCecController.allocateLogicalAddress(DEVICE_PLAYBACK, ADDR_PLAYBACK_1, mCallback);
mTestLooper.dispatchAll();
@@ -159,14 +159,14 @@ public class HdmiCecControllerTest {
}
@Test
- public void testAllocatLogicalAddress_PlaybackNoPreferredNotOcuppied() {
+ public void testAllocateLogicalAddress_PlaybackNoPreferredNotOccupied() {
mHdmiCecController.allocateLogicalAddress(DEVICE_PLAYBACK, ADDR_UNREGISTERED, mCallback);
mTestLooper.dispatchAll();
assertEquals(ADDR_PLAYBACK_1, mLogicalAddress);
}
@Test
- public void testAllocatLogicalAddress_PlaybackNoPreferredFirstOcuppied() {
+ public void testAllocateLogicalAddress_PlaybackNoPreferredFirstOccupied() {
mNativeWrapper.setPollAddressResponse(ADDR_PLAYBACK_1, SendMessageResult.SUCCESS);
mHdmiCecController.allocateLogicalAddress(DEVICE_PLAYBACK, ADDR_UNREGISTERED, mCallback);
mTestLooper.dispatchAll();
@@ -174,7 +174,7 @@ public class HdmiCecControllerTest {
}
@Test
- public void testAllocatLogicalAddress_PlaybackNonPreferredFirstTwoOcuppied() {
+ public void testAllocateLogicalAddress_PlaybackNonPreferredFirstTwoOccupied() {
mNativeWrapper.setPollAddressResponse(ADDR_PLAYBACK_1, SendMessageResult.SUCCESS);
mNativeWrapper.setPollAddressResponse(ADDR_PLAYBACK_2, SendMessageResult.SUCCESS);
mHdmiCecController.allocateLogicalAddress(DEVICE_PLAYBACK, ADDR_UNREGISTERED, mCallback);
@@ -183,7 +183,7 @@ public class HdmiCecControllerTest {
}
@Test
- public void testAllocatLogicalAddress_PlaybackNonPreferredAllOcupied() {
+ public void testAllocateLogicalAddress_PlaybackNonPreferredAllOccupied() {
mNativeWrapper.setPollAddressResponse(ADDR_PLAYBACK_1, SendMessageResult.SUCCESS);
mNativeWrapper.setPollAddressResponse(ADDR_PLAYBACK_2, SendMessageResult.SUCCESS);
mNativeWrapper.setPollAddressResponse(ADDR_PLAYBACK_3, SendMessageResult.SUCCESS);
diff --git a/services/tests/servicestests/src/com/android/server/hdmi/HdmiCecLocalDeviceAudioSystemTest.java b/services/tests/servicestests/src/com/android/server/hdmi/HdmiCecLocalDeviceAudioSystemTest.java
index 953a03c2d9e3..415ae0731edb 100644
--- a/services/tests/servicestests/src/com/android/server/hdmi/HdmiCecLocalDeviceAudioSystemTest.java
+++ b/services/tests/servicestests/src/com/android/server/hdmi/HdmiCecLocalDeviceAudioSystemTest.java
@@ -188,8 +188,8 @@ public class HdmiCecLocalDeviceAudioSystemTest {
mHdmiControlService.setIoLooper(mMyLooper);
mNativeWrapper = new FakeNativeWrapper();
mNativeWrapper.setPhysicalAddress(SELF_PHYSICAL_ADDRESS);
- mHdmiCecController =
- HdmiCecController.createWithNativeWrapper(mHdmiControlService, mNativeWrapper);
+ mHdmiCecController = HdmiCecController.createWithNativeWrapper(
+ mHdmiControlService, mNativeWrapper, mHdmiControlService.getAtomWriter());
mHdmiControlService.setCecController(mHdmiCecController);
mHdmiControlService.setHdmiMhlController(HdmiMhlControllerStub.create(mHdmiControlService));
mHdmiControlService.setMessageValidator(new HdmiCecMessageValidator(mHdmiControlService));
diff --git a/services/tests/servicestests/src/com/android/server/hdmi/HdmiCecLocalDevicePlaybackTest.java b/services/tests/servicestests/src/com/android/server/hdmi/HdmiCecLocalDevicePlaybackTest.java
index 0c35797b38ea..d160a3fab186 100644
--- a/services/tests/servicestests/src/com/android/server/hdmi/HdmiCecLocalDevicePlaybackTest.java
+++ b/services/tests/servicestests/src/com/android/server/hdmi/HdmiCecLocalDevicePlaybackTest.java
@@ -119,8 +119,8 @@ public class HdmiCecLocalDevicePlaybackTest {
mHdmiCecLocalDevicePlayback.init();
mHdmiControlService.setIoLooper(mMyLooper);
mNativeWrapper = new FakeNativeWrapper();
- mHdmiCecController =
- HdmiCecController.createWithNativeWrapper(mHdmiControlService, mNativeWrapper);
+ mHdmiCecController = HdmiCecController.createWithNativeWrapper(
+ mHdmiControlService, mNativeWrapper, mHdmiControlService.getAtomWriter());
mHdmiControlService.setCecController(mHdmiCecController);
mHdmiControlService.setHdmiMhlController(HdmiMhlControllerStub.create(mHdmiControlService));
mHdmiControlService.setMessageValidator(new HdmiCecMessageValidator(mHdmiControlService));
diff --git a/services/tests/servicestests/src/com/android/server/hdmi/HdmiCecLocalDeviceTest.java b/services/tests/servicestests/src/com/android/server/hdmi/HdmiCecLocalDeviceTest.java
index 526c1d07296f..e54f2c9e6b2e 100644
--- a/services/tests/servicestests/src/com/android/server/hdmi/HdmiCecLocalDeviceTest.java
+++ b/services/tests/servicestests/src/com/android/server/hdmi/HdmiCecLocalDeviceTest.java
@@ -117,9 +117,8 @@ public class HdmiCecLocalDeviceTest {
}
};
mHdmiControlService.setIoLooper(mTestLooper.getLooper());
- mHdmiCecController =
- HdmiCecController.createWithNativeWrapper(
- mHdmiControlService, new FakeNativeWrapper());
+ mHdmiCecController = HdmiCecController.createWithNativeWrapper(
+ mHdmiControlService, new FakeNativeWrapper(), mHdmiControlService.getAtomWriter());
mHdmiControlService.setCecController(mHdmiCecController);
mHdmiLocalDevice = new MyHdmiCecLocalDevice(mHdmiControlService, DEVICE_TV);
mMessageValidator =
diff --git a/services/tests/servicestests/src/com/android/server/hdmi/HdmiCecLocalDeviceTvTest.java b/services/tests/servicestests/src/com/android/server/hdmi/HdmiCecLocalDeviceTvTest.java
index b88573ae168a..ce1cdf369076 100644
--- a/services/tests/servicestests/src/com/android/server/hdmi/HdmiCecLocalDeviceTvTest.java
+++ b/services/tests/servicestests/src/com/android/server/hdmi/HdmiCecLocalDeviceTvTest.java
@@ -97,8 +97,8 @@ public class HdmiCecLocalDeviceTvTest {
mHdmiCecLocalDeviceTv.init();
mHdmiControlService.setIoLooper(mMyLooper);
mNativeWrapper = new FakeNativeWrapper();
- mHdmiCecController =
- HdmiCecController.createWithNativeWrapper(mHdmiControlService, mNativeWrapper);
+ mHdmiCecController = HdmiCecController.createWithNativeWrapper(
+ mHdmiControlService, mNativeWrapper, mHdmiControlService.getAtomWriter());
mHdmiControlService.setCecController(mHdmiCecController);
mHdmiControlService.setHdmiMhlController(HdmiMhlControllerStub.create(mHdmiControlService));
mHdmiControlService.setMessageValidator(new HdmiCecMessageValidator(mHdmiControlService));
diff --git a/services/tests/servicestests/src/com/android/server/hdmi/HdmiControlServiceBinderAPITest.java b/services/tests/servicestests/src/com/android/server/hdmi/HdmiControlServiceBinderAPITest.java
index c6cf9b116a1d..7560a34e63cc 100644
--- a/services/tests/servicestests/src/com/android/server/hdmi/HdmiControlServiceBinderAPITest.java
+++ b/services/tests/servicestests/src/com/android/server/hdmi/HdmiControlServiceBinderAPITest.java
@@ -15,28 +15,28 @@
*/
package com.android.server.hdmi;
-import static android.os.SystemClock.sleep;
import static com.android.server.hdmi.HdmiControlService.INITIATED_BY_ENABLE_CEC;
+
import static com.google.common.truth.Truth.assertThat;
+
import static junit.framework.Assert.assertEquals;
import android.hardware.hdmi.HdmiControlManager;
import android.hardware.hdmi.HdmiPortInfo;
import android.hardware.hdmi.IHdmiControlCallback;
import android.os.Looper;
-import android.os.SystemProperties;
import android.os.test.TestLooper;
-import android.util.Slog;
import androidx.test.InstrumentationRegistry;
import androidx.test.filters.SmallTest;
-import java.util.ArrayList;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.junit.runners.JUnit4;
+import java.util.ArrayList;
+
/**
* Tests for {@link HdmiControlServiceBinderAPITest} class.
*/
@@ -158,8 +158,8 @@ public class HdmiControlServiceBinderAPITest {
mHdmiControlService.setIoLooper(mMyLooper);
mNativeWrapper = new FakeNativeWrapper();
- mHdmiCecController =
- HdmiCecController.createWithNativeWrapper(mHdmiControlService, mNativeWrapper);
+ mHdmiCecController = HdmiCecController.createWithNativeWrapper(
+ mHdmiControlService, mNativeWrapper, mHdmiControlService.getAtomWriter());
mHdmiControlService.setCecController(mHdmiCecController);
mHdmiControlService.setHdmiMhlController(HdmiMhlControllerStub.create(mHdmiControlService));
mHdmiControlService.setMessageValidator(new HdmiCecMessageValidator(mHdmiControlService));
diff --git a/services/tests/servicestests/src/com/android/server/hdmi/HdmiControlServiceTest.java b/services/tests/servicestests/src/com/android/server/hdmi/HdmiControlServiceTest.java
index 31cf59ee7bde..4849dd417041 100644
--- a/services/tests/servicestests/src/com/android/server/hdmi/HdmiControlServiceTest.java
+++ b/services/tests/servicestests/src/com/android/server/hdmi/HdmiControlServiceTest.java
@@ -158,8 +158,8 @@ public class HdmiControlServiceTest {
mHdmiControlService.setIoLooper(mMyLooper);
mNativeWrapper = new FakeNativeWrapper();
- mHdmiCecController =
- HdmiCecController.createWithNativeWrapper(mHdmiControlService, mNativeWrapper);
+ mHdmiCecController = HdmiCecController.createWithNativeWrapper(
+ mHdmiControlService, mNativeWrapper, mHdmiControlService.getAtomWriter());
mHdmiControlService.setCecController(mHdmiCecController);
mHdmiControlService.setHdmiMhlController(HdmiMhlControllerStub.create(mHdmiControlService));
mHdmiControlService.setMessageValidator(new HdmiCecMessageValidator(mHdmiControlService));
diff --git a/services/tests/servicestests/src/com/android/server/location/timezone/ControllerImplTest.java b/services/tests/servicestests/src/com/android/server/location/timezone/ControllerImplTest.java
new file mode 100644
index 000000000000..dbaad6633e98
--- /dev/null
+++ b/services/tests/servicestests/src/com/android/server/location/timezone/ControllerImplTest.java
@@ -0,0 +1,528 @@
+/*
+ * 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.location.timezone;
+
+import static android.location.timezone.LocationTimeZoneEvent.EVENT_TYPE_SUCCESS;
+import static android.location.timezone.LocationTimeZoneEvent.EVENT_TYPE_UNCERTAIN;
+
+import static com.android.server.location.timezone.ControllerImpl.UNCERTAINTY_DELAY;
+import static com.android.server.location.timezone.LocationTimeZoneProvider.ProviderState.PROVIDER_STATE_DISABLED;
+import static com.android.server.location.timezone.LocationTimeZoneProvider.ProviderState.PROVIDER_STATE_ENABLED;
+import static com.android.server.location.timezone.TestSupport.USER1_CONFIG_GEO_DETECTION_DISABLED;
+import static com.android.server.location.timezone.TestSupport.USER1_CONFIG_GEO_DETECTION_ENABLED;
+import static com.android.server.location.timezone.TestSupport.USER1_ID;
+import static com.android.server.location.timezone.TestSupport.USER2_CONFIG_GEO_DETECTION_ENABLED;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertTrue;
+import static org.junit.Assert.fail;
+
+import static java.util.Arrays.asList;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.annotation.UserIdInt;
+import android.location.timezone.LocationTimeZoneEvent;
+import android.os.UserHandle;
+import android.platform.test.annotations.Presubmit;
+import android.util.IndentingPrintWriter;
+
+import com.android.server.timezonedetector.ConfigurationInternal;
+import com.android.server.timezonedetector.GeolocationTimeZoneSuggestion;
+import com.android.server.timezonedetector.TestState;
+
+import org.junit.Before;
+import org.junit.Test;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.List;
+import java.util.Objects;
+
+/** Tests for {@link ControllerImpl}. */
+@Presubmit
+public class ControllerImplTest {
+
+ private static final long ARBITRARY_TIME = 12345L;
+
+ private static final LocationTimeZoneEvent USER1_SUCCESS_LOCATION_TIME_ZONE_EVENT1 =
+ createLocationTimeZoneEvent(USER1_ID, EVENT_TYPE_SUCCESS, asList("Europe/London"));
+ private static final LocationTimeZoneEvent USER1_SUCCESS_LOCATION_TIME_ZONE_EVENT2 =
+ createLocationTimeZoneEvent(USER1_ID, EVENT_TYPE_SUCCESS, asList("Europe/Paris"));
+ private static final LocationTimeZoneEvent USER1_UNCERTAIN_LOCATION_TIME_ZONE_EVENT =
+ createLocationTimeZoneEvent(USER1_ID, EVENT_TYPE_UNCERTAIN, null);
+
+ private TestThreadingDomain mTestThreadingDomain;
+ private TestCallback mTestCallback;
+ private TestLocationTimeZoneProvider mTestLocationTimeZoneProvider;
+
+ @Before
+ public void setUp() {
+ // For simplicity, the TestThreadingDomain uses the test's main thread. To execute posted
+ // runnables, the test must call methods on mTestThreadingDomain otherwise those runnables
+ // will never get a chance to execute.
+ mTestThreadingDomain = new TestThreadingDomain();
+ mTestCallback = new TestCallback(mTestThreadingDomain);
+ mTestLocationTimeZoneProvider =
+ new TestLocationTimeZoneProvider(mTestThreadingDomain, "primary");
+ }
+
+ @Test
+ public void initialState_enabled() {
+ ControllerImpl controllerImpl =
+ new ControllerImpl(mTestThreadingDomain, mTestLocationTimeZoneProvider);
+ TestEnvironment testEnvironment = new TestEnvironment(
+ mTestThreadingDomain, controllerImpl, USER1_CONFIG_GEO_DETECTION_ENABLED);
+ controllerImpl.initialize(testEnvironment, mTestCallback);
+
+ mTestLocationTimeZoneProvider.assertInitialized();
+
+ mTestLocationTimeZoneProvider.assertIsEnabled(USER1_CONFIG_GEO_DETECTION_ENABLED);
+ mTestThreadingDomain.assertNextQueueItemIsDelayed(UNCERTAINTY_DELAY);
+ mTestCallback.assertNoSuggestionMade();
+ }
+
+ @Test
+ public void initialState_disabled() {
+ ControllerImpl controllerImpl =
+ new ControllerImpl(mTestThreadingDomain, mTestLocationTimeZoneProvider);
+ TestEnvironment testEnvironment = new TestEnvironment(
+ mTestThreadingDomain, controllerImpl, USER1_CONFIG_GEO_DETECTION_DISABLED);
+ controllerImpl.initialize(testEnvironment, mTestCallback);
+
+ mTestLocationTimeZoneProvider.assertInitialized();
+
+ mTestLocationTimeZoneProvider.assertIsDisabled();
+ mTestThreadingDomain.assertQueueEmpty();
+ mTestCallback.assertNoSuggestionMade();
+ }
+
+ @Test
+ public void enabled_uncertaintySuggestionSentIfNoEventReceived() {
+ ControllerImpl controllerImpl =
+ new ControllerImpl(mTestThreadingDomain, mTestLocationTimeZoneProvider);
+ TestEnvironment testEnvironment = new TestEnvironment(
+ mTestThreadingDomain, controllerImpl, USER1_CONFIG_GEO_DETECTION_ENABLED);
+ controllerImpl.initialize(testEnvironment, mTestCallback);
+
+ mTestLocationTimeZoneProvider.assertIsEnabled(USER1_CONFIG_GEO_DETECTION_ENABLED);
+ mTestCallback.assertNoSuggestionMade();
+ mTestThreadingDomain.assertSingleDelayedQueueItem(UNCERTAINTY_DELAY);
+
+ // Simulate time passing with no event being received.
+ mTestThreadingDomain.executeNext();
+
+ mTestLocationTimeZoneProvider.assertIsEnabled(USER1_CONFIG_GEO_DETECTION_ENABLED);
+ mTestCallback.assertUncertainSuggestionMadeAndCommit();
+ mTestThreadingDomain.assertQueueEmpty();
+ }
+
+ @Test
+ public void enabled_uncertaintySuggestionCancelledIfEventReceived() {
+ ControllerImpl controllerImpl =
+ new ControllerImpl(mTestThreadingDomain, mTestLocationTimeZoneProvider);
+ TestEnvironment testEnvironment = new TestEnvironment(
+ mTestThreadingDomain, controllerImpl, USER1_CONFIG_GEO_DETECTION_ENABLED);
+ controllerImpl.initialize(testEnvironment, mTestCallback);
+
+ mTestLocationTimeZoneProvider.assertIsEnabled(USER1_CONFIG_GEO_DETECTION_ENABLED);
+ mTestThreadingDomain.assertSingleDelayedQueueItem(UNCERTAINTY_DELAY);
+ mTestCallback.assertNoSuggestionMade();
+
+ // Simulate a location event being received by the provider. This should cause a suggestion
+ // to be made, and the timeout to be cleared.
+ mTestLocationTimeZoneProvider.simulateLocationTimeZoneEvent(
+ USER1_SUCCESS_LOCATION_TIME_ZONE_EVENT1);
+
+ mTestLocationTimeZoneProvider.assertIsEnabled(USER1_CONFIG_GEO_DETECTION_ENABLED);
+ mTestThreadingDomain.assertQueueEmpty();
+ mTestCallback.assertSuggestionMadeAndCommit(
+ USER1_SUCCESS_LOCATION_TIME_ZONE_EVENT1.getTimeZoneIds());
+ }
+
+ @Test
+ public void enabled_repeatedCertainty() {
+ ControllerImpl controllerImpl =
+ new ControllerImpl(mTestThreadingDomain, mTestLocationTimeZoneProvider);
+ TestEnvironment testEnvironment = new TestEnvironment(
+ mTestThreadingDomain, controllerImpl, USER1_CONFIG_GEO_DETECTION_ENABLED);
+ controllerImpl.initialize(testEnvironment, mTestCallback);
+
+ mTestLocationTimeZoneProvider.assertIsEnabled(USER1_CONFIG_GEO_DETECTION_ENABLED);
+ mTestThreadingDomain.assertSingleDelayedQueueItem(UNCERTAINTY_DELAY);
+ mTestCallback.assertNoSuggestionMade();
+
+ // Simulate a location event being received by the provider. This should cause a suggestion
+ // to be made, and the timeout to be cleared.
+ mTestLocationTimeZoneProvider.simulateLocationTimeZoneEvent(
+ USER1_SUCCESS_LOCATION_TIME_ZONE_EVENT1);
+
+ mTestLocationTimeZoneProvider.assertIsEnabled(USER1_CONFIG_GEO_DETECTION_ENABLED);
+ mTestThreadingDomain.assertQueueEmpty();
+ mTestCallback.assertSuggestionMadeAndCommit(
+ USER1_SUCCESS_LOCATION_TIME_ZONE_EVENT1.getTimeZoneIds());
+
+ // A second, identical event should not cause another suggestion.
+ mTestLocationTimeZoneProvider.simulateLocationTimeZoneEvent(
+ USER1_SUCCESS_LOCATION_TIME_ZONE_EVENT1);
+
+ mTestLocationTimeZoneProvider.assertIsEnabled(USER1_CONFIG_GEO_DETECTION_ENABLED);
+ mTestThreadingDomain.assertQueueEmpty();
+ mTestCallback.assertNoSuggestionMade();
+
+ // And a third, different event should cause another suggestion.
+ mTestLocationTimeZoneProvider.simulateLocationTimeZoneEvent(
+ USER1_SUCCESS_LOCATION_TIME_ZONE_EVENT2);
+
+ mTestLocationTimeZoneProvider.assertIsEnabled(USER1_CONFIG_GEO_DETECTION_ENABLED);
+ mTestThreadingDomain.assertQueueEmpty();
+ mTestCallback.assertSuggestionMadeAndCommit(
+ USER1_SUCCESS_LOCATION_TIME_ZONE_EVENT2.getTimeZoneIds());
+ }
+
+ @Test
+ public void enabled_briefUncertaintyTriggersNoSuggestion() {
+ ControllerImpl controllerImpl =
+ new ControllerImpl(mTestThreadingDomain, mTestLocationTimeZoneProvider);
+ TestEnvironment testEnvironment = new TestEnvironment(
+ mTestThreadingDomain, controllerImpl, USER1_CONFIG_GEO_DETECTION_ENABLED);
+ controllerImpl.initialize(testEnvironment, mTestCallback);
+
+ mTestLocationTimeZoneProvider.assertIsEnabled(USER1_CONFIG_GEO_DETECTION_ENABLED);
+ mTestThreadingDomain.assertSingleDelayedQueueItem(UNCERTAINTY_DELAY);
+ mTestCallback.assertNoSuggestionMade();
+
+ // Simulate a location event being received by the provider. This should cause a suggestion
+ // to be made, and the timeout to be cleared.
+ mTestLocationTimeZoneProvider.simulateLocationTimeZoneEvent(
+ USER1_SUCCESS_LOCATION_TIME_ZONE_EVENT1);
+
+ mTestLocationTimeZoneProvider.assertIsEnabled(USER1_CONFIG_GEO_DETECTION_ENABLED);
+ mTestThreadingDomain.assertQueueEmpty();
+ mTestCallback.assertSuggestionMadeAndCommit(
+ USER1_SUCCESS_LOCATION_TIME_ZONE_EVENT1.getTimeZoneIds());
+
+ // Uncertainty should cause
+ mTestLocationTimeZoneProvider.simulateLocationTimeZoneEvent(
+ USER1_UNCERTAIN_LOCATION_TIME_ZONE_EVENT);
+
+ mTestLocationTimeZoneProvider.assertIsEnabled(USER1_CONFIG_GEO_DETECTION_ENABLED);
+ mTestThreadingDomain.assertSingleDelayedQueueItem(UNCERTAINTY_DELAY);
+ mTestCallback.assertNoSuggestionMade();
+
+ // And a third event should cause yet another suggestion.
+ mTestLocationTimeZoneProvider.simulateLocationTimeZoneEvent(
+ USER1_SUCCESS_LOCATION_TIME_ZONE_EVENT2);
+
+ mTestLocationTimeZoneProvider.assertIsEnabled(USER1_CONFIG_GEO_DETECTION_ENABLED);
+ mTestThreadingDomain.assertQueueEmpty();
+ mTestCallback.assertSuggestionMadeAndCommit(
+ USER1_SUCCESS_LOCATION_TIME_ZONE_EVENT2.getTimeZoneIds());
+ }
+
+ @Test
+ public void configChanges_enableAndDisable() {
+ ControllerImpl controllerImpl =
+ new ControllerImpl(mTestThreadingDomain, mTestLocationTimeZoneProvider);
+ TestEnvironment testEnvironment = new TestEnvironment(
+ mTestThreadingDomain, controllerImpl, USER1_CONFIG_GEO_DETECTION_DISABLED);
+ controllerImpl.initialize(testEnvironment, mTestCallback);
+
+ mTestLocationTimeZoneProvider.assertIsDisabled();
+ mTestThreadingDomain.assertQueueEmpty();
+ mTestCallback.assertNoSuggestionMade();
+
+ // Now signal a config change so that geo detection is enabled.
+ testEnvironment.simulateConfigChange(USER1_CONFIG_GEO_DETECTION_ENABLED);
+
+ mTestLocationTimeZoneProvider.assertIsEnabled(USER1_CONFIG_GEO_DETECTION_ENABLED);
+ mTestThreadingDomain.assertNextQueueItemIsDelayed(UNCERTAINTY_DELAY);
+ mTestCallback.assertNoSuggestionMade();
+
+ // Now signal a config change so that geo detection is disabled.
+ testEnvironment.simulateConfigChange(USER1_CONFIG_GEO_DETECTION_DISABLED);
+
+ mTestLocationTimeZoneProvider.assertIsDisabled();
+ mTestThreadingDomain.assertQueueEmpty();
+ mTestCallback.assertNoSuggestionMade();
+ }
+
+ @Test
+ public void configChanges_disableWithPreviousSuggestion() {
+ ControllerImpl controllerImpl =
+ new ControllerImpl(mTestThreadingDomain, mTestLocationTimeZoneProvider);
+ TestEnvironment testEnvironment = new TestEnvironment(
+ mTestThreadingDomain, controllerImpl, USER1_CONFIG_GEO_DETECTION_ENABLED);
+ controllerImpl.initialize(testEnvironment, mTestCallback);
+
+ mTestLocationTimeZoneProvider.assertIsEnabled(USER1_CONFIG_GEO_DETECTION_ENABLED);
+ mTestThreadingDomain.assertSingleDelayedQueueItem(UNCERTAINTY_DELAY);
+ mTestCallback.assertNoSuggestionMade();
+
+ // Simulate a location event being received by the provider. This should cause a suggestion
+ // to be made, and the timeout to be cleared.
+ mTestLocationTimeZoneProvider.simulateLocationTimeZoneEvent(
+ USER1_SUCCESS_LOCATION_TIME_ZONE_EVENT1);
+
+ mTestLocationTimeZoneProvider.assertIsEnabled(USER1_CONFIG_GEO_DETECTION_ENABLED);
+ mTestThreadingDomain.assertQueueEmpty();
+ mTestCallback.assertSuggestionMadeAndCommit(
+ USER1_SUCCESS_LOCATION_TIME_ZONE_EVENT1.getTimeZoneIds());
+
+ // Simulate the user disabling the provider.
+ testEnvironment.simulateConfigChange(USER1_CONFIG_GEO_DETECTION_DISABLED);
+
+ // Because there had been a previous suggestion, the controller should withdraw it
+ // immediately to let the downstream components know that the provider can no longer be sure
+ // of the time zone.
+ mTestLocationTimeZoneProvider.assertIsDisabled();
+ mTestThreadingDomain.assertQueueEmpty();
+ mTestCallback.assertSuggestionMadeAndCommit(null);
+ }
+
+ @Test
+ public void configChanges_userSwitch_enabledToEnabled() {
+ ControllerImpl controllerImpl =
+ new ControllerImpl(mTestThreadingDomain, mTestLocationTimeZoneProvider);
+ TestEnvironment testEnvironment = new TestEnvironment(
+ mTestThreadingDomain, controllerImpl, USER1_CONFIG_GEO_DETECTION_ENABLED);
+ controllerImpl.initialize(testEnvironment, mTestCallback);
+
+ // There should be a runnable scheduled to suggest uncertainty if no event is received.
+ mTestLocationTimeZoneProvider.assertIsEnabled(USER1_CONFIG_GEO_DETECTION_ENABLED);
+ mTestThreadingDomain.assertSingleDelayedQueueItem(UNCERTAINTY_DELAY);
+ mTestCallback.assertNoSuggestionMade();
+
+ // Have the provider suggest a time zone.
+ mTestLocationTimeZoneProvider.simulateLocationTimeZoneEvent(
+ USER1_SUCCESS_LOCATION_TIME_ZONE_EVENT1);
+
+ // Receiving a "success" provider event should cause a suggestion to be made synchronously,
+ // and also clear the scheduled uncertainty suggestion.
+ mTestLocationTimeZoneProvider.assertIsEnabled(USER1_CONFIG_GEO_DETECTION_ENABLED);
+ mTestThreadingDomain.assertQueueEmpty();
+ mTestCallback.assertSuggestionMadeAndCommit(
+ USER1_SUCCESS_LOCATION_TIME_ZONE_EVENT1.getTimeZoneIds());
+
+ // Simulate the user change (but geo detection still enabled).
+ testEnvironment.simulateConfigChange(USER2_CONFIG_GEO_DETECTION_ENABLED);
+
+ // We expect the provider to end up in PROVIDER_STATE_ENABLED, but it should have been
+ // disabled when the user changed.
+ // The controller should schedule a runnable to make a suggestion if the provider doesn't
+ // send a success event.
+ int[] expectedStateTransitions = { PROVIDER_STATE_DISABLED, PROVIDER_STATE_ENABLED };
+ mTestLocationTimeZoneProvider.assertStateChangesAndCommit(expectedStateTransitions);
+ mTestLocationTimeZoneProvider.assertConfig(USER2_CONFIG_GEO_DETECTION_ENABLED);
+ mTestThreadingDomain.assertSingleDelayedQueueItem(UNCERTAINTY_DELAY);
+ mTestCallback.assertNoSuggestionMade();
+
+ // Simulate no event being received, and time passing.
+ mTestThreadingDomain.executeNext();
+
+ mTestLocationTimeZoneProvider.assertIsEnabled(USER2_CONFIG_GEO_DETECTION_ENABLED);
+ mTestThreadingDomain.assertQueueEmpty();
+ mTestCallback.assertUncertainSuggestionMadeAndCommit();
+ }
+
+ private static LocationTimeZoneEvent createLocationTimeZoneEvent(@UserIdInt int userId,
+ int eventType, @Nullable List<String> timeZoneIds) {
+ LocationTimeZoneEvent.Builder builder = new LocationTimeZoneEvent.Builder()
+ .setElapsedRealtimeNanos(ARBITRARY_TIME)
+ .setUserHandle(UserHandle.of(userId))
+ .setEventType(eventType);
+ if (timeZoneIds != null) {
+ builder.setTimeZoneIds(timeZoneIds);
+ }
+ return builder.build();
+ }
+
+ private static class TestEnvironment extends LocationTimeZoneProviderController.Environment {
+
+ private final LocationTimeZoneProviderController mController;
+ private ConfigurationInternal mConfigurationInternal;
+
+ TestEnvironment(ThreadingDomain threadingDomain,
+ LocationTimeZoneProviderController controller,
+ ConfigurationInternal configurationInternal) {
+ super(threadingDomain);
+ mController = Objects.requireNonNull(controller);
+ mConfigurationInternal = Objects.requireNonNull(configurationInternal);
+ }
+
+ @Override
+ ConfigurationInternal getCurrentUserConfigurationInternal() {
+ return mConfigurationInternal;
+ }
+
+ void simulateConfigChange(ConfigurationInternal newConfig) {
+ ConfigurationInternal oldConfig = mConfigurationInternal;
+ mConfigurationInternal = Objects.requireNonNull(newConfig);
+ if (Objects.equals(oldConfig, newConfig)) {
+ fail("Bad test? No config change when one was expected");
+ }
+ mController.onConfigChanged();
+ }
+ }
+
+ private static class TestCallback extends LocationTimeZoneProviderController.Callback {
+
+ private TestState<GeolocationTimeZoneSuggestion> mLatestSuggestion = new TestState<>();
+
+ TestCallback(ThreadingDomain threadingDomain) {
+ super(threadingDomain);
+ }
+
+ @Override
+ void suggest(GeolocationTimeZoneSuggestion suggestion) {
+ mLatestSuggestion.set(suggestion);
+ }
+
+ void assertSuggestionMadeAndCommit(@Nullable List<String> expectedZoneIds) {
+ mLatestSuggestion.assertHasBeenSet();
+ assertEquals(expectedZoneIds, mLatestSuggestion.getLatest().getZoneIds());
+ mLatestSuggestion.commitLatest();
+ }
+
+ void assertNoSuggestionMade() {
+ mLatestSuggestion.assertHasNotBeenSet();
+ }
+
+ void assertUncertainSuggestionMadeAndCommit() {
+ // An "uncertain" suggestion has null time zone IDs.
+ assertSuggestionMadeAndCommit(null);
+ }
+ }
+
+ private static class TestLocationTimeZoneProvider extends LocationTimeZoneProvider {
+
+ /** Used to track historic provider states for tests. */
+ private final TestState<ProviderState> mTestProviderState = new TestState<>();
+ private boolean mInitialized;
+
+ /**
+ * Creates the instance.
+ */
+ TestLocationTimeZoneProvider(ThreadingDomain threadingDomain, String providerName) {
+ super(threadingDomain, providerName);
+ }
+
+ @Override
+ void onInitialize() {
+ mInitialized = true;
+ }
+
+ @Override
+ void onSetCurrentState(ProviderState newState) {
+ mTestProviderState.set(newState);
+ }
+
+ @Override
+ void onEnable() {
+ // Nothing needed for tests.
+ }
+
+ @Override
+ void onDisable() {
+ // Nothing needed for tests.
+ }
+
+ @Override
+ void logWarn(String msg) {
+ System.out.println(msg);
+ }
+
+ @Override
+ public void dump(IndentingPrintWriter pw, String[] args) {
+ // Nothing needed for tests.
+ }
+
+ /** Asserts that {@link #initialize(ProviderListener)} has been called. */
+ void assertInitialized() {
+ assertTrue(mInitialized);
+ }
+
+ void assertIsDisabled() {
+ // Disabled providers don't hold config.
+ assertConfig(null);
+ assertIsEnabledAndCommit(false);
+ }
+
+ /**
+ * Asserts the provider's config matches the expected, and the current state is set
+ * accordinly. Commits the latest changes to the state.
+ */
+ void assertIsEnabled(@NonNull ConfigurationInternal expectedConfig) {
+ assertConfig(expectedConfig);
+
+ boolean expectIsEnabled = expectedConfig.getAutoDetectionEnabledBehavior();
+ assertIsEnabledAndCommit(expectIsEnabled);
+ }
+
+ private void assertIsEnabledAndCommit(boolean enabled) {
+ ProviderState currentState = mCurrentState.get();
+ if (enabled) {
+ assertEquals(PROVIDER_STATE_ENABLED, currentState.stateEnum);
+ } else {
+ assertEquals(PROVIDER_STATE_DISABLED, currentState.stateEnum);
+ }
+ mTestProviderState.commitLatest();
+ }
+
+ void assertConfig(@NonNull ConfigurationInternal expectedConfig) {
+ ProviderState currentState = mCurrentState.get();
+ assertEquals(expectedConfig, currentState.currentUserConfiguration);
+ }
+
+ void simulateLocationTimeZoneEvent(@NonNull LocationTimeZoneEvent event) {
+ handleLocationTimeZoneEvent(event);
+ }
+
+ /**
+ * Asserts the most recent state changes. The ordering is such that the last element in the
+ * provided array is expected to be the current state.
+ */
+ void assertStateChangesAndCommit(int... expectedProviderStates) {
+ if (expectedProviderStates.length == 0) {
+ mTestProviderState.assertHasNotBeenSet();
+ } else {
+ mTestProviderState.assertChangeCount(expectedProviderStates.length);
+
+ List<ProviderState> previousProviderStates = new ArrayList<>();
+ for (int i = 0; i < expectedProviderStates.length; i++) {
+ previousProviderStates.add(mTestProviderState.getPrevious(i));
+ }
+ // The loop above will produce a list with the most recent state in element 0. So,
+ // reverse the list as the arguments to this method are expected to be in order
+ // oldest...latest.
+ Collections.reverse(previousProviderStates);
+
+ boolean allMatch = true;
+ for (int i = 0; i < expectedProviderStates.length; i++) {
+ allMatch = allMatch && expectedProviderStates[i]
+ == previousProviderStates.get(i).stateEnum;
+ }
+ if (!allMatch) {
+ fail("Provider state enums expected=" + Arrays.toString(expectedProviderStates)
+ + " but states were"
+ + " actually=" + previousProviderStates);
+ }
+ }
+ mTestProviderState.commitLatest();
+ }
+ }
+}
diff --git a/services/tests/servicestests/src/com/android/server/location/timezone/HandlerThreadingDomainTest.java b/services/tests/servicestests/src/com/android/server/location/timezone/HandlerThreadingDomainTest.java
new file mode 100644
index 000000000000..cbaf0f391375
--- /dev/null
+++ b/services/tests/servicestests/src/com/android/server/location/timezone/HandlerThreadingDomainTest.java
@@ -0,0 +1,208 @@
+/*
+ * 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.location.timezone;
+
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertSame;
+import static org.junit.Assert.assertTrue;
+import static org.junit.Assert.fail;
+
+import android.os.Handler;
+import android.os.HandlerThread;
+import android.platform.test.annotations.Presubmit;
+
+import com.android.server.location.timezone.ThreadingDomain.SingleRunnableQueue;
+
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+
+import java.util.Objects;
+import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.TimeUnit;
+import java.util.concurrent.atomic.AtomicBoolean;
+
+/** Tests for {@link HandlerThreadingDomain}. */
+@Presubmit
+public class HandlerThreadingDomainTest {
+
+ private HandlerThread mHandlerThread;
+ private Handler mTestHandler;
+
+ @Before
+ public void setUp() {
+ mHandlerThread = new HandlerThread("HandlerThreadingDomainTest");
+ mHandlerThread.start();
+ mTestHandler = new Handler(mHandlerThread.getLooper());
+ }
+
+ @After
+ public void tearDown() throws Exception {
+ mHandlerThread.quit();
+ mHandlerThread.join();
+ }
+
+ @Test
+ public void getLockObject() {
+ ThreadingDomain domain = new HandlerThreadingDomain(mTestHandler);
+ assertSame("LockObject must be consistent", domain.getLockObject(), domain.getLockObject());
+ }
+
+ @Test
+ public void assertCurrentThread() throws Exception {
+ ThreadingDomain domain = new HandlerThreadingDomain(mTestHandler);
+
+ // Expect an exception (current thread != handler thread)
+ try {
+ domain.assertCurrentThread();
+ fail("Expected exception");
+ } catch (RuntimeException expected) {
+ // Expected
+ }
+
+ // Expect no exception (current thread == handler thread)
+ AtomicBoolean exceptionThrown = new AtomicBoolean(true);
+ LatchedRunnable testCode = new LatchedRunnable(() -> {
+ domain.assertCurrentThread();
+ exceptionThrown.set(false);
+ });
+ mTestHandler.post(testCode);
+ testCode.assertCompletesWithin(60, TimeUnit.SECONDS);
+ assertFalse(exceptionThrown.get());
+ }
+
+ @Test
+ public void post() throws Exception {
+ ThreadingDomain domain = new HandlerThreadingDomain(mTestHandler);
+ AtomicBoolean ranOnExpectedThread = new AtomicBoolean(false);
+ LatchedRunnable testLogic = new LatchedRunnable(() -> {
+ ranOnExpectedThread.set(Thread.currentThread() == mTestHandler.getLooper().getThread());
+ });
+ domain.post(testLogic);
+ testLogic.assertCompletesWithin(60, TimeUnit.SECONDS);
+ assertTrue(ranOnExpectedThread.get());
+ }
+
+ @Test
+ public void postDelayed() throws Exception {
+ ThreadingDomain domain = new HandlerThreadingDomain(mTestHandler);
+
+ long beforeExecutionNanos = System.nanoTime();
+ AtomicBoolean ranOnExpectedThread = new AtomicBoolean(false);
+ LatchedRunnable testLogic = new LatchedRunnable(() -> {
+ ranOnExpectedThread.set(Thread.currentThread() == mTestHandler.getLooper().getThread());
+ });
+ domain.postDelayed(testLogic, 5000);
+
+ testLogic.assertCompletesWithin(60, TimeUnit.SECONDS);
+ assertTrue(ranOnExpectedThread.get());
+
+ long afterExecutionNanos = System.nanoTime();
+ assertTrue(afterExecutionNanos - beforeExecutionNanos >= TimeUnit.SECONDS.toNanos(5));
+ }
+
+ @Test
+ public void singleRunnableHandler_runSynchronously() throws Exception {
+ ThreadingDomain domain = new HandlerThreadingDomain(mTestHandler);
+ SingleRunnableQueue singleRunnableQueue = domain.createSingleRunnableQueue();
+
+ AtomicBoolean testPassed = new AtomicBoolean(false);
+ // Calls to SingleRunnableQueue must be made on the handler thread it is associated with,
+ // so this uses runWithScissors() to block until the lambda has completed.
+ mTestHandler.runWithScissors(() -> {
+ Thread testThread = Thread.currentThread();
+ CountDownLatch latch = new CountDownLatch(1);
+ singleRunnableQueue.runSynchronously(() -> {
+ assertSame(Thread.currentThread(), testThread);
+ latch.countDown();
+ });
+ assertTrue(awaitWithRuntimeException(latch, 60, TimeUnit.SECONDS));
+ testPassed.set(true);
+ }, TimeUnit.SECONDS.toMillis(60));
+ assertTrue(testPassed.get());
+ }
+
+ @Test
+ public void singleRunnableHandler_runDelayed() throws Exception {
+ ThreadingDomain domain = new HandlerThreadingDomain(mTestHandler);
+ SingleRunnableQueue singleRunnableQueue = domain.createSingleRunnableQueue();
+
+ long beforeExecutionNanos = System.nanoTime();
+
+ Runnable noOpRunnable = () -> {
+ // Deliberately do nothing
+ };
+ LatchedRunnable firstRunnable = new LatchedRunnable(noOpRunnable);
+ LatchedRunnable secondRunnable = new LatchedRunnable(noOpRunnable);
+
+ // Calls to SingleRunnableQueue must be made on the handler thread it is associated with,
+ // so this uses runWithScissors() to block until the runDelayedTestRunnable has completed.
+ Runnable runDelayedTestRunnable = () -> {
+ singleRunnableQueue.runDelayed(firstRunnable, TimeUnit.SECONDS.toMillis(10));
+
+ // The second runnable posted must clear the first.
+ singleRunnableQueue.runDelayed(secondRunnable, TimeUnit.SECONDS.toMillis(10));
+ };
+ mTestHandler.runWithScissors(runDelayedTestRunnable, TimeUnit.SECONDS.toMillis(60));
+
+ // Now wait for the second runnable to complete
+ secondRunnable.assertCompletesWithin(60, TimeUnit.SECONDS);
+ assertFalse(firstRunnable.isComplete());
+
+ long afterExecutionNanos = System.nanoTime();
+ assertTrue(afterExecutionNanos - beforeExecutionNanos >= TimeUnit.SECONDS.toNanos(10));
+ }
+
+ private static boolean awaitWithRuntimeException(
+ CountDownLatch latch, long timeout, TimeUnit timeUnit) {
+ try {
+ return latch.await(timeout, timeUnit);
+ } catch (InterruptedException e) {
+ throw new RuntimeException(e);
+ }
+ }
+
+ private static class LatchedRunnable implements Runnable {
+
+ private final CountDownLatch mLatch = new CountDownLatch(1);
+ private final Runnable mRunnable;
+
+ LatchedRunnable(Runnable mRunnable) {
+ this.mRunnable = Objects.requireNonNull(mRunnable);
+ }
+
+ @Override
+ public void run() {
+ try {
+ mRunnable.run();
+ } finally {
+ mLatch.countDown();
+ }
+ }
+
+ boolean isComplete() {
+ return mLatch.getCount() == 0;
+ }
+
+ boolean waitForCompletion(long timeout, TimeUnit unit) {
+ return awaitWithRuntimeException(mLatch, timeout, unit);
+ }
+
+ void assertCompletesWithin(long timeout, TimeUnit unit) {
+ assertTrue("Runnable did not execute in time", waitForCompletion(timeout, unit));
+ }
+ }
+}
diff --git a/services/tests/servicestests/src/com/android/server/location/timezone/NullLocationTimeZoneProviderTest.java b/services/tests/servicestests/src/com/android/server/location/timezone/NullLocationTimeZoneProviderTest.java
new file mode 100644
index 000000000000..7c882fc1f154
--- /dev/null
+++ b/services/tests/servicestests/src/com/android/server/location/timezone/NullLocationTimeZoneProviderTest.java
@@ -0,0 +1,134 @@
+/*
+ * 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.location.timezone;
+
+import static com.android.server.location.timezone.LocationTimeZoneProvider.ProviderState.PROVIDER_STATE_DISABLED;
+import static com.android.server.location.timezone.LocationTimeZoneProvider.ProviderState.PROVIDER_STATE_ENABLED;
+import static com.android.server.location.timezone.LocationTimeZoneProvider.ProviderState.PROVIDER_STATE_PERM_FAILED;
+import static com.android.server.location.timezone.TestSupport.USER1_CONFIG_GEO_DETECTION_ENABLED;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNull;
+import static org.junit.Assert.assertSame;
+
+import android.platform.test.annotations.Presubmit;
+import android.util.IndentingPrintWriter;
+
+import com.android.server.location.timezone.LocationTimeZoneProvider.ProviderState;
+import com.android.server.timezonedetector.ConfigurationInternal;
+import com.android.server.timezonedetector.TestState;
+
+import org.junit.Before;
+import org.junit.Test;
+
+/**
+ * Tests for {@link NullLocationTimeZoneProvider} and, indirectly, the class it extends
+ * {@link LocationTimeZoneProvider}.
+ */
+@Presubmit
+public class NullLocationTimeZoneProviderTest {
+
+ private TestThreadingDomain mTestThreadingDomain;
+
+ private TestController mTestController;
+
+ @Before
+ public void setUp() {
+ mTestThreadingDomain = new TestThreadingDomain();
+ mTestController = new TestController(mTestThreadingDomain);
+ }
+
+ @Test
+ public void initialization() {
+ String providerName = "primary";
+ NullLocationTimeZoneProvider provider =
+ new NullLocationTimeZoneProvider(mTestThreadingDomain, providerName);
+ provider.initialize(providerState -> mTestController.onProviderStateChange(providerState));
+
+ ProviderState currentState = provider.getCurrentState();
+ assertEquals(PROVIDER_STATE_DISABLED, currentState.stateEnum);
+ assertNull(currentState.currentUserConfiguration);
+ assertSame(provider, currentState.provider);
+ mTestThreadingDomain.assertQueueEmpty();
+ }
+
+ @Test
+ public void enableSchedulesPermFailure() {
+ String providerName = "primary";
+ NullLocationTimeZoneProvider provider =
+ new NullLocationTimeZoneProvider(mTestThreadingDomain, providerName);
+ provider.initialize(providerState -> mTestController.onProviderStateChange(providerState));
+
+ ConfigurationInternal config = USER1_CONFIG_GEO_DETECTION_ENABLED;
+ provider.enable(config);
+
+ // The StubbedProvider should enters enabled state, but immediately schedule a runnable to
+ // switch to perm failure.
+ ProviderState currentState = provider.getCurrentState();
+ assertSame(provider, currentState.provider);
+ assertEquals(PROVIDER_STATE_ENABLED, currentState.stateEnum);
+ assertEquals(config, currentState.currentUserConfiguration);
+ mTestThreadingDomain.assertSingleImmediateQueueItem();
+ // Entering enabled() does not trigger an onProviderStateChanged() as it is requested by the
+ // controller.
+ mTestController.assertProviderChangeNotTriggered();
+
+ // Check the queued runnable causes the provider to go into perm failed state.
+ mTestThreadingDomain.executeNext();
+
+ // Entering perm failed triggers an onProviderStateChanged() as it is asynchronously
+ // triggered.
+ mTestController.assertProviderChangeTriggered(PROVIDER_STATE_PERM_FAILED);
+ }
+
+ /** A test stand-in for the {@link LocationTimeZoneProviderController}. */
+ private static class TestController extends LocationTimeZoneProviderController {
+
+ private TestState<ProviderState> mProviderState = new TestState<>();
+
+ TestController(ThreadingDomain threadingDomain) {
+ super(threadingDomain);
+ }
+
+ @Override
+ void initialize(Environment environment, Callback callback) {
+ // Not needed for provider testing.
+ }
+
+ @Override
+ void onConfigChanged() {
+ // Not needed for provider testing.
+ }
+
+ void onProviderStateChange(ProviderState providerState) {
+ this.mProviderState.set(providerState);
+ }
+
+ @Override
+ public void dump(IndentingPrintWriter pw, String[] args) {
+ // Not needed for provider testing.
+ }
+
+ void assertProviderChangeTriggered(int expectedStateEnum) {
+ assertEquals(expectedStateEnum, mProviderState.getLatest().stateEnum);
+ mProviderState.commitLatest();
+ }
+
+ public void assertProviderChangeNotTriggered() {
+ mProviderState.assertHasNotBeenSet();
+ }
+ }
+}
diff --git a/services/tests/servicestests/src/com/android/server/location/timezone/TestSupport.java b/services/tests/servicestests/src/com/android/server/location/timezone/TestSupport.java
new file mode 100644
index 000000000000..192ade7847b0
--- /dev/null
+++ b/services/tests/servicestests/src/com/android/server/location/timezone/TestSupport.java
@@ -0,0 +1,53 @@
+/*
+ * 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.location.timezone;
+
+import android.annotation.UserIdInt;
+
+import com.android.server.timezonedetector.ConfigurationInternal;
+
+/** Shared test support code for this package. */
+final class TestSupport {
+ static final @UserIdInt int USER1_ID = 9999;
+
+ static final ConfigurationInternal USER1_CONFIG_GEO_DETECTION_ENABLED =
+ createUserConfig(USER1_ID, true);
+
+ static final ConfigurationInternal USER1_CONFIG_GEO_DETECTION_DISABLED =
+ createUserConfig(USER1_ID, false);
+
+ static final @UserIdInt int USER2_ID = 1234567890;
+
+ static final ConfigurationInternal USER2_CONFIG_GEO_DETECTION_ENABLED =
+ createUserConfig(USER2_ID, true);
+
+ static final ConfigurationInternal USER2_CONFIG_GEO_DETECTION_DISABLED =
+ createUserConfig(USER2_ID, false);
+
+ private TestSupport() {
+ }
+
+ private static ConfigurationInternal createUserConfig(
+ @UserIdInt int userId, boolean geoDetectionEnabled) {
+ return new ConfigurationInternal.Builder(userId)
+ .setUserConfigAllowed(true)
+ .setAutoDetectionSupported(true)
+ .setAutoDetectionEnabled(true)
+ .setLocationEnabled(true)
+ .setGeoDetectionEnabled(geoDetectionEnabled)
+ .build();
+ }
+}
diff --git a/services/tests/servicestests/src/com/android/server/location/timezone/TestThreadingDomain.java b/services/tests/servicestests/src/com/android/server/location/timezone/TestThreadingDomain.java
new file mode 100644
index 000000000000..70ff22d17513
--- /dev/null
+++ b/services/tests/servicestests/src/com/android/server/location/timezone/TestThreadingDomain.java
@@ -0,0 +1,128 @@
+/*
+ * 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.location.timezone;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertTrue;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+
+import java.time.Duration;
+import java.util.LinkedList;
+import java.util.Objects;
+
+/**
+ * A ThreadingDomain that simulates idealized post() semantics. Execution takes place in zero time,
+ * exactly when scheduled, when the test code explicitly requests it. Execution takes place on the
+ * test's main thread.
+ */
+class TestThreadingDomain extends ThreadingDomain {
+
+ static class QueuedRunnable {
+ @NonNull public final Runnable runnable;
+ @Nullable public final Object token;
+ public final long executionTimeMillis;
+
+ QueuedRunnable(@NonNull Runnable runnable, @Nullable Object token,
+ long executionTimeMillis) {
+ this.runnable = Objects.requireNonNull(runnable);
+ this.token = token;
+ this.executionTimeMillis = executionTimeMillis;
+ }
+
+ @Override
+ public String toString() {
+ return "QueuedRunnable{"
+ + "runnable=" + runnable
+ + ", token=" + token
+ + ", executionTimeMillis=" + executionTimeMillis
+ + '}';
+ }
+ }
+
+ private long mCurrentTimeMillis;
+ private LinkedList<QueuedRunnable> mQueue = new LinkedList<>();
+
+ TestThreadingDomain() {
+ // Pick an arbitrary time.
+ mCurrentTimeMillis = 123456L;
+ }
+
+ @Override
+ Thread getThread() {
+ return Thread.currentThread();
+ }
+
+ @Override
+ void post(Runnable r) {
+ mQueue.add(new QueuedRunnable(r, null, mCurrentTimeMillis));
+ }
+
+ @Override
+ void postDelayed(Runnable r, long delayMillis) {
+ mQueue.add(new QueuedRunnable(r, null, mCurrentTimeMillis + delayMillis));
+ }
+
+ @Override
+ void postDelayed(Runnable r, Object token, long delayMillis) {
+ mQueue.add(new QueuedRunnable(r, token, mCurrentTimeMillis + delayMillis));
+ }
+
+ @Override
+ void removeQueuedRunnables(Object token) {
+ mQueue.removeIf(runnable -> runnable.token != null && runnable.token.equals(token));
+ }
+
+ void assertSingleDelayedQueueItem(Duration expectedDelay) {
+ assertQueueLength(1);
+ assertNextQueueItemIsDelayed(expectedDelay);
+ }
+
+ void assertSingleImmediateQueueItem() {
+ assertQueueLength(1);
+ assertNextQueueItemIsImmediate();
+ }
+
+ void assertQueueLength(int expectedLength) {
+ assertEquals(expectedLength, mQueue.size());
+ }
+
+ void assertNextQueueItemIsImmediate() {
+ assertTrue(getNextQueueItemDelayMillis() == 0);
+ }
+
+ void assertNextQueueItemIsDelayed(Duration expectedDelay) {
+ assertTrue(getNextQueueItemDelayMillis() == expectedDelay.toMillis());
+ }
+
+ void assertQueueEmpty() {
+ assertTrue(mQueue.isEmpty());
+ }
+
+ long getNextQueueItemDelayMillis() {
+ assertQueueLength(1);
+ return mQueue.getFirst().executionTimeMillis - mCurrentTimeMillis;
+ }
+
+ void executeNext() {
+ assertQueueLength(1);
+
+ QueuedRunnable queued = mQueue.removeFirst();
+ mCurrentTimeMillis = queued.executionTimeMillis;
+ queued.runnable.run();
+ }
+}
diff --git a/services/tests/servicestests/src/com/android/server/locksettings/SyntheticPasswordTests.java b/services/tests/servicestests/src/com/android/server/locksettings/SyntheticPasswordTests.java
index 2bd0e55f7d21..2d6605ae222f 100644
--- a/services/tests/servicestests/src/com/android/server/locksettings/SyntheticPasswordTests.java
+++ b/services/tests/servicestests/src/com/android/server/locksettings/SyntheticPasswordTests.java
@@ -511,10 +511,24 @@ public class SyntheticPasswordTests extends BaseLockSettingsServiceTests {
LockscreenCredential password = newPassword("password");
initializeCredentialUnderSP(password, PRIMARY_USER_ID);
assertTrue(mService.setLockCredential(password, password, PRIMARY_USER_ID));
+ assertNoOrphanedFilesLeft(PRIMARY_USER_ID);
+ }
+
+ @Test
+ public void testAddingEscrowToken_NoOrphanedFilesLeft() throws Exception {
+ final byte[] token = "some-high-entropy-secure-token".getBytes();
+ for (int i = 0; i < 16; i++) {
+ long handle = mLocalService.addEscrowToken(token, PRIMARY_USER_ID, null);
+ assertTrue(mLocalService.isEscrowTokenActive(handle, PRIMARY_USER_ID));
+ mLocalService.removeEscrowToken(handle, PRIMARY_USER_ID);
+ }
+ assertNoOrphanedFilesLeft(PRIMARY_USER_ID);
+ }
+ private void assertNoOrphanedFilesLeft(int userId) {
String handleString = String.format("%016x",
- mService.getSyntheticPasswordHandleLocked(PRIMARY_USER_ID));
- File directory = mStorage.getSyntheticPasswordDirectoryForUser(PRIMARY_USER_ID);
+ mService.getSyntheticPasswordHandleLocked(userId));
+ File directory = mStorage.getSyntheticPasswordDirectoryForUser(userId);
for (File file : directory.listFiles()) {
String[] parts = file.getName().split("\\.");
if (!parts[0].equals(handleString) && !parts[0].equals("0000000000000000")) {
diff --git a/services/tests/servicestests/src/com/android/server/pm/PackageManagerSettingsTests.java b/services/tests/servicestests/src/com/android/server/pm/PackageManagerSettingsTests.java
index dac05424e01f..bc747832e253 100644
--- a/services/tests/servicestests/src/com/android/server/pm/PackageManagerSettingsTests.java
+++ b/services/tests/servicestests/src/com/android/server/pm/PackageManagerSettingsTests.java
@@ -62,6 +62,7 @@ import com.android.permission.persistence.RuntimePermissionsPersistence;
import com.android.server.LocalServices;
import com.android.server.pm.parsing.pkg.PackageImpl;
import com.android.server.pm.parsing.pkg.ParsedPackage;
+import com.android.server.pm.permission.LegacyPermissionDataProvider;
import com.android.server.pm.permission.PermissionSettings;
import com.google.common.truth.Truth;
@@ -94,6 +95,8 @@ public class PackageManagerSettingsTests {
PermissionSettings mPermissionSettings;
@Mock
RuntimePermissionsPersistence mRuntimePermissionsPersistence;
+ @Mock
+ LegacyPermissionDataProvider mPermissionDataProvider;
@Before
public void initializeMocks() {
@@ -115,7 +118,7 @@ public class PackageManagerSettingsTests {
final Context context = InstrumentationRegistry.getContext();
final Object lock = new Object();
Settings settings = new Settings(context.getFilesDir(), mPermissionSettings,
- mRuntimePermissionsPersistence, lock);
+ mRuntimePermissionsPersistence, mPermissionDataProvider, lock);
assertThat(settings.readLPw(createFakeUsers()), is(true));
verifyKeySetMetaData(settings);
}
@@ -129,7 +132,7 @@ public class PackageManagerSettingsTests {
final Context context = InstrumentationRegistry.getContext();
final Object lock = new Object();
Settings settings = new Settings(context.getFilesDir(), mPermissionSettings,
- mRuntimePermissionsPersistence, lock);
+ mRuntimePermissionsPersistence, mPermissionDataProvider, lock);
assertThat(settings.readLPw(createFakeUsers()), is(true));
// write out, read back in and verify the same
@@ -145,7 +148,7 @@ public class PackageManagerSettingsTests {
final Context context = InstrumentationRegistry.getContext();
final Object lock = new Object();
Settings settings = new Settings(context.getFilesDir(), mPermissionSettings,
- mRuntimePermissionsPersistence, lock);
+ mRuntimePermissionsPersistence, mPermissionDataProvider, lock);
assertThat(settings.readLPw(createFakeUsers()), is(true));
assertThat(settings.getPackageLPr(PACKAGE_NAME_3), is(notNullValue()));
assertThat(settings.getPackageLPr(PACKAGE_NAME_1), is(notNullValue()));
@@ -167,13 +170,13 @@ public class PackageManagerSettingsTests {
final Context context = InstrumentationRegistry.getContext();
final Object lock = new Object();
Settings settings = new Settings(context.getFilesDir(), mPermissionSettings,
- mRuntimePermissionsPersistence, lock);
+ mRuntimePermissionsPersistence, mPermissionDataProvider, lock);
assertThat(settings.readLPw(createFakeUsers()), is(true));
settings.writeLPr();
// Create Settings again to make it read from the new files
settings = new Settings(context.getFilesDir(), mPermissionSettings,
- mRuntimePermissionsPersistence, lock);
+ mRuntimePermissionsPersistence, mPermissionDataProvider, lock);
assertThat(settings.readLPw(createFakeUsers()), is(true));
PackageSetting ps = settings.getPackageLPr(PACKAGE_NAME_2);
@@ -196,7 +199,8 @@ public class PackageManagerSettingsTests {
writePackageRestrictions_noSuspendingPackageXml(0);
final Object lock = new Object();
final Context context = InstrumentationRegistry.getTargetContext();
- final Settings settingsUnderTest = new Settings(context.getFilesDir(), null, null, lock);
+ final Settings settingsUnderTest = new Settings(context.getFilesDir(), null, null, null,
+ lock);
settingsUnderTest.mPackages.put(PACKAGE_NAME_1, createPackageSetting(PACKAGE_NAME_1));
settingsUnderTest.mPackages.put(PACKAGE_NAME_2, createPackageSetting(PACKAGE_NAME_2));
settingsUnderTest.readPackageRestrictionsLPr(0);
@@ -219,7 +223,8 @@ public class PackageManagerSettingsTests {
writePackageRestrictions_noSuspendParamsMapXml(0);
final Object lock = new Object();
final Context context = InstrumentationRegistry.getTargetContext();
- final Settings settingsUnderTest = new Settings(context.getFilesDir(), null, null, lock);
+ final Settings settingsUnderTest = new Settings(context.getFilesDir(), null, null, null,
+ lock);
settingsUnderTest.mPackages.put(PACKAGE_NAME_1, createPackageSetting(PACKAGE_NAME_1));
settingsUnderTest.readPackageRestrictionsLPr(0);
@@ -246,7 +251,7 @@ public class PackageManagerSettingsTests {
@Test
public void testReadWritePackageRestrictions_suspendInfo() {
final Context context = InstrumentationRegistry.getTargetContext();
- final Settings settingsUnderTest = new Settings(context.getFilesDir(), null, null,
+ final Settings settingsUnderTest = new Settings(context.getFilesDir(), null, null, null,
new Object());
final PackageSetting ps1 = createPackageSetting(PACKAGE_NAME_1);
final PackageSetting ps2 = createPackageSetting(PACKAGE_NAME_2);
@@ -344,7 +349,7 @@ public class PackageManagerSettingsTests {
@Test
public void testReadWritePackageRestrictions_distractionFlags() {
final Context context = InstrumentationRegistry.getTargetContext();
- final Settings settingsUnderTest = new Settings(context.getFilesDir(), null, null,
+ final Settings settingsUnderTest = new Settings(context.getFilesDir(), null, null, null,
new Object());
final PackageSetting ps1 = createPackageSetting(PACKAGE_NAME_1);
final PackageSetting ps2 = createPackageSetting(PACKAGE_NAME_2);
@@ -389,7 +394,7 @@ public class PackageManagerSettingsTests {
final Context context = InstrumentationRegistry.getTargetContext();
final Object lock = new Object();
final Settings settingsUnderTest = new Settings(context.getFilesDir(), mPermissionSettings,
- mRuntimePermissionsPersistence, lock);
+ mRuntimePermissionsPersistence, mPermissionDataProvider, lock);
final PackageSetting ps1 = createPackageSetting(PACKAGE_NAME_1);
ps1.appId = Process.FIRST_APPLICATION_UID;
ps1.pkg = ((ParsedPackage) PackageImpl.forTesting(PACKAGE_NAME_1).hideAsParsed())
@@ -465,7 +470,7 @@ public class PackageManagerSettingsTests {
final Context context = InstrumentationRegistry.getContext();
final Object lock = new Object();
Settings settings = new Settings(context.getFilesDir(), mPermissionSettings,
- mRuntimePermissionsPersistence, lock);
+ mRuntimePermissionsPersistence, mPermissionDataProvider, lock);
assertThat(settings.readLPw(createFakeUsers()), is(true));
// Enable/Disable a package
@@ -638,7 +643,7 @@ public class PackageManagerSettingsTests {
final Context context = InstrumentationRegistry.getContext();
final Object lock = new Object();
final Settings testSettings01 = new Settings(context.getFilesDir(), mPermissionSettings,
- mRuntimePermissionsPersistence, lock);
+ mRuntimePermissionsPersistence, mPermissionDataProvider, lock);
final SharedUserSetting testUserSetting01 = createSharedUserSetting(
testSettings01, "TestUser", 10064, 0 /*pkgFlags*/, 0 /*pkgPrivateFlags*/);
final PackageSetting testPkgSetting01 =
@@ -748,7 +753,7 @@ public class PackageManagerSettingsTests {
final Context context = InstrumentationRegistry.getContext();
final Object lock = new Object();
final Settings testSettings01 = new Settings(context.getFilesDir(), mPermissionSettings,
- mRuntimePermissionsPersistence, lock);
+ mRuntimePermissionsPersistence, mPermissionDataProvider, lock);
final SharedUserSetting testUserSetting01 = createSharedUserSetting(
testSettings01, "TestUser", 10064, 0 /*pkgFlags*/, 0 /*pkgPrivateFlags*/);
final PackageSetting testPkgSetting01 = Settings.createNewSetting(
diff --git a/services/tests/servicestests/src/com/android/server/pm/SELinuxMMACTest.java b/services/tests/servicestests/src/com/android/server/pm/SELinuxMMACTest.java
index a550b27a62a2..f1930d7268d7 100644
--- a/services/tests/servicestests/src/com/android/server/pm/SELinuxMMACTest.java
+++ b/services/tests/servicestests/src/com/android/server/pm/SELinuxMMACTest.java
@@ -44,7 +44,8 @@ import org.mockito.junit.MockitoJUnitRunner;
public class SELinuxMMACTest {
private static final String PACKAGE_NAME = "my.package";
- private static final int OPT_IN_VERSION = Build.VERSION_CODES.R;
+ private static final int LATEST_OPT_IN_VERSION = Build.VERSION_CODES.S;
+ private static final int R_OPT_IN_VERSION = Build.VERSION_CODES.R;
@Mock
PlatformCompat mMockCompatibility;
@@ -56,7 +57,17 @@ public class SELinuxMMACTest {
argThat(argument -> argument.packageName.equals(pkg.getPackageName()))))
.thenReturn(true);
assertThat(SELinuxMMAC.getSeInfo(pkg, null, mMockCompatibility),
- is("default:targetSdkVersion=" + OPT_IN_VERSION));
+ is("default:targetSdkVersion=" + LATEST_OPT_IN_VERSION));
+ }
+
+ @Test
+ public void getSeInfoOptInToR() {
+ AndroidPackage pkg = makePackage(Build.VERSION_CODES.P);
+ when(mMockCompatibility.isChangeEnabledInternal(eq(SELinuxMMAC.SELINUX_R_CHANGES),
+ argThat(argument -> argument.packageName.equals(pkg.getPackageName()))))
+ .thenReturn(true);
+ assertThat(SELinuxMMAC.getSeInfo(pkg, null, mMockCompatibility),
+ is("default:targetSdkVersion=" + R_OPT_IN_VERSION));
}
@Test
@@ -70,13 +81,33 @@ public class SELinuxMMACTest {
}
@Test
- public void getSeInfoNoOptInButAlreadyR() {
- AndroidPackage pkg = makePackage(OPT_IN_VERSION);
+ public void getSeInfoNoOptInButAlreadyLatest() {
+ AndroidPackage pkg = makePackage(LATEST_OPT_IN_VERSION);
when(mMockCompatibility.isChangeEnabledInternal(eq(SELinuxMMAC.SELINUX_LATEST_CHANGES),
argThat(argument -> argument.packageName.equals(pkg.getPackageName()))))
.thenReturn(false);
assertThat(SELinuxMMAC.getSeInfo(pkg, null, mMockCompatibility),
- is("default:targetSdkVersion=" + OPT_IN_VERSION));
+ is("default:targetSdkVersion=" + LATEST_OPT_IN_VERSION));
+ }
+
+ @Test
+ public void getSeInfoNoOptInButAlreadyR() {
+ AndroidPackage pkg = makePackage(R_OPT_IN_VERSION);
+ when(mMockCompatibility.isChangeEnabledInternal(eq(SELinuxMMAC.SELINUX_R_CHANGES),
+ argThat(argument -> argument.packageName.equals(pkg.getPackageName()))))
+ .thenReturn(false);
+ assertThat(SELinuxMMAC.getSeInfo(pkg, null, mMockCompatibility),
+ is("default:targetSdkVersion=" + R_OPT_IN_VERSION));
+ }
+
+ @Test
+ public void getSeInfoOptInRButLater() {
+ AndroidPackage pkg = makePackage(R_OPT_IN_VERSION + 1);
+ when(mMockCompatibility.isChangeEnabledInternal(eq(SELinuxMMAC.SELINUX_R_CHANGES),
+ argThat(argument -> argument.packageName.equals(pkg.getPackageName()))))
+ .thenReturn(true);
+ assertThat(SELinuxMMAC.getSeInfo(pkg, null, mMockCompatibility),
+ is("default:targetSdkVersion=" + (R_OPT_IN_VERSION + 1)));
}
private AndroidPackage makePackage(int targetSdkVersion) {
diff --git a/services/tests/servicestests/src/com/android/server/soundtrigger_middleware/SoundTriggerMiddlewareImplTest.java b/services/tests/servicestests/src/com/android/server/soundtrigger_middleware/SoundTriggerMiddlewareImplTest.java
index ebcf10dd019f..509eb2563376 100644
--- a/services/tests/servicestests/src/com/android/server/soundtrigger_middleware/SoundTriggerMiddlewareImplTest.java
+++ b/services/tests/servicestests/src/com/android/server/soundtrigger_middleware/SoundTriggerMiddlewareImplTest.java
@@ -59,6 +59,7 @@ import android.os.HidlMemoryUtil;
import android.os.HwParcel;
import android.os.IHwBinder;
import android.os.IHwInterface;
+import android.os.ParcelFileDescriptor;
import android.os.RemoteException;
import android.os.SharedMemory;
import android.system.ErrnoException;
@@ -126,7 +127,7 @@ public class SoundTriggerMiddlewareImplTest {
model.uuid = "12345678-2345-3456-4567-abcdef987654";
model.vendorUuid = "87654321-5432-6543-7654-456789fedcba";
byte[] data = new byte[]{91, 92, 93, 94, 95};
- model.data = byteArrayToFileDescriptor(data);
+ model.data = new ParcelFileDescriptor(byteArrayToFileDescriptor(data));
model.dataSize = data.length;
return model;
}
diff --git a/services/tests/servicestests/src/com/android/server/timezonedetector/TestState.java b/services/tests/servicestests/src/com/android/server/timezonedetector/TestState.java
index 7049efa1cc2f..97b8360172f2 100644
--- a/services/tests/servicestests/src/com/android/server/timezonedetector/TestState.java
+++ b/services/tests/servicestests/src/com/android/server/timezonedetector/TestState.java
@@ -76,7 +76,7 @@ public class TestState<T> {
/** Asserts the number of times {@link #set} has been called. */
public void assertChangeCount(int expectedCount) {
- assertEquals(expectedCount, mValues.size());
+ assertEquals(expectedCount, getChangeCount());
}
/**
@@ -89,4 +89,25 @@ public class TestState<T> {
}
return mInitialValue;
}
+
+ /** Returns the number of times {@link #set} has been called. */
+ public int getChangeCount() {
+ return mValues.size();
+ }
+
+ /**
+ * Returns an historic value of the state. Values for {@code age} can be from {@code 0}, the
+ * latest value, through {@code getChangeCount() - 1}, which returns the oldest change, to
+ * {@code getChangeCount()}, which returns the initial value. Values outside of this range will
+ * cause {@link IndexOutOfBoundsException} to be thrown.
+ */
+ public T getPrevious(int age) {
+ int size = mValues.size();
+ if (age < size) {
+ return mValues.get(size - 1 - age);
+ } else if (age == size) {
+ return mInitialValue;
+ }
+ throw new IndexOutOfBoundsException("age=" + age + " is too big.");
+ }
}
diff --git a/services/tests/servicestests/src/com/android/server/usage/AppStandbyControllerTests.java b/services/tests/servicestests/src/com/android/server/usage/AppStandbyControllerTests.java
index 2aeab209162a..2c645268e190 100644
--- a/services/tests/servicestests/src/com/android/server/usage/AppStandbyControllerTests.java
+++ b/services/tests/servicestests/src/com/android/server/usage/AppStandbyControllerTests.java
@@ -112,6 +112,8 @@ public class AppStandbyControllerTests {
private static final int UID_SYSTEM_HEADFULL = 10002;
private static final String PACKAGE_SYSTEM_HEADLESS = "com.example.system.headless";
private static final int UID_SYSTEM_HEADLESS = 10003;
+ private static final String PACKAGE_WELLBEING = "com.example.wellbeing";
+ private static final int UID_WELLBEING = 10004;
private static final int USER_ID = 0;
private static final int USER_ID2 = 10;
private static final UserHandle USER_HANDLE_USER2 = new UserHandle(USER_ID2);
@@ -221,6 +223,11 @@ public class AppStandbyControllerTests {
}
@Override
+ boolean isWellbeingPackage(String packageName) {
+ return PACKAGE_WELLBEING.equals(packageName);
+ }
+
+ @Override
void updatePowerWhitelistCache() {
}
@@ -332,6 +339,12 @@ public class AppStandbyControllerTests {
pish.packageName = PACKAGE_SYSTEM_HEADLESS;
packages.add(pish);
+ PackageInfo piw = new PackageInfo();
+ piw.applicationInfo = new ApplicationInfo();
+ piw.applicationInfo.uid = UID_WELLBEING;
+ piw.packageName = PACKAGE_WELLBEING;
+ packages.add(piw);
+
doReturn(packages).when(mockPm).getInstalledPackagesAsUser(anyInt(), anyInt());
try {
for (int i = 0; i < packages.size(); ++i) {
@@ -1520,6 +1533,25 @@ public class AppStandbyControllerTests {
assertBucket(STANDBY_BUCKET_RARE, PACKAGE_1);
}
+ @Test
+ public void testWellbeingAppElevated() {
+ reportEvent(mController, USER_INTERACTION, mInjector.mElapsedRealtime, PACKAGE_WELLBEING);
+ assertBucket(STANDBY_BUCKET_ACTIVE, PACKAGE_WELLBEING);
+ reportEvent(mController, USER_INTERACTION, mInjector.mElapsedRealtime, PACKAGE_1);
+ assertBucket(STANDBY_BUCKET_ACTIVE, PACKAGE_1);
+ mInjector.mElapsedRealtime += RESTRICTED_THRESHOLD;
+
+ // Make sure the default wellbeing app does not get lowered below WORKING_SET.
+ mController.setAppStandbyBucket(PACKAGE_WELLBEING, USER_ID, STANDBY_BUCKET_RARE,
+ REASON_MAIN_TIMEOUT);
+ assertBucket(STANDBY_BUCKET_WORKING_SET, PACKAGE_WELLBEING);
+
+ // A non default wellbeing app should be able to fall lower than WORKING_SET.
+ mController.setAppStandbyBucket(PACKAGE_1, USER_ID, STANDBY_BUCKET_RARE,
+ REASON_MAIN_TIMEOUT);
+ assertBucket(STANDBY_BUCKET_RARE, PACKAGE_1);
+ }
+
private String getAdminAppsStr(int userId) {
return getAdminAppsStr(userId, mController.getActiveAdminAppsForTest(userId));
}
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 9e7226e7cacf..1a4b119a6c99 100755
--- a/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java
+++ b/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java
@@ -69,6 +69,7 @@ import static org.mockito.Matchers.anyString;
import static org.mockito.Matchers.eq;
import static org.mockito.Mockito.any;
import static org.mockito.Mockito.anyInt;
+import static org.mockito.Mockito.atLeastOnce;
import static org.mockito.Mockito.clearInvocations;
import static org.mockito.Mockito.doAnswer;
import static org.mockito.Mockito.doNothing;
@@ -102,10 +103,12 @@ import android.app.StatsManager;
import android.app.admin.DevicePolicyManagerInternal;
import android.app.usage.UsageStatsManagerInternal;
import android.companion.ICompanionDeviceManager;
+import android.content.BroadcastReceiver;
import android.content.ComponentName;
import android.content.ContentUris;
import android.content.Context;
import android.content.Intent;
+import android.content.IntentFilter;
import android.content.pm.ActivityInfo;
import android.content.pm.ApplicationInfo;
import android.content.pm.IPackageManager;
@@ -282,6 +285,7 @@ public class NotificationManagerServiceTest extends UiServiceTestCase {
NotificationHistoryManager mHistoryManager;
@Mock
StatsManager mStatsManager;
+ BroadcastReceiver mPackageIntentReceiver;
NotificationRecordLoggerFake mNotificationRecordLogger = new NotificationRecordLoggerFake();
private InstanceIdSequence mNotificationInstanceIdSequence = new InstanceIdSequenceFake(
1 << 30);
@@ -480,6 +484,28 @@ public class NotificationManagerServiceTest extends UiServiceTestCase {
mShortcutHelper.setLauncherApps(mLauncherApps);
mShortcutHelper.setShortcutServiceInternal(mShortcutServiceInternal);
+ // Capture PackageIntentReceiver
+ ArgumentCaptor<BroadcastReceiver> broadcastReceiverCaptor =
+ ArgumentCaptor.forClass(BroadcastReceiver.class);
+ ArgumentCaptor<IntentFilter> intentFilterCaptor =
+ ArgumentCaptor.forClass(IntentFilter.class);
+
+ verify(mContext, atLeastOnce()).registerReceiverAsUser(broadcastReceiverCaptor.capture(),
+ any(), intentFilterCaptor.capture(), any(), any());
+ List<BroadcastReceiver> broadcastReceivers = broadcastReceiverCaptor.getAllValues();
+ List<IntentFilter> intentFilters = intentFilterCaptor.getAllValues();
+
+ for (int i = 0; i < intentFilters.size(); i++) {
+ final IntentFilter filter = intentFilters.get(i);
+ if (filter.hasAction(Intent.ACTION_DISTRACTING_PACKAGES_CHANGED)
+ && filter.hasAction(Intent.ACTION_PACKAGES_UNSUSPENDED)
+ && filter.hasAction(Intent.ACTION_PACKAGES_SUSPENDED)) {
+ mPackageIntentReceiver = broadcastReceivers.get(i);
+ break;
+ }
+ }
+ assertNotNull("package intent receiver should exist", mPackageIntentReceiver);
+
// Pretend the shortcut exists
List<ShortcutInfo> shortcutInfos = new ArrayList<>();
ShortcutInfo info = mock(ShortcutInfo.class);
@@ -526,7 +552,13 @@ public class NotificationManagerServiceTest extends UiServiceTestCase {
public void tearDown() throws Exception {
if (mFile != null) mFile.delete();
clearDeviceConfig();
- mService.unregisterDeviceConfigChange();
+
+ try {
+ mService.onDestroy();
+ } catch (IllegalStateException e) {
+ // can throw if a broadcast receiver was never registered
+ }
+
InstrumentationRegistry.getInstrumentation()
.getUiAutomation().dropShellPermissionIdentity();
}
@@ -2533,10 +2565,11 @@ public class NotificationManagerServiceTest extends UiServiceTestCase {
@Test
public void testHasCompanionDevice_noService() {
- mService = new TestableNotificationManagerService(mContext, mNotificationRecordLogger,
+ NotificationManagerService noManService =
+ new TestableNotificationManagerService(mContext, mNotificationRecordLogger,
mNotificationInstanceIdSequence);
- assertFalse(mService.hasCompanionDevice(mListener));
+ assertFalse(noManService.hasCompanionDevice(mListener));
}
@Test
@@ -4347,13 +4380,13 @@ public class NotificationManagerServiceTest extends UiServiceTestCase {
mService.addNotification(notif2);
// on broadcast, hide the 2 notifications
- mService.simulatePackageSuspendBroadcast(true, PKG);
+ simulatePackageSuspendBroadcast(true, PKG, notif1.getUid());
ArgumentCaptor<List> captorHide = ArgumentCaptor.forClass(List.class);
verify(mListeners, times(1)).notifyHiddenLocked(captorHide.capture());
assertEquals(2, captorHide.getValue().size());
// on broadcast, unhide the 2 notifications
- mService.simulatePackageSuspendBroadcast(false, PKG);
+ simulatePackageSuspendBroadcast(false, PKG, notif1.getUid());
ArgumentCaptor<List> captorUnhide = ArgumentCaptor.forClass(List.class);
verify(mListeners, times(1)).notifyUnhiddenLocked(captorUnhide.capture());
assertEquals(2, captorUnhide.getValue().size());
@@ -4370,7 +4403,24 @@ public class NotificationManagerServiceTest extends UiServiceTestCase {
mService.addNotification(notif2);
// on broadcast, nothing is hidden since no notifications are of package "test_package"
- mService.simulatePackageSuspendBroadcast(true, "test_package");
+ simulatePackageSuspendBroadcast(true, "test_package", notif1.getUid());
+ ArgumentCaptor<List> captor = ArgumentCaptor.forClass(List.class);
+ verify(mListeners, times(1)).notifyHiddenLocked(captor.capture());
+ assertEquals(0, captor.getValue().size());
+ }
+
+ @Test
+ public void testNotificationFromDifferentUserHidden() {
+ // post 2 notification from this package
+ final NotificationRecord notif1 = generateNotificationRecord(
+ mTestNotificationChannel, 1, null, true);
+ final NotificationRecord notif2 = generateNotificationRecord(
+ mTestNotificationChannel, 2, null, false);
+ mService.addNotification(notif1);
+ mService.addNotification(notif2);
+
+ // on broadcast, nothing is hidden since no notifications are of user 10 with package PKG
+ simulatePackageSuspendBroadcast(true, PKG, 10);
ArgumentCaptor<List> captor = ArgumentCaptor.forClass(List.class);
verify(mListeners, times(1)).notifyHiddenLocked(captor.capture());
assertEquals(0, captor.getValue().size());
@@ -4387,16 +4437,18 @@ public class NotificationManagerServiceTest extends UiServiceTestCase {
mService.addNotification(pkgB);
// on broadcast, hide one of the packages
- mService.simulatePackageDistractionBroadcast(
- PackageManager.RESTRICTION_HIDE_NOTIFICATIONS, new String[] {"a"});
+ simulatePackageDistractionBroadcast(
+ PackageManager.RESTRICTION_HIDE_NOTIFICATIONS, new String[] {"a"},
+ new int[] {1000});
ArgumentCaptor<List<NotificationRecord>> captorHide = ArgumentCaptor.forClass(List.class);
verify(mListeners, times(1)).notifyHiddenLocked(captorHide.capture());
assertEquals(1, captorHide.getValue().size());
assertEquals("a", captorHide.getValue().get(0).getSbn().getPackageName());
// on broadcast, unhide the package
- mService.simulatePackageDistractionBroadcast(
- PackageManager.RESTRICTION_HIDE_FROM_SUGGESTIONS, new String[] {"a"});
+ simulatePackageDistractionBroadcast(
+ PackageManager.RESTRICTION_HIDE_FROM_SUGGESTIONS, new String[] {"a"},
+ new int[] {1000});
ArgumentCaptor<List<NotificationRecord>> captorUnhide = ArgumentCaptor.forClass(List.class);
verify(mListeners, times(1)).notifyUnhiddenLocked(captorUnhide.capture());
assertEquals(1, captorUnhide.getValue().size());
@@ -4414,8 +4466,9 @@ public class NotificationManagerServiceTest extends UiServiceTestCase {
mService.addNotification(pkgB);
// on broadcast, hide one of the packages
- mService.simulatePackageDistractionBroadcast(
- PackageManager.RESTRICTION_HIDE_NOTIFICATIONS, new String[] {"a", "b"});
+ simulatePackageDistractionBroadcast(
+ PackageManager.RESTRICTION_HIDE_NOTIFICATIONS, new String[] {"a", "b"},
+ new int[] {1000, 1001});
ArgumentCaptor<List<NotificationRecord>> captorHide = ArgumentCaptor.forClass(List.class);
// should be called only once.
@@ -4425,8 +4478,9 @@ public class NotificationManagerServiceTest extends UiServiceTestCase {
assertEquals("b", captorHide.getValue().get(1).getSbn().getPackageName());
// on broadcast, unhide the package
- mService.simulatePackageDistractionBroadcast(
- PackageManager.RESTRICTION_HIDE_FROM_SUGGESTIONS, new String[] {"a", "b"});
+ simulatePackageDistractionBroadcast(
+ PackageManager.RESTRICTION_HIDE_FROM_SUGGESTIONS, new String[] {"a", "b"},
+ new int[] {1000, 1001});
ArgumentCaptor<List<NotificationRecord>> captorUnhide = ArgumentCaptor.forClass(List.class);
// should be called only once.
@@ -4444,8 +4498,9 @@ public class NotificationManagerServiceTest extends UiServiceTestCase {
mService.addNotification(notif1);
// on broadcast, nothing is hidden since no notifications are of package "test_package"
- mService.simulatePackageDistractionBroadcast(
- PackageManager.RESTRICTION_HIDE_NOTIFICATIONS, new String[] {"test_package"});
+ simulatePackageDistractionBroadcast(
+ PackageManager.RESTRICTION_HIDE_NOTIFICATIONS, new String[] {"test_package"},
+ new int[]{notif1.getUid()});
ArgumentCaptor<List> captor = ArgumentCaptor.forClass(List.class);
verify(mListeners, times(1)).notifyHiddenLocked(captor.capture());
assertEquals(0, captor.getValue().size());
@@ -7011,4 +7066,34 @@ public class NotificationManagerServiceTest extends UiServiceTestCase {
assertTrue(mService.isVisibleToListener(sbn, info));
}
+ private void simulatePackageSuspendBroadcast(boolean suspend, String pkg,
+ int uid) {
+ // mimics receive broadcast that package is (un)suspended
+ // but does not actually (un)suspend the package
+ final Bundle extras = new Bundle();
+ extras.putStringArray(Intent.EXTRA_CHANGED_PACKAGE_LIST,
+ new String[]{pkg});
+ extras.putIntArray(Intent.EXTRA_CHANGED_UID_LIST, new int[]{uid});
+
+ final String action = suspend ? Intent.ACTION_PACKAGES_SUSPENDED
+ : Intent.ACTION_PACKAGES_UNSUSPENDED;
+ final Intent intent = new Intent(action);
+ intent.putExtras(extras);
+
+ mPackageIntentReceiver.onReceive(getContext(), intent);
+ }
+
+ private void simulatePackageDistractionBroadcast(int flag, String[] pkgs, int[] uids) {
+ // mimics receive broadcast that package is (un)distracting
+ // but does not actually register that info with packagemanager
+ final Bundle extras = new Bundle();
+ extras.putStringArray(Intent.EXTRA_CHANGED_PACKAGE_LIST, pkgs);
+ extras.putInt(Intent.EXTRA_DISTRACTION_RESTRICTIONS, flag);
+ extras.putIntArray(Intent.EXTRA_CHANGED_UID_LIST, uids);
+
+ final Intent intent = new Intent(Intent.ACTION_DISTRACTING_PACKAGES_CHANGED);
+ intent.putExtras(extras);
+
+ mPackageIntentReceiver.onReceive(getContext(), intent);
+ }
}
diff --git a/services/tests/wmtests/src/com/android/server/wm/ActivityStackTests.java b/services/tests/wmtests/src/com/android/server/wm/ActivityStackTests.java
index a60f93a3b14a..caf8a720e26c 100644
--- a/services/tests/wmtests/src/com/android/server/wm/ActivityStackTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/ActivityStackTests.java
@@ -1142,7 +1142,8 @@ public class ActivityStackTests extends WindowTestsBase {
final ActivityRecord topActivity = new ActivityBuilder(mAtm).setTask(mTask).build();
topActivity.info.flags |= FLAG_RESUME_WHILE_PAUSING;
- mStack.startPausingLocked(false /* userLeaving */, false /* uiSleeping */, topActivity);
+ mStack.startPausingLocked(false /* userLeaving */, false /* uiSleeping */, topActivity,
+ "test");
verify(mStack).completePauseLocked(anyBoolean(), eq(topActivity));
}
diff --git a/services/tests/wmtests/src/com/android/server/wm/ActivityStarterTests.java b/services/tests/wmtests/src/com/android/server/wm/ActivityStarterTests.java
index 076047b35604..e47881917b2c 100644
--- a/services/tests/wmtests/src/com/android/server/wm/ActivityStarterTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/ActivityStarterTests.java
@@ -63,6 +63,7 @@ import static org.mockito.ArgumentMatchers.anyBoolean;
import static org.mockito.ArgumentMatchers.anyInt;
import static org.mockito.ArgumentMatchers.anyString;
import static org.mockito.ArgumentMatchers.eq;
+import static org.mockito.ArgumentMatchers.notNull;
import android.app.ActivityOptions;
import android.app.IApplicationThread;
@@ -670,7 +671,7 @@ public class ActivityStarterTests extends WindowTestsBase {
doReturn(callerIsRecents).when(recentTasks).isCallerRecents(callingUid);
// caller is temp allowed
if (callerIsTempAllowed) {
- callerApp.addAllowBackgroundActivityStartsToken(new Binder(), null);
+ callerApp.addOrUpdateAllowBackgroundActivityStartsToken(new Binder(), null);
}
// caller is instrumenting with background activity starts privileges
callerApp.setInstrumenting(callerIsInstrumentingWithBackgroundActivityStartPrivileges,
@@ -800,6 +801,7 @@ public class ActivityStarterTests extends WindowTestsBase {
final Task topTask = new TaskBuilder(mSupervisor).setParentTask(topStack).build();
new ActivityBuilder(mAtm).setTask(topTask).build();
+ doReturn(mActivityMetricsLogger).when(mSupervisor).getActivityMetricsLogger();
// Start activity with the same intent as {@code singleTaskActivity} on secondary display.
final ActivityOptions options = ActivityOptions.makeBasic()
.setLaunchDisplayId(secondaryDisplay.mDisplayId);
@@ -813,6 +815,9 @@ public class ActivityStarterTests extends WindowTestsBase {
// Ensure secondary display only creates two stacks.
verify(secondaryTaskContainer, times(2)).createStack(anyInt(), anyInt(), anyBoolean());
+ // The metrics logger should receive the same result and non-null options.
+ verify(mActivityMetricsLogger).notifyActivityLaunched(any() /* launchingState */,
+ eq(result), eq(singleTaskActivity), notNull() /* options */);
}
@Test
diff --git a/services/tests/wmtests/src/com/android/server/wm/DisplayAreaOrganizerTest.java b/services/tests/wmtests/src/com/android/server/wm/DisplayAreaOrganizerTest.java
index f4b50dc6b553..54b2b3b4a009 100644
--- a/services/tests/wmtests/src/com/android/server/wm/DisplayAreaOrganizerTest.java
+++ b/services/tests/wmtests/src/com/android/server/wm/DisplayAreaOrganizerTest.java
@@ -18,6 +18,8 @@ package com.android.server.wm;
import static android.window.DisplayAreaOrganizer.FEATURE_VENDOR_FIRST;
+import static com.google.common.truth.Truth.assertThat;
+
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.verify;
@@ -39,6 +41,10 @@ import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
+/**
+ * Build/Install/Run:
+ * atest WmTests:DisplayAreaOrganizerTest
+ */
@SmallTest
@Presubmit
@RunWith(WindowTestRunner.class)
@@ -61,14 +67,22 @@ public class DisplayAreaOrganizerTest extends WindowTestsBase {
}
private IDisplayAreaOrganizer registerMockOrganizer(int feature) {
- final IDisplayAreaOrganizer organizer = mock(IDisplayAreaOrganizer.class);
- when(organizer.asBinder()).thenReturn(new Binder());
+ return registerMockOrganizer(feature, new Binder());
+ }
+ private IDisplayAreaOrganizer registerMockOrganizer(int feature, Binder binder) {
+ final IDisplayAreaOrganizer organizer = createMockOrganizer(binder);
mWm.mAtmService.mWindowOrganizerController.mDisplayAreaOrganizerController
.registerOrganizer(organizer, feature);
return organizer;
}
+ private IDisplayAreaOrganizer createMockOrganizer(Binder binder) {
+ final IDisplayAreaOrganizer organizer = mock(IDisplayAreaOrganizer.class);
+ when(organizer.asBinder()).thenReturn(binder);
+ return organizer;
+ }
+
private void unregisterMockOrganizer(IDisplayAreaOrganizer organizer) {
mWm.mAtmService.mWindowOrganizerController.mDisplayAreaOrganizerController
.unregisterOrganizer(organizer);
@@ -99,4 +113,16 @@ public class DisplayAreaOrganizerTest extends WindowTestsBase {
// Ensure it was still only called once if the bounds didn't change
verify(organizer).onDisplayAreaInfoChanged(any());
}
+
+ @Test
+ public void testUnregisterOrganizer() {
+ final Binder binder = new Binder();
+ registerMockOrganizer(FEATURE_VENDOR_FIRST, binder);
+
+ assertThat(mTestDisplayArea.mOrganizer).isNotNull();
+
+ unregisterMockOrganizer(createMockOrganizer(binder));
+
+ assertThat(mTestDisplayArea.mOrganizer).isNull();
+ }
}
diff --git a/services/tests/wmtests/src/com/android/server/wm/DisplayPolicyLayoutTests.java b/services/tests/wmtests/src/com/android/server/wm/DisplayPolicyLayoutTests.java
index 94e40413f9f8..951118125f6e 100644
--- a/services/tests/wmtests/src/com/android/server/wm/DisplayPolicyLayoutTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/DisplayPolicyLayoutTests.java
@@ -124,7 +124,7 @@ public class DisplayPolicyLayoutTests extends DisplayPolicyTestsBase {
spyOn(mNavBarWindow);
// Disabling this call for most tests since it can override the systemUiFlags when called.
- doReturn(0).when(mDisplayPolicy).updateSystemUiVisibilityLw();
+ doReturn(false).when(mDisplayPolicy).updateSystemUiVisibilityLw();
updateDisplayFrames();
}
diff --git a/services/tests/wmtests/src/com/android/server/wm/DisplayPolicyTests.java b/services/tests/wmtests/src/com/android/server/wm/DisplayPolicyTests.java
index b77d21c0f711..a55423a7baf6 100644
--- a/services/tests/wmtests/src/com/android/server/wm/DisplayPolicyTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/DisplayPolicyTests.java
@@ -19,9 +19,9 @@ package com.android.server.wm;
import static android.view.InsetsState.ITYPE_IME;
import static android.view.InsetsState.ITYPE_NAVIGATION_BAR;
import static android.view.Surface.ROTATION_0;
-import static android.view.View.SYSTEM_UI_FLAG_LIGHT_NAVIGATION_BAR;
import static android.view.ViewGroup.LayoutParams.MATCH_PARENT;
import static android.view.ViewGroup.LayoutParams.WRAP_CONTENT;
+import static android.view.WindowInsetsController.APPEARANCE_LIGHT_NAVIGATION_BARS;
import static android.view.WindowInsetsController.BEHAVIOR_SHOW_BARS_BY_SWIPE;
import static android.view.WindowInsetsController.BEHAVIOR_SHOW_BARS_BY_TOUCH;
import static android.view.WindowManager.LayoutParams.FLAG_ALT_FOCUSABLE_IM;
@@ -78,8 +78,7 @@ public class DisplayPolicyTests extends WindowTestsBase {
attrs.flags =
FLAG_LAYOUT_IN_SCREEN | FLAG_LAYOUT_INSET_DECOR | FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS;
attrs.format = PixelFormat.OPAQUE;
- attrs.systemUiVisibility = attrs.subtreeSystemUiVisibility = win.mSystemUiVisibility =
- hasLightNavBar ? SYSTEM_UI_FLAG_LIGHT_NAVIGATION_BAR : 0;
+ attrs.insetsFlags.appearance = hasLightNavBar ? APPEARANCE_LIGHT_NAVIGATION_BARS : 0;
return win;
}
@@ -103,8 +102,7 @@ public class DisplayPolicyTests extends WindowTestsBase {
attrs.flags = FLAG_NOT_FOCUSABLE | FLAG_LAYOUT_IN_SCREEN
| (drawNavBar ? FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS : 0);
attrs.format = PixelFormat.TRANSPARENT;
- attrs.systemUiVisibility = attrs.subtreeSystemUiVisibility = win.mSystemUiVisibility =
- hasLightNavBar ? SYSTEM_UI_FLAG_LIGHT_NAVIGATION_BAR : 0;
+ attrs.insetsFlags.appearance = hasLightNavBar ? APPEARANCE_LIGHT_NAVIGATION_BARS : 0;
win.mHasSurface = visible;
return win;
}
@@ -163,6 +161,7 @@ public class DisplayPolicyTests extends WindowTestsBase {
@Test
public void testUpdateLightNavigationBarLw() {
+ DisplayPolicy displayPolicy = mDisplayContent.getDisplayPolicy();
final WindowState opaqueDarkNavBar = createOpaqueFullscreen(false);
final WindowState opaqueLightNavBar = createOpaqueFullscreen(true);
@@ -171,50 +170,50 @@ public class DisplayPolicyTests extends WindowTestsBase {
final WindowState imeDrawDarkNavBar = createInputMethodWindow(true, true, false);
final WindowState imeDrawLightNavBar = createInputMethodWindow(true, true, true);
- assertEquals(SYSTEM_UI_FLAG_LIGHT_NAVIGATION_BAR,
- DisplayPolicy.updateLightNavigationBarLw(
- SYSTEM_UI_FLAG_LIGHT_NAVIGATION_BAR, null, null,
+ assertEquals(APPEARANCE_LIGHT_NAVIGATION_BARS,
+ displayPolicy.updateLightNavigationBarLw(
+ APPEARANCE_LIGHT_NAVIGATION_BARS, null, null,
null, null));
- // Opaque top fullscreen window overrides SYSTEM_UI_FLAG_LIGHT_NAVIGATION_BAR flag.
- assertEquals(0, DisplayPolicy.updateLightNavigationBarLw(
+ // Opaque top fullscreen window overrides APPEARANCE_LIGHT_NAVIGATION_BARS flag.
+ assertEquals(0, displayPolicy.updateLightNavigationBarLw(
0, opaqueDarkNavBar, opaqueDarkNavBar, null, opaqueDarkNavBar));
- assertEquals(0, DisplayPolicy.updateLightNavigationBarLw(
- SYSTEM_UI_FLAG_LIGHT_NAVIGATION_BAR, opaqueDarkNavBar, opaqueDarkNavBar, null,
+ assertEquals(0, displayPolicy.updateLightNavigationBarLw(
+ APPEARANCE_LIGHT_NAVIGATION_BARS, opaqueDarkNavBar, opaqueDarkNavBar, null,
opaqueDarkNavBar));
- assertEquals(SYSTEM_UI_FLAG_LIGHT_NAVIGATION_BAR,
- DisplayPolicy.updateLightNavigationBarLw(0, opaqueLightNavBar,
+ assertEquals(APPEARANCE_LIGHT_NAVIGATION_BARS,
+ displayPolicy.updateLightNavigationBarLw(0, opaqueLightNavBar,
opaqueLightNavBar, null, opaqueLightNavBar));
- assertEquals(SYSTEM_UI_FLAG_LIGHT_NAVIGATION_BAR,
- DisplayPolicy.updateLightNavigationBarLw(SYSTEM_UI_FLAG_LIGHT_NAVIGATION_BAR,
+ assertEquals(APPEARANCE_LIGHT_NAVIGATION_BARS,
+ displayPolicy.updateLightNavigationBarLw(APPEARANCE_LIGHT_NAVIGATION_BARS,
opaqueLightNavBar, opaqueLightNavBar, null, opaqueLightNavBar));
- // Dimming window clears SYSTEM_UI_FLAG_LIGHT_NAVIGATION_BAR.
- assertEquals(0, DisplayPolicy.updateLightNavigationBarLw(
+ // Dimming window clears APPEARANCE_LIGHT_NAVIGATION_BARS.
+ assertEquals(0, displayPolicy.updateLightNavigationBarLw(
0, opaqueDarkNavBar, dimming, null, dimming));
- assertEquals(0, DisplayPolicy.updateLightNavigationBarLw(
+ assertEquals(0, displayPolicy.updateLightNavigationBarLw(
0, opaqueLightNavBar, dimming, null, dimming));
- assertEquals(0, DisplayPolicy.updateLightNavigationBarLw(
- SYSTEM_UI_FLAG_LIGHT_NAVIGATION_BAR, opaqueDarkNavBar, dimming, null, dimming));
- assertEquals(0, DisplayPolicy.updateLightNavigationBarLw(
- SYSTEM_UI_FLAG_LIGHT_NAVIGATION_BAR, opaqueLightNavBar, dimming, null, dimming));
- assertEquals(0, DisplayPolicy.updateLightNavigationBarLw(
- SYSTEM_UI_FLAG_LIGHT_NAVIGATION_BAR, opaqueLightNavBar, dimming, imeDrawLightNavBar,
+ assertEquals(0, displayPolicy.updateLightNavigationBarLw(
+ APPEARANCE_LIGHT_NAVIGATION_BARS, opaqueDarkNavBar, dimming, null, dimming));
+ assertEquals(0, displayPolicy.updateLightNavigationBarLw(
+ APPEARANCE_LIGHT_NAVIGATION_BARS, opaqueLightNavBar, dimming, null, dimming));
+ assertEquals(0, displayPolicy.updateLightNavigationBarLw(
+ APPEARANCE_LIGHT_NAVIGATION_BARS, opaqueLightNavBar, dimming, imeDrawLightNavBar,
dimming));
- // IME window clears SYSTEM_UI_FLAG_LIGHT_NAVIGATION_BAR
- assertEquals(0, DisplayPolicy.updateLightNavigationBarLw(
- SYSTEM_UI_FLAG_LIGHT_NAVIGATION_BAR, null, null, imeDrawDarkNavBar,
+ // IME window clears APPEARANCE_LIGHT_NAVIGATION_BARS
+ assertEquals(0, displayPolicy.updateLightNavigationBarLw(
+ APPEARANCE_LIGHT_NAVIGATION_BARS, null, null, imeDrawDarkNavBar,
imeDrawDarkNavBar));
- // Even if the top fullscreen has SYSTEM_UI_FLAG_LIGHT_NAVIGATION_BAR, IME window wins.
- assertEquals(0, DisplayPolicy.updateLightNavigationBarLw(
- SYSTEM_UI_FLAG_LIGHT_NAVIGATION_BAR, opaqueLightNavBar, opaqueLightNavBar,
+ // Even if the top fullscreen has APPEARANCE_LIGHT_NAVIGATION_BARS, IME window wins.
+ assertEquals(0, displayPolicy.updateLightNavigationBarLw(
+ APPEARANCE_LIGHT_NAVIGATION_BARS, opaqueLightNavBar, opaqueLightNavBar,
imeDrawDarkNavBar, imeDrawDarkNavBar));
- // IME window should be able to use SYSTEM_UI_FLAG_LIGHT_NAVIGATION_BAR.
- assertEquals(SYSTEM_UI_FLAG_LIGHT_NAVIGATION_BAR,
- DisplayPolicy.updateLightNavigationBarLw(0, opaqueDarkNavBar,
+ // IME window should be able to use APPEARANCE_LIGHT_NAVIGATION_BARS.
+ assertEquals(APPEARANCE_LIGHT_NAVIGATION_BARS,
+ displayPolicy.updateLightNavigationBarLw(0, opaqueDarkNavBar,
opaqueDarkNavBar, imeDrawLightNavBar, imeDrawLightNavBar));
}
diff --git a/services/tests/wmtests/src/com/android/server/wm/DisplayPolicyTestsBase.java b/services/tests/wmtests/src/com/android/server/wm/DisplayPolicyTestsBase.java
index af8cb02a86fe..94ffcdab4fa7 100644
--- a/services/tests/wmtests/src/com/android/server/wm/DisplayPolicyTestsBase.java
+++ b/services/tests/wmtests/src/com/android/server/wm/DisplayPolicyTestsBase.java
@@ -45,7 +45,6 @@ import android.util.Pair;
import android.view.DisplayCutout;
import android.view.DisplayInfo;
import android.view.Gravity;
-import android.view.View;
import android.view.WindowManagerGlobal;
import com.android.internal.R;
@@ -99,11 +98,9 @@ public class DisplayPolicyTestsBase extends WindowTestsBase {
mStatusBarWindow.mAttrs.gravity = Gravity.TOP;
addWindow(mStatusBarWindow);
- mDisplayPolicy.mLastSystemUiFlags |= View.STATUS_BAR_TRANSPARENT;
mNavBarWindow.mAttrs.gravity = Gravity.BOTTOM;
addWindow(mNavBarWindow);
- mDisplayPolicy.mLastSystemUiFlags |= View.NAVIGATION_BAR_TRANSPARENT;
// Update source frame and visibility of insets providers.
mDisplayContent.getInsetsStateController().onPostLayout();
diff --git a/services/tests/wmtests/src/com/android/server/wm/HighRefreshRateBlacklistTest.java b/services/tests/wmtests/src/com/android/server/wm/HighRefreshRateDenylistTest.java
index f53894ad9ec5..c3e1922a09cc 100644
--- a/services/tests/wmtests/src/com/android/server/wm/HighRefreshRateBlacklistTest.java
+++ b/services/tests/wmtests/src/com/android/server/wm/HighRefreshRateDenylistTest.java
@@ -40,107 +40,107 @@ import java.util.concurrent.Executor;
/**
* Build/Install/Run:
- * atest WmTests:HighRefreshRateBlacklistTest
+ * atest WmTests:HighRefreshRateDenylistTest
*/
@SmallTest
@Presubmit
-public class HighRefreshRateBlacklistTest {
+public class HighRefreshRateDenylistTest {
private static final String APP1 = "com.android.sample1";
private static final String APP2 = "com.android.sample2";
private static final String APP3 = "com.android.sample3";
- private HighRefreshRateBlacklist mBlacklist;
+ private HighRefreshRateDenylist mDenylist;
@After
public void tearDown() {
- mBlacklist.dispose();
+ mDenylist.dispose();
}
@Test
- public void testDefaultBlacklist() {
+ public void testDefaultDenylist() {
final Resources r = createResources(APP1, APP2);
- mBlacklist = new HighRefreshRateBlacklist(r, new FakeDeviceConfig());
+ mDenylist = new HighRefreshRateDenylist(r, new FakeDeviceConfig());
- assertTrue(mBlacklist.isBlacklisted(APP1));
- assertTrue(mBlacklist.isBlacklisted(APP2));
- assertFalse(mBlacklist.isBlacklisted(APP3));
+ assertTrue(mDenylist.isDenylisted(APP1));
+ assertTrue(mDenylist.isDenylisted(APP2));
+ assertFalse(mDenylist.isDenylisted(APP3));
}
@Test
- public void testNoDefaultBlacklist() {
+ public void testNoDefaultDenylist() {
final Resources r = createResources();
- mBlacklist = new HighRefreshRateBlacklist(r, new FakeDeviceConfig());
+ mDenylist = new HighRefreshRateDenylist(r, new FakeDeviceConfig());
- assertFalse(mBlacklist.isBlacklisted(APP1));
+ assertFalse(mDenylist.isDenylisted(APP1));
}
@Test
- public void testDefaultBlacklistIsOverriddenByDeviceConfig() {
+ public void testDefaultDenylistIsOverriddenByDeviceConfig() {
final Resources r = createResources(APP1);
final FakeDeviceConfig config = new FakeDeviceConfig();
- config.setBlacklist(APP2 + "," + APP3);
- mBlacklist = new HighRefreshRateBlacklist(r, config);
+ config.setDenylist(APP2 + "," + APP3);
+ mDenylist = new HighRefreshRateDenylist(r, config);
- assertFalse(mBlacklist.isBlacklisted(APP1));
- assertTrue(mBlacklist.isBlacklisted(APP2));
- assertTrue(mBlacklist.isBlacklisted(APP3));
+ assertFalse(mDenylist.isDenylisted(APP1));
+ assertTrue(mDenylist.isDenylisted(APP2));
+ assertTrue(mDenylist.isDenylisted(APP3));
}
@Test
- public void testDefaultBlacklistIsOverriddenByEmptyDeviceConfig() {
+ public void testDefaultDenylistIsOverriddenByEmptyDeviceConfig() {
final Resources r = createResources(APP1);
final FakeDeviceConfig config = new FakeDeviceConfig();
- config.setBlacklist("");
- mBlacklist = new HighRefreshRateBlacklist(r, config);
+ config.setDenylist("");
+ mDenylist = new HighRefreshRateDenylist(r, config);
- assertFalse(mBlacklist.isBlacklisted(APP1));
+ assertFalse(mDenylist.isDenylisted(APP1));
}
@Test
- public void testDefaultBlacklistIsOverriddenByDeviceConfigUpdate() {
+ public void testDefaultDenylistIsOverriddenByDeviceConfigUpdate() {
final Resources r = createResources(APP1);
final FakeDeviceConfig config = new FakeDeviceConfig();
- mBlacklist = new HighRefreshRateBlacklist(r, config);
+ mDenylist = new HighRefreshRateDenylist(r, config);
// First check that the default denylist is in effect
- assertTrue(mBlacklist.isBlacklisted(APP1));
- assertFalse(mBlacklist.isBlacklisted(APP2));
- assertFalse(mBlacklist.isBlacklisted(APP3));
+ assertTrue(mDenylist.isDenylisted(APP1));
+ assertFalse(mDenylist.isDenylisted(APP2));
+ assertFalse(mDenylist.isDenylisted(APP3));
// Then confirm that the DeviceConfig list has propagated and taken effect.
- config.setBlacklist(APP2 + "," + APP3);
- assertFalse(mBlacklist.isBlacklisted(APP1));
- assertTrue(mBlacklist.isBlacklisted(APP2));
- assertTrue(mBlacklist.isBlacklisted(APP3));
+ config.setDenylist(APP2 + "," + APP3);
+ assertFalse(mDenylist.isDenylisted(APP1));
+ assertTrue(mDenylist.isDenylisted(APP2));
+ assertTrue(mDenylist.isDenylisted(APP3));
// Finally make sure we go back to the default list if the DeviceConfig gets deleted.
- config.setBlacklist(null);
- assertTrue(mBlacklist.isBlacklisted(APP1));
- assertFalse(mBlacklist.isBlacklisted(APP2));
- assertFalse(mBlacklist.isBlacklisted(APP3));
+ config.setDenylist(null);
+ assertTrue(mDenylist.isDenylisted(APP1));
+ assertFalse(mDenylist.isDenylisted(APP2));
+ assertFalse(mDenylist.isDenylisted(APP3));
}
@Test
public void testOverriddenByDeviceConfigUnrelatedFlagChanged() {
final Resources r = createResources(APP1);
final FakeDeviceConfig config = new FakeDeviceConfig();
- mBlacklist = new HighRefreshRateBlacklist(r, config);
- config.setBlacklist(APP2 + "," + APP3);
- assertFalse(mBlacklist.isBlacklisted(APP1));
- assertTrue(mBlacklist.isBlacklisted(APP2));
- assertTrue(mBlacklist.isBlacklisted(APP3));
+ mDenylist = new HighRefreshRateDenylist(r, config);
+ config.setDenylist(APP2 + "," + APP3);
+ assertFalse(mDenylist.isDenylisted(APP1));
+ assertTrue(mDenylist.isDenylisted(APP2));
+ assertTrue(mDenylist.isDenylisted(APP3));
// Change an unrelated flag in our namespace and verify that the denylist is intact
config.putPropertyAndNotify(DeviceConfig.NAMESPACE_DISPLAY_MANAGER, "someKey", "someValue");
- assertFalse(mBlacklist.isBlacklisted(APP1));
- assertTrue(mBlacklist.isBlacklisted(APP2));
- assertTrue(mBlacklist.isBlacklisted(APP3));
+ assertFalse(mDenylist.isDenylisted(APP1));
+ assertTrue(mDenylist.isDenylisted(APP2));
+ assertTrue(mDenylist.isDenylisted(APP3));
}
- private Resources createResources(String... defaultBlacklist) {
+ private Resources createResources(String... defaultDenylist) {
Resources r = mock(Resources.class);
when(r.getStringArray(R.array.config_highRefreshRateBlacklist))
- .thenReturn(defaultBlacklist);
+ .thenReturn(defaultDenylist);
return r;
}
@@ -160,9 +160,9 @@ public class HighRefreshRateBlacklistTest {
super.addOnPropertiesChangedListener(namespace, executor, listener);
}
- void setBlacklist(String blacklist) {
+ void setDenylist(String denylist) {
putPropertyAndNotify(DeviceConfig.NAMESPACE_DISPLAY_MANAGER,
- KEY_HIGH_REFRESH_RATE_BLACKLIST, blacklist);
+ KEY_HIGH_REFRESH_RATE_BLACKLIST, denylist);
}
}
}
diff --git a/services/tests/wmtests/src/com/android/server/wm/InsetsPolicyTest.java b/services/tests/wmtests/src/com/android/server/wm/InsetsPolicyTest.java
index 608305c33168..8c02faf7ac09 100644
--- a/services/tests/wmtests/src/com/android/server/wm/InsetsPolicyTest.java
+++ b/services/tests/wmtests/src/com/android/server/wm/InsetsPolicyTest.java
@@ -22,7 +22,6 @@ import static android.app.WindowConfiguration.WINDOWING_MODE_SPLIT_SCREEN_PRIMAR
import static android.view.InsetsState.ITYPE_NAVIGATION_BAR;
import static android.view.InsetsState.ITYPE_STATUS_BAR;
import static android.view.ViewGroup.LayoutParams.WRAP_CONTENT;
-import static android.view.ViewRootImpl.NEW_INSETS_MODE_FULL;
import static android.view.WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE;
import static android.view.WindowManager.LayoutParams.PRIVATE_FLAG_FORCE_SHOW_STATUS_BAR;
import static android.view.WindowManager.LayoutParams.PRIVATE_FLAG_STATUS_FORCE_SHOW_NAVIGATION;
@@ -38,20 +37,22 @@ import static org.junit.Assert.assertNull;
import static org.junit.Assert.assertTrue;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.anyBoolean;
+import static org.mockito.Mockito.clearInvocations;
import static org.mockito.Mockito.doAnswer;
import static org.mockito.Mockito.doNothing;
import static org.mockito.Mockito.spy;
+import static org.mockito.Mockito.verify;
+import android.app.StatusBarManager;
import android.platform.test.annotations.Presubmit;
import android.view.InsetsSourceControl;
import android.view.InsetsState;
-import android.view.test.InsetsModeSession;
import androidx.test.filters.SmallTest;
-import org.junit.AfterClass;
+import com.android.server.statusbar.StatusBarManagerInternal;
+
import org.junit.Before;
-import org.junit.BeforeClass;
import org.junit.Test;
import org.junit.runner.RunWith;
@@ -59,19 +60,6 @@ import org.junit.runner.RunWith;
@Presubmit
@RunWith(WindowTestRunner.class)
public class InsetsPolicyTest extends WindowTestsBase {
- private static InsetsModeSession sInsetsModeSession;
-
- @BeforeClass
- public static void setUpOnce() {
- // To let the insets provider control the insets visibility, the insets mode has to be
- // NEW_INSETS_MODE_FULL.
- sInsetsModeSession = new InsetsModeSession(NEW_INSETS_MODE_FULL);
- }
-
- @AfterClass
- public static void tearDownOnce() {
- sInsetsModeSession.close();
- }
@Before
public void setup() {
@@ -222,6 +210,23 @@ public class InsetsPolicyTest extends WindowTestsBase {
assertNotNull(fullscreenAppControls);
assertEquals(1, fullscreenAppControls.length);
assertEquals(ITYPE_STATUS_BAR, fullscreenAppControls[0].getType());
+
+ // Assume mFocusedWindow is updated but mTopFullscreenOpaqueWindowState hasn't.
+ final WindowState newFocusedFullscreenApp = addWindow(TYPE_APPLICATION, "newFullscreenApp");
+ final InsetsState newRequestedState = new InsetsState();
+ newRequestedState.getSource(ITYPE_STATUS_BAR).setVisible(true);
+ newFocusedFullscreenApp.updateRequestedInsetsState(newRequestedState);
+ // Make sure status bar is hidden by previous insets state.
+ mDisplayContent.getInsetsPolicy().updateBarControlTarget(fullscreenApp);
+
+ final StatusBarManagerInternal sbmi =
+ mDisplayContent.getDisplayPolicy().getStatusBarManagerInternal();
+ clearInvocations(sbmi);
+ mDisplayContent.getInsetsPolicy().updateBarControlTarget(newFocusedFullscreenApp);
+ // The status bar should be shown by newFocusedFullscreenApp even
+ // mTopFullscreenOpaqueWindowState is still fullscreenApp.
+ verify(sbmi).setWindowState(mDisplayContent.mDisplayId, StatusBarManager.WINDOW_STATUS_BAR,
+ StatusBarManager.WINDOW_STATE_SHOWING);
}
@UseTestDisplay(addWindows = W_ACTIVITY)
@@ -312,6 +317,15 @@ public class InsetsPolicyTest extends WindowTestsBase {
final InsetsState state = policy.getInsetsForDispatch(mAppWindow);
state.setSourceVisible(ITYPE_STATUS_BAR, true);
state.setSourceVisible(ITYPE_NAVIGATION_BAR, true);
+
+ final InsetsState clientState = mAppWindow.getInsetsState();
+ // The transient bar states for client should be invisible.
+ assertFalse(clientState.getSource(ITYPE_STATUS_BAR).isVisible());
+ assertFalse(clientState.getSource(ITYPE_NAVIGATION_BAR).isVisible());
+ // The original state shouldn't be modified.
+ assertTrue(state.getSource(ITYPE_STATUS_BAR).isVisible());
+ assertTrue(state.getSource(ITYPE_NAVIGATION_BAR).isVisible());
+
policy.onInsetsModified(mAppWindow, state);
waitUntilWindowAnimatorIdle();
diff --git a/services/tests/wmtests/src/com/android/server/wm/InsetsStateControllerTest.java b/services/tests/wmtests/src/com/android/server/wm/InsetsStateControllerTest.java
index 59f8cc8c3412..4a4974355ea2 100644
--- a/services/tests/wmtests/src/com/android/server/wm/InsetsStateControllerTest.java
+++ b/services/tests/wmtests/src/com/android/server/wm/InsetsStateControllerTest.java
@@ -23,7 +23,6 @@ import static android.app.WindowConfiguration.WINDOWING_MODE_SPLIT_SCREEN_PRIMAR
import static android.view.InsetsState.ITYPE_IME;
import static android.view.InsetsState.ITYPE_NAVIGATION_BAR;
import static android.view.InsetsState.ITYPE_STATUS_BAR;
-import static android.view.ViewRootImpl.NEW_INSETS_MODE_FULL;
import static android.view.WindowManager.LayoutParams.FLAG_ALT_FOCUSABLE_IM;
import static android.view.WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE;
import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION;
@@ -43,15 +42,11 @@ import static org.mockito.Mockito.verify;
import android.graphics.Rect;
import android.platform.test.annotations.Presubmit;
-import android.util.IntArray;
import android.view.InsetsSourceControl;
import android.view.InsetsState;
-import android.view.test.InsetsModeSession;
import androidx.test.filters.SmallTest;
-import org.junit.AfterClass;
-import org.junit.BeforeClass;
import org.junit.Test;
import org.junit.runner.RunWith;
@@ -59,19 +54,6 @@ import org.junit.runner.RunWith;
@Presubmit
@RunWith(WindowTestRunner.class)
public class InsetsStateControllerTest extends WindowTestsBase {
- private static InsetsModeSession sInsetsModeSession;
-
- @BeforeClass
- public static void setUpOnce() {
- // To let the insets provider control the insets visibility, the insets mode has to be
- // NEW_INSETS_MODE_FULL.
- sInsetsModeSession = new InsetsModeSession(NEW_INSETS_MODE_FULL);
- }
-
- @AfterClass
- public static void tearDownOnce() {
- sInsetsModeSession.close();
- }
@Test
public void testStripForDispatch_notOwn() {
diff --git a/services/tests/wmtests/src/com/android/server/wm/RefreshRatePolicyTest.java b/services/tests/wmtests/src/com/android/server/wm/RefreshRatePolicyTest.java
index e887be0c48c2..77a4b0507a42 100644
--- a/services/tests/wmtests/src/com/android/server/wm/RefreshRatePolicyTest.java
+++ b/services/tests/wmtests/src/com/android/server/wm/RefreshRatePolicyTest.java
@@ -48,7 +48,7 @@ public class RefreshRatePolicyTest extends WindowTestsBase {
private static final int LOW_MODE_ID = 3;
private RefreshRatePolicy mPolicy;
- private HighRefreshRateBlacklist mBlacklist = mock(HighRefreshRateBlacklist.class);
+ private HighRefreshRateDenylist mDenylist = mock(HighRefreshRateDenylist.class);
@Before
public void setUp() {
@@ -61,7 +61,7 @@ public class RefreshRatePolicyTest extends WindowTestsBase {
defaultMode.getPhysicalWidth(), defaultMode.getPhysicalHeight(), 60),
};
di.defaultModeId = 1;
- mPolicy = new RefreshRatePolicy(mWm, di, mBlacklist);
+ mPolicy = new RefreshRatePolicy(mWm, di, mDenylist);
}
@Test
@@ -81,7 +81,7 @@ public class RefreshRatePolicyTest extends WindowTestsBase {
final WindowState blacklistedWindow = createWindow(null, TYPE_BASE_APPLICATION,
"blacklistedWindow");
blacklistedWindow.mAttrs.packageName = "com.android.test";
- when(mBlacklist.isBlacklisted("com.android.test")).thenReturn(true);
+ when(mDenylist.isDenylisted("com.android.test")).thenReturn(true);
assertEquals(LOW_MODE_ID, mPolicy.getPreferredModeId(blacklistedWindow));
}
@@ -90,7 +90,7 @@ public class RefreshRatePolicyTest extends WindowTestsBase {
final WindowState overrideWindow = createWindow(null, TYPE_BASE_APPLICATION,
"overrideWindow");
overrideWindow.mAttrs.preferredDisplayModeId = LOW_MODE_ID;
- when(mBlacklist.isBlacklisted("com.android.test")).thenReturn(true);
+ when(mDenylist.isDenylisted("com.android.test")).thenReturn(true);
assertEquals(LOW_MODE_ID, mPolicy.getPreferredModeId(overrideWindow));
}
diff --git a/services/tests/wmtests/src/com/android/server/wm/ScreenDecorWindowTests.java b/services/tests/wmtests/src/com/android/server/wm/ScreenDecorWindowTests.java
index ef74861e9422..25ba6db38e05 100644
--- a/services/tests/wmtests/src/com/android/server/wm/ScreenDecorWindowTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/ScreenDecorWindowTests.java
@@ -68,6 +68,7 @@ import com.android.compatibility.common.util.SystemUtil;
import org.junit.After;
import org.junit.Before;
+import org.junit.Ignore;
import org.junit.Test;
import java.util.ArrayList;
@@ -142,6 +143,9 @@ public class ScreenDecorWindowTests {
assertInsetGreaterOrEqual(mTestActivity, RIGHT, mDecorThickness);
}
+ // Decor windows (i.e windows using PRIVATE_FLAG_IS_SCREEN_DECOR) are no longer supported.
+ // PRIVATE_FLAG_IS_SCREEN_DECOR and related code will be deprecated/removed soon.
+ @Ignore
@Test
public void testMultipleDecors() {
// Test 2 decor windows on-top.
diff --git a/services/tests/wmtests/src/com/android/server/wm/SizeCompatTests.java b/services/tests/wmtests/src/com/android/server/wm/SizeCompatTests.java
index edcf0d4f5501..f1540731da8b 100644
--- a/services/tests/wmtests/src/com/android/server/wm/SizeCompatTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/SizeCompatTests.java
@@ -566,7 +566,7 @@ public class SizeCompatTests extends WindowTestsBase {
assertEquals(new Rect(mActivity.getBounds().left, 0, dh - mActivity.getBounds().right, 0),
mActivity.getLetterboxInsets());
- final StatusBarController statusBarController =
+ final BarController statusBarController =
mActivity.mDisplayContent.getDisplayPolicy().getStatusBarController();
// The activity doesn't fill the display, so the letterbox of the rotated activity is
// overlapped with the rotated content frame of status bar. Hence the status bar shouldn't
diff --git a/services/tests/wmtests/src/com/android/server/wm/SystemServicesTestRule.java b/services/tests/wmtests/src/com/android/server/wm/SystemServicesTestRule.java
index 6a29c5b5424a..6f5389ddacce 100644
--- a/services/tests/wmtests/src/com/android/server/wm/SystemServicesTestRule.java
+++ b/services/tests/wmtests/src/com/android/server/wm/SystemServicesTestRule.java
@@ -236,6 +236,7 @@ public class SystemServicesTestRule implements TestRule {
inputChannels[0].dispose();
mInputChannel = inputChannels[1];
doReturn(mInputChannel).when(mImService).monitorInput(anyString(), anyInt());
+ doReturn(mInputChannel).when(mImService).createInputChannel(anyString());
// StatusBarManagerInternal
final StatusBarManagerInternal sbmi = mock(StatusBarManagerInternal.class);
@@ -318,6 +319,9 @@ public class SystemServicesTestRule implements TestRule {
display.setDisplayWindowingMode(WINDOWING_MODE_FULLSCREEN);
spyOn(display);
final TaskDisplayArea taskDisplayArea = display.getDefaultTaskDisplayArea();
+
+ // Set the default focused TDA.
+ display.setLastFocusedTaskDisplayArea(taskDisplayArea);
spyOn(taskDisplayArea);
final Task homeStack = taskDisplayArea.getStack(
WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_HOME);
@@ -334,7 +338,7 @@ public class SystemServicesTestRule implements TestRule {
// HighRefreshRateBlacklist with DeviceConfig. We need to undo that here to avoid
// leaking mWmService.
mWmService.mConstants.dispose();
- mWmService.mHighRefreshRateBlacklist.dispose();
+ mWmService.mHighRefreshRateDenylist.dispose();
// This makes sure the posted messages without delay are processed, e.g.
// DisplayPolicy#release, WindowManagerService#setAnimationScale.
diff --git a/services/tests/wmtests/src/com/android/server/wm/TaskDisplayAreaTests.java b/services/tests/wmtests/src/com/android/server/wm/TaskDisplayAreaTests.java
index bc3b3a4d7ad1..8b025e34401a 100644
--- a/services/tests/wmtests/src/com/android/server/wm/TaskDisplayAreaTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/TaskDisplayAreaTests.java
@@ -29,6 +29,9 @@ import static android.app.WindowConfiguration.WINDOWING_MODE_SPLIT_SCREEN_PRIMAR
import static android.app.WindowConfiguration.WINDOWING_MODE_SPLIT_SCREEN_SECONDARY;
import static android.app.WindowConfiguration.WINDOWING_MODE_UNDEFINED;
import static android.content.pm.ActivityInfo.RESIZE_MODE_UNRESIZEABLE;
+import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE;
+import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_UNSET;
+import static android.window.DisplayAreaOrganizer.FEATURE_VENDOR_FIRST;
import static com.android.dx.mockito.inline.extended.ExtendedMockito.doReturn;
import static com.android.dx.mockito.inline.extended.ExtendedMockito.spyOn;
@@ -242,6 +245,58 @@ public class TaskDisplayAreaTests extends WindowTestsBase {
assertEquals(rootWindowContainer.getOrientation(), rootHomeTask.getOrientation());
}
+ @Test
+ public void testIsLastFocused() {
+ final TaskDisplayArea firstTaskDisplayArea = mDisplayContent.getDefaultTaskDisplayArea();
+ final TaskDisplayArea secondTaskDisplayArea = createTaskDisplayArea(
+ mDisplayContent, mRootWindowContainer.mWmService, "TestTaskDisplayArea",
+ FEATURE_VENDOR_FIRST);
+ final Task firstStack = firstTaskDisplayArea.createStack(
+ WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD, false /* onTop */);
+ final Task secondStack = secondTaskDisplayArea.createStack(
+ WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD, false /* onTop */);
+ final ActivityRecord firstActivity = new ActivityBuilder(mAtm).setCreateTask(true)
+ .setStack(firstStack).build();
+ final ActivityRecord secondActivity = new ActivityBuilder(mAtm).setCreateTask(true)
+ .setStack(secondStack).build();
+
+ // Activity on TDA1 is focused
+ mDisplayContent.setFocusedApp(firstActivity);
+
+ assertThat(firstTaskDisplayArea.isLastFocused()).isTrue();
+ assertThat(secondTaskDisplayArea.isLastFocused()).isFalse();
+
+ // No focused app, TDA1 is still recorded as last focused.
+ mDisplayContent.setFocusedApp(null);
+
+ assertThat(firstTaskDisplayArea.isLastFocused()).isTrue();
+ assertThat(secondTaskDisplayArea.isLastFocused()).isFalse();
+
+ // Activity on TDA2 is focused
+ mDisplayContent.setFocusedApp(secondActivity);
+
+ assertThat(firstTaskDisplayArea.isLastFocused()).isFalse();
+ assertThat(secondTaskDisplayArea.isLastFocused()).isTrue();
+ }
+
+ @Test
+ public void testIgnoreOrientationRequest() {
+ final TaskDisplayArea taskDisplayArea = mDisplayContent.getDefaultTaskDisplayArea();
+ final Task stack = taskDisplayArea.createStack(
+ WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD, false /* onTop */);
+ final ActivityRecord activity = new ActivityBuilder(mAtm).setCreateTask(true)
+ .setStack(stack).build();
+
+ mDisplayContent.setFocusedApp(activity);
+ activity.setRequestedOrientation(SCREEN_ORIENTATION_LANDSCAPE);
+
+ assertThat(taskDisplayArea.getOrientation()).isEqualTo(SCREEN_ORIENTATION_LANDSCAPE);
+
+ taskDisplayArea.setIgnoreOrientationRequest(true /* ignoreOrientationRequest */);
+
+ assertThat(taskDisplayArea.getOrientation()).isEqualTo(SCREEN_ORIENTATION_UNSET);
+ }
+
private void assertGetOrCreateStack(int windowingMode, int activityType, Task candidateTask,
boolean reuseCandidate) {
final TaskDisplayArea taskDisplayArea = candidateTask.getDisplayArea();
diff --git a/services/tests/wmtests/src/com/android/server/wm/TaskRecordTests.java b/services/tests/wmtests/src/com/android/server/wm/TaskRecordTests.java
index a908bfef98de..d2b7ac4c3b24 100644
--- a/services/tests/wmtests/src/com/android/server/wm/TaskRecordTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/TaskRecordTests.java
@@ -33,6 +33,7 @@ import static android.util.DisplayMetrics.DENSITY_DEFAULT;
import static android.view.IWindowManager.FIXED_TO_USER_ROTATION_ENABLED;
import static android.view.Surface.ROTATION_0;
import static android.view.Surface.ROTATION_90;
+import static android.window.DisplayAreaOrganizer.FEATURE_VENDOR_FIRST;
import static com.android.dx.mockito.inline.extended.ExtendedMockito.doAnswer;
import static com.android.dx.mockito.inline.extended.ExtendedMockito.doReturn;
@@ -1001,7 +1002,7 @@ public class TaskRecordTests extends WindowTestsBase {
public void testNotSpecifyOrientationByFloatingTask() {
final Task task = getTestTask();
final ActivityRecord activity = task.getTopMostActivity();
- final WindowContainer<?> taskDisplayArea = task.getParent();
+ final TaskDisplayArea taskDisplayArea = task.getDisplayArea();
activity.setRequestedOrientation(SCREEN_ORIENTATION_LANDSCAPE);
assertEquals(SCREEN_ORIENTATION_LANDSCAPE, taskDisplayArea.getOrientation());
@@ -1012,6 +1013,42 @@ public class TaskRecordTests extends WindowTestsBase {
}
@Test
+ public void testNotSpecifyOrientation_taskDisplayAreaNotFocused() {
+ final TaskDisplayArea firstTaskDisplayArea = mDisplayContent.getDefaultTaskDisplayArea();
+ final TaskDisplayArea secondTaskDisplayArea = createTaskDisplayArea(
+ mDisplayContent, mRootWindowContainer.mWmService, "TestTaskDisplayArea",
+ FEATURE_VENDOR_FIRST);
+ final Task firstStack = firstTaskDisplayArea.createStack(
+ WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD, false /* onTop */);
+ final Task secondStack = secondTaskDisplayArea.createStack(
+ WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD, false /* onTop */);
+ final ActivityRecord firstActivity = new ActivityBuilder(mAtm).setCreateTask(true)
+ .setStack(firstStack).build();
+ final ActivityRecord secondActivity = new ActivityBuilder(mAtm).setCreateTask(true)
+ .setStack(secondStack).build();
+ firstActivity.setRequestedOrientation(SCREEN_ORIENTATION_LANDSCAPE);
+ secondActivity.setRequestedOrientation(SCREEN_ORIENTATION_PORTRAIT);
+
+ // Activity on TDA1 is focused
+ mDisplayContent.setFocusedApp(firstActivity);
+
+ assertEquals(SCREEN_ORIENTATION_LANDSCAPE, firstTaskDisplayArea.getOrientation());
+ assertEquals(SCREEN_ORIENTATION_UNSET, secondTaskDisplayArea.getOrientation());
+
+ // No focused app, TDA1 is still recorded as last focused.
+ mDisplayContent.setFocusedApp(null);
+
+ assertEquals(SCREEN_ORIENTATION_LANDSCAPE, firstTaskDisplayArea.getOrientation());
+ assertEquals(SCREEN_ORIENTATION_UNSET, secondTaskDisplayArea.getOrientation());
+
+ // Activity on TDA2 is focused
+ mDisplayContent.setFocusedApp(secondActivity);
+
+ assertEquals(SCREEN_ORIENTATION_UNSET, firstTaskDisplayArea.getOrientation());
+ assertEquals(SCREEN_ORIENTATION_PORTRAIT, secondTaskDisplayArea.getOrientation());
+ }
+
+ @Test
public void testNotifyOrientationChangeCausedByConfigurationChange() {
final Task task = getTestTask();
final ActivityRecord activity = task.getTopMostActivity();
diff --git a/services/tests/wmtests/src/com/android/server/wm/TestDisplayContent.java b/services/tests/wmtests/src/com/android/server/wm/TestDisplayContent.java
index 213c1f52aa37..ee16a76bcff8 100644
--- a/services/tests/wmtests/src/com/android/server/wm/TestDisplayContent.java
+++ b/services/tests/wmtests/src/com/android/server/wm/TestDisplayContent.java
@@ -156,6 +156,10 @@ class TestDisplayContent extends DisplayContent {
// threads immediately after adding it to hierarchy. Calling doAnswer() type of stubbing
// reduces chance of races, but still doesn't eliminate race conditions.
mService.mRootWindowContainer.addChild(newDisplay, mPosition);
+
+ // Set the default focused TDA.
+ newDisplay.setLastFocusedTaskDisplayArea(newDisplay.getDefaultTaskDisplayArea());
+
return newDisplay;
}
}
diff --git a/services/tests/wmtests/src/com/android/server/wm/WindowManagerServiceTests.java b/services/tests/wmtests/src/com/android/server/wm/WindowManagerServiceTests.java
index 2510385f4580..5b7cf5a72f0a 100644
--- a/services/tests/wmtests/src/com/android/server/wm/WindowManagerServiceTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/WindowManagerServiceTests.java
@@ -27,6 +27,8 @@ import static android.window.DisplayAreaOrganizer.FEATURE_VENDOR_FIRST;
import static androidx.test.platform.app.InstrumentationRegistry.getInstrumentation;
+import static com.android.dx.mockito.inline.extended.ExtendedMockito.doNothing;
+import static com.android.dx.mockito.inline.extended.ExtendedMockito.doReturn;
import static com.android.dx.mockito.inline.extended.ExtendedMockito.never;
import static com.android.dx.mockito.inline.extended.ExtendedMockito.spyOn;
@@ -164,4 +166,14 @@ public class WindowManagerServiceTests extends WindowTestsBase {
verify(mWm.mAtmService).setFocusedTask(tappedTask.mTaskId);
}
+
+ @Test
+ public void testDismissKeyguardCanWakeUp() {
+ doReturn(true).when(mWm).checkCallingPermission(anyString(), anyString());
+ spyOn(mWm.mAtmInternal);
+ doReturn(true).when(mWm.mAtmInternal).isDreaming();
+ doNothing().when(mWm.mAtmService.mStackSupervisor).wakeUp(anyString());
+ mWm.dismissKeyguard(null, "test-dismiss-keyguard");
+ verify(mWm.mAtmService.mStackSupervisor).wakeUp(anyString());
+ }
}
diff --git a/services/tests/wmtests/src/com/android/server/wm/WindowOrganizerTests.java b/services/tests/wmtests/src/com/android/server/wm/WindowOrganizerTests.java
index 11eaf8c5cea5..0152fc607f73 100644
--- a/services/tests/wmtests/src/com/android/server/wm/WindowOrganizerTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/WindowOrganizerTests.java
@@ -26,6 +26,9 @@ import static android.app.WindowConfiguration.WINDOWING_MODE_PINNED;
import static android.app.WindowConfiguration.WINDOWING_MODE_SPLIT_SCREEN_PRIMARY;
import static android.app.WindowConfiguration.WINDOWING_MODE_SPLIT_SCREEN_SECONDARY;
import static android.app.WindowConfiguration.WINDOWING_MODE_UNDEFINED;
+import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE;
+import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_UNSET;
+import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED;
import static android.content.res.Configuration.SCREEN_HEIGHT_DP_UNDEFINED;
import static android.content.res.Configuration.SCREEN_WIDTH_DP_UNDEFINED;
import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION;
@@ -42,6 +45,8 @@ import static com.android.dx.mockito.inline.extended.ExtendedMockito.when;
import static com.android.server.wm.DisplayArea.Type.ABOVE_TASKS;
import static com.android.server.wm.WindowContainer.POSITION_TOP;
+import static com.google.common.truth.Truth.assertThat;
+
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertNotNull;
@@ -361,6 +366,45 @@ public class WindowOrganizerTests extends WindowTestsBase {
}
@Test
+ public void testSetIgnoreOrientationRequest() {
+ removeGlobalMinSizeRestriction();
+ final TaskDisplayArea taskDisplayArea = mDisplayContent.getDefaultTaskDisplayArea();
+ final Task stack = taskDisplayArea.createStack(
+ WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD, false /* onTop */);
+ final ActivityRecord activity = new ActivityBuilder(mAtm).setCreateTask(true)
+ .setStack(stack).build();
+ taskDisplayArea.setIgnoreOrientationRequest(true /* ignoreOrientationRequest */);
+ mDisplayContent.setFocusedApp(activity);
+ activity.setRequestedOrientation(SCREEN_ORIENTATION_LANDSCAPE);
+
+ // TDA returns UNSET when ignoreOrientationRequest == true
+ // DC is UNSPECIFIED because it is using the previous (default) when TDA returns UNSET.
+ assertThat(taskDisplayArea.getOrientation()).isEqualTo(SCREEN_ORIENTATION_UNSET);
+ assertThat(mDisplayContent.getLastOrientation()).isEqualTo(SCREEN_ORIENTATION_UNSPECIFIED);
+
+ WindowContainerTransaction t = new WindowContainerTransaction();
+ t.setIgnoreOrientationRequest(
+ taskDisplayArea.mRemoteToken.toWindowContainerToken(),
+ false /* ignoreOrientationRequest */);
+ mWm.mAtmService.mWindowOrganizerController.applyTransaction(t);
+
+ // TDA returns app request orientation when ignoreOrientationRequest == false
+ // DC uses the same as TDA returns when it is not UNSET.
+ assertThat(mDisplayContent.getLastOrientation()).isEqualTo(SCREEN_ORIENTATION_LANDSCAPE);
+ assertThat(taskDisplayArea.getOrientation()).isEqualTo(SCREEN_ORIENTATION_LANDSCAPE);
+
+ t.setIgnoreOrientationRequest(
+ taskDisplayArea.mRemoteToken.toWindowContainerToken(),
+ true /* ignoreOrientationRequest */);
+ mWm.mAtmService.mWindowOrganizerController.applyTransaction(t);
+
+ // TDA returns UNSET when ignoreOrientationRequest == true
+ // DC is LANDSCAPE because it is using the previous when TDA returns UNSET.
+ assertThat(taskDisplayArea.getOrientation()).isEqualTo(SCREEN_ORIENTATION_UNSET);
+ assertThat(mDisplayContent.getLastOrientation()).isEqualTo(SCREEN_ORIENTATION_LANDSCAPE);
+ }
+
+ @Test
public void testOverrideConfigSize() {
removeGlobalMinSizeRestriction();
final Task stack = new TaskBuilder(mSupervisor)
diff --git a/services/voiceinteraction/java/com/android/server/soundtrigger/SoundTriggerHelper.java b/services/voiceinteraction/java/com/android/server/soundtrigger/SoundTriggerHelper.java
index edbdd4e94ac8..b6506b408714 100644
--- a/services/voiceinteraction/java/com/android/server/soundtrigger/SoundTriggerHelper.java
+++ b/services/voiceinteraction/java/com/android/server/soundtrigger/SoundTriggerHelper.java
@@ -58,7 +58,9 @@ import java.util.ArrayList;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;
+import java.util.Objects;
import java.util.UUID;
+import java.util.function.BiFunction;
/**
* Helper for {@link SoundTrigger} APIs. Supports two types of models:
@@ -116,6 +118,9 @@ public class SoundTriggerHelper implements SoundTrigger.StatusListener {
private PowerSaveModeListener mPowerSaveModeListener;
+ private final BiFunction<Integer, SoundTrigger.StatusListener, SoundTriggerModule>
+ mModuleProvider;
+
// Handler to process call state changes will delay to allow time for the audio
// and sound trigger HALs to process the end of call notifications
// before we re enable pending recognition requests.
@@ -123,15 +128,17 @@ public class SoundTriggerHelper implements SoundTrigger.StatusListener {
private static final int MSG_CALL_STATE_CHANGED = 0;
private static final int CALL_INACTIVE_MSG_DELAY_MS = 1000;
- SoundTriggerHelper(Context context) {
+ SoundTriggerHelper(Context context,
+ @NonNull BiFunction<Integer, SoundTrigger.StatusListener,
+ SoundTriggerModule> moduleProvider) {
ArrayList <ModuleProperties> modules = new ArrayList<>();
+ mModuleProvider = moduleProvider;
int status = SoundTrigger.listModules(modules);
mContext = context;
mTelephonyManager = (TelephonyManager) context.getSystemService(Context.TELEPHONY_SERVICE);
mPowerManager = (PowerManager) context.getSystemService(Context.POWER_SERVICE);
mModelDataMap = new HashMap<UUID, ModelData>();
mKeyphraseUuidMap = new HashMap<Integer, UUID>();
- mPhoneStateListener = new MyCallStateListener();
if (status != SoundTrigger.STATUS_OK || modules.size() == 0) {
Slog.w(TAG, "listModules status=" + status + ", # of modules=" + modules.size());
mModuleProperties = null;
@@ -145,6 +152,7 @@ public class SoundTriggerHelper implements SoundTrigger.StatusListener {
if (looper == null) {
looper = Looper.getMainLooper();
}
+ mPhoneStateListener = new MyCallStateListener(looper);
if (looper != null) {
mHandler = new Handler(looper) {
@Override
@@ -264,7 +272,7 @@ public class SoundTriggerHelper implements SoundTrigger.StatusListener {
private int prepareForRecognition(ModelData modelData) {
if (mModule == null) {
- mModule = SoundTrigger.attachModule(mModuleProperties.getId(), this, null);
+ mModule = mModuleProvider.apply(mModuleProperties.getId(), this);
if (mModule == null) {
Slog.w(TAG, "prepareForRecognition: cannot attach to sound trigger module");
return STATUS_ERROR;
@@ -1042,6 +1050,10 @@ public class SoundTriggerHelper implements SoundTrigger.StatusListener {
}
class MyCallStateListener extends PhoneStateListener {
+ MyCallStateListener(@NonNull Looper looper) {
+ super(Objects.requireNonNull(looper));
+ }
+
@Override
public void onCallStateChanged(int state, String arg1) {
if (DBG) Slog.d(TAG, "onCallStateChanged: " + state);
diff --git a/services/voiceinteraction/java/com/android/server/soundtrigger/SoundTriggerInternal.java b/services/voiceinteraction/java/com/android/server/soundtrigger/SoundTriggerInternal.java
index 54dffdc4c13a..f77d4907cf03 100644
--- a/services/voiceinteraction/java/com/android/server/soundtrigger/SoundTriggerInternal.java
+++ b/services/voiceinteraction/java/com/android/server/soundtrigger/SoundTriggerInternal.java
@@ -16,6 +16,7 @@
package com.android.server.soundtrigger;
+import android.annotation.NonNull;
import android.annotation.Nullable;
import android.hardware.soundtrigger.IRecognitionStatusCallback;
import android.hardware.soundtrigger.ModelParams;
@@ -25,6 +26,7 @@ import android.hardware.soundtrigger.SoundTrigger.KeyphraseSoundModel;
import android.hardware.soundtrigger.SoundTrigger.ModelParamRange;
import android.hardware.soundtrigger.SoundTrigger.ModuleProperties;
import android.hardware.soundtrigger.SoundTrigger.RecognitionConfig;
+import android.media.permission.Identity;
import com.android.server.voiceinteraction.VoiceInteractionManagerService;
@@ -35,97 +37,114 @@ import java.io.PrintWriter;
* Provides a local service for managing voice-related recoginition models. This is primarily used
* by the {@link VoiceInteractionManagerService}.
*/
-public abstract class SoundTriggerInternal {
+public interface SoundTriggerInternal {
/**
- * Return codes for {@link #startRecognition(int, KeyphraseSoundModel,
+ * Return codes for {@link Session#startRecognition(int, KeyphraseSoundModel,
* IRecognitionStatusCallback, RecognitionConfig)},
- * {@link #stopRecognition(int, IRecognitionStatusCallback)}
+ * {@link Session#stopRecognition(int, IRecognitionStatusCallback)}
*/
- public static final int STATUS_ERROR = SoundTrigger.STATUS_ERROR;
- public static final int STATUS_OK = SoundTrigger.STATUS_OK;
+ int STATUS_ERROR = SoundTrigger.STATUS_ERROR;
+ int STATUS_OK = SoundTrigger.STATUS_OK;
- /**
- * Starts recognition for the given keyphraseId.
- *
- * @param keyphraseId The identifier of the keyphrase for which
- * the recognition is to be started.
- * @param soundModel The sound model to use for recognition.
- * @param listener The listener for the recognition events related to the given keyphrase.
- * @return One of {@link #STATUS_ERROR} or {@link #STATUS_OK}.
- */
- public abstract int startRecognition(int keyphraseId, KeyphraseSoundModel soundModel,
- IRecognitionStatusCallback listener, RecognitionConfig recognitionConfig);
+ Session attachAsOriginator(@NonNull Identity originatorIdentity);
+
+ Session attachAsMiddleman(@NonNull Identity middlemanIdentity,
+ @NonNull Identity originatorIdentity);
/**
- * Stops recognition for the given {@link Keyphrase} if a recognition is
- * currently active.
- *
- * @param keyphraseId The identifier of the keyphrase for which
- * the recognition is to be stopped.
- * @param listener The listener for the recognition events related to the given keyphrase.
- *
- * @return One of {@link #STATUS_ERROR} or {@link #STATUS_OK}.
+ * Dumps service-wide information.
*/
- public abstract int stopRecognition(int keyphraseId, IRecognitionStatusCallback listener);
+ void dump(FileDescriptor fd, PrintWriter pw, String[] args);
- public abstract ModuleProperties getModuleProperties();
+ interface Session {
+ /**
+ * Starts recognition for the given keyphraseId.
+ *
+ * @param keyphraseId The identifier of the keyphrase for which
+ * the recognition is to be started.
+ * @param soundModel The sound model to use for recognition.
+ * @param listener The listener for the recognition events related to the given
+ * keyphrase.
+ * @return One of {@link #STATUS_ERROR} or {@link #STATUS_OK}.
+ */
+ int startRecognition(int keyphraseId, KeyphraseSoundModel soundModel,
+ IRecognitionStatusCallback listener, RecognitionConfig recognitionConfig);
- /**
- * Set a model specific {@link ModelParams} with the given value. This
- * parameter will keep its value for the duration the model is loaded regardless of starting and
- * stopping recognition. Once the model is unloaded, the value will be lost.
- * {@link SoundTriggerInternal#queryParameter} should be checked first before calling this
- * method.
- *
- * @param keyphraseId The identifier of the keyphrase for which
- * to modify model parameters
- * @param modelParam {@link ModelParams}
- * @param value Value to set
- * @return - {@link SoundTrigger#STATUS_OK} in case of success
- * - {@link SoundTrigger#STATUS_NO_INIT} if the native service cannot be reached
- * - {@link SoundTrigger#STATUS_BAD_VALUE} invalid input parameter
- * - {@link SoundTrigger#STATUS_INVALID_OPERATION} if the call is out of sequence or
- * if API is not supported by HAL
- */
- public abstract int setParameter(int keyphraseId, @ModelParams int modelParam, int value);
+ /**
+ * Stops recognition for the given {@link Keyphrase} if a recognition is
+ * currently active.
+ *
+ * @param keyphraseId The identifier of the keyphrase for which
+ * the recognition is to be stopped.
+ * @param listener The listener for the recognition events related to the given
+ * keyphrase.
+ * @return One of {@link #STATUS_ERROR} or {@link #STATUS_OK}.
+ */
+ int stopRecognition(int keyphraseId, IRecognitionStatusCallback listener);
- /**
- * Get a model specific {@link ModelParams}. This parameter will keep its value
- * for the duration the model is loaded regardless of starting and stopping recognition.
- * Once the model is unloaded, the value will be lost. If the value is not set, a default
- * value is returned. See ModelParams for parameter default values.
- * {@link SoundTriggerInternal#queryParameter} should be checked first before calling this
- * method.
- *
- * @param keyphraseId The identifier of the keyphrase for which
- * to modify model parameters
- * @param modelParam {@link ModelParams}
- * @return value of parameter
- * @throws UnsupportedOperationException if hal or model do not support this API.
- * queryParameter should be checked first.
- * @throws IllegalArgumentException if invalid model handle or parameter is passed.
- * queryParameter should be checked first.
- */
- public abstract int getParameter(int keyphraseId, @ModelParams int modelParam);
+ ModuleProperties getModuleProperties();
- /**
- * Determine if parameter control is supported for the given model handle.
- * This method should be checked prior to calling {@link SoundTriggerInternal#setParameter}
- * or {@link SoundTriggerInternal#getParameter}.
- *
- * @param keyphraseId The identifier of the keyphrase for which
- * to modify model parameters
- * @param modelParam {@link ModelParams}
- * @return supported range of parameter, null if not supported
- */
- @Nullable
- public abstract ModelParamRange queryParameter(int keyphraseId,
- @ModelParams int modelParam);
+ /**
+ * Set a model specific {@link ModelParams} with the given value. This
+ * parameter will keep its value for the duration the model is loaded regardless of starting
+ * and
+ * stopping recognition. Once the model is unloaded, the value will be lost.
+ * {@link #queryParameter} should be checked first before calling this
+ * method.
+ *
+ * @param keyphraseId The identifier of the keyphrase for which
+ * to modify model parameters
+ * @param modelParam {@link ModelParams}
+ * @param value Value to set
+ * @return - {@link SoundTrigger#STATUS_OK} in case of success
+ * - {@link SoundTrigger#STATUS_NO_INIT} if the native service cannot be reached
+ * - {@link SoundTrigger#STATUS_BAD_VALUE} invalid input parameter
+ * - {@link SoundTrigger#STATUS_INVALID_OPERATION} if the call is out of sequence or
+ * if API is not supported by HAL
+ */
+ int setParameter(int keyphraseId, @ModelParams int modelParam, int value);
- /**
- * Unloads (and stops if running) the given keyphraseId
- */
- public abstract int unloadKeyphraseModel(int keyphaseId);
+ /**
+ * Get a model specific {@link ModelParams}. This parameter will keep its value
+ * for the duration the model is loaded regardless of starting and stopping recognition.
+ * Once the model is unloaded, the value will be lost. If the value is not set, a default
+ * value is returned. See ModelParams for parameter default values.
+ * {{@link #queryParameter}} should be checked first before calling this
+ * method.
+ *
+ * @param keyphraseId The identifier of the keyphrase for which
+ * to modify model parameters
+ * @param modelParam {@link ModelParams}
+ * @return value of parameter
+ * @throws UnsupportedOperationException if hal or model do not support this API.
+ * queryParameter should be checked first.
+ * @throws IllegalArgumentException if invalid model handle or parameter is passed.
+ * queryParameter should be checked first.
+ */
+ int getParameter(int keyphraseId, @ModelParams int modelParam);
+
+ /**
+ * Determine if parameter control is supported for the given model handle.
+ * This method should be checked prior to calling {@link #setParameter}
+ * or {@link #getParameter}.
+ *
+ * @param keyphraseId The identifier of the keyphrase for which
+ * to modify model parameters
+ * @param modelParam {@link ModelParams}
+ * @return supported range of parameter, null if not supported
+ */
+ @Nullable
+ ModelParamRange queryParameter(int keyphraseId,
+ @ModelParams int modelParam);
+
+ /**
+ * Unloads (and stops if running) the given keyphraseId
+ */
+ int unloadKeyphraseModel(int keyphaseId);
- public abstract void dump(FileDescriptor fd, PrintWriter pw, String[] args);
+ /**
+ * Dumps session-wide information.
+ */
+ void dump(FileDescriptor fd, PrintWriter pw, String[] args);
+ }
}
diff --git a/services/voiceinteraction/java/com/android/server/soundtrigger/SoundTriggerService.java b/services/voiceinteraction/java/com/android/server/soundtrigger/SoundTriggerService.java
index 26d46dbd2ab7..a73e73e37fff 100644
--- a/services/voiceinteraction/java/com/android/server/soundtrigger/SoundTriggerService.java
+++ b/services/voiceinteraction/java/com/android/server/soundtrigger/SoundTriggerService.java
@@ -17,6 +17,7 @@
package com.android.server.soundtrigger;
import static android.Manifest.permission.BIND_SOUND_TRIGGER_DETECTION_SERVICE;
+import static android.Manifest.permission.SOUNDTRIGGER_DELEGATE_IDENTITY;
import static android.content.Context.BIND_AUTO_CREATE;
import static android.content.Context.BIND_FOREGROUND_SERVICE;
import static android.content.Context.BIND_INCLUDE_CAPABILITIES;
@@ -25,7 +26,6 @@ import static android.content.pm.PackageManager.GET_SERVICES;
import static android.content.pm.PackageManager.MATCH_DEBUG_TRIAGED_MISSING;
import static android.hardware.soundtrigger.SoundTrigger.STATUS_BAD_VALUE;
import static android.hardware.soundtrigger.SoundTrigger.STATUS_ERROR;
-import static android.hardware.soundtrigger.SoundTrigger.STATUS_NO_INIT;
import static android.hardware.soundtrigger.SoundTrigger.STATUS_OK;
import static android.provider.Settings.Global.MAX_SOUND_TRIGGER_DETECTION_SERVICE_OPS_PER_DAY;
import static android.provider.Settings.Global.SOUND_TRIGGER_DETECTION_SERVICE_OP_TIMEOUT;
@@ -35,6 +35,7 @@ import static com.android.internal.util.function.pooled.PooledLambda.obtainMessa
import android.Manifest;
import android.annotation.NonNull;
import android.annotation.Nullable;
+import android.app.ActivityThread;
import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
@@ -54,6 +55,10 @@ import android.media.AudioAttributes;
import android.media.AudioFormat;
import android.media.AudioRecord;
import android.media.MediaRecorder;
+import android.media.permission.Identity;
+import android.media.permission.IdentityContext;
+import android.media.permission.PermissionUtil;
+import android.media.permission.SafeCloseable;
import android.media.soundtrigger.ISoundTriggerDetectionService;
import android.media.soundtrigger.ISoundTriggerDetectionServiceClient;
import android.media.soundtrigger.SoundTriggerDetectionService;
@@ -75,6 +80,7 @@ import android.util.Slog;
import com.android.internal.annotations.GuardedBy;
import com.android.internal.app.ISoundTriggerService;
+import com.android.internal.app.ISoundTriggerSession;
import com.android.server.SystemService;
import java.io.FileDescriptor;
@@ -104,10 +110,6 @@ public class SoundTriggerService extends SystemService {
private final SoundTriggerServiceStub mServiceStub;
private final LocalSoundTriggerService mLocalSoundTriggerService;
private SoundTriggerDbHelper mDbHelper;
- private SoundTriggerHelper mSoundTriggerHelper;
- private final TreeMap<UUID, SoundModel> mLoadedModels;
- private Object mCallbacksLock;
- private final TreeMap<UUID, IRecognitionStatusCallback> mCallbacks;
class SoundModelStatTracker {
private class SoundModelStat {
@@ -192,9 +194,6 @@ public class SoundTriggerService extends SystemService {
mContext = context;
mServiceStub = new SoundTriggerServiceStub();
mLocalSoundTriggerService = new LocalSoundTriggerService(context);
- mLoadedModels = new TreeMap<UUID, SoundModel>();
- mCallbacksLock = new Object();
- mCallbacks = new TreeMap<>();
mLock = new Object();
mSoundModelStatTracker = new SoundModelStatTracker();
}
@@ -208,34 +207,54 @@ public class SoundTriggerService extends SystemService {
@Override
public void onBootPhase(int phase) {
Slog.d(TAG, "onBootPhase: " + phase + " : " + isSafeMode());
- if (PHASE_DEVICE_SPECIFIC_SERVICES_READY == phase) {
- if (isSafeMode()) {
- Slog.w(TAG, "not enabling SoundTriggerService in safe mode");
- return;
- }
-
- initSoundTriggerHelper();
- mLocalSoundTriggerService.setSoundTriggerHelper(mSoundTriggerHelper);
- } else if (PHASE_THIRD_PARTY_APPS_CAN_START == phase) {
+ if (PHASE_THIRD_PARTY_APPS_CAN_START == phase) {
mDbHelper = new SoundTriggerDbHelper(mContext);
}
}
- private synchronized void initSoundTriggerHelper() {
- if (mSoundTriggerHelper == null) {
- mSoundTriggerHelper = new SoundTriggerHelper(mContext);
- }
+ private SoundTriggerHelper newSoundTriggerHelper() {
+ Identity middlemanIdentity = new Identity();
+ middlemanIdentity.packageName = ActivityThread.currentOpPackageName();
+
+ Identity originatorIdentity = IdentityContext.getNonNull();
+
+ return new SoundTriggerHelper(mContext,
+ (moduleId, listener) -> SoundTrigger.attachModuleAsMiddleman(moduleId, listener,
+ null,
+ middlemanIdentity, originatorIdentity));
}
- private synchronized boolean isInitialized() {
- if (mSoundTriggerHelper == null ) {
- Slog.e(TAG, "SoundTriggerHelper not initialized.");
- return false;
+ class SoundTriggerServiceStub extends ISoundTriggerService.Stub {
+ @Override
+ public ISoundTriggerSession attachAsOriginator(Identity originatorIdentity) {
+ try (SafeCloseable ignored = PermissionUtil.establishIdentityDirect(
+ originatorIdentity)) {
+ return new SoundTriggerSessionStub(newSoundTriggerHelper());
+ }
+ }
+
+ @Override
+ public ISoundTriggerSession attachAsMiddleman(Identity originatorIdentity,
+ Identity middlemanIdentity) {
+ try (SafeCloseable ignored = PermissionUtil.establishIdentityIndirect(mContext,
+ SOUNDTRIGGER_DELEGATE_IDENTITY, middlemanIdentity,
+ originatorIdentity)) {
+ return new SoundTriggerSessionStub(newSoundTriggerHelper());
+ }
}
- return true;
}
- class SoundTriggerServiceStub extends ISoundTriggerService.Stub {
+ class SoundTriggerSessionStub extends ISoundTriggerSession.Stub {
+ private final SoundTriggerHelper mSoundTriggerHelper;
+ private final TreeMap<UUID, SoundModel> mLoadedModels = new TreeMap<>();
+ private final Object mCallbacksLock = new Object();
+ private final TreeMap<UUID, IRecognitionStatusCallback> mCallbacks = new TreeMap<>();
+
+ SoundTriggerSessionStub(
+ SoundTriggerHelper soundTriggerHelper) {
+ mSoundTriggerHelper = soundTriggerHelper;
+ }
+
@Override
public boolean onTransact(int code, Parcel data, Parcel reply, int flags)
throws RemoteException {
@@ -255,7 +274,6 @@ public class SoundTriggerService extends SystemService {
public int startRecognition(ParcelUuid parcelUuid, IRecognitionStatusCallback callback,
RecognitionConfig config) {
enforceCallingPermission(Manifest.permission.MANAGE_SOUND_TRIGGER);
- if (!isInitialized()) return STATUS_ERROR;
if (DEBUG) {
Slog.i(TAG, "startRecognition(): Uuid : " + parcelUuid);
}
@@ -291,8 +309,6 @@ public class SoundTriggerService extends SystemService {
sEventLogger.log(new SoundTriggerLogger.StringEvent("stopRecognition(): Uuid : "
+ parcelUuid));
- if (!isInitialized()) return STATUS_ERROR;
-
int ret = mSoundTriggerHelper.stopGenericRecognition(parcelUuid.getUuid(), callback);
if (ret == STATUS_OK) {
mSoundModelStatTracker.onStop(parcelUuid.getUuid());
@@ -338,13 +354,11 @@ public class SoundTriggerService extends SystemService {
sEventLogger.log(new SoundTriggerLogger.StringEvent("deleteSoundModel(): id = "
+ soundModelId));
- if (isInitialized()) {
- // Unload the model if it is loaded.
- mSoundTriggerHelper.unloadGenericSoundModel(soundModelId.getUuid());
+ // Unload the model if it is loaded.
+ mSoundTriggerHelper.unloadGenericSoundModel(soundModelId.getUuid());
- // Stop tracking recognition if it is started.
- mSoundModelStatTracker.onStop(soundModelId.getUuid());
- }
+ // Stop tracking recognition if it is started.
+ mSoundModelStatTracker.onStop(soundModelId.getUuid());
mDbHelper.deleteGenericSoundModel(soundModelId.getUuid());
}
@@ -352,7 +366,6 @@ public class SoundTriggerService extends SystemService {
@Override
public int loadGenericSoundModel(GenericSoundModel soundModel) {
enforceCallingPermission(Manifest.permission.MANAGE_SOUND_TRIGGER);
- if (!isInitialized()) return STATUS_ERROR;
if (soundModel == null || soundModel.getUuid() == null) {
Slog.e(TAG, "Invalid sound model");
@@ -387,7 +400,6 @@ public class SoundTriggerService extends SystemService {
@Override
public int loadKeyphraseSoundModel(KeyphraseSoundModel soundModel) {
enforceCallingPermission(Manifest.permission.MANAGE_SOUND_TRIGGER);
- if (!isInitialized()) return STATUS_ERROR;
if (soundModel == null || soundModel.getUuid() == null) {
Slog.e(TAG, "Invalid sound model");
@@ -440,7 +452,6 @@ public class SoundTriggerService extends SystemService {
enforceDetectionPermissions(detectionService);
- if (!isInitialized()) return STATUS_ERROR;
if (DEBUG) {
Slog.i(TAG, "startRecognition(): id = " + soundModelId);
}
@@ -510,7 +521,6 @@ public class SoundTriggerService extends SystemService {
@Override
public int stopRecognitionForService(ParcelUuid soundModelId) {
enforceCallingPermission(Manifest.permission.MANAGE_SOUND_TRIGGER);
- if (!isInitialized()) return STATUS_ERROR;
if (DEBUG) {
Slog.i(TAG, "stopRecognition(): id = " + soundModelId);
}
@@ -577,7 +587,6 @@ public class SoundTriggerService extends SystemService {
@Override
public int unloadSoundModel(ParcelUuid soundModelId) {
enforceCallingPermission(Manifest.permission.MANAGE_SOUND_TRIGGER);
- if (!isInitialized()) return STATUS_ERROR;
if (DEBUG) {
Slog.i(TAG, "unloadSoundModel(): id = " + soundModelId);
}
@@ -628,7 +637,6 @@ public class SoundTriggerService extends SystemService {
@Override
public boolean isRecognitionActive(ParcelUuid parcelUuid) {
enforceCallingPermission(Manifest.permission.MANAGE_SOUND_TRIGGER);
- if (!isInitialized()) return false;
synchronized (mCallbacksLock) {
IRecognitionStatusCallback callback = mCallbacks.get(parcelUuid.getUuid());
if (callback == null) {
@@ -642,7 +650,6 @@ public class SoundTriggerService extends SystemService {
public int getModelState(ParcelUuid soundModelId) {
enforceCallingPermission(Manifest.permission.MANAGE_SOUND_TRIGGER);
int ret = STATUS_ERROR;
- if (!isInitialized()) return ret;
if (DEBUG) {
Slog.i(TAG, "getModelState(): id = " + soundModelId);
}
@@ -681,7 +688,6 @@ public class SoundTriggerService extends SystemService {
@Nullable
public ModuleProperties getModuleProperties() {
enforceCallingPermission(Manifest.permission.MANAGE_SOUND_TRIGGER);
- if (!isInitialized()) return null;
if (DEBUG) {
Slog.i(TAG, "getModuleProperties()");
}
@@ -698,7 +704,6 @@ public class SoundTriggerService extends SystemService {
public int setParameter(ParcelUuid soundModelId,
@ModelParams int modelParam, int value) {
enforceCallingPermission(Manifest.permission.MANAGE_SOUND_TRIGGER);
- if (!isInitialized()) return STATUS_NO_INIT;
if (DEBUG) {
Slog.d(TAG, "setParameter(): id=" + soundModelId
+ ", param=" + modelParam
@@ -731,9 +736,6 @@ public class SoundTriggerService extends SystemService {
@ModelParams int modelParam)
throws UnsupportedOperationException, IllegalArgumentException {
enforceCallingPermission(Manifest.permission.MANAGE_SOUND_TRIGGER);
- if (!isInitialized()) {
- throw new UnsupportedOperationException("SoundTriggerHelper not initialized");
- }
if (DEBUG) {
Slog.d(TAG, "getParameter(): id=" + soundModelId
+ ", param=" + modelParam);
@@ -763,7 +765,6 @@ public class SoundTriggerService extends SystemService {
public ModelParamRange queryParameter(@NonNull ParcelUuid soundModelId,
@ModelParams int modelParam) {
enforceCallingPermission(Manifest.permission.MANAGE_SOUND_TRIGGER);
- if (!isInitialized()) return null;
if (DEBUG) {
Slog.d(TAG, "queryParameter(): id=" + soundModelId
+ ", param=" + modelParam);
@@ -788,712 +789,745 @@ public class SoundTriggerService extends SystemService {
return mSoundTriggerHelper.queryParameter(soundModel.getUuid(), modelParam);
}
}
- }
-
- /**
- * Counts the number of operations added in the last 24 hours.
- */
- private static class NumOps {
- private final Object mLock = new Object();
-
- @GuardedBy("mLock")
- private int[] mNumOps = new int[24];
- @GuardedBy("mLock")
- private long mLastOpsHourSinceBoot;
/**
- * Clear buckets of new hours that have elapsed since last operation.
+ * Local end for a {@link SoundTriggerDetectionService}. Operations are queued up and
+ * executed when the service connects.
*
- * <p>I.e. when the last operation was triggered at 1:40 and the current operation was
- * triggered at 4:03, the buckets "2, 3, and 4" are cleared.
+ * <p>If operations take too long they are forcefully aborted.
*
- * @param currentTime Current elapsed time since boot in ns
+ * <p>This also limits the amount of operations in 24 hours.
*/
- void clearOldOps(long currentTime) {
- synchronized (mLock) {
- long numHoursSinceBoot = TimeUnit.HOURS.convert(currentTime, TimeUnit.NANOSECONDS);
-
- // Clear buckets of new hours that have elapsed since last operation
- // I.e. when the last operation was triggered at 1:40 and the current
- // operation was triggered at 4:03, the bucket "2, 3, and 4" is cleared
- if (mLastOpsHourSinceBoot != 0) {
- for (long hour = mLastOpsHourSinceBoot + 1; hour <= numHoursSinceBoot; hour++) {
- mNumOps[(int) (hour % 24)] = 0;
+ private class RemoteSoundTriggerDetectionService
+ extends IRecognitionStatusCallback.Stub implements ServiceConnection {
+ private static final int MSG_STOP_ALL_PENDING_OPERATIONS = 1;
+
+ private final Object mRemoteServiceLock = new Object();
+
+ /** UUID of the model the service is started for */
+ private final @NonNull
+ ParcelUuid mPuuid;
+ /** Params passed into the start method for the service */
+ private final @Nullable
+ Bundle mParams;
+ /** Component name passed when starting the service */
+ private final @NonNull
+ ComponentName mServiceName;
+ /** User that started the service */
+ private final @NonNull
+ UserHandle mUser;
+ /** Configuration of the recognition the service is handling */
+ private final @NonNull
+ RecognitionConfig mRecognitionConfig;
+ /** Wake lock keeping the remote service alive */
+ private final @NonNull
+ PowerManager.WakeLock mRemoteServiceWakeLock;
+
+ private final @NonNull
+ Handler mHandler;
+
+ /** Callbacks that are called by the service */
+ private final @NonNull
+ ISoundTriggerDetectionServiceClient mClient;
+
+ /** Operations that are pending because the service is not yet connected */
+ @GuardedBy("mRemoteServiceLock")
+ private final ArrayList<Operation> mPendingOps = new ArrayList<>();
+ /** Operations that have been send to the service but have no yet finished */
+ @GuardedBy("mRemoteServiceLock")
+ private final ArraySet<Integer> mRunningOpIds = new ArraySet<>();
+ /** The number of operations executed in each of the last 24 hours */
+ private final NumOps mNumOps;
+
+ /** The service binder if connected */
+ @GuardedBy("mRemoteServiceLock")
+ private @Nullable
+ ISoundTriggerDetectionService mService;
+ /** Whether the service has been bound */
+ @GuardedBy("mRemoteServiceLock")
+ private boolean mIsBound;
+ /** Whether the service has been destroyed */
+ @GuardedBy("mRemoteServiceLock")
+ private boolean mIsDestroyed;
+ /**
+ * Set once a final op is scheduled. No further ops can be added and the service is
+ * destroyed once the op finishes.
+ */
+ @GuardedBy("mRemoteServiceLock")
+ private boolean mDestroyOnceRunningOpsDone;
+
+ /** Total number of operations performed by this service */
+ @GuardedBy("mRemoteServiceLock")
+ private int mNumTotalOpsPerformed;
+
+ /**
+ * Create a new remote sound trigger detection service. This only binds to the service
+ * when operations are in flight. Each operation has a certain time it can run. Once no
+ * operations are allowed to run anymore, {@link #stopAllPendingOperations() all
+ * operations are aborted and stopped} and the service is disconnected.
+ *
+ * @param modelUuid The UUID of the model the recognition is for
+ * @param params The params passed to each method of the service
+ * @param serviceName The component name of the service
+ * @param user The user of the service
+ * @param config The configuration of the recognition
+ */
+ public RemoteSoundTriggerDetectionService(@NonNull UUID modelUuid,
+ @Nullable Bundle params, @NonNull ComponentName serviceName,
+ @NonNull UserHandle user, @NonNull RecognitionConfig config) {
+ mPuuid = new ParcelUuid(modelUuid);
+ mParams = params;
+ mServiceName = serviceName;
+ mUser = user;
+ mRecognitionConfig = config;
+ mHandler = new Handler(Looper.getMainLooper());
+
+ PowerManager pm = ((PowerManager) mContext.getSystemService(Context.POWER_SERVICE));
+ mRemoteServiceWakeLock = pm.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK,
+ "RemoteSoundTriggerDetectionService " + mServiceName.getPackageName() + ":"
+ + mServiceName.getClassName());
+
+ synchronized (mLock) {
+ NumOps numOps = mNumOpsPerPackage.get(mServiceName.getPackageName());
+ if (numOps == null) {
+ numOps = new NumOps();
+ mNumOpsPerPackage.put(mServiceName.getPackageName(), numOps);
}
+ mNumOps = numOps;
}
- }
- }
- /**
- * Add a new operation.
- *
- * @param currentTime Current elapsed time since boot in ns
- */
- void addOp(long currentTime) {
- synchronized (mLock) {
- long numHoursSinceBoot = TimeUnit.HOURS.convert(currentTime, TimeUnit.NANOSECONDS);
-
- mNumOps[(int) (numHoursSinceBoot % 24)]++;
- mLastOpsHourSinceBoot = numHoursSinceBoot;
- }
- }
-
- /**
- * Get the total operations added in the last 24 hours.
- *
- * @return The total number of operations added in the last 24 hours
- */
- int getOpsAdded() {
- synchronized (mLock) {
- int totalOperationsInLastDay = 0;
- for (int i = 0; i < 24; i++) {
- totalOperationsInLastDay += mNumOps[i];
- }
-
- return totalOperationsInLastDay;
- }
- }
- }
-
- /**
- * A single operation run in a {@link RemoteSoundTriggerDetectionService}.
- *
- * <p>Once the remote service is connected either setup + execute or setup + stop is executed.
- */
- private static class Operation {
- private interface ExecuteOp {
- void run(int opId, ISoundTriggerDetectionService service) throws RemoteException;
- }
-
- private final @Nullable Runnable mSetupOp;
- private final @NonNull ExecuteOp mExecuteOp;
- private final @Nullable Runnable mDropOp;
-
- private Operation(@Nullable Runnable setupOp, @NonNull ExecuteOp executeOp,
- @Nullable Runnable cancelOp) {
- mSetupOp = setupOp;
- mExecuteOp = executeOp;
- mDropOp = cancelOp;
- }
-
- private void setup() {
- if (mSetupOp != null) {
- mSetupOp.run();
- }
- }
-
- void run(int opId, @NonNull ISoundTriggerDetectionService service) throws RemoteException {
- setup();
- mExecuteOp.run(opId, service);
- }
-
- void drop() {
- setup();
-
- if (mDropOp != null) {
- mDropOp.run();
- }
- }
- }
-
- /**
- * Local end for a {@link SoundTriggerDetectionService}. Operations are queued up and executed
- * when the service connects.
- *
- * <p>If operations take too long they are forcefully aborted.
- *
- * <p>This also limits the amount of operations in 24 hours.
- */
- private class RemoteSoundTriggerDetectionService
- extends IRecognitionStatusCallback.Stub implements ServiceConnection {
- private static final int MSG_STOP_ALL_PENDING_OPERATIONS = 1;
-
- private final Object mRemoteServiceLock = new Object();
-
- /** UUID of the model the service is started for */
- private final @NonNull ParcelUuid mPuuid;
- /** Params passed into the start method for the service */
- private final @Nullable Bundle mParams;
- /** Component name passed when starting the service */
- private final @NonNull ComponentName mServiceName;
- /** User that started the service */
- private final @NonNull UserHandle mUser;
- /** Configuration of the recognition the service is handling */
- private final @NonNull RecognitionConfig mRecognitionConfig;
- /** Wake lock keeping the remote service alive */
- private final @NonNull PowerManager.WakeLock mRemoteServiceWakeLock;
-
- private final @NonNull Handler mHandler;
-
- /** Callbacks that are called by the service */
- private final @NonNull ISoundTriggerDetectionServiceClient mClient;
-
- /** Operations that are pending because the service is not yet connected */
- @GuardedBy("mRemoteServiceLock")
- private final ArrayList<Operation> mPendingOps = new ArrayList<>();
- /** Operations that have been send to the service but have no yet finished */
- @GuardedBy("mRemoteServiceLock")
- private final ArraySet<Integer> mRunningOpIds = new ArraySet<>();
- /** The number of operations executed in each of the last 24 hours */
- private final NumOps mNumOps;
-
- /** The service binder if connected */
- @GuardedBy("mRemoteServiceLock")
- private @Nullable ISoundTriggerDetectionService mService;
- /** Whether the service has been bound */
- @GuardedBy("mRemoteServiceLock")
- private boolean mIsBound;
- /** Whether the service has been destroyed */
- @GuardedBy("mRemoteServiceLock")
- private boolean mIsDestroyed;
- /**
- * Set once a final op is scheduled. No further ops can be added and the service is
- * destroyed once the op finishes.
- */
- @GuardedBy("mRemoteServiceLock")
- private boolean mDestroyOnceRunningOpsDone;
-
- /** Total number of operations performed by this service */
- @GuardedBy("mRemoteServiceLock")
- private int mNumTotalOpsPerformed;
-
- /**
- * Create a new remote sound trigger detection service. This only binds to the service when
- * operations are in flight. Each operation has a certain time it can run. Once no
- * operations are allowed to run anymore, {@link #stopAllPendingOperations() all operations
- * are aborted and stopped} and the service is disconnected.
- *
- * @param modelUuid The UUID of the model the recognition is for
- * @param params The params passed to each method of the service
- * @param serviceName The component name of the service
- * @param user The user of the service
- * @param config The configuration of the recognition
- */
- public RemoteSoundTriggerDetectionService(@NonNull UUID modelUuid,
- @Nullable Bundle params, @NonNull ComponentName serviceName, @NonNull UserHandle user,
- @NonNull RecognitionConfig config) {
- mPuuid = new ParcelUuid(modelUuid);
- mParams = params;
- mServiceName = serviceName;
- mUser = user;
- mRecognitionConfig = config;
- mHandler = new Handler(Looper.getMainLooper());
-
- PowerManager pm = ((PowerManager) mContext.getSystemService(Context.POWER_SERVICE));
- mRemoteServiceWakeLock = pm.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK,
- "RemoteSoundTriggerDetectionService " + mServiceName.getPackageName() + ":"
- + mServiceName.getClassName());
-
- synchronized (mLock) {
- NumOps numOps = mNumOpsPerPackage.get(mServiceName.getPackageName());
- if (numOps == null) {
- numOps = new NumOps();
- mNumOpsPerPackage.put(mServiceName.getPackageName(), numOps);
- }
- mNumOps = numOps;
- }
-
- mClient = new ISoundTriggerDetectionServiceClient.Stub() {
- @Override
- public void onOpFinished(int opId) {
- long token = Binder.clearCallingIdentity();
- try {
- synchronized (mRemoteServiceLock) {
- mRunningOpIds.remove(opId);
-
- if (mRunningOpIds.isEmpty() && mPendingOps.isEmpty()) {
- if (mDestroyOnceRunningOpsDone) {
- destroy();
- } else {
- disconnectLocked();
+ mClient = new ISoundTriggerDetectionServiceClient.Stub() {
+ @Override
+ public void onOpFinished(int opId) {
+ long token = Binder.clearCallingIdentity();
+ try {
+ synchronized (mRemoteServiceLock) {
+ mRunningOpIds.remove(opId);
+
+ if (mRunningOpIds.isEmpty() && mPendingOps.isEmpty()) {
+ if (mDestroyOnceRunningOpsDone) {
+ destroy();
+ } else {
+ disconnectLocked();
+ }
}
}
+ } finally {
+ Binder.restoreCallingIdentity(token);
}
- } finally {
- Binder.restoreCallingIdentity(token);
}
- }
- };
- }
+ };
+ }
- @Override
- public boolean pingBinder() {
- return !(mIsDestroyed || mDestroyOnceRunningOpsDone);
- }
+ @Override
+ public boolean pingBinder() {
+ return !(mIsDestroyed || mDestroyOnceRunningOpsDone);
+ }
- /**
- * Disconnect from the service, but allow to re-connect when new operations are triggered.
- */
- @GuardedBy("mRemoteServiceLock")
- private void disconnectLocked() {
- if (mService != null) {
- try {
- mService.removeClient(mPuuid);
- } catch (Exception e) {
- Slog.e(TAG, mPuuid + ": Cannot remove client", e);
+ /**
+ * Disconnect from the service, but allow to re-connect when new operations are
+ * triggered.
+ */
+ @GuardedBy("mRemoteServiceLock")
+ private void disconnectLocked() {
+ if (mService != null) {
+ try {
+ mService.removeClient(mPuuid);
+ } catch (Exception e) {
+ Slog.e(TAG, mPuuid + ": Cannot remove client", e);
- sEventLogger.log(new SoundTriggerLogger.StringEvent(mPuuid
- + ": Cannot remove client"));
+ sEventLogger.log(new SoundTriggerLogger.StringEvent(mPuuid
+ + ": Cannot remove client"));
- }
+ }
- mService = null;
- }
+ mService = null;
+ }
- if (mIsBound) {
- mContext.unbindService(RemoteSoundTriggerDetectionService.this);
- mIsBound = false;
+ if (mIsBound) {
+ mContext.unbindService(RemoteSoundTriggerDetectionService.this);
+ mIsBound = false;
- synchronized (mCallbacksLock) {
- mRemoteServiceWakeLock.release();
+ synchronized (mCallbacksLock) {
+ mRemoteServiceWakeLock.release();
+ }
}
}
- }
- /**
- * Disconnect, do not allow to reconnect to the service. All further operations will be
- * dropped.
- */
- private void destroy() {
- if (DEBUG) Slog.v(TAG, mPuuid + ": destroy");
+ /**
+ * Disconnect, do not allow to reconnect to the service. All further operations will be
+ * dropped.
+ */
+ private void destroy() {
+ if (DEBUG) Slog.v(TAG, mPuuid + ": destroy");
- sEventLogger.log(new SoundTriggerLogger.StringEvent(mPuuid + ": destroy"));
+ sEventLogger.log(new SoundTriggerLogger.StringEvent(mPuuid + ": destroy"));
- synchronized (mRemoteServiceLock) {
- disconnectLocked();
+ synchronized (mRemoteServiceLock) {
+ disconnectLocked();
- mIsDestroyed = true;
- }
+ mIsDestroyed = true;
+ }
- // The callback is removed before the flag is set
- if (!mDestroyOnceRunningOpsDone) {
- synchronized (mCallbacksLock) {
- mCallbacks.remove(mPuuid.getUuid());
+ // The callback is removed before the flag is set
+ if (!mDestroyOnceRunningOpsDone) {
+ synchronized (mCallbacksLock) {
+ mCallbacks.remove(mPuuid.getUuid());
+ }
}
}
- }
- /**
- * Stop all pending operations and then disconnect for the service.
- */
- private void stopAllPendingOperations() {
- synchronized (mRemoteServiceLock) {
- if (mIsDestroyed) {
- return;
- }
+ /**
+ * Stop all pending operations and then disconnect for the service.
+ */
+ private void stopAllPendingOperations() {
+ synchronized (mRemoteServiceLock) {
+ if (mIsDestroyed) {
+ return;
+ }
- if (mService != null) {
- int numOps = mRunningOpIds.size();
- for (int i = 0; i < numOps; i++) {
- try {
- mService.onStopOperation(mPuuid, mRunningOpIds.valueAt(i));
- } catch (Exception e) {
- Slog.e(TAG, mPuuid + ": Could not stop operation "
- + mRunningOpIds.valueAt(i), e);
+ if (mService != null) {
+ int numOps = mRunningOpIds.size();
+ for (int i = 0; i < numOps; i++) {
+ try {
+ mService.onStopOperation(mPuuid, mRunningOpIds.valueAt(i));
+ } catch (Exception e) {
+ Slog.e(TAG, mPuuid + ": Could not stop operation "
+ + mRunningOpIds.valueAt(i), e);
- sEventLogger.log(new SoundTriggerLogger.StringEvent(mPuuid
- + ": Could not stop operation " + mRunningOpIds.valueAt(i)));
+ sEventLogger.log(new SoundTriggerLogger.StringEvent(mPuuid
+ + ": Could not stop operation " + mRunningOpIds.valueAt(
+ i)));
+ }
}
+
+ mRunningOpIds.clear();
}
- mRunningOpIds.clear();
+ disconnectLocked();
}
-
- disconnectLocked();
}
- }
- /**
- * Verify that the service has the expected properties and then bind to the service
- */
- private void bind() {
- long token = Binder.clearCallingIdentity();
- try {
- Intent i = new Intent();
- i.setComponent(mServiceName);
+ /**
+ * Verify that the service has the expected properties and then bind to the service
+ */
+ private void bind() {
+ long token = Binder.clearCallingIdentity();
+ try {
+ Intent i = new Intent();
+ i.setComponent(mServiceName);
- ResolveInfo ri = mContext.getPackageManager().resolveServiceAsUser(i,
- GET_SERVICES | GET_META_DATA | MATCH_DEBUG_TRIAGED_MISSING,
- mUser.getIdentifier());
+ ResolveInfo ri = mContext.getPackageManager().resolveServiceAsUser(i,
+ GET_SERVICES | GET_META_DATA | MATCH_DEBUG_TRIAGED_MISSING,
+ mUser.getIdentifier());
- if (ri == null) {
- Slog.w(TAG, mPuuid + ": " + mServiceName + " not found");
+ if (ri == null) {
+ Slog.w(TAG, mPuuid + ": " + mServiceName + " not found");
- sEventLogger.log(new SoundTriggerLogger.StringEvent(mPuuid
- + ": " + mServiceName + " not found"));
+ sEventLogger.log(new SoundTriggerLogger.StringEvent(mPuuid
+ + ": " + mServiceName + " not found"));
- return;
- }
+ return;
+ }
- if (!BIND_SOUND_TRIGGER_DETECTION_SERVICE
- .equals(ri.serviceInfo.permission)) {
- Slog.w(TAG, mPuuid + ": " + mServiceName + " does not require "
- + BIND_SOUND_TRIGGER_DETECTION_SERVICE);
+ if (!BIND_SOUND_TRIGGER_DETECTION_SERVICE
+ .equals(ri.serviceInfo.permission)) {
+ Slog.w(TAG, mPuuid + ": " + mServiceName + " does not require "
+ + BIND_SOUND_TRIGGER_DETECTION_SERVICE);
- sEventLogger.log(new SoundTriggerLogger.StringEvent(mPuuid
- + ": " + mServiceName + " does not require "
- + BIND_SOUND_TRIGGER_DETECTION_SERVICE));
+ sEventLogger.log(new SoundTriggerLogger.StringEvent(mPuuid
+ + ": " + mServiceName + " does not require "
+ + BIND_SOUND_TRIGGER_DETECTION_SERVICE));
- return;
- }
+ return;
+ }
- mIsBound = mContext.bindServiceAsUser(i, this,
- BIND_AUTO_CREATE | BIND_FOREGROUND_SERVICE | BIND_INCLUDE_CAPABILITIES,
- mUser);
+ mIsBound = mContext.bindServiceAsUser(i, this,
+ BIND_AUTO_CREATE | BIND_FOREGROUND_SERVICE | BIND_INCLUDE_CAPABILITIES,
+ mUser);
- if (mIsBound) {
- mRemoteServiceWakeLock.acquire();
- } else {
- Slog.w(TAG, mPuuid + ": Could not bind to " + mServiceName);
+ if (mIsBound) {
+ mRemoteServiceWakeLock.acquire();
+ } else {
+ Slog.w(TAG, mPuuid + ": Could not bind to " + mServiceName);
- sEventLogger.log(new SoundTriggerLogger.StringEvent(mPuuid
- + ": Could not bind to " + mServiceName));
+ sEventLogger.log(new SoundTriggerLogger.StringEvent(mPuuid
+ + ": Could not bind to " + mServiceName));
+ }
+ } finally {
+ Binder.restoreCallingIdentity(token);
}
- } finally {
- Binder.restoreCallingIdentity(token);
}
- }
- /**
- * Run an operation (i.e. send it do the service). If the service is not connected, this
- * binds the service and then runs the operation once connected.
- *
- * @param op The operation to run
- */
- private void runOrAddOperation(Operation op) {
- synchronized (mRemoteServiceLock) {
- if (mIsDestroyed || mDestroyOnceRunningOpsDone) {
- Slog.w(TAG, mPuuid + ": Dropped operation as already destroyed or marked for "
- + "destruction");
+ /**
+ * Run an operation (i.e. send it do the service). If the service is not connected, this
+ * binds the service and then runs the operation once connected.
+ *
+ * @param op The operation to run
+ */
+ private void runOrAddOperation(Operation op) {
+ synchronized (mRemoteServiceLock) {
+ if (mIsDestroyed || mDestroyOnceRunningOpsDone) {
+ Slog.w(TAG,
+ mPuuid + ": Dropped operation as already destroyed or marked for "
+ + "destruction");
+
+ sEventLogger.log(new SoundTriggerLogger.StringEvent(mPuuid
+ + ":Dropped operation as already destroyed or marked for "
+ + "destruction"));
+
+ op.drop();
+ return;
+ }
- sEventLogger.log(new SoundTriggerLogger.StringEvent(mPuuid
- + ":Dropped operation as already destroyed or marked for destruction"));
+ if (mService == null) {
+ mPendingOps.add(op);
- op.drop();
- return;
- }
+ if (!mIsBound) {
+ bind();
+ }
+ } else {
+ long currentTime = System.nanoTime();
+ mNumOps.clearOldOps(currentTime);
- if (mService == null) {
- mPendingOps.add(op);
+ // Drop operation if too many were executed in the last 24 hours.
+ int opsAllowed = Settings.Global.getInt(mContext.getContentResolver(),
+ MAX_SOUND_TRIGGER_DETECTION_SERVICE_OPS_PER_DAY,
+ Integer.MAX_VALUE);
- if (!mIsBound) {
- bind();
- }
- } else {
- long currentTime = System.nanoTime();
- mNumOps.clearOldOps(currentTime);
-
- // Drop operation if too many were executed in the last 24 hours.
- int opsAllowed = Settings.Global.getInt(mContext.getContentResolver(),
- MAX_SOUND_TRIGGER_DETECTION_SERVICE_OPS_PER_DAY,
- Integer.MAX_VALUE);
-
- // As we currently cannot dropping an op safely, disable throttling
- int opsAdded = mNumOps.getOpsAdded();
- if (false && mNumOps.getOpsAdded() >= opsAllowed) {
- try {
- if (DEBUG || opsAllowed + 10 > opsAdded) {
- Slog.w(TAG, mPuuid + ": Dropped operation as too many operations "
- + "were run in last 24 hours");
+ // As we currently cannot dropping an op safely, disable throttling
+ int opsAdded = mNumOps.getOpsAdded();
+ if (false && mNumOps.getOpsAdded() >= opsAllowed) {
+ try {
+ if (DEBUG || opsAllowed + 10 > opsAdded) {
+ Slog.w(TAG,
+ mPuuid + ": Dropped operation as too many operations "
+ + "were run in last 24 hours");
- sEventLogger.log(new SoundTriggerLogger.StringEvent(mPuuid
- + ": Dropped operation as too many operations "
- + "were run in last 24 hours"));
+ sEventLogger.log(new SoundTriggerLogger.StringEvent(mPuuid
+ + ": Dropped operation as too many operations "
+ + "were run in last 24 hours"));
- }
+ }
- op.drop();
- } catch (Exception e) {
- Slog.e(TAG, mPuuid + ": Could not drop operation", e);
+ op.drop();
+ } catch (Exception e) {
+ Slog.e(TAG, mPuuid + ": Could not drop operation", e);
- sEventLogger.log(new SoundTriggerLogger.StringEvent(mPuuid
+ sEventLogger.log(new SoundTriggerLogger.StringEvent(mPuuid
+ ": Could not drop operation"));
- }
- } else {
- mNumOps.addOp(currentTime);
+ }
+ } else {
+ mNumOps.addOp(currentTime);
- // Find a free opID
- int opId = mNumTotalOpsPerformed;
- do {
- mNumTotalOpsPerformed++;
- } while (mRunningOpIds.contains(opId));
+ // Find a free opID
+ int opId = mNumTotalOpsPerformed;
+ do {
+ mNumTotalOpsPerformed++;
+ } while (mRunningOpIds.contains(opId));
- // Run OP
- try {
- if (DEBUG) Slog.v(TAG, mPuuid + ": runOp " + opId);
+ // Run OP
+ try {
+ if (DEBUG) Slog.v(TAG, mPuuid + ": runOp " + opId);
- sEventLogger.log(new SoundTriggerLogger.StringEvent(mPuuid
+ sEventLogger.log(new SoundTriggerLogger.StringEvent(mPuuid
+ ": runOp " + opId));
- op.run(opId, mService);
- mRunningOpIds.add(opId);
- } catch (Exception e) {
- Slog.e(TAG, mPuuid + ": Could not run operation " + opId, e);
+ op.run(opId, mService);
+ mRunningOpIds.add(opId);
+ } catch (Exception e) {
+ Slog.e(TAG, mPuuid + ": Could not run operation " + opId, e);
- sEventLogger.log(new SoundTriggerLogger.StringEvent(mPuuid
+ sEventLogger.log(new SoundTriggerLogger.StringEvent(mPuuid
+ ": Could not run operation " + opId));
+ }
}
- }
- // Unbind from service if no operations are left (i.e. if the operation failed)
- if (mPendingOps.isEmpty() && mRunningOpIds.isEmpty()) {
- if (mDestroyOnceRunningOpsDone) {
- destroy();
+ // Unbind from service if no operations are left (i.e. if the operation
+ // failed)
+ if (mPendingOps.isEmpty() && mRunningOpIds.isEmpty()) {
+ if (mDestroyOnceRunningOpsDone) {
+ destroy();
+ } else {
+ disconnectLocked();
+ }
} else {
- disconnectLocked();
+ mHandler.removeMessages(MSG_STOP_ALL_PENDING_OPERATIONS);
+ mHandler.sendMessageDelayed(obtainMessage(
+ RemoteSoundTriggerDetectionService::stopAllPendingOperations,
+ this)
+ .setWhat(MSG_STOP_ALL_PENDING_OPERATIONS),
+ Settings.Global.getLong(mContext.getContentResolver(),
+ SOUND_TRIGGER_DETECTION_SERVICE_OP_TIMEOUT,
+ Long.MAX_VALUE));
}
- } else {
- mHandler.removeMessages(MSG_STOP_ALL_PENDING_OPERATIONS);
- mHandler.sendMessageDelayed(obtainMessage(
- RemoteSoundTriggerDetectionService::stopAllPendingOperations, this)
- .setWhat(MSG_STOP_ALL_PENDING_OPERATIONS),
- Settings.Global.getLong(mContext.getContentResolver(),
- SOUND_TRIGGER_DETECTION_SERVICE_OP_TIMEOUT,
- Long.MAX_VALUE));
}
}
}
- }
-
- @Override
- public void onKeyphraseDetected(SoundTrigger.KeyphraseRecognitionEvent event) {
- Slog.w(TAG, mPuuid + "->" + mServiceName + ": IGNORED onKeyphraseDetected(" + event
- + ")");
-
- sEventLogger.log(new SoundTriggerLogger.StringEvent(mPuuid + "->" + mServiceName
- + ": IGNORED onKeyphraseDetected(" + event + ")"));
- }
+ @Override
+ public void onKeyphraseDetected(SoundTrigger.KeyphraseRecognitionEvent event) {
+ Slog.w(TAG, mPuuid + "->" + mServiceName + ": IGNORED onKeyphraseDetected(" + event
+ + ")");
- /**
- * Create an AudioRecord enough for starting and releasing the data buffered for the event.
- *
- * @param event The event that was received
- * @return The initialized AudioRecord
- */
- private @NonNull AudioRecord createAudioRecordForEvent(
- @NonNull SoundTrigger.GenericRecognitionEvent event)
- throws IllegalArgumentException, UnsupportedOperationException {
- AudioAttributes.Builder attributesBuilder = new AudioAttributes.Builder();
- attributesBuilder.setInternalCapturePreset(MediaRecorder.AudioSource.HOTWORD);
- AudioAttributes attributes = attributesBuilder.build();
-
- AudioFormat originalFormat = event.getCaptureFormat();
-
- sEventLogger.log(new SoundTriggerLogger.StringEvent("createAudioRecordForEvent"));
-
- return (new AudioRecord.Builder())
- .setAudioAttributes(attributes)
- .setAudioFormat((new AudioFormat.Builder())
- .setChannelMask(originalFormat.getChannelMask())
- .setEncoding(originalFormat.getEncoding())
- .setSampleRate(originalFormat.getSampleRate())
- .build())
- .setSessionId(event.getCaptureSession())
- .build();
- }
+ sEventLogger.log(new SoundTriggerLogger.StringEvent(mPuuid + "->" + mServiceName
+ + ": IGNORED onKeyphraseDetected(" + event + ")"));
+ }
- @Override
- public void onGenericSoundTriggerDetected(SoundTrigger.GenericRecognitionEvent event) {
- if (DEBUG) Slog.v(TAG, mPuuid + ": Generic sound trigger event: " + event);
-
- sEventLogger.log(new SoundTriggerLogger.StringEvent(mPuuid
- + ": Generic sound trigger event: " + event));
-
- runOrAddOperation(new Operation(
- // always execute:
- () -> {
- if (!mRecognitionConfig.allowMultipleTriggers) {
- // Unregister this remoteService once op is done
- synchronized (mCallbacksLock) {
- mCallbacks.remove(mPuuid.getUuid());
- }
- mDestroyOnceRunningOpsDone = true;
- }
- },
- // execute if not throttled:
- (opId, service) -> service.onGenericRecognitionEvent(mPuuid, opId, event),
- // execute if throttled:
- () -> {
- if (event.isCaptureAvailable()) {
- try {
- AudioRecord capturedData = createAudioRecordForEvent(event);
- capturedData.startRecording();
- capturedData.release();
- } catch (IllegalArgumentException | UnsupportedOperationException e) {
- Slog.w(TAG, mPuuid + ": createAudioRecordForEvent(" + event
- + "), failed to create AudioRecord");
- }
- }
- }));
- }
+ /**
+ * Create an AudioRecord enough for starting and releasing the data buffered for the event.
+ *
+ * @param event The event that was received
+ * @return The initialized AudioRecord
+ */
+ private @NonNull AudioRecord createAudioRecordForEvent(
+ @NonNull SoundTrigger.GenericRecognitionEvent event)
+ throws IllegalArgumentException, UnsupportedOperationException {
+ AudioAttributes.Builder attributesBuilder = new AudioAttributes.Builder();
+ attributesBuilder.setInternalCapturePreset(MediaRecorder.AudioSource.HOTWORD);
+ AudioAttributes attributes = attributesBuilder.build();
+
+ AudioFormat originalFormat = event.getCaptureFormat();
+
+ sEventLogger.log(new SoundTriggerLogger.StringEvent("createAudioRecordForEvent"));
+
+ return (new AudioRecord.Builder())
+ .setAudioAttributes(attributes)
+ .setAudioFormat((new AudioFormat.Builder())
+ .setChannelMask(originalFormat.getChannelMask())
+ .setEncoding(originalFormat.getEncoding())
+ .setSampleRate(originalFormat.getSampleRate())
+ .build())
+ .setSessionId(event.getCaptureSession())
+ .build();
+ }
- @Override
- public void onError(int status) {
- if (DEBUG) Slog.v(TAG, mPuuid + ": onError: " + status);
+ @Override
+ public void onGenericSoundTriggerDetected(SoundTrigger.GenericRecognitionEvent event) {
+ if (DEBUG) Slog.v(TAG, mPuuid + ": Generic sound trigger event: " + event);
- sEventLogger.log(new SoundTriggerLogger.StringEvent(mPuuid
- + ": onError: " + status));
+ sEventLogger.log(new SoundTriggerLogger.StringEvent(mPuuid
+ + ": Generic sound trigger event: " + event));
- runOrAddOperation(
- new Operation(
- // always execute:
- () -> {
+ runOrAddOperation(new Operation(
+ // always execute:
+ () -> {
+ if (!mRecognitionConfig.allowMultipleTriggers) {
// Unregister this remoteService once op is done
synchronized (mCallbacksLock) {
mCallbacks.remove(mPuuid.getUuid());
}
mDestroyOnceRunningOpsDone = true;
- },
- // execute if not throttled:
- (opId, service) -> service.onError(mPuuid, opId, status),
- // nothing to do if throttled
- null));
- }
+ }
+ },
+ // execute if not throttled:
+ (opId, service) -> service.onGenericRecognitionEvent(mPuuid, opId, event),
+ // execute if throttled:
+ () -> {
+ if (event.isCaptureAvailable()) {
+ try {
+ AudioRecord capturedData = createAudioRecordForEvent(event);
+ capturedData.startRecording();
+ capturedData.release();
+ } catch (IllegalArgumentException | UnsupportedOperationException e) {
+ Slog.w(TAG, mPuuid + ": createAudioRecordForEvent(" + event
+ + "), failed to create AudioRecord");
+ }
+ }
+ }));
+ }
- @Override
- public void onRecognitionPaused() {
- Slog.i(TAG, mPuuid + "->" + mServiceName + ": IGNORED onRecognitionPaused");
+ @Override
+ public void onError(int status) {
+ if (DEBUG) Slog.v(TAG, mPuuid + ": onError: " + status);
+
+ sEventLogger.log(new SoundTriggerLogger.StringEvent(mPuuid
+ + ": onError: " + status));
+
+ runOrAddOperation(
+ new Operation(
+ // always execute:
+ () -> {
+ // Unregister this remoteService once op is done
+ synchronized (mCallbacksLock) {
+ mCallbacks.remove(mPuuid.getUuid());
+ }
+ mDestroyOnceRunningOpsDone = true;
+ },
+ // execute if not throttled:
+ (opId, service) -> service.onError(mPuuid, opId, status),
+ // nothing to do if throttled
+ null));
+ }
- sEventLogger.log(new SoundTriggerLogger.StringEvent(mPuuid
- + "->" + mServiceName + ": IGNORED onRecognitionPaused"));
+ @Override
+ public void onRecognitionPaused() {
+ Slog.i(TAG, mPuuid + "->" + mServiceName + ": IGNORED onRecognitionPaused");
- }
+ sEventLogger.log(new SoundTriggerLogger.StringEvent(mPuuid
+ + "->" + mServiceName + ": IGNORED onRecognitionPaused"));
- @Override
- public void onRecognitionResumed() {
- Slog.i(TAG, mPuuid + "->" + mServiceName + ": IGNORED onRecognitionResumed");
+ }
- sEventLogger.log(new SoundTriggerLogger.StringEvent(mPuuid
- + "->" + mServiceName + ": IGNORED onRecognitionResumed"));
+ @Override
+ public void onRecognitionResumed() {
+ Slog.i(TAG, mPuuid + "->" + mServiceName + ": IGNORED onRecognitionResumed");
- }
+ sEventLogger.log(new SoundTriggerLogger.StringEvent(mPuuid
+ + "->" + mServiceName + ": IGNORED onRecognitionResumed"));
- @Override
- public void onServiceConnected(ComponentName name, IBinder service) {
- if (DEBUG) Slog.v(TAG, mPuuid + ": onServiceConnected(" + service + ")");
+ }
- sEventLogger.log(new SoundTriggerLogger.StringEvent(mPuuid
- + ": onServiceConnected(" + service + ")"));
+ @Override
+ public void onServiceConnected(ComponentName name, IBinder service) {
+ if (DEBUG) Slog.v(TAG, mPuuid + ": onServiceConnected(" + service + ")");
- synchronized (mRemoteServiceLock) {
- mService = ISoundTriggerDetectionService.Stub.asInterface(service);
+ sEventLogger.log(new SoundTriggerLogger.StringEvent(mPuuid
+ + ": onServiceConnected(" + service + ")"));
- try {
- mService.setClient(mPuuid, mParams, mClient);
- } catch (Exception e) {
- Slog.e(TAG, mPuuid + ": Could not init " + mServiceName, e);
- return;
+ synchronized (mRemoteServiceLock) {
+ mService = ISoundTriggerDetectionService.Stub.asInterface(service);
+
+ try {
+ mService.setClient(mPuuid, mParams, mClient);
+ } catch (Exception e) {
+ Slog.e(TAG, mPuuid + ": Could not init " + mServiceName, e);
+ return;
+ }
+
+ while (!mPendingOps.isEmpty()) {
+ runOrAddOperation(mPendingOps.remove(0));
+ }
}
+ }
+
+ @Override
+ public void onServiceDisconnected(ComponentName name) {
+ if (DEBUG) Slog.v(TAG, mPuuid + ": onServiceDisconnected");
- while (!mPendingOps.isEmpty()) {
- runOrAddOperation(mPendingOps.remove(0));
+ sEventLogger.log(new SoundTriggerLogger.StringEvent(mPuuid
+ + ": onServiceDisconnected"));
+
+ synchronized (mRemoteServiceLock) {
+ mService = null;
}
}
- }
- @Override
- public void onServiceDisconnected(ComponentName name) {
- if (DEBUG) Slog.v(TAG, mPuuid + ": onServiceDisconnected");
+ @Override
+ public void onBindingDied(ComponentName name) {
+ if (DEBUG) Slog.v(TAG, mPuuid + ": onBindingDied");
- sEventLogger.log(new SoundTriggerLogger.StringEvent(mPuuid
- + ": onServiceDisconnected"));
+ sEventLogger.log(new SoundTriggerLogger.StringEvent(mPuuid
+ + ": onBindingDied"));
- synchronized (mRemoteServiceLock) {
- mService = null;
+ synchronized (mRemoteServiceLock) {
+ destroy();
+ }
+ }
+
+ @Override
+ public void onNullBinding(ComponentName name) {
+ Slog.w(TAG, name + " for model " + mPuuid + " returned a null binding");
+
+ sEventLogger.log(new SoundTriggerLogger.StringEvent(name + " for model "
+ + mPuuid + " returned a null binding"));
+
+ synchronized (mRemoteServiceLock) {
+ disconnectLocked();
+ }
}
}
+ }
- @Override
- public void onBindingDied(ComponentName name) {
- if (DEBUG) Slog.v(TAG, mPuuid + ": onBindingDied");
+ /**
+ * Counts the number of operations added in the last 24 hours.
+ */
+ private static class NumOps {
+ private final Object mLock = new Object();
+
+ @GuardedBy("mLock")
+ private int[] mNumOps = new int[24];
+ @GuardedBy("mLock")
+ private long mLastOpsHourSinceBoot;
- sEventLogger.log(new SoundTriggerLogger.StringEvent(mPuuid
- + ": onBindingDied"));
+ /**
+ * Clear buckets of new hours that have elapsed since last operation.
+ *
+ * <p>I.e. when the last operation was triggered at 1:40 and the current operation was
+ * triggered at 4:03, the buckets "2, 3, and 4" are cleared.
+ *
+ * @param currentTime Current elapsed time since boot in ns
+ */
+ void clearOldOps(long currentTime) {
+ synchronized (mLock) {
+ long numHoursSinceBoot = TimeUnit.HOURS.convert(currentTime, TimeUnit.NANOSECONDS);
- synchronized (mRemoteServiceLock) {
- destroy();
+ // Clear buckets of new hours that have elapsed since last operation
+ // I.e. when the last operation was triggered at 1:40 and the current
+ // operation was triggered at 4:03, the bucket "2, 3, and 4" is cleared
+ if (mLastOpsHourSinceBoot != 0) {
+ for (long hour = mLastOpsHourSinceBoot + 1; hour <= numHoursSinceBoot; hour++) {
+ mNumOps[(int) (hour % 24)] = 0;
+ }
+ }
}
}
- @Override
- public void onNullBinding(ComponentName name) {
- Slog.w(TAG, name + " for model " + mPuuid + " returned a null binding");
+ /**
+ * Add a new operation.
+ *
+ * @param currentTime Current elapsed time since boot in ns
+ */
+ void addOp(long currentTime) {
+ synchronized (mLock) {
+ long numHoursSinceBoot = TimeUnit.HOURS.convert(currentTime, TimeUnit.NANOSECONDS);
- sEventLogger.log(new SoundTriggerLogger.StringEvent(name + " for model "
- + mPuuid + " returned a null binding"));
+ mNumOps[(int) (numHoursSinceBoot % 24)]++;
+ mLastOpsHourSinceBoot = numHoursSinceBoot;
+ }
+ }
- synchronized (mRemoteServiceLock) {
- disconnectLocked();
+ /**
+ * Get the total operations added in the last 24 hours.
+ *
+ * @return The total number of operations added in the last 24 hours
+ */
+ int getOpsAdded() {
+ synchronized (mLock) {
+ int totalOperationsInLastDay = 0;
+ for (int i = 0; i < 24; i++) {
+ totalOperationsInLastDay += mNumOps[i];
+ }
+
+ return totalOperationsInLastDay;
}
}
}
- public final class LocalSoundTriggerService extends SoundTriggerInternal {
- private final Context mContext;
- private SoundTriggerHelper mSoundTriggerHelper;
-
- LocalSoundTriggerService(Context context) {
- mContext = context;
+ /**
+ * A single operation run in a {@link RemoteSoundTriggerDetectionService}.
+ *
+ * <p>Once the remote service is connected either setup + execute or setup + stop is executed.
+ */
+ private static class Operation {
+ private interface ExecuteOp {
+ void run(int opId, ISoundTriggerDetectionService service) throws RemoteException;
}
- synchronized void setSoundTriggerHelper(SoundTriggerHelper helper) {
- mSoundTriggerHelper = helper;
+ private final @Nullable Runnable mSetupOp;
+ private final @NonNull ExecuteOp mExecuteOp;
+ private final @Nullable Runnable mDropOp;
+
+ private Operation(@Nullable Runnable setupOp, @NonNull ExecuteOp executeOp,
+ @Nullable Runnable cancelOp) {
+ mSetupOp = setupOp;
+ mExecuteOp = executeOp;
+ mDropOp = cancelOp;
}
- @Override
- public int startRecognition(int keyphraseId, KeyphraseSoundModel soundModel,
- IRecognitionStatusCallback listener, RecognitionConfig recognitionConfig) {
- if (!isInitialized()) throw new UnsupportedOperationException();
- return mSoundTriggerHelper.startKeyphraseRecognition(keyphraseId, soundModel, listener,
- recognitionConfig);
+ private void setup() {
+ if (mSetupOp != null) {
+ mSetupOp.run();
+ }
}
- @Override
- public synchronized int stopRecognition(int keyphraseId, IRecognitionStatusCallback listener) {
- if (!isInitialized()) throw new UnsupportedOperationException();
- return mSoundTriggerHelper.stopKeyphraseRecognition(keyphraseId, listener);
+ void run(int opId, @NonNull ISoundTriggerDetectionService service) throws RemoteException {
+ setup();
+ mExecuteOp.run(opId, service);
}
- @Override
- public ModuleProperties getModuleProperties() {
- if (!isInitialized()) throw new UnsupportedOperationException();
- return mSoundTriggerHelper.getModuleProperties();
+ void drop() {
+ setup();
+
+ if (mDropOp != null) {
+ mDropOp.run();
+ }
}
+ }
- @Override
- public int setParameter(int keyphraseId, @ModelParams int modelParam, int value) {
- if (!isInitialized()) throw new UnsupportedOperationException();
- return mSoundTriggerHelper.setKeyphraseParameter(keyphraseId, modelParam, value);
+ public final class LocalSoundTriggerService implements SoundTriggerInternal {
+ private final Context mContext;
+ LocalSoundTriggerService(Context context) {
+ mContext = context;
}
- @Override
- public int getParameter(int keyphraseId, @ModelParams int modelParam) {
- if (!isInitialized()) throw new UnsupportedOperationException();
- return mSoundTriggerHelper.getKeyphraseParameter(keyphraseId, modelParam);
+ private class SessionImpl implements Session {
+ private final @NonNull SoundTriggerHelper mSoundTriggerHelper;
+
+ private SessionImpl(
+ @NonNull SoundTriggerHelper soundTriggerHelper) {
+ mSoundTriggerHelper = soundTriggerHelper;
+ }
+
+ @Override
+ public int startRecognition(int keyphraseId, KeyphraseSoundModel soundModel,
+ IRecognitionStatusCallback listener, RecognitionConfig recognitionConfig) {
+ return mSoundTriggerHelper.startKeyphraseRecognition(keyphraseId, soundModel,
+ listener,
+ recognitionConfig);
+ }
+
+ @Override
+ public synchronized int stopRecognition(int keyphraseId,
+ IRecognitionStatusCallback listener) {
+ return mSoundTriggerHelper.stopKeyphraseRecognition(keyphraseId, listener);
+ }
+
+ @Override
+ public ModuleProperties getModuleProperties() {
+ return mSoundTriggerHelper.getModuleProperties();
+ }
+
+ @Override
+ public int setParameter(int keyphraseId, @ModelParams int modelParam, int value) {
+ return mSoundTriggerHelper.setKeyphraseParameter(keyphraseId, modelParam, value);
+ }
+
+ @Override
+ public int getParameter(int keyphraseId, @ModelParams int modelParam) {
+ return mSoundTriggerHelper.getKeyphraseParameter(keyphraseId, modelParam);
+ }
+
+ @Override
+ @Nullable
+ public ModelParamRange queryParameter(int keyphraseId, @ModelParams int modelParam) {
+ return mSoundTriggerHelper.queryKeyphraseParameter(keyphraseId, modelParam);
+ }
+
+ @Override
+ public int unloadKeyphraseModel(int keyphraseId) {
+ return mSoundTriggerHelper.unloadKeyphraseSoundModel(keyphraseId);
+ }
+
+ @Override
+ public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
+ mSoundTriggerHelper.dump(fd, pw, args);
+ }
}
@Override
- @Nullable
- public ModelParamRange queryParameter(int keyphraseId, @ModelParams int modelParam) {
- if (!isInitialized()) throw new UnsupportedOperationException();
- return mSoundTriggerHelper.queryKeyphraseParameter(keyphraseId, modelParam);
+ public Session attachAsOriginator(@NonNull Identity originatorIdentity) {
+ try (SafeCloseable ignored = PermissionUtil.establishIdentityDirect(
+ originatorIdentity)) {
+ return new SessionImpl(newSoundTriggerHelper());
+ }
}
@Override
- public int unloadKeyphraseModel(int keyphraseId) {
- if (!isInitialized()) throw new UnsupportedOperationException();
- return mSoundTriggerHelper.unloadKeyphraseSoundModel(keyphraseId);
+ public Session attachAsMiddleman(@NonNull Identity middlemanIdentity,
+ @NonNull Identity originatorIdentity) {
+ try (SafeCloseable ignored = PermissionUtil.establishIdentityIndirect(mContext,
+ SOUNDTRIGGER_DELEGATE_IDENTITY, middlemanIdentity, originatorIdentity)) {
+ return new SessionImpl(newSoundTriggerHelper());
+ }
}
@Override
public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
- if (!isInitialized()) return;
- mSoundTriggerHelper.dump(fd, pw, args);
// log
sEventLogger.dump(pw);
@@ -1503,18 +1537,6 @@ public class SoundTriggerService extends SystemService {
// stats
mSoundModelStatTracker.dump(pw);
}
-
- private synchronized boolean isInitialized() {
- if (mSoundTriggerHelper == null ) {
- Slog.e(TAG, "SoundTriggerHelper not initialized.");
-
- sEventLogger.log(new SoundTriggerLogger.StringEvent(
- "SoundTriggerHelper not initialized."));
-
- return false;
- }
- return true;
- }
}
private void enforceCallingPermission(String permission) {
diff --git a/services/voiceinteraction/java/com/android/server/voiceinteraction/VoiceInteractionManagerService.java b/services/voiceinteraction/java/com/android/server/voiceinteraction/VoiceInteractionManagerService.java
index a2215ceed36a..f4fc185d3b5c 100644
--- a/services/voiceinteraction/java/com/android/server/voiceinteraction/VoiceInteractionManagerService.java
+++ b/services/voiceinteraction/java/com/android/server/voiceinteraction/VoiceInteractionManagerService.java
@@ -23,6 +23,7 @@ import android.annotation.Nullable;
import android.annotation.UserIdInt;
import android.app.ActivityManager;
import android.app.ActivityManagerInternal;
+import android.app.ActivityThread;
import android.app.AppGlobals;
import android.app.role.OnRoleHoldersChangedListener;
import android.app.role.RoleManager;
@@ -48,6 +49,11 @@ import android.hardware.soundtrigger.SoundTrigger.KeyphraseSoundModel;
import android.hardware.soundtrigger.SoundTrigger.ModelParamRange;
import android.hardware.soundtrigger.SoundTrigger.ModuleProperties;
import android.hardware.soundtrigger.SoundTrigger.RecognitionConfig;
+import android.media.permission.ClearCallingIdentityContext;
+import android.media.permission.Identity;
+import android.media.permission.IdentityContext;
+import android.media.permission.PermissionUtil;
+import android.media.permission.SafeCloseable;
import android.os.Binder;
import android.os.Bundle;
import android.os.Handler;
@@ -69,6 +75,7 @@ import android.service.voice.VoiceInteractionServiceInfo;
import android.service.voice.VoiceInteractionSession;
import android.speech.RecognitionService;
import android.text.TextUtils;
+import android.util.ArrayMap;
import android.util.ArraySet;
import android.util.Log;
import android.util.Slog;
@@ -78,6 +85,7 @@ import com.android.internal.app.IVoiceActionCheckCallback;
import com.android.internal.app.IVoiceInteractionManagerService;
import com.android.internal.app.IVoiceInteractionSessionListener;
import com.android.internal.app.IVoiceInteractionSessionShowCallback;
+import com.android.internal.app.IVoiceInteractionSoundTriggerSession;
import com.android.internal.app.IVoiceInteractor;
import com.android.internal.content.PackageMonitor;
import com.android.internal.os.BackgroundThread;
@@ -93,7 +101,10 @@ import com.android.server.wm.ActivityTaskManagerInternal;
import java.io.FileDescriptor;
import java.io.PrintWriter;
+import java.lang.ref.WeakReference;
+import java.util.LinkedList;
import java.util.List;
+import java.util.ListIterator;
import java.util.Locale;
import java.util.Objects;
import java.util.concurrent.Executor;
@@ -111,7 +122,8 @@ public class VoiceInteractionManagerService extends SystemService {
final ActivityManagerInternal mAmInternal;
final ActivityTaskManagerInternal mAtmInternal;
final UserManagerInternal mUserManagerInternal;
- final ArraySet<Integer> mLoadedKeyphraseIds = new ArraySet<>();
+ final ArrayMap<Integer, VoiceInteractionManagerServiceStub.SoundTriggerSession>
+ mLoadedKeyphraseIds = new ArrayMap<>();
ShortcutServiceInternal mShortcutServiceInternal;
SoundTriggerInternal mSoundTriggerInternal;
@@ -236,12 +248,28 @@ public class VoiceInteractionManagerService extends SystemService {
private boolean mTemporarilyDisabled;
private final boolean mEnableService;
+ private final List<WeakReference<SoundTriggerSession>> mSessions = new LinkedList<>();
VoiceInteractionManagerServiceStub() {
mEnableService = shouldEnableService(mContext);
new RoleObserver(mContext.getMainExecutor());
}
+ @Override
+ public @NonNull IVoiceInteractionSoundTriggerSession createSoundTriggerSessionAsOriginator(
+ @NonNull Identity originatorIdentity) {
+ Objects.requireNonNull(originatorIdentity);
+ try (SafeCloseable ignored = PermissionUtil.establishIdentityDirect(
+ originatorIdentity)) {
+ SoundTriggerSession session = new SoundTriggerSession(
+ mSoundTriggerInternal.attachAsOriginator(IdentityContext.getNonNull()));
+ synchronized (mSessions) {
+ mSessions.add(new WeakReference<>(session));
+ }
+ return session;
+ }
+ }
+
// TODO: VI Make sure the caller is the current user or profile
void startLocalVoiceInteraction(final IBinder token, Bundle options) {
if (mImpl == null) return;
@@ -1014,12 +1042,15 @@ public class VoiceInteractionManagerService extends SystemService {
final long caller = Binder.clearCallingIdentity();
boolean deleted = false;
try {
- int unloadStatus = mSoundTriggerInternal.unloadKeyphraseModel(keyphraseId);
- if (unloadStatus != SoundTriggerInternal.STATUS_OK) {
- Slog.w(TAG, "Unable to unload keyphrase sound model:" + unloadStatus);
+ SoundTriggerSession session = mLoadedKeyphraseIds.get(keyphraseId);
+ if (session != null) {
+ int unloadStatus = session.unloadKeyphraseModel(keyphraseId);
+ if (unloadStatus != SoundTriggerInternal.STATUS_OK) {
+ Slog.w(TAG, "Unable to unload keyphrase sound model:" + unloadStatus);
+ }
+ deleted = mDbHelper.deleteKeyphraseSoundModel(keyphraseId, callingUserId,
+ bcp47Locale);
}
- deleted = mDbHelper.deleteKeyphraseSoundModel(
- keyphraseId, callingUserId, bcp47Locale);
return deleted ? SoundTriggerInternal.STATUS_OK : SoundTriggerInternal.STATUS_ERROR;
} finally {
if (deleted) {
@@ -1092,132 +1123,145 @@ public class VoiceInteractionManagerService extends SystemService {
return null;
}
- @Override
- public ModuleProperties getDspModuleProperties() {
- // Allow the call if this is the current voice interaction service.
- synchronized (this) {
- enforceIsCurrentVoiceInteractionService();
+ class SoundTriggerSession extends IVoiceInteractionSoundTriggerSession.Stub {
+ final SoundTriggerInternal.Session mSession;
- final long caller = Binder.clearCallingIdentity();
- try {
- return mSoundTriggerInternal.getModuleProperties();
- } finally {
- Binder.restoreCallingIdentity(caller);
- }
+ SoundTriggerSession(
+ SoundTriggerInternal.Session session) {
+ mSession = session;
}
- }
- @Override
- public int startRecognition(int keyphraseId, String bcp47Locale,
- IRecognitionStatusCallback callback, RecognitionConfig recognitionConfig) {
- // Allow the call if this is the current voice interaction service.
- synchronized (this) {
- enforceIsCurrentVoiceInteractionService();
+ @Override
+ public ModuleProperties getDspModuleProperties() {
+ // Allow the call if this is the current voice interaction service.
+ synchronized (VoiceInteractionManagerServiceStub.this) {
+ enforceIsCurrentVoiceInteractionService();
- if (callback == null || recognitionConfig == null || bcp47Locale == null) {
- throw new IllegalArgumentException("Illegal argument(s) in startRecognition");
+ final long caller = Binder.clearCallingIdentity();
+ try {
+ return mSession.getModuleProperties();
+ } finally {
+ Binder.restoreCallingIdentity(caller);
+ }
}
}
- final int callingUserId = UserHandle.getCallingUserId();
- final long caller = Binder.clearCallingIdentity();
- try {
- KeyphraseSoundModel soundModel =
- mDbHelper.getKeyphraseSoundModel(keyphraseId, callingUserId, bcp47Locale);
- if (soundModel == null
- || soundModel.getUuid() == null
- || soundModel.getKeyphrases() == null) {
- Slog.w(TAG, "No matching sound model found in startRecognition");
- return SoundTriggerInternal.STATUS_ERROR;
- } else {
- // Regardless of the status of the start recognition, we need to make sure
- // that we unload this model if needed later.
- synchronized (this) {
- mLoadedKeyphraseIds.add(keyphraseId);
+ @Override
+ public int startRecognition(int keyphraseId, String bcp47Locale,
+ IRecognitionStatusCallback callback, RecognitionConfig recognitionConfig) {
+ // Allow the call if this is the current voice interaction service.
+ synchronized (VoiceInteractionManagerServiceStub.this) {
+ enforceIsCurrentVoiceInteractionService();
+
+ if (callback == null || recognitionConfig == null || bcp47Locale == null) {
+ throw new IllegalArgumentException("Illegal argument(s) in startRecognition");
}
- return mSoundTriggerInternal.startRecognition(
- keyphraseId, soundModel, callback, recognitionConfig);
}
- } finally {
- Binder.restoreCallingIdentity(caller);
- }
- }
- @Override
- public int stopRecognition(int keyphraseId, IRecognitionStatusCallback callback) {
- // Allow the call if this is the current voice interaction service.
- synchronized (this) {
- enforceIsCurrentVoiceInteractionService();
+ final int callingUserId = UserHandle.getCallingUserId();
+ final long caller = Binder.clearCallingIdentity();
+ try {
+ KeyphraseSoundModel soundModel =
+ mDbHelper.getKeyphraseSoundModel(keyphraseId, callingUserId, bcp47Locale);
+ if (soundModel == null
+ || soundModel.getUuid() == null
+ || soundModel.getKeyphrases() == null) {
+ Slog.w(TAG, "No matching sound model found in startRecognition");
+ return SoundTriggerInternal.STATUS_ERROR;
+ } else {
+ // Regardless of the status of the start recognition, we need to make sure
+ // that we unload this model if needed later.
+ synchronized (VoiceInteractionManagerServiceStub.this) {
+ mLoadedKeyphraseIds.put(keyphraseId, this);
+ }
+ return mSession.startRecognition(
+ keyphraseId, soundModel, callback, recognitionConfig);
+ }
+ } finally {
+ Binder.restoreCallingIdentity(caller);
+ }
}
- final long caller = Binder.clearCallingIdentity();
- try {
- return mSoundTriggerInternal.stopRecognition(keyphraseId, callback);
- } finally {
- Binder.restoreCallingIdentity(caller);
- }
- }
+ @Override
+ public int stopRecognition(int keyphraseId, IRecognitionStatusCallback callback) {
+ // Allow the call if this is the current voice interaction service.
+ synchronized (VoiceInteractionManagerServiceStub.this) {
+ enforceIsCurrentVoiceInteractionService();
+ }
- @Override
- public int setParameter(int keyphraseId, @ModelParams int modelParam, int value) {
- // Allow the call if this is the current voice interaction service.
- synchronized (this) {
- enforceIsCurrentVoiceInteractionService();
+ final long caller = Binder.clearCallingIdentity();
+ try {
+ return mSession.stopRecognition(keyphraseId, callback);
+ } finally {
+ Binder.restoreCallingIdentity(caller);
+ }
}
- final long caller = Binder.clearCallingIdentity();
- try {
- return mSoundTriggerInternal.setParameter(keyphraseId, modelParam, value);
- } finally {
- Binder.restoreCallingIdentity(caller);
- }
- }
+ @Override
+ public int setParameter(int keyphraseId, @ModelParams int modelParam, int value) {
+ // Allow the call if this is the current voice interaction service.
+ synchronized (VoiceInteractionManagerServiceStub.this) {
+ enforceIsCurrentVoiceInteractionService();
+ }
- @Override
- public int getParameter(int keyphraseId, @ModelParams int modelParam) {
- // Allow the call if this is the current voice interaction service.
- synchronized (this) {
- enforceIsCurrentVoiceInteractionService();
+ final long caller = Binder.clearCallingIdentity();
+ try {
+ return mSession.setParameter(keyphraseId, modelParam, value);
+ } finally {
+ Binder.restoreCallingIdentity(caller);
+ }
}
- final long caller = Binder.clearCallingIdentity();
- try {
- return mSoundTriggerInternal.getParameter(keyphraseId, modelParam);
- } finally {
- Binder.restoreCallingIdentity(caller);
- }
- }
+ @Override
+ public int getParameter(int keyphraseId, @ModelParams int modelParam) {
+ // Allow the call if this is the current voice interaction service.
+ synchronized (VoiceInteractionManagerServiceStub.this) {
+ enforceIsCurrentVoiceInteractionService();
+ }
- @Override
- @Nullable
- public ModelParamRange queryParameter(int keyphraseId, @ModelParams int modelParam) {
- // Allow the call if this is the current voice interaction service.
- synchronized (this) {
- enforceIsCurrentVoiceInteractionService();
+ final long caller = Binder.clearCallingIdentity();
+ try {
+ return mSession.getParameter(keyphraseId, modelParam);
+ } finally {
+ Binder.restoreCallingIdentity(caller);
+ }
}
- final long caller = Binder.clearCallingIdentity();
- try {
- return mSoundTriggerInternal.queryParameter(keyphraseId, modelParam);
- } finally {
- Binder.restoreCallingIdentity(caller);
+ @Override
+ @Nullable
+ public ModelParamRange queryParameter(int keyphraseId, @ModelParams int modelParam) {
+ // Allow the call if this is the current voice interaction service.
+ synchronized (VoiceInteractionManagerServiceStub.this) {
+ enforceIsCurrentVoiceInteractionService();
+ }
+
+ final long caller = Binder.clearCallingIdentity();
+ try {
+ return mSession.queryParameter(keyphraseId, modelParam);
+ } finally {
+ Binder.restoreCallingIdentity(caller);
+ }
}
- }
- private synchronized void unloadAllKeyphraseModels() {
- for (int i = 0; i < mLoadedKeyphraseIds.size(); i++) {
+ private int unloadKeyphraseModel(int keyphraseId) {
final long caller = Binder.clearCallingIdentity();
try {
- int status = mSoundTriggerInternal.unloadKeyphraseModel(
- mLoadedKeyphraseIds.valueAt(i));
- if (status != SoundTriggerInternal.STATUS_OK) {
- Slog.w(TAG, "Failed to unload keyphrase " + mLoadedKeyphraseIds.valueAt(i)
- + ":" + status);
- }
+ return mSession.unloadKeyphraseModel(keyphraseId);
} finally {
Binder.restoreCallingIdentity(caller);
}
}
+ }
+
+ private synchronized void unloadAllKeyphraseModels() {
+ for (int i = 0; i < mLoadedKeyphraseIds.size(); i++) {
+ int id = mLoadedKeyphraseIds.keyAt(i);
+ SoundTriggerSession session = mLoadedKeyphraseIds.valueAt(i);
+ int status = session.unloadKeyphraseModel(id);
+ if (status != SoundTriggerInternal.STATUS_OK) {
+ Slog.w(TAG, "Failed to unload keyphrase " + id + ":" + status);
+ }
+ }
mLoadedKeyphraseIds.clear();
}
@@ -1420,7 +1464,24 @@ public class VoiceInteractionManagerService extends SystemService {
mImpl.dumpLocked(fd, pw, args);
}
}
+
mSoundTriggerInternal.dump(fd, pw, args);
+
+ // Dump all sessions.
+ synchronized (mSessions) {
+ ListIterator<WeakReference<SoundTriggerSession>> iter =
+ mSessions.listIterator();
+ while (iter.hasNext()) {
+ SoundTriggerSession session = iter.next().get();
+ if (session != null) {
+ session.dump(fd, args);
+ } else {
+ // Session is obsolete, now is a good chance to remove it from the list.
+ iter.remove();
+ }
+ }
+ }
+
}
@Override
diff --git a/telephony/api/system-current.txt b/telephony/api/system-current.txt
index ec8e8e7910e8..0a7e1581815e 100644
--- a/telephony/api/system-current.txt
+++ b/telephony/api/system-current.txt
@@ -27,6 +27,26 @@ package android.telephony {
field @NonNull public static final android.os.Parcelable.Creator<android.telephony.CallAttributes> CREATOR;
}
+ public final class CallForwardingInfo implements android.os.Parcelable {
+ ctor public CallForwardingInfo(boolean, int, @Nullable String, int);
+ method public int describeContents();
+ method @Nullable public String getNumber();
+ method public int getReason();
+ method public int getTimeoutSeconds();
+ method public boolean isEnabled();
+ field @NonNull public static final android.os.Parcelable.Creator<android.telephony.CallForwardingInfo> CREATOR;
+ field public static final int ERROR_FDN_CHECK_FAILURE = 2; // 0x2
+ field public static final int ERROR_NOT_SUPPORTED = 3; // 0x3
+ field public static final int ERROR_UNKNOWN = 1; // 0x1
+ field public static final int REASON_ALL = 4; // 0x4
+ field public static final int REASON_ALL_CONDITIONAL = 5; // 0x5
+ field public static final int REASON_BUSY = 1; // 0x1
+ field public static final int REASON_NOT_REACHABLE = 3; // 0x3
+ field public static final int REASON_NO_REPLY = 2; // 0x2
+ field public static final int REASON_UNCONDITIONAL = 0; // 0x0
+ field public static final int SUCCESS = 0; // 0x0
+ }
+
public final class CallQuality implements android.os.Parcelable {
ctor public CallQuality(int, int, int, int, int, int, int, int, int, int, int);
ctor public CallQuality(int, int, int, int, int, int, int, int, int, int, int, boolean, boolean, boolean);
@@ -656,6 +676,8 @@ package android.telephony {
method @Deprecated @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public java.util.List<android.service.carrier.CarrierIdentifier> getAllowedCarriers(int);
method @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public long getAllowedNetworkTypes();
method @Nullable @RequiresPermission(android.Manifest.permission.INTERACT_ACROSS_USERS) public android.content.ComponentName getAndUpdateDefaultRespondViaMessageApplication();
+ method @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public void getCallForwarding(int, @NonNull java.util.concurrent.Executor, @NonNull android.telephony.TelephonyManager.CallForwardingInfoCallback);
+ method @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public void getCallWaitingStatus(@NonNull java.util.concurrent.Executor, @NonNull java.util.function.Consumer<java.lang.Integer>);
method @Nullable @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public android.telephony.ImsiEncryptionInfo getCarrierInfoForImsiEncryption(int);
method public java.util.List<java.lang.String> getCarrierPackageNamesForIntent(android.content.Intent);
method public java.util.List<java.lang.String> getCarrierPackageNamesForIntentAndPhone(android.content.Intent, int);
@@ -732,6 +754,8 @@ package android.telephony {
method @RequiresPermission(android.Manifest.permission.CONNECTIVITY_INTERNAL) public void resetSettings();
method @Deprecated @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public int setAllowedCarriers(int, java.util.List<android.service.carrier.CarrierIdentifier>);
method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public boolean setAllowedNetworkTypes(long);
+ method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public void setCallForwarding(@NonNull android.telephony.CallForwardingInfo, @Nullable java.util.concurrent.Executor, @Nullable java.util.function.Consumer<java.lang.Integer>);
+ method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public void setCallWaitingStatus(boolean, @Nullable java.util.concurrent.Executor, @Nullable java.util.function.Consumer<java.lang.Integer>);
method @Deprecated @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public void setCarrierDataEnabled(boolean);
method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public int setCarrierRestrictionRules(@NonNull android.telephony.CarrierRestrictionRules);
method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public void setDataActivationState(int);
@@ -769,6 +793,10 @@ package android.telephony {
field public static final String ACTION_SIM_APPLICATION_STATE_CHANGED = "android.telephony.action.SIM_APPLICATION_STATE_CHANGED";
field public static final String ACTION_SIM_CARD_STATE_CHANGED = "android.telephony.action.SIM_CARD_STATE_CHANGED";
field public static final String ACTION_SIM_SLOT_STATUS_CHANGED = "android.telephony.action.SIM_SLOT_STATUS_CHANGED";
+ field public static final int CALL_WAITING_STATUS_DISABLED = 2; // 0x2
+ field public static final int CALL_WAITING_STATUS_ENABLED = 1; // 0x1
+ field public static final int CALL_WAITING_STATUS_NOT_SUPPORTED = 4; // 0x4
+ field public static final int CALL_WAITING_STATUS_UNKNOWN_ERROR = 3; // 0x3
field public static final int CARRIER_PRIVILEGE_STATUS_ERROR_LOADING_RULES = -2; // 0xfffffffe
field public static final int CARRIER_PRIVILEGE_STATUS_HAS_ACCESS = 1; // 0x1
field public static final int CARRIER_PRIVILEGE_STATUS_NO_ACCESS = 0; // 0x0
@@ -823,6 +851,11 @@ package android.telephony {
field public static final int SRVCC_STATE_HANDOVER_STARTED = 0; // 0x0
}
+ public static interface TelephonyManager.CallForwardingInfoCallback {
+ method public void onCallForwardingInfoAvailable(@NonNull android.telephony.CallForwardingInfo);
+ method public void onError(int);
+ }
+
public final class UiccAccessRule implements android.os.Parcelable {
ctor public UiccAccessRule(byte[], @Nullable String, long);
method public int describeContents();
diff --git a/telephony/java/android/telephony/CallForwardingInfo.java b/telephony/java/android/telephony/CallForwardingInfo.java
index 7e777fae46eb..2106f7fc4bb8 100644
--- a/telephony/java/android/telephony/CallForwardingInfo.java
+++ b/telephony/java/android/telephony/CallForwardingInfo.java
@@ -20,6 +20,7 @@ import android.annotation.IntDef;
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.annotation.SuppressLint;
+import android.annotation.SystemApi;
import android.os.Parcel;
import android.os.Parcelable;
@@ -33,50 +34,35 @@ import java.util.Objects;
* Defines the call forwarding information.
* @hide
*/
+@SystemApi
public final class CallForwardingInfo implements Parcelable {
private static final String TAG = "CallForwardingInfo";
/**
- * Indicates the call forwarding status is inactive.
- *
- * @hide
+ * Indicates that the operation was successful.
*/
- public static final int STATUS_INACTIVE = 0;
+ public static final int SUCCESS = 0;
/**
- * Indicates the call forwarding status is active.
- *
- * @hide
+ * Indicates that setting or retrieving the call forwarding info failed with an unknown error.
*/
- public static final int STATUS_ACTIVE = 1;
+ public static final int ERROR_UNKNOWN = 1;
/**
- * Indicates the call forwarding could not be enabled because the recipient is not on
+ * Indicates that call forwarding is not enabled because the recipient is not on a
* Fixed Dialing Number (FDN) list.
- *
- * @hide
*/
- public static final int STATUS_FDN_CHECK_FAILURE = 2;
+ public static final int ERROR_FDN_CHECK_FAILURE = 2;
/**
- * Indicates the call forwarding status is with an unknown error.
- *
- * @hide
+ * Indicates that call forwarding is not supported on the network at this time.
*/
- public static final int STATUS_UNKNOWN_ERROR = 3;
+ public static final int ERROR_NOT_SUPPORTED = 3;
/**
- * Indicates the call forwarding is not supported (e.g. called via CDMA).
- *
- * @hide
- */
- public static final int STATUS_NOT_SUPPORTED = 4;
-
- /**
- * Indicates the call forwarding reason is "unconditional".
+ * Indicates that call forwarding reason is "unconditional".
* Reference: 3GPP TS 27.007 version 10.3.0 Release 10 - 7.11 Call forwarding number
* and conditions +CCFC
- * @hide
*/
public static final int REASON_UNCONDITIONAL = 0;
@@ -84,7 +70,6 @@ public final class CallForwardingInfo implements Parcelable {
* Indicates the call forwarding status is "busy".
* Reference: 3GPP TS 27.007 version 10.3.0 Release 10 - 7.11 Call forwarding number
* and conditions +CCFC
- * @hide
*/
public static final int REASON_BUSY = 1;
@@ -92,7 +77,6 @@ public final class CallForwardingInfo implements Parcelable {
* Indicates the call forwarding reason is "no reply".
* Reference: 3GPP TS 27.007 version 10.3.0 Release 10 - 7.11 Call forwarding number
* and conditions +CCFC
- * @hide
*/
public static final int REASON_NO_REPLY = 2;
@@ -100,7 +84,6 @@ public final class CallForwardingInfo implements Parcelable {
* Indicates the call forwarding reason is "not reachable".
* Reference: 3GPP TS 27.007 version 10.3.0 Release 10 - 7.11 Call forwarding number
* and conditions +CCFC
- * @hide
*/
public static final int REASON_NOT_REACHABLE = 3;
@@ -109,7 +92,6 @@ public final class CallForwardingInfo implements Parcelable {
* simultaneously (unconditional, busy, no reply, and not reachable).
* Reference: 3GPP TS 27.007 version 10.3.0 Release 10 - 7.11 Call forwarding number
* and conditions +CCFC
- * @hide
*/
public static final int REASON_ALL = 4;
@@ -118,26 +100,25 @@ public final class CallForwardingInfo implements Parcelable {
* forwarding reasons simultaneously (busy, no reply, and not reachable).
* Reference: 3GPP TS 27.007 version 10.3.0 Release 10 - 7.11 Call forwarding number
* and conditions +CCFC
- * @hide
*/
public static final int REASON_ALL_CONDITIONAL = 5;
/**
- * Call forwarding function status
+ * Call forwarding errors
+ * @hide
*/
- @IntDef(prefix = { "STATUS_" }, value = {
- STATUS_ACTIVE,
- STATUS_INACTIVE,
- STATUS_UNKNOWN_ERROR,
- STATUS_NOT_SUPPORTED,
- STATUS_FDN_CHECK_FAILURE
+ @IntDef(prefix = { "ERROR_" }, value = {
+ ERROR_UNKNOWN,
+ ERROR_NOT_SUPPORTED,
+ ERROR_FDN_CHECK_FAILURE
})
@Retention(RetentionPolicy.SOURCE)
- public @interface CallForwardingStatus {
+ public @interface CallForwardingError{
}
/**
* Call forwarding reason types
+ * @hide
*/
@IntDef(flag = true, prefix = { "REASON_" }, value = {
REASON_UNCONDITIONAL,
@@ -152,9 +133,9 @@ public final class CallForwardingInfo implements Parcelable {
}
/**
- * The call forwarding status.
+ * Whether call forwarding is enabled for this reason.
*/
- private int mStatus;
+ private boolean mEnabled;
/**
* The call forwarding reason indicates the condition under which calls will be forwarded.
@@ -178,39 +159,35 @@ public final class CallForwardingInfo implements Parcelable {
/**
* Construct a CallForwardingInfo.
*
- * @param status the call forwarding status
+ * @param enabled Whether to enable call forwarding for the reason specified
+ * in {@link #getReason()}.
* @param reason the call forwarding reason
* @param number the phone number to which calls will be forwarded
* @param timeSeconds the timeout (in seconds) before the forwarding is attempted
- * @hide
*/
- public CallForwardingInfo(@CallForwardingStatus int status, @CallForwardingReason int reason,
+ public CallForwardingInfo(boolean enabled, @CallForwardingReason int reason,
@Nullable String number, int timeSeconds) {
- mStatus = status;
+ mEnabled = enabled;
mReason = reason;
mNumber = number;
mTimeSeconds = timeSeconds;
}
/**
- * Returns the call forwarding status.
- *
- * @return the call forwarding status.
+ * Whether call forwarding is enabled for the reason from {@link #getReason()}.
*
- * @hide
+ * @return {@code true} if enabled, {@code false} otherwise.
*/
- public @CallForwardingStatus int getStatus() {
- return mStatus;
+ public boolean isEnabled() {
+ return mEnabled;
}
/**
* Returns the call forwarding reason. The call forwarding reason indicates the condition
- * under which calls will be forwarded. For example, {@link #REASON_NO_REPLY} indicates
- * that calls will be forward to {@link #getNumber()} when the user fails to answer the call.
+ * under which calls will be forwarded. For example, {@link #REASON_NO_REPLY} indicates
+ * that calls will be forwarded when the user fails to answer the call.
*
* @return the call forwarding reason.
- *
- * @hide
*/
public @CallForwardingReason int getReason() {
return mReason;
@@ -220,9 +197,7 @@ public final class CallForwardingInfo implements Parcelable {
* Returns the phone number to which calls will be forwarded.
*
* @return the number calls will be forwarded to, or {@code null} if call forwarding
- * is being disabled.
- *
- * @hide
+ * is disabled.
*/
@Nullable
public String getNumber() {
@@ -230,16 +205,14 @@ public final class CallForwardingInfo implements Parcelable {
}
/**
- * Gets the timeout (in seconds) before the forwarding is attempted. For example,
+ * Gets the timeout (in seconds) before forwarding is attempted. For example,
* if {@link #REASON_NO_REPLY} is the call forwarding reason, the device will wait this
- * duration of time before forwarding the call to {@link #getNumber()}.
+ * duration of time before forwarding the call to the number returned by {@link #getNumber()}.
*
* Reference: 3GPP TS 27.007 version 10.3.0 Release 10
* 7.11 Call forwarding number and conditions +CCFC
*
* @return the timeout (in seconds) before the forwarding is attempted.
- *
- * @hide
*/
@SuppressLint("MethodNameUnits")
public int getTimeoutSeconds() {
@@ -257,14 +230,14 @@ public final class CallForwardingInfo implements Parcelable {
@Override
public void writeToParcel(Parcel out, int flags) {
out.writeString(mNumber);
- out.writeInt(mStatus);
+ out.writeBoolean(mEnabled);
out.writeInt(mReason);
out.writeInt(mTimeSeconds);
}
private CallForwardingInfo(Parcel in) {
mNumber = in.readString();
- mStatus = in.readInt();
+ mEnabled = in.readBoolean();
mReason = in.readInt();
mTimeSeconds = in.readInt();
}
@@ -281,7 +254,7 @@ public final class CallForwardingInfo implements Parcelable {
}
CallForwardingInfo other = (CallForwardingInfo) o;
- return mStatus == other.mStatus
+ return mEnabled == other.mEnabled
&& mNumber == other.mNumber
&& mReason == other.mReason
&& mTimeSeconds == other.mTimeSeconds;
@@ -292,7 +265,7 @@ public final class CallForwardingInfo implements Parcelable {
*/
@Override
public int hashCode() {
- return Objects.hash(mStatus, mNumber, mReason, mTimeSeconds);
+ return Objects.hash(mEnabled, mNumber, mReason, mTimeSeconds);
}
public static final @NonNull Parcelable.Creator<CallForwardingInfo> CREATOR =
@@ -313,7 +286,7 @@ public final class CallForwardingInfo implements Parcelable {
*/
@Override
public String toString() {
- return "[CallForwardingInfo: status=" + mStatus
+ return "[CallForwardingInfo: enabled=" + mEnabled
+ ", reason= " + mReason
+ ", timeSec= " + mTimeSeconds + " seconds"
+ ", number=" + Rlog.pii(TAG, mNumber) + "]";
diff --git a/telephony/java/android/telephony/CarrierConfigManager.java b/telephony/java/android/telephony/CarrierConfigManager.java
index 4de1abfee7fc..47bc566a331a 100755
--- a/telephony/java/android/telephony/CarrierConfigManager.java
+++ b/telephony/java/android/telephony/CarrierConfigManager.java
@@ -32,6 +32,7 @@ import android.os.RemoteException;
import android.service.carrier.CarrierService;
import android.telecom.TelecomManager;
import android.telephony.ims.ImsReasonInfo;
+import android.telephony.ims.ImsSsData;
import com.android.internal.telephony.ICarrierConfigLoader;
import com.android.telephony.Rlog;
@@ -66,6 +67,18 @@ public class CarrierConfigManager {
public static final String EXTRA_SUBSCRIPTION_INDEX =
SubscriptionManager.EXTRA_SUBSCRIPTION_INDEX;
+ /**
+ * Service class flag if no specific service class is specified.
+ * Reference: 3GPP TS 27.007 Section 7.4 Facility lock +CLCK
+ */
+ public static final int SERVICE_CLASS_NONE = ImsSsData.SERVICE_CLASS_NONE;
+
+ /**
+ * Service class flag for voice telephony.
+ * Reference: 3GPP TS 27.007 Section 7.4 Facility lock +CLCK
+ */
+ public static final int SERVICE_CLASS_VOICE = ImsSsData.SERVICE_CLASS_VOICE;
+
private final Context mContext;
/**
@@ -212,6 +225,18 @@ public class CarrierConfigManager {
"call_barring_supports_deactivate_all_bool";
/**
+ * Specifies the service class for call barring service. Default value is
+ * {@link #SERVICE_CLASS_VOICE}.
+ * The value set as below:
+ * <ul>
+ * <li>0: {@link #SERVICE_CLASS_NONE}</li>
+ * <li>1: {@link #SERVICE_CLASS_VOICE}</li>
+ * </ul>
+ */
+ public static final String KEY_CALL_BARRING_DEFAULT_SERVICE_CLASS_INT =
+ "call_barring_default_service_class_int";
+
+ /**
* Flag indicating whether the Phone app should ignore EVENT_SIM_NETWORK_LOCKED
* events from the Sim.
* If true, this will prevent the IccNetworkDepersonalizationPanel from being shown, and
@@ -3889,10 +3914,23 @@ public class CarrierConfigManager {
* Indicating whether DUN APN should be disabled when the device is roaming. In that case,
* the default APN (i.e. internet) will be used for tethering.
*
+ * This config is only available when using Preset APN(not user edited) as Preferred APN.
+ *
+ * @hide
+ */
+ public static final String KEY_DISABLE_DUN_APN_WHILE_ROAMING_WITH_PRESET_APN_BOOL =
+ "disable_dun_apn_while_roaming_with_preset_apn_bool";
+
+ /**
+ * Where there is no preferred APN, specifies the carrier's default preferred APN.
+ * Specifies the {@link android.provider.Telephony.Carriers.APN} of the default preferred apn.
+ *
+ * This config is only available with Preset APN(not user edited).
+ *
* @hide
*/
- public static final String KEY_DISABLE_DUN_APN_WHILE_ROAMING =
- "disable_dun_apn_while_roaming";
+ public static final String KEY_DEFAULT_PREFERRED_APN_NAME_STRING =
+ "default_preferred_apn_name_string";
/** The default value for every variable. */
private final static PersistableBundle sDefaults;
@@ -3968,6 +4006,7 @@ public class CarrierConfigManager {
sDefaults.putBoolean(KEY_CALL_BARRING_VISIBILITY_BOOL, false);
sDefaults.putBoolean(KEY_CALL_BARRING_SUPPORTS_PASSWORD_CHANGE_BOOL, true);
sDefaults.putBoolean(KEY_CALL_BARRING_SUPPORTS_DEACTIVATE_ALL_BOOL, true);
+ sDefaults.putInt(KEY_CALL_BARRING_DEFAULT_SERVICE_CLASS_INT, SERVICE_CLASS_VOICE);
sDefaults.putBoolean(KEY_CALL_FORWARDING_VISIBILITY_BOOL, true);
sDefaults.putBoolean(KEY_CALL_FORWARDING_WHEN_UNREACHABLE_SUPPORTED_BOOL, true);
sDefaults.putBoolean(KEY_CALL_FORWARDING_WHEN_UNANSWERED_SUPPORTED_BOOL, true);
@@ -4432,7 +4471,8 @@ public class CarrierConfigManager {
"ims:2", "cbs:2", "ia:2", "emergency:2", "mcx:3", "xcap:3"
});
sDefaults.putStringArray(KEY_MISSED_INCOMING_CALL_SMS_PATTERN_STRING_ARRAY, new String[0]);
- sDefaults.putBoolean(KEY_DISABLE_DUN_APN_WHILE_ROAMING, false);
+ sDefaults.putBoolean(KEY_DISABLE_DUN_APN_WHILE_ROAMING_WITH_PRESET_APN_BOOL, false);
+ sDefaults.putString(KEY_DEFAULT_PREFERRED_APN_NAME_STRING, "");
}
/**
diff --git a/telephony/java/android/telephony/CellIdentityNr.java b/telephony/java/android/telephony/CellIdentityNr.java
index 905f90800305..3923c756033f 100644
--- a/telephony/java/android/telephony/CellIdentityNr.java
+++ b/telephony/java/android/telephony/CellIdentityNr.java
@@ -138,7 +138,11 @@ public final class CellIdentityNr extends CellIdentity {
@NonNull
@Override
public CellLocation asCellLocation() {
- return new GsmCellLocation();
+ GsmCellLocation cl = new GsmCellLocation();
+ int tac = mTac != CellInfo.UNAVAILABLE ? mTac : -1;
+ cl.setLacAndCid(tac, -1);
+ cl.setPsc(0);
+ return cl;
}
@Override
diff --git a/telephony/java/android/telephony/TelephonyManager.java b/telephony/java/android/telephony/TelephonyManager.java
index 969016b1c017..0ec27933dc36 100644
--- a/telephony/java/android/telephony/TelephonyManager.java
+++ b/telephony/java/android/telephony/TelephonyManager.java
@@ -99,6 +99,8 @@ import com.android.internal.annotations.GuardedBy;
import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.telephony.CellNetworkScanResult;
import com.android.internal.telephony.IBooleanConsumer;
+import com.android.internal.telephony.ICallForwardingInfoCallback;
+import com.android.internal.telephony.IIntegerConsumer;
import com.android.internal.telephony.INumberVerificationCallback;
import com.android.internal.telephony.IOns;
import com.android.internal.telephony.IPhoneSubInfo;
@@ -306,6 +308,8 @@ public class TelephonyManager {
private static boolean sServiceHandleCacheEnabled = true;
@GuardedBy("sCacheLock")
+ private static ITelephony sITelephony;
+ @GuardedBy("sCacheLock")
private static IPhoneSubInfo sIPhoneSubInfo;
@GuardedBy("sCacheLock")
private static ISub sISub;
@@ -4180,7 +4184,7 @@ public class TelephonyManager {
}
}
- /**
+ /**
* @param keyAvailability bitmask that defines the availabilty of keys for a type.
* @param keyType the key type which is being checked. (WLAN, EPDG)
* @return true if the digit at position keyType is 1, else false.
@@ -5499,13 +5503,39 @@ public class TelephonyManager {
}
}
- /**
- * @hide
- */
+ /**
+ * @hide
+ */
@UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P)
private ITelephony getITelephony() {
- return ITelephony.Stub.asInterface(TelephonyFrameworkInitializer
- .getTelephonyServiceManager().getTelephonyServiceRegisterer().get());
+ // Keeps cache disabled until test fixes are checked into AOSP.
+ if (!sServiceHandleCacheEnabled) {
+ return ITelephony.Stub.asInterface(
+ TelephonyFrameworkInitializer
+ .getTelephonyServiceManager()
+ .getTelephonyServiceRegisterer()
+ .get());
+ }
+
+ if (sITelephony == null) {
+ ITelephony temp = ITelephony.Stub.asInterface(
+ TelephonyFrameworkInitializer
+ .getTelephonyServiceManager()
+ .getTelephonyServiceRegisterer()
+ .get());
+ synchronized (sCacheLock) {
+ if (sITelephony == null && temp != null) {
+ try {
+ sITelephony = temp;
+ sITelephony.asBinder().linkToDeath(sServiceDeath, 0);
+ } catch (Exception e) {
+ // something has gone horribly wrong
+ sITelephony = null;
+ }
+ }
+ }
+ }
+ return sITelephony;
}
private IOns getIOns() {
@@ -12992,195 +13022,304 @@ public class TelephonyManager {
}
/**
- * Gets the voice call forwarding info {@link CallForwardingInfo}, given the call forward
- * reason.
+ * Callback to be used with {@link #getCallForwarding}
+ * @hide
+ */
+ @SystemApi
+ public interface CallForwardingInfoCallback {
+ /**
+ * Called when the call forwarding info is successfully retrieved from the network.
+ * @param info information about how calls are forwarded
+ */
+ void onCallForwardingInfoAvailable(@NonNull CallForwardingInfo info);
+
+ /**
+ * Called when there was an error retrieving the call forwarding information.
+ * @param error
+ */
+ void onError(@CallForwardingInfo.CallForwardingError int error);
+ }
+
+ /**
+ * Gets the voice call forwarding info for a given call forwarding reason.
*
- * @param callForwardingReason the call forwarding reasons
+ * This method queries the network for the currently set call forwarding configuration for the
+ * provided call forwarding reason. When the network has provided its response, the result will
+ * be supplied via the provided {@link Executor} on the provided
+ * {@link CallForwardingInfoCallback}.
*
- * @throws IllegalArgumentException if callForwardingReason is not any of
- * {@link CallForwardingInfo.REASON_UNCONDITIONAL}, {@link CallForwardingInfo.REASON_BUSY},
- * {@link CallForwardingInfo.REASON_NO_REPLY}, {@link CallForwardingInfo.REASON_NOT_REACHABLE},
- * {@link CallForwardingInfo.REASON_ALL}, {@link CallForwardingInfo.REASON_ALL_CONDITIONAL}
+ * @param callForwardingReason the call forwarding reason to query.
+ * @param executor The executor on which to execute the callback once the result is ready.
+ * @param callback The callback the results should be delivered on.
*
- * @return {@link CallForwardingInfo} with the status {@link CallForwardingInfo#STATUS_ACTIVE}
- * or {@link CallForwardingInfo#STATUS_INACTIVE} and the target phone number to forward calls
- * to, if it's available. Otherwise, it will return a {@link CallForwardingInfo} with status
- * {@link CallForwardingInfo#STATUS_UNKNOWN_ERROR},
- * {@link CallForwardingInfo#STATUS_NOT_SUPPORTED},
- * or {@link CallForwardingInfo#STATUS_FDN_CHECK_FAILURE} depending on the situation.
+ * @throws IllegalArgumentException if callForwardingReason is not any of
+ * {@link CallForwardingInfo#REASON_UNCONDITIONAL}, {@link CallForwardingInfo#REASON_BUSY},
+ * {@link CallForwardingInfo#REASON_NO_REPLY}, {@link CallForwardingInfo#REASON_NOT_REACHABLE},
+ * {@link CallForwardingInfo#REASON_ALL}, or {@link CallForwardingInfo#REASON_ALL_CONDITIONAL}
*
* @hide
*/
@RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE)
- @NonNull
- public CallForwardingInfo getCallForwarding(@CallForwardingReason int callForwardingReason) {
+ @SystemApi
+ public void getCallForwarding(@CallForwardingReason int callForwardingReason,
+ @NonNull Executor executor, @NonNull CallForwardingInfoCallback callback) {
if (callForwardingReason < CallForwardingInfo.REASON_UNCONDITIONAL
|| callForwardingReason > CallForwardingInfo.REASON_ALL_CONDITIONAL) {
throw new IllegalArgumentException("callForwardingReason is out of range");
}
+
+ ICallForwardingInfoCallback internalCallback = new ICallForwardingInfoCallback.Stub() {
+ @Override
+ public void onCallForwardingInfoAvailable(CallForwardingInfo info) {
+ executor.execute(() ->
+ Binder.withCleanCallingIdentity(() ->
+ callback.onCallForwardingInfoAvailable(info)));
+ }
+
+ @Override
+ public void onError(int error) {
+ executor.execute(() ->
+ Binder.withCleanCallingIdentity(() ->
+ callback.onError(error)));
+ }
+ };
+
try {
ITelephony telephony = getITelephony();
if (telephony != null) {
- return telephony.getCallForwarding(getSubId(), callForwardingReason);
+ telephony.getCallForwarding(getSubId(), callForwardingReason, internalCallback);
}
} catch (RemoteException ex) {
Rlog.e(TAG, "getCallForwarding RemoteException", ex);
- } catch (NullPointerException ex) {
- Rlog.e(TAG, "getCallForwarding NPE", ex);
+ ex.rethrowAsRuntimeException();
}
- return new CallForwardingInfo(
- CallForwardingInfo.STATUS_UNKNOWN_ERROR, 0 /* reason */, null /* number */,
- 0 /* timeout */);
}
/**
- * Sets the voice call forwarding info including status (enable/disable), call forwarding
- * reason, the number to forward, and the timeout before the forwarding is attempted.
+ * Sets voice call forwarding behavior as described by the provided {@link CallForwardingInfo}.
*
- * @param callForwardingInfo {@link CallForwardingInfo} to setup the call forwarding.
- * Enabling if {@link CallForwardingInfo#getStatus()} returns
- * {@link CallForwardingInfo#STATUS_ACTIVE}; Disabling if
- * {@link CallForwardingInfo#getStatus()} returns {@link CallForwardingInfo#STATUS_INACTIVE}.
+ * This method will enable call forwarding if the provided {@link CallForwardingInfo} returns
+ * {@code true} from its {@link CallForwardingInfo#isEnabled()} method, and disables call
+ * forwarding otherwise.
*
- * @throws IllegalArgumentException if any of the following for parameter callForwardingInfo:
- * 0) it is {@code null}.
- * 1) {@link CallForwardingInfo#getStatus()} returns neither
- * {@link CallForwardingInfo#STATUS_ACTIVE} nor {@link CallForwardingInfo#STATUS_INACTIVE}.
- * 2) {@link CallForwardingInfo#getReason()} is not any of
- * {@link CallForwardingInfo.REASON_UNCONDITIONAL}, {@link CallForwardingInfo.REASON_BUSY},
- * {@link CallForwardingInfo.REASON_NO_REPLY}, {@link CallForwardingInfo.REASON_NOT_REACHABLE},
- * {@link CallForwardingInfo.REASON_ALL}, {@link CallForwardingInfo.REASON_ALL_CONDITIONAL}
- * 3) {@link CallForwardingInfo#getNumber()} returns {@code null}.
- * 4) {@link CallForwardingInfo#getTimeoutSeconds()} doesn't return a positive value.
+ * If you wish to be notified about the results of this operation, provide an {@link Executor}
+ * and {@link Consumer<Integer>} to be notified asynchronously when the operation completes.
*
- * @return {@code true} to indicate it was set successfully; {@code false} otherwise.
+ * @param callForwardingInfo Info about whether calls should be forwarded and where they
+ * should be forwarded to.
+ * @param executor The executor on which the listener will be called. Must be non-null if
+ * {@code listener} is non-null.
+ * @param resultListener Asynchronous listener that'll be called when the operation completes.
+ * Called with {@link CallForwardingInfo#SUCCESS} if the operation
+ * succeeded and an error code from {@link CallForwardingInfo}
+ * if it failed.
*
+ * @throws IllegalArgumentException if any of the following are true for the parameter
+ * callForwardingInfo:
+ * <ul>
+ * <li>it is {@code null}.</li>
+ * <li>{@link CallForwardingInfo#getReason()} is not any of:
+ * <ul>
+ * <li>{@link CallForwardingInfo#REASON_UNCONDITIONAL}</li>
+ * <li>{@link CallForwardingInfo#REASON_BUSY}</li>
+ * <li>{@link CallForwardingInfo#REASON_NO_REPLY}</li>
+ * <li>{@link CallForwardingInfo#REASON_NOT_REACHABLE}</li>
+ * <li>{@link CallForwardingInfo#REASON_ALL}</li>
+ * <li>{@link CallForwardingInfo#REASON_ALL_CONDITIONAL}</li>
+ * </ul>
+ * <li>{@link CallForwardingInfo#getNumber()} returns {@code null} when enabling call
+ * forwarding</li>
+ * <li>{@link CallForwardingInfo#getTimeoutSeconds()} returns a non-positive value when
+ * enabling call forwarding</li>
+ * </ul>
* @hide
*/
@RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE)
- public boolean setCallForwarding(@NonNull CallForwardingInfo callForwardingInfo) {
+ @SystemApi
+ public void setCallForwarding(@NonNull CallForwardingInfo callForwardingInfo,
+ @Nullable @CallbackExecutor Executor executor,
+ @Nullable @CallForwardingInfo.CallForwardingError Consumer<Integer> resultListener) {
if (callForwardingInfo == null) {
throw new IllegalArgumentException("callForwardingInfo is null");
}
- int callForwardingStatus = callForwardingInfo.getStatus();
- if (callForwardingStatus != CallForwardingInfo.STATUS_ACTIVE
- && callForwardingStatus != CallForwardingInfo.STATUS_INACTIVE) {
- throw new IllegalArgumentException(
- "callForwardingStatus is neither active nor inactive");
- }
int callForwardingReason = callForwardingInfo.getReason();
if (callForwardingReason < CallForwardingInfo.REASON_UNCONDITIONAL
|| callForwardingReason > CallForwardingInfo.REASON_ALL_CONDITIONAL) {
throw new IllegalArgumentException("callForwardingReason is out of range");
}
- if (callForwardingInfo.getNumber() == null) {
- throw new IllegalArgumentException("callForwarding number is null");
+ if (callForwardingInfo.isEnabled()) {
+ if (callForwardingInfo.getNumber() == null) {
+ throw new IllegalArgumentException("callForwarding number is null");
+ }
+ if (callForwardingInfo.getTimeoutSeconds() <= 0) {
+ throw new IllegalArgumentException("callForwarding timeout isn't positive");
+ }
}
- if (callForwardingInfo.getTimeoutSeconds() <= 0) {
- throw new IllegalArgumentException("callForwarding timeout isn't positive");
+ if (resultListener != null) {
+ Objects.requireNonNull(executor);
}
+
+ IIntegerConsumer internalCallback = new IIntegerConsumer.Stub() {
+ @Override
+ public void accept(int result) {
+ executor.execute(() ->
+ Binder.withCleanCallingIdentity(() -> resultListener.accept(result)));
+ }
+ };
+
try {
ITelephony telephony = getITelephony();
if (telephony != null) {
- return telephony.setCallForwarding(getSubId(), callForwardingInfo);
+ telephony.setCallForwarding(getSubId(), callForwardingInfo, internalCallback);
}
} catch (RemoteException ex) {
Rlog.e(TAG, "setCallForwarding RemoteException", ex);
+ ex.rethrowAsRuntimeException();
} catch (NullPointerException ex) {
Rlog.e(TAG, "setCallForwarding NPE", ex);
+ throw ex;
}
- return false;
}
/**
- * Indicates the call waiting status is active.
+ * Indicates that call waiting is enabled.
*
* @hide
*/
- public static final int CALL_WAITING_STATUS_ACTIVE = 1;
+ @SystemApi
+ public static final int CALL_WAITING_STATUS_ENABLED = 1;
/**
- * Indicates the call waiting status is inactive.
+ * Indicates that call waiting is disabled.
*
* @hide
*/
- public static final int CALL_WAITING_STATUS_INACTIVE = 2;
+ @SystemApi
+ public static final int CALL_WAITING_STATUS_DISABLED = 2;
/**
- * Indicates the call waiting status is with an unknown error.
+ * Indicates there was an unknown error retrieving the call waiting status.
*
* @hide
*/
+ @SystemApi
public static final int CALL_WAITING_STATUS_UNKNOWN_ERROR = 3;
/**
- * Indicates the call waiting is not supported (e.g. called via CDMA).
+ * Indicates the call waiting is not supported on the current network.
*
* @hide
*/
+ @SystemApi
public static final int CALL_WAITING_STATUS_NOT_SUPPORTED = 4;
/**
- * Call waiting function status
- *
* @hide
*/
@IntDef(prefix = { "CALL_WAITING_STATUS_" }, value = {
- CALL_WAITING_STATUS_ACTIVE,
- CALL_WAITING_STATUS_INACTIVE,
- CALL_WAITING_STATUS_NOT_SUPPORTED,
- CALL_WAITING_STATUS_UNKNOWN_ERROR
+ CALL_WAITING_STATUS_ENABLED,
+ CALL_WAITING_STATUS_DISABLED,
+ CALL_WAITING_STATUS_UNKNOWN_ERROR,
+ CALL_WAITING_STATUS_NOT_SUPPORTED,
})
@Retention(RetentionPolicy.SOURCE)
public @interface CallWaitingStatus {
}
/**
- * Gets the status of voice call waiting function. Call waiting function enables the waiting
- * for the incoming call when it reaches the user who is busy to make another call and allows
- * users to decide whether to switch to the incoming call.
+ * Retrieves the call waiting status of this device from the network.
+ *
+ * When call waiting is enabled, an incoming call that arrives when the user is already on
+ * an active call will be held in a waiting state while the user is notified instead of being
+ * rejected with a busy signal.
*
- * @return the status of call waiting function.
+ * @param executor The executor on which the result listener will be called.
+ * @param resultListener A {@link Consumer} that will be called with the result fetched
+ * from the network. The result will be one of:
+ * <ul>
+ * <li>{@link #CALL_WAITING_STATUS_ENABLED}}</li>
+ * <li>{@link #CALL_WAITING_STATUS_DISABLED}}</li>
+ * <li>{@link #CALL_WAITING_STATUS_UNKNOWN_ERROR}}</li>
+ * <li>{@link #CALL_WAITING_STATUS_NOT_SUPPORTED}}</li>
+ * </ul>
* @hide
*/
+ @SystemApi
@RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE)
- public @CallWaitingStatus int getCallWaitingStatus() {
+ public void getCallWaitingStatus(@NonNull Executor executor,
+ @NonNull @CallWaitingStatus Consumer<Integer> resultListener) {
+ Objects.requireNonNull(executor);
+ Objects.requireNonNull(resultListener);
+
+ IIntegerConsumer internalCallback = new IIntegerConsumer.Stub() {
+ @Override
+ public void accept(int result) {
+ executor.execute(() -> Binder.withCleanCallingIdentity(
+ () -> resultListener.accept(result)));
+ }
+ };
+
try {
ITelephony telephony = getITelephony();
if (telephony != null) {
- return telephony.getCallWaitingStatus(getSubId());
+ telephony.getCallWaitingStatus(getSubId(), internalCallback);
}
} catch (RemoteException ex) {
Rlog.e(TAG, "getCallWaitingStatus RemoteException", ex);
+ ex.rethrowAsRuntimeException();
} catch (NullPointerException ex) {
Rlog.e(TAG, "getCallWaitingStatus NPE", ex);
+ throw ex;
}
- return CALL_WAITING_STATUS_UNKNOWN_ERROR;
}
/**
- * Sets the status for voice call waiting function. Call waiting function enables the waiting
- * for the incoming call when it reaches the user who is busy to make another call and allows
- * users to decide whether to switch to the incoming call.
+ * Sets the call waiting status of this device with the network.
+ *
+ * If you wish to be notified about the results of this operation, provide an {@link Executor}
+ * and {@link Consumer<Integer>} to be notified asynchronously when the operation completes.
*
- * @param isEnable {@code true} to enable; {@code false} to disable.
- * @return {@code true} to indicate it was set successfully; {@code false} otherwise.
+ * @see #getCallWaitingStatus for a description of the call waiting functionality.
*
+ * @param enabled {@code true} to enable; {@code false} to disable.
+ * @param executor The executor on which the listener will be called. Must be non-null if
+ * {@code listener} is non-null.
+ * @param resultListener Asynchronous listener that'll be called when the operation completes.
+ * Called with the new call waiting status (either
+ * {@link #CALL_WAITING_STATUS_ENABLED} or
+ * {@link #CALL_WAITING_STATUS_DISABLED} if the operation succeeded and
+ * {@link #CALL_WAITING_STATUS_NOT_SUPPORTED} or
+ * {@link #CALL_WAITING_STATUS_UNKNOWN_ERROR} if it failed.
* @hide
*/
+ @SystemApi
@RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE)
- public boolean setCallWaitingStatus(boolean isEnable) {
+ public void setCallWaitingStatus(boolean enabled, @Nullable Executor executor,
+ @Nullable Consumer<Integer> resultListener) {
+ if (resultListener != null) {
+ Objects.requireNonNull(executor);
+ }
+
+ IIntegerConsumer internalCallback = new IIntegerConsumer.Stub() {
+ @Override
+ public void accept(int result) {
+ executor.execute(() ->
+ Binder.withCleanCallingIdentity(() -> resultListener.accept(result)));
+ }
+ };
+
try {
ITelephony telephony = getITelephony();
if (telephony != null) {
- return telephony.setCallWaitingStatus(getSubId(), isEnable);
+ telephony.setCallWaitingStatus(getSubId(), enabled, internalCallback);
}
} catch (RemoteException ex) {
Rlog.e(TAG, "setCallWaitingStatus RemoteException", ex);
+ ex.rethrowAsRuntimeException();
} catch (NullPointerException ex) {
Rlog.e(TAG, "setCallWaitingStatus NPE", ex);
+ throw ex;
}
- return false;
}
/**
@@ -13401,6 +13540,10 @@ public class TelephonyManager {
*/
private static void resetServiceCache() {
synchronized (sCacheLock) {
+ if (sITelephony != null) {
+ sITelephony.asBinder().unlinkToDeath(sServiceDeath, 0);
+ sITelephony = null;
+ }
if (sISub != null) {
sISub.asBinder().unlinkToDeath(sServiceDeath, 0);
sISub = null;
@@ -13417,9 +13560,9 @@ public class TelephonyManager {
}
}
- /**
- * @hide
- */
+ /**
+ * @hide
+ */
static IPhoneSubInfo getSubscriberInfoService() {
// Keeps cache disabled until test fixes are checked into AOSP.
if (!sServiceHandleCacheEnabled) {
diff --git a/telephony/java/android/telephony/gsm/GsmCellLocation.java b/telephony/java/android/telephony/gsm/GsmCellLocation.java
index 30cea0e6dd59..bc8ee1dd9359 100644
--- a/telephony/java/android/telephony/gsm/GsmCellLocation.java
+++ b/telephony/java/android/telephony/gsm/GsmCellLocation.java
@@ -55,7 +55,7 @@ public class GsmCellLocation extends CellLocation {
}
/**
- * @return gsm cell id, -1 if unknown, 0xffff max legal value
+ * @return gsm cell id, -1 if unknown or invalid, 0xffff max legal value
*/
public int getCid() {
return mCid;
diff --git a/telephony/java/android/telephony/ims/ImsService.java b/telephony/java/android/telephony/ims/ImsService.java
index 2b3072eefe2e..da7311c08307 100644
--- a/telephony/java/android/telephony/ims/ImsService.java
+++ b/telephony/java/android/telephony/ims/ImsService.java
@@ -137,18 +137,30 @@ public class ImsService extends Service {
}
@Override
- public IImsMmTelFeature createMmTelFeature(int slotId, IImsFeatureStatusCallback c) {
- return createMmTelFeatureInternal(slotId, c);
+ public IImsMmTelFeature createMmTelFeature(int slotId) {
+ return createMmTelFeatureInternal(slotId);
}
@Override
- public IImsRcsFeature createRcsFeature(int slotId, IImsFeatureStatusCallback c) {
- return createRcsFeatureInternal(slotId, c);
+ public IImsRcsFeature createRcsFeature(int slotId) {
+ return createRcsFeatureInternal(slotId);
}
@Override
- public void removeImsFeature(int slotId, int featureType, IImsFeatureStatusCallback c) {
- ImsService.this.removeImsFeature(slotId, featureType, c);
+ public void addFeatureStatusCallback(int slotId, int featureType,
+ IImsFeatureStatusCallback c) {
+ ImsService.this.addImsFeatureStatusCallback(slotId, featureType, c);
+ }
+
+ @Override
+ public void removeFeatureStatusCallback(int slotId, int featureType,
+ IImsFeatureStatusCallback c) {
+ ImsService.this.removeImsFeatureStatusCallback(slotId, featureType, c);
+ }
+
+ @Override
+ public void removeImsFeature(int slotId, int featureType) {
+ ImsService.this.removeImsFeature(slotId, featureType);
}
@Override
@@ -204,11 +216,10 @@ public class ImsService extends Service {
return mFeaturesBySlot.get(slotId);
}
- private IImsMmTelFeature createMmTelFeatureInternal(int slotId,
- IImsFeatureStatusCallback c) {
+ private IImsMmTelFeature createMmTelFeatureInternal(int slotId) {
MmTelFeature f = createMmTelFeature(slotId);
if (f != null) {
- setupFeature(f, slotId, ImsFeature.FEATURE_MMTEL, c);
+ setupFeature(f, slotId, ImsFeature.FEATURE_MMTEL);
return f.getBinder();
} else {
Log.e(LOG_TAG, "createMmTelFeatureInternal: null feature returned.");
@@ -216,11 +227,10 @@ public class ImsService extends Service {
}
}
- private IImsRcsFeature createRcsFeatureInternal(int slotId,
- IImsFeatureStatusCallback c) {
+ private IImsRcsFeature createRcsFeatureInternal(int slotId) {
RcsFeature f = createRcsFeature(slotId);
if (f != null) {
- setupFeature(f, slotId, ImsFeature.FEATURE_RCS, c);
+ setupFeature(f, slotId, ImsFeature.FEATURE_RCS);
return f.getBinder();
} else {
Log.e(LOG_TAG, "createRcsFeatureInternal: null feature returned.");
@@ -228,13 +238,45 @@ public class ImsService extends Service {
}
}
- private void setupFeature(ImsFeature f, int slotId, int featureType,
- IImsFeatureStatusCallback c) {
+ private void setupFeature(ImsFeature f, int slotId, int featureType) {
f.initialize(this, slotId);
- f.addImsFeatureStatusCallback(c);
addImsFeature(slotId, featureType, f);
}
+ private void addImsFeatureStatusCallback(int slotId, int featureType,
+ IImsFeatureStatusCallback c) {
+ synchronized (mFeaturesBySlot) {
+ // get ImsFeature associated with the slot/feature
+ SparseArray<ImsFeature> features = mFeaturesBySlot.get(slotId);
+ if (features == null) {
+ Log.w(LOG_TAG, "Can not add ImsFeatureStatusCallback - no features on slot "
+ + slotId);
+ return;
+ }
+ ImsFeature f = features.get(featureType);
+ if (f != null) {
+ f.addImsFeatureStatusCallback(c);
+ }
+ }
+ }
+
+ private void removeImsFeatureStatusCallback(int slotId, int featureType,
+ IImsFeatureStatusCallback c) {
+ synchronized (mFeaturesBySlot) {
+ // get ImsFeature associated with the slot/feature
+ SparseArray<ImsFeature> features = mFeaturesBySlot.get(slotId);
+ if (features == null) {
+ Log.w(LOG_TAG, "Can not remove ImsFeatureStatusCallback - no features on slot "
+ + slotId);
+ return;
+ }
+ ImsFeature f = features.get(featureType);
+ if (f != null) {
+ f.removeImsFeatureStatusCallback(c);
+ }
+ }
+ }
+
private void addImsFeature(int slotId, int featureType, ImsFeature f) {
synchronized (mFeaturesBySlot) {
// Get SparseArray for Features, by querying slot Id
@@ -248,8 +290,7 @@ public class ImsService extends Service {
}
}
- private void removeImsFeature(int slotId, int featureType,
- IImsFeatureStatusCallback c) {
+ private void removeImsFeature(int slotId, int featureType) {
synchronized (mFeaturesBySlot) {
// get ImsFeature associated with the slot/feature
SparseArray<ImsFeature> features = mFeaturesBySlot.get(slotId);
@@ -264,7 +305,6 @@ public class ImsService extends Service {
+ featureType + " exists on slot " + slotId);
return;
}
- f.removeImsFeatureStatusCallback(c);
f.onFeatureRemoved();
features.remove(featureType);
}
diff --git a/telephony/java/android/telephony/ims/aidl/IImsServiceController.aidl b/telephony/java/android/telephony/ims/aidl/IImsServiceController.aidl
index c7da681b86a3..c956cbcc816c 100644
--- a/telephony/java/android/telephony/ims/aidl/IImsServiceController.aidl
+++ b/telephony/java/android/telephony/ims/aidl/IImsServiceController.aidl
@@ -31,12 +31,14 @@ import com.android.ims.internal.IImsFeatureStatusCallback;
*/
interface IImsServiceController {
void setListener(IImsServiceControllerListener l);
- IImsMmTelFeature createMmTelFeature(int slotId, in IImsFeatureStatusCallback c);
- IImsRcsFeature createRcsFeature(int slotId, in IImsFeatureStatusCallback c);
+ IImsMmTelFeature createMmTelFeature(int slotId);
+ IImsRcsFeature createRcsFeature(int slotId);
ImsFeatureConfiguration querySupportedImsFeatures();
+ void addFeatureStatusCallback(int slotId, int featureType, in IImsFeatureStatusCallback c);
+ void removeFeatureStatusCallback(int slotId, int featureType, in IImsFeatureStatusCallback c);
// Synchronous call to ensure the ImsService is ready before continuing with feature creation.
void notifyImsServiceReadyForFeatureCreation();
- void removeImsFeature(int slotId, int featureType, in IImsFeatureStatusCallback c);
+ void removeImsFeature(int slotId, int featureType);
IImsConfig getConfig(int slotId);
IImsRegistration getRegistration(int slotId);
oneway void enableIms(int slotId);
diff --git a/telephony/java/android/telephony/ims/compat/ImsService.java b/telephony/java/android/telephony/ims/compat/ImsService.java
index eafbb14539f5..41d1d726b3f4 100644
--- a/telephony/java/android/telephony/ims/compat/ImsService.java
+++ b/telephony/java/android/telephony/ims/compat/ImsService.java
@@ -21,7 +21,6 @@ import android.app.Service;
import android.compat.annotation.UnsupportedAppUsage;
import android.content.Intent;
import android.os.IBinder;
-import android.os.RemoteException;
import android.telephony.CarrierConfigManager;
import android.telephony.ims.compat.feature.ImsFeature;
import android.telephony.ims.compat.feature.MMTelFeature;
@@ -91,25 +90,35 @@ public class ImsService extends Service {
protected final IBinder mImsServiceController = new IImsServiceController.Stub() {
@Override
- public IImsMMTelFeature createEmergencyMMTelFeature(int slotId,
- IImsFeatureStatusCallback c) {
- return createEmergencyMMTelFeatureInternal(slotId, c);
+ public IImsMMTelFeature createEmergencyMMTelFeature(int slotId) {
+ return createEmergencyMMTelFeatureInternal(slotId);
}
@Override
- public IImsMMTelFeature createMMTelFeature(int slotId, IImsFeatureStatusCallback c) {
- return createMMTelFeatureInternal(slotId, c);
+ public IImsMMTelFeature createMMTelFeature(int slotId) {
+ return createMMTelFeatureInternal(slotId);
}
@Override
- public IImsRcsFeature createRcsFeature(int slotId, IImsFeatureStatusCallback c) {
- return createRcsFeatureInternal(slotId, c);
+ public IImsRcsFeature createRcsFeature(int slotId) {
+ return createRcsFeatureInternal(slotId);
}
@Override
- public void removeImsFeature(int slotId, int featureType, IImsFeatureStatusCallback c)
- throws RemoteException {
- ImsService.this.removeImsFeature(slotId, featureType, c);
+ public void removeImsFeature(int slotId, int featureType) {
+ ImsService.this.removeImsFeature(slotId, featureType);
+ }
+
+ @Override
+ public void addFeatureStatusCallback(int slotId, int featureType,
+ IImsFeatureStatusCallback c) {
+ addImsFeatureStatusCallback(slotId, featureType, c);
+ }
+
+ @Override
+ public void removeFeatureStatusCallback(int slotId, int featureType,
+ IImsFeatureStatusCallback c) {
+ removeImsFeatureStatusCallback(slotId, featureType, c);
}
};
@@ -137,46 +146,40 @@ public class ImsService extends Service {
return mFeaturesBySlot.get(slotId);
}
- private IImsMMTelFeature createEmergencyMMTelFeatureInternal(int slotId,
- IImsFeatureStatusCallback c) {
+ private IImsMMTelFeature createEmergencyMMTelFeatureInternal(int slotId) {
MMTelFeature f = onCreateEmergencyMMTelImsFeature(slotId);
if (f != null) {
- setupFeature(f, slotId, ImsFeature.EMERGENCY_MMTEL, c);
+ setupFeature(f, slotId, ImsFeature.EMERGENCY_MMTEL);
return f.getBinder();
} else {
return null;
}
}
- private IImsMMTelFeature createMMTelFeatureInternal(int slotId,
- IImsFeatureStatusCallback c) {
+ private IImsMMTelFeature createMMTelFeatureInternal(int slotId) {
MMTelFeature f = onCreateMMTelImsFeature(slotId);
if (f != null) {
- setupFeature(f, slotId, ImsFeature.MMTEL, c);
+ setupFeature(f, slotId, ImsFeature.MMTEL);
return f.getBinder();
} else {
return null;
}
}
- private IImsRcsFeature createRcsFeatureInternal(int slotId,
- IImsFeatureStatusCallback c) {
+ private IImsRcsFeature createRcsFeatureInternal(int slotId) {
RcsFeature f = onCreateRcsFeature(slotId);
if (f != null) {
- setupFeature(f, slotId, ImsFeature.RCS, c);
+ setupFeature(f, slotId, ImsFeature.RCS);
return f.getBinder();
} else {
return null;
}
}
- private void setupFeature(ImsFeature f, int slotId, int featureType,
- IImsFeatureStatusCallback c) {
+ private void setupFeature(ImsFeature f, int slotId, int featureType) {
f.setContext(this);
f.setSlotId(slotId);
- f.addImsFeatureStatusCallback(c);
addImsFeature(slotId, featureType, f);
- // TODO: Remove once new onFeatureReady AIDL is merged in.
f.onFeatureReady();
}
@@ -193,12 +196,45 @@ public class ImsService extends Service {
}
}
- private void removeImsFeature(int slotId, int featureType,
+ private void addImsFeatureStatusCallback(int slotId, int featureType,
+ IImsFeatureStatusCallback c) {
+ synchronized (mFeaturesBySlot) {
+ // get ImsFeature associated with the slot/feature
+ SparseArray<ImsFeature> features = mFeaturesBySlot.get(slotId);
+ if (features == null) {
+ Log.w(LOG_TAG, "Can not add ImsFeatureStatusCallback. No ImsFeatures exist on"
+ + " slot " + slotId);
+ return;
+ }
+ ImsFeature f = features.get(featureType);
+ if (f != null) {
+ f.addImsFeatureStatusCallback(c);
+ }
+ }
+ }
+
+ private void removeImsFeatureStatusCallback(int slotId, int featureType,
IImsFeatureStatusCallback c) {
synchronized (mFeaturesBySlot) {
// get ImsFeature associated with the slot/feature
SparseArray<ImsFeature> features = mFeaturesBySlot.get(slotId);
if (features == null) {
+ Log.w(LOG_TAG, "Can not remove ImsFeatureStatusCallback. No ImsFeatures exist on"
+ + " slot " + slotId);
+ return;
+ }
+ ImsFeature f = features.get(featureType);
+ if (f != null) {
+ f.removeImsFeatureStatusCallback(c);
+ }
+ }
+ }
+
+ private void removeImsFeature(int slotId, int featureType) {
+ synchronized (mFeaturesBySlot) {
+ // get ImsFeature associated with the slot/feature
+ SparseArray<ImsFeature> features = mFeaturesBySlot.get(slotId);
+ if (features == null) {
Log.w(LOG_TAG, "Can not remove ImsFeature. No ImsFeatures exist on slot "
+ slotId);
return;
@@ -209,7 +245,6 @@ public class ImsService extends Service {
+ featureType + " exists on slot " + slotId);
return;
}
- f.removeImsFeatureStatusCallback(c);
f.onFeatureRemoved();
features.remove(featureType);
}
diff --git a/telephony/java/com/android/ims/internal/IImsServiceController.aidl b/telephony/java/com/android/ims/internal/IImsServiceController.aidl
index 857089fac33a..e9528f474082 100644
--- a/telephony/java/com/android/ims/internal/IImsServiceController.aidl
+++ b/telephony/java/com/android/ims/internal/IImsServiceController.aidl
@@ -25,8 +25,10 @@ import com.android.ims.internal.IImsRcsFeature;
* {@hide}
*/
interface IImsServiceController {
- IImsMMTelFeature createEmergencyMMTelFeature(int slotId, in IImsFeatureStatusCallback c);
- IImsMMTelFeature createMMTelFeature(int slotId, in IImsFeatureStatusCallback c);
- IImsRcsFeature createRcsFeature(int slotId, in IImsFeatureStatusCallback c);
- void removeImsFeature(int slotId, int featureType, in IImsFeatureStatusCallback c);
+ IImsMMTelFeature createEmergencyMMTelFeature(int slotId);
+ IImsMMTelFeature createMMTelFeature(int slotId);
+ IImsRcsFeature createRcsFeature(int slotId);
+ void removeImsFeature(int slotId, int featureType);
+ void addFeatureStatusCallback(int slotId, int featureType, in IImsFeatureStatusCallback c);
+ void removeFeatureStatusCallback(int slotId, int featureType, in IImsFeatureStatusCallback c);
}
diff --git a/telephony/java/com/android/internal/telephony/ICallForwardingInfoCallback.aidl b/telephony/java/com/android/internal/telephony/ICallForwardingInfoCallback.aidl
new file mode 100644
index 000000000000..4d3b9f4636df
--- /dev/null
+++ b/telephony/java/com/android/internal/telephony/ICallForwardingInfoCallback.aidl
@@ -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.internal.telephony;
+
+import android.telephony.CallForwardingInfo;
+
+// Callback interface for the getCallForwarding API in TelephonyManager.
+oneway interface ICallForwardingInfoCallback {
+ void onCallForwardingInfoAvailable(in CallForwardingInfo info);
+ void onError(int error);
+} \ No newline at end of file
diff --git a/telephony/java/com/android/internal/telephony/ITelephony.aidl b/telephony/java/com/android/internal/telephony/ITelephony.aidl
index 4021d0a2888f..02a74ba53ccb 100644
--- a/telephony/java/com/android/internal/telephony/ITelephony.aidl
+++ b/telephony/java/com/android/internal/telephony/ITelephony.aidl
@@ -58,6 +58,7 @@ import android.telephony.ims.aidl.IImsRegistrationCallback;
import com.android.ims.internal.IImsServiceFeatureCallback;
import com.android.internal.telephony.CellNetworkScanResult;
import com.android.internal.telephony.IBooleanConsumer;
+import com.android.internal.telephony.ICallForwardingInfoCallback;
import com.android.internal.telephony.IIntegerConsumer;
import com.android.internal.telephony.INumberVerificationCallback;
import com.android.internal.telephony.OperatorInfo;
@@ -1648,78 +1649,15 @@ interface ITelephony {
*/
void carrierActionResetAll(int subId);
- /**
- * Gets the voice call forwarding info {@link CallForwardingInfo}, given the call forward
- * reason.
- *
- * @param callForwardingReason the call forwarding reasons which are the bitwise-OR combination
- * of the following constants:
- * <ol>
- * <li>{@link CallForwardingInfo#REASON_BUSY} </li>
- * <li>{@link CallForwardingInfo#REASON_NO_REPLY} </li>
- * <li>{@link CallForwardingInfo#REASON_NOT_REACHABLE} </li>
- * </ol>
- *
- * @throws IllegalArgumentException if callForwardingReason is not a bitwise-OR combination
- * of {@link CallForwardingInfo.REASON_BUSY}, {@link CallForwardingInfo.REASON_BUSY},
- * {@link CallForwardingInfo.REASON_NOT_REACHABLE}
- *
- * @return {@link CallForwardingInfo} with the status {@link CallForwardingInfo#STATUS_ACTIVE}
- * or {@link CallForwardingInfo#STATUS_INACTIVE} and the target phone number to forward calls
- * to, if it's available. Otherwise, it will return a {@link CallForwardingInfo} with status
- * {@link CallForwardingInfo#STATUS_NOT_SUPPORTED} or
- * {@link CallForwardingInfo#STATUS_FDN_CHECK_FAILURE} depending on the situation.
- *
- * @hide
- */
- CallForwardingInfo getCallForwarding(int subId, int callForwardingReason);
+ void getCallForwarding(int subId, int callForwardingReason,
+ ICallForwardingInfoCallback callback);
- /**
- * Sets the voice call forwarding info including status (enable/disable), call forwarding
- * reason, the number to forward, and the timeout before the forwarding is attempted.
- *
- * @param callForwardingInfo {@link CallForwardingInfo} to setup the call forwarding.
- * Enabling if {@link CallForwardingInfo#getStatus()} returns
- * {@link CallForwardingInfo#STATUS_ACTIVE}; Disabling if
- * {@link CallForwardingInfo#getStatus()} returns {@link CallForwardingInfo#STATUS_INACTIVE}.
- *
- * @throws IllegalArgumentException if any of the following:
- * 0) callForwardingInfo is null.
- * 1) {@link CallForwardingInfo#getStatus()} for callForwardingInfo returns neither
- * {@link CallForwardingInfo#STATUS_ACTIVE} nor {@link CallForwardingInfo#STATUS_INACTIVE}.
- * 2) {@link CallForwardingInfo#getReason()} for callForwardingInfo doesn't return the
- * bitwise-OR combination of {@link CallForwardingInfo.REASON_BUSY},
- * {@link CallForwardingInfo.REASON_BUSY}, {@link CallForwardingInfo.REASON_NOT_REACHABLE}
- * 3) {@link CallForwardingInfo#getNumber()} for callForwardingInfo returns null.
- * 4) {@link CallForwardingInfo#getTimeout()} for callForwardingInfo returns nagetive value.
- *
- * @return {@code true} to indicate it was set successfully; {@code false} otherwise.
- *
- * @hide
- */
- boolean setCallForwarding(int subId, in CallForwardingInfo callForwardingInfo);
+ void setCallForwarding(int subId, in CallForwardingInfo callForwardingInfo,
+ IIntegerConsumer callback);
- /**
- * Gets the status of voice call waiting function. Call waiting function enables the waiting
- * for the incoming call when it reaches the user who is busy to make another call and allows
- * users to decide whether to switch to the incoming call.
- *
- * @return the status of call waiting function.
- * @hide
- */
- int getCallWaitingStatus(int subId);
+ void getCallWaitingStatus(int subId, IIntegerConsumer callback);
- /**
- * Sets the status for voice call waiting function. Call waiting function enables the waiting
- * for the incoming call when it reaches the user who is busy to make another call and allows
- * users to decide whether to switch to the incoming call.
- *
- * @param isEnable {@code true} to enable; {@code false} to disable.
- * @return {@code true} to indicate it was set successfully; {@code false} otherwise.
- *
- * @hide
- */
- boolean setCallWaitingStatus(int subId, boolean isEnable);
+ void setCallWaitingStatus(int subId, boolean enabled, IIntegerConsumer callback);
/**
* Get Client request stats which will contain statistical information
diff --git a/tests/FlickerTests/Android.bp b/tests/FlickerTests/Android.bp
index a53ea16ba7c4..d430db525f4c 100644
--- a/tests/FlickerTests/Android.bp
+++ b/tests/FlickerTests/Android.bp
@@ -29,7 +29,8 @@ android_test {
"flickerlib",
"truth-prebuilt",
"launcher-helper-lib",
- "launcher-aosp-tapl"
+ "launcher-aosp-tapl",
+ "platform-test-annotations",
],
}
@@ -50,6 +51,7 @@ android_test {
"truth-prebuilt",
"app-helpers-core",
"launcher-helper-lib",
- "launcher-aosp-tapl"
+ "launcher-aosp-tapl",
+ "platform-test-annotations",
],
} \ No newline at end of file
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/ime/CloseImeAutoOpenWindowToHomeTest.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/ime/CloseImeAutoOpenWindowToHomeTest.kt
index b64811b2767d..c1ba21a2977a 100644
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/ime/CloseImeAutoOpenWindowToHomeTest.kt
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/ime/CloseImeAutoOpenWindowToHomeTest.kt
@@ -16,6 +16,7 @@
package com.android.server.wm.flicker.ime
+import android.platform.test.annotations.Presubmit
import android.view.Surface
import androidx.test.filters.RequiresDevice
import com.android.server.wm.flicker.dsl.flicker
@@ -39,6 +40,7 @@ import org.junit.runners.Parameterized
* Test IME window closing back to app window transitions.
* To run this test: `atest FlickerTests:CloseImeWindowToAppTest`
*/
+@Presubmit
@RequiresDevice
@RunWith(Parameterized::class)
@FixMethodOrder(MethodSorters.NAME_ASCENDING)
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/ime/CloseImeWindowToAppTest.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/ime/CloseImeWindowToAppTest.kt
index 0940c192517e..2c0072270378 100644
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/ime/CloseImeWindowToAppTest.kt
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/ime/CloseImeWindowToAppTest.kt
@@ -16,6 +16,7 @@
package com.android.server.wm.flicker.ime
+import android.platform.test.annotations.Presubmit
import android.view.Surface
import androidx.test.filters.RequiresDevice
import com.android.server.wm.flicker.NonRotationTestBase
@@ -39,6 +40,7 @@ import org.junit.runners.Parameterized
* Test IME window closing back to app window transitions.
* To run this test: `atest FlickerTests:CloseImeWindowToAppTest`
*/
+@Presubmit
@RequiresDevice
@RunWith(Parameterized::class)
@FixMethodOrder(MethodSorters.NAME_ASCENDING)
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/ime/CloseImeWindowToHomeTest.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/ime/CloseImeWindowToHomeTest.kt
index c2e87dbbf24b..4697adcbce80 100644
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/ime/CloseImeWindowToHomeTest.kt
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/ime/CloseImeWindowToHomeTest.kt
@@ -16,6 +16,7 @@
package com.android.server.wm.flicker.ime
+import android.platform.test.annotations.Presubmit
import android.view.Surface
import androidx.test.filters.RequiresDevice
import com.android.server.wm.flicker.NonRotationTestBase
@@ -40,6 +41,7 @@ import org.junit.runners.Parameterized
* Test IME window closing to home transitions.
* To run this test: `atest FlickerTests:CloseImeWindowToHomeTest`
*/
+@Presubmit
@RequiresDevice
@RunWith(Parameterized::class)
@FixMethodOrder(MethodSorters.NAME_ASCENDING)
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/ime/OpenImeWindowTest.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/ime/OpenImeWindowTest.kt
index 11ccb69c46cf..2caa8f399be7 100644
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/ime/OpenImeWindowTest.kt
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/ime/OpenImeWindowTest.kt
@@ -16,6 +16,7 @@
package com.android.server.wm.flicker.ime
+import android.platform.test.annotations.Presubmit
import android.view.Surface
import androidx.test.filters.RequiresDevice
import com.android.server.wm.flicker.NonRotationTestBase
@@ -39,6 +40,7 @@ import org.junit.runners.Parameterized
* Test IME window opening transitions.
* To run this test: `atest FlickerTests:OpenImeWindowTest`
*/
+@Presubmit
@RequiresDevice
@RunWith(Parameterized::class)
@FixMethodOrder(MethodSorters.NAME_ASCENDING)
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppColdTest.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppColdTest.kt
index 17590723da8d..2c9c8ba75a10 100644
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppColdTest.kt
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppColdTest.kt
@@ -16,6 +16,7 @@
package com.android.server.wm.flicker.launch
+import android.platform.test.annotations.Presubmit
import android.view.Surface
import androidx.test.filters.RequiresDevice
import com.android.server.wm.flicker.dsl.flicker
@@ -38,6 +39,7 @@ import org.junit.runners.Parameterized
* Test cold launch app from launcher.
* To run this test: `atest FlickerTests:OpenAppColdTest`
*/
+@Presubmit
@RequiresDevice
@RunWith(Parameterized::class)
@FixMethodOrder(MethodSorters.NAME_ASCENDING)
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/splitscreen/SplitScreenToLauncherTest.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/splitscreen/SplitScreenToLauncherTest.kt
index 87c863338edb..7447bdae9abe 100644
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/splitscreen/SplitScreenToLauncherTest.kt
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/splitscreen/SplitScreenToLauncherTest.kt
@@ -16,6 +16,7 @@
package com.android.server.wm.flicker.splitscreen
+import android.platform.test.annotations.Presubmit
import android.view.Surface
import androidx.test.filters.RequiresDevice
import com.android.server.wm.flicker.NonRotationTestBase
@@ -43,6 +44,7 @@ import org.junit.runners.Parameterized
* Test open app to split screen.
* To run this test: `atest FlickerTests:SplitScreenToLauncherTest`
*/
+@Presubmit
@RequiresDevice
@RunWith(Parameterized::class)
@FixMethodOrder(MethodSorters.NAME_ASCENDING)
diff --git a/tests/ManagedProfileLifecycleStressTest/src/com/android/test/stress/ManagedProfileLifecycleStressTest.java b/tests/ManagedProfileLifecycleStressTest/src/com/android/test/stress/ManagedProfileLifecycleStressTest.java
index 026677e09bed..99dde859028a 100644
--- a/tests/ManagedProfileLifecycleStressTest/src/com/android/test/stress/ManagedProfileLifecycleStressTest.java
+++ b/tests/ManagedProfileLifecycleStressTest/src/com/android/test/stress/ManagedProfileLifecycleStressTest.java
@@ -20,6 +20,7 @@ import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertTrue;
import static org.junit.Assert.fail;
+import com.android.tradefed.device.CollectingOutputReceiver;
import com.android.tradefed.log.LogUtil.CLog;
import com.android.tradefed.testtype.DeviceJUnit4ClassRunner;
import com.android.tradefed.testtype.junit4.BaseHostJUnit4Test;
@@ -62,13 +63,62 @@ public class ManagedProfileLifecycleStressTest extends BaseHostJUnit4Test {
CLog.w("Iteration N" + iteration);
final int userId = createManagedProfile();
startUser(userId);
- installPackageAsUser(DUMMY_DPC_APK, true /* grantPermissions */, userId, "-t");
+ installPackageAsUser(
+ DUMMY_DPC_APK, /* grantPermissions= */true, userId, /* options= */"-t");
setProfileOwner(DUMMY_DPC_COMPONENT, userId);
removeUser(userId);
}
CLog.w("Completed " + iteration + " iterations.");
}
+ /**
+ * Create, start, and kill managed profiles in a loop with waitForBroadcastIdle after each user
+ * operation.
+ */
+ @Test
+ public void testCreateStartDeleteStable() throws Exception {
+ // Disable package verifier for ADB installs.
+ getDevice().executeShellCommand("settings put global verifier_verify_adb_installs 0");
+ int iteration = 0;
+ final long deadline = System.nanoTime() + TimeUnit.MINUTES.toNanos(TIME_LIMIT_MINUTES);
+ while (System.nanoTime() < deadline) {
+ iteration++;
+ CLog.w("Iteration N" + iteration);
+ final int userId = createManagedProfile();
+ waitForBroadcastIdle();
+
+ startUser(userId);
+ waitForBroadcastIdle();
+
+ installPackageAsUser(
+ DUMMY_DPC_APK, /* grantPermissions= */true, userId, /* options= */"-t");
+
+ setProfileOwner(DUMMY_DPC_COMPONENT, userId);
+
+ removeUser(userId);
+ waitForBroadcastIdle();
+ }
+ CLog.w("Completed " + iteration + " iterations.");
+ }
+
+ private void waitForBroadcastIdle() throws Exception {
+ final CollectingOutputReceiver receiver = new CollectingOutputReceiver();
+ // We allow 8min for the command to complete and 4min for the command to start to
+ // output something.
+ getDevice().executeShellCommand(
+ "am wait-for-broadcast-idle",
+ receiver,
+ /* maxTimeoutForCommand= */8,
+ /* maxTimeoutToOutputShellResponse= */4,
+ TimeUnit.MINUTES,
+ /* retryAttempts= */0);
+ final String output = receiver.getOutput();
+ if (!output.contains("All broadcast queues are idle!")) {
+ CLog.e("Output from 'am wait-for-broadcast-idle': %s", output);
+ fail("'am wait-for-broadcase-idle' did not complete.");
+ }
+ }
+
private int createManagedProfile() throws Exception {
final String output = getDevice().executeShellCommand(
"pm create-user --profileOf 0 --managed TestProfile");
diff --git a/tests/SoundTriggerTestApp/src/com/android/test/soundtrigger/SoundTriggerTestService.java b/tests/SoundTriggerTestApp/src/com/android/test/soundtrigger/SoundTriggerTestService.java
index 9324ba0b8b72..380e29984c63 100644
--- a/tests/SoundTriggerTestApp/src/com/android/test/soundtrigger/SoundTriggerTestService.java
+++ b/tests/SoundTriggerTestApp/src/com/android/test/soundtrigger/SoundTriggerTestService.java
@@ -23,7 +23,6 @@ import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
import android.content.pm.PackageManager;
-import android.hardware.soundtrigger.SoundTrigger.GenericSoundModel;
import android.media.AudioAttributes;
import android.media.AudioFormat;
import android.media.AudioManager;
@@ -31,6 +30,7 @@ import android.media.AudioRecord;
import android.media.AudioTrack;
import android.media.MediaPlayer;
import android.media.soundtrigger.SoundTriggerDetector;
+import android.media.soundtrigger.SoundTriggerManager;
import android.net.Uri;
import android.os.Binder;
import android.os.IBinder;
@@ -232,8 +232,8 @@ public class SoundTriggerTestService extends Service {
public AudioTrack captureAudioTrack;
}
- private GenericSoundModel createNewSoundModel(ModelInfo modelInfo) {
- return new GenericSoundModel(modelInfo.modelUuid, modelInfo.vendorUuid,
+ private SoundTriggerManager.Model createNewSoundModel(ModelInfo modelInfo) {
+ return SoundTriggerManager.Model.create(modelInfo.modelUuid, modelInfo.vendorUuid,
modelInfo.modelData);
}
@@ -246,16 +246,16 @@ public class SoundTriggerTestService extends Service {
postMessage("Loading model: " + modelInfo.name);
- GenericSoundModel soundModel = createNewSoundModel(modelInfo);
+ SoundTriggerManager.Model soundModel = createNewSoundModel(modelInfo);
boolean status = mSoundTriggerUtil.addOrUpdateSoundModel(soundModel);
if (status) {
postToast("Successfully loaded " + modelInfo.name + ", UUID="
- + soundModel.getUuid());
+ + soundModel.getModelUuid());
setModelState(modelInfo, "Loaded");
} else {
postErrorToast("Failed to load " + modelInfo.name + ", UUID="
- + soundModel.getUuid() + "!");
+ + soundModel.getModelUuid() + "!");
setModelState(modelInfo, "Failed to load");
}
}
@@ -269,7 +269,7 @@ public class SoundTriggerTestService extends Service {
postMessage("Unloading model: " + modelInfo.name);
- GenericSoundModel soundModel = mSoundTriggerUtil.getSoundModel(modelUuid);
+ SoundTriggerManager.Model soundModel = mSoundTriggerUtil.getSoundModel(modelUuid);
if (soundModel == null) {
postErrorToast("Sound model not found for " + modelInfo.name + "!");
return;
@@ -278,11 +278,11 @@ public class SoundTriggerTestService extends Service {
boolean status = mSoundTriggerUtil.deleteSoundModel(modelUuid);
if (status) {
postToast("Successfully unloaded " + modelInfo.name + ", UUID="
- + soundModel.getUuid());
+ + soundModel.getModelUuid());
setModelState(modelInfo, "Unloaded");
} else {
postErrorToast("Failed to unload " +
- modelInfo.name + ", UUID=" + soundModel.getUuid() + "!");
+ modelInfo.name + ", UUID=" + soundModel.getModelUuid() + "!");
setModelState(modelInfo, "Failed to unload");
}
}
@@ -294,12 +294,12 @@ public class SoundTriggerTestService extends Service {
return;
}
postMessage("Reloading model: " + modelInfo.name);
- GenericSoundModel soundModel = mSoundTriggerUtil.getSoundModel(modelUuid);
+ SoundTriggerManager.Model soundModel = mSoundTriggerUtil.getSoundModel(modelUuid);
if (soundModel == null) {
postErrorToast("Sound model not found for " + modelInfo.name + "!");
return;
}
- GenericSoundModel updated = createNewSoundModel(modelInfo);
+ SoundTriggerManager.Model updated = createNewSoundModel(modelInfo);
boolean status = mSoundTriggerUtil.addOrUpdateSoundModel(updated);
if (status) {
postToast("Successfully reloaded " + modelInfo.name + ", UUID="
diff --git a/tests/SoundTriggerTestApp/src/com/android/test/soundtrigger/SoundTriggerUtil.java b/tests/SoundTriggerTestApp/src/com/android/test/soundtrigger/SoundTriggerUtil.java
index 6b89b62bb1e3..cfe8c855ac81 100644
--- a/tests/SoundTriggerTestApp/src/com/android/test/soundtrigger/SoundTriggerUtil.java
+++ b/tests/SoundTriggerTestApp/src/com/android/test/soundtrigger/SoundTriggerUtil.java
@@ -18,7 +18,6 @@ package com.android.test.soundtrigger;
import android.annotation.Nullable;
import android.content.Context;
-import android.hardware.soundtrigger.SoundTrigger.GenericSoundModel;
import android.media.soundtrigger.SoundTriggerDetector;
import android.media.soundtrigger.SoundTriggerManager;
import android.os.RemoteException;
@@ -37,13 +36,10 @@ import java.util.UUID;
public class SoundTriggerUtil {
private static final String TAG = "SoundTriggerTestUtil";
- private final ISoundTriggerService mSoundTriggerService;
private final SoundTriggerManager mSoundTriggerManager;
private final Context mContext;
public SoundTriggerUtil(Context context) {
- mSoundTriggerService = ISoundTriggerService.Stub.asInterface(
- ServiceManager.getService(Context.SOUND_TRIGGER_SERVICE));
mSoundTriggerManager = (SoundTriggerManager) context.getSystemService(
Context.SOUND_TRIGGER_SERVICE);
mContext = context;
@@ -55,15 +51,11 @@ public class SoundTriggerUtil {
*
* @param soundModel The sound model to add/update.
*/
- public boolean addOrUpdateSoundModel(GenericSoundModel soundModel) {
- try {
- if (soundModel == null) {
- throw new RuntimeException("Bad sound model");
- }
- mSoundTriggerService.updateSoundModel(soundModel);
- } catch (RemoteException e) {
- Log.e(TAG, "RemoteException in updateSoundModel", e);
+ public boolean addOrUpdateSoundModel(SoundTriggerManager.Model soundModel) {
+ if (soundModel == null) {
+ throw new RuntimeException("Bad sound model");
}
+ mSoundTriggerManager.updateModel(soundModel);
return true;
}
@@ -71,19 +63,15 @@ public class SoundTriggerUtil {
* Gets the sound model for the given keyphrase, null if none exists.
* If a sound model for a given keyphrase exists, and it needs to be updated,
* it should be obtained using this method, updated and then passed in to
- * {@link #addOrUpdateSoundModel(GenericSoundModel)} without changing the IDs.
+ * {@link #addOrUpdateSoundModel(SoundTriggerManager.Model)} without changing the IDs.
*
* @param modelId The model ID to look-up the sound model for.
* @return The sound model if one was found, null otherwise.
*/
@Nullable
- public GenericSoundModel getSoundModel(UUID modelId) {
- GenericSoundModel model = null;
- try {
- model = mSoundTriggerService.getSoundModel(new ParcelUuid(modelId));
- } catch (RemoteException e) {
- Log.e(TAG, "RemoteException in updateKeyphraseSoundModel");
- }
+ public SoundTriggerManager.Model getSoundModel(UUID modelId) {
+ SoundTriggerManager.Model model = null;
+ model = mSoundTriggerManager.getModel(modelId);
if (model == null) {
Log.w(TAG, "No models present for the given keyphrase ID");
@@ -100,12 +88,7 @@ public class SoundTriggerUtil {
* @return {@code true} if the call succeeds, {@code false} otherwise.
*/
public boolean deleteSoundModel(UUID modelId) {
- try {
- mSoundTriggerService.deleteSoundModel(new ParcelUuid(modelId));
- } catch (RemoteException e) {
- Log.e(TAG, "RemoteException in deleteSoundModel");
- return false;
- }
+ mSoundTriggerManager.deleteModel(modelId);
return true;
}
diff --git a/tests/net/integration/util/com/android/server/NetworkAgentWrapper.java b/tests/net/integration/util/com/android/server/NetworkAgentWrapper.java
index 9f0b41fa0cdf..c895420157d7 100644
--- a/tests/net/integration/util/com/android/server/NetworkAgentWrapper.java
+++ b/tests/net/integration/util/com/android/server/NetworkAgentWrapper.java
@@ -67,6 +67,9 @@ public class NetworkAgentWrapper implements TestableNetworkCallback.HasNetwork {
private NetworkAgent mNetworkAgent;
private int mStartKeepaliveError = SocketKeepalive.ERROR_UNSUPPORTED;
private int mStopKeepaliveError = SocketKeepalive.NO_KEEPALIVE;
+ // Controls how test network agent is going to wait before responding to keepalive
+ // start/stop. Useful when simulate KeepaliveTracker is waiting for response from modem.
+ private long mKeepaliveResponseDelay = 0L;
private Integer mExpectedKeepaliveSlot = null;
public NetworkAgentWrapper(int transport, LinkProperties linkProperties, Context context)
@@ -134,12 +137,17 @@ public class NetworkAgentWrapper implements TestableNetworkCallback.HasNetwork {
if (mWrapper.mExpectedKeepaliveSlot != null) {
assertEquals((int) mWrapper.mExpectedKeepaliveSlot, slot);
}
- onSocketKeepaliveEvent(slot, mWrapper.mStartKeepaliveError);
+ mWrapper.mHandlerThread.getThreadHandler().postDelayed(
+ () -> onSocketKeepaliveEvent(slot, mWrapper.mStartKeepaliveError),
+ mWrapper.mKeepaliveResponseDelay);
}
@Override
public void stopSocketKeepalive(Message msg) {
- onSocketKeepaliveEvent(msg.arg1, mWrapper.mStopKeepaliveError);
+ final int slot = msg.arg1;
+ mWrapper.mHandlerThread.getThreadHandler().postDelayed(
+ () -> onSocketKeepaliveEvent(slot, mWrapper.mStopKeepaliveError),
+ mWrapper.mKeepaliveResponseDelay);
}
@Override
@@ -248,6 +256,10 @@ public class NetworkAgentWrapper implements TestableNetworkCallback.HasNetwork {
mStopKeepaliveError = reason;
}
+ public void setKeepaliveResponseDelay(long delay) {
+ mKeepaliveResponseDelay = delay;
+ }
+
public void setExpectedKeepaliveSlot(Integer slot) {
mExpectedKeepaliveSlot = slot;
}
diff --git a/tests/net/java/com/android/server/ConnectivityServiceTest.java b/tests/net/java/com/android/server/ConnectivityServiceTest.java
index 1f23bf38c2f2..7dfac9c8c357 100644
--- a/tests/net/java/com/android/server/ConnectivityServiceTest.java
+++ b/tests/net/java/com/android/server/ConnectivityServiceTest.java
@@ -4292,6 +4292,32 @@ public class ConnectivityServiceTest {
myNet = connectKeepaliveNetwork(lp);
mWiFiNetworkAgent.setStartKeepaliveEvent(SocketKeepalive.SUCCESS);
+ // Check that a stop followed by network disconnects does not result in crash.
+ try (SocketKeepalive ka = mCm.createSocketKeepalive(
+ myNet, testSocket, myIPv4, dstIPv4, executor, callback)) {
+ ka.start(validKaInterval);
+ callback.expectStarted();
+ // Delay the response of keepalive events in networkAgent long enough to make sure
+ // the follow-up network disconnection will be processed first.
+ mWiFiNetworkAgent.setKeepaliveResponseDelay(3 * TIMEOUT_MS);
+ ka.stop();
+
+ // Make sure the stop has been processed. Wait for executor idle is needed to prevent
+ // flaky since the actual stop call to the service is delegated to executor thread.
+ waitForIdleSerialExecutor(executor, TIMEOUT_MS);
+ waitForIdle();
+
+ mWiFiNetworkAgent.disconnect();
+ mWiFiNetworkAgent.expectDisconnected();
+ callback.expectStopped();
+ callback.assertNoCallback();
+ }
+
+ // Reconnect.
+ waitForIdle();
+ myNet = connectKeepaliveNetwork(lp);
+ mWiFiNetworkAgent.setStartKeepaliveEvent(SocketKeepalive.SUCCESS);
+
// Check that keepalive slots start from 1 and increment. The first one gets slot 1.
mWiFiNetworkAgent.setExpectedKeepaliveSlot(1);
int srcPort2 = 0;
diff --git a/tools/validatekeymaps/Main.cpp b/tools/validatekeymaps/Main.cpp
index 7c150f9c8db9..0af6266d1642 100644
--- a/tools/validatekeymaps/Main.cpp
+++ b/tools/validatekeymaps/Main.cpp
@@ -115,13 +115,13 @@ static bool validateFile(const char* filename) {
}
case FILETYPE_INPUTDEVICECONFIGURATION: {
- PropertyMap* map;
- status_t status = PropertyMap::load(String8(filename), &map);
- if (status) {
- error("Error %d parsing input device configuration file.\n\n", status);
+ android::base::Result<std::unique_ptr<PropertyMap>> propertyMap =
+ PropertyMap::load(String8(filename));
+ if (!propertyMap.ok()) {
+ error("Error %d parsing input device configuration file.\n\n",
+ propertyMap.error().code());
return false;
}
- delete map;
break;
}
diff --git a/wifi/api/current.txt b/wifi/api/current.txt
index 3f5c673eeb81..1c297e7058dd 100644
--- a/wifi/api/current.txt
+++ b/wifi/api/current.txt
@@ -798,9 +798,15 @@ package android.net.wifi.hotspot2.pps {
method public int describeContents();
method public String getFqdn();
method public String getFriendlyName();
+ method @Nullable public long[] getMatchAllOis();
+ method @Nullable public long[] getMatchAnyOis();
+ method @Nullable public String[] getOtherHomePartners();
method public long[] getRoamingConsortiumOis();
method public void setFqdn(String);
method public void setFriendlyName(String);
+ method public void setMatchAllOis(@Nullable long[]);
+ method public void setMatchAnyOis(@Nullable long[]);
+ method public void setOtherHomePartners(@Nullable String[]);
method public void setRoamingConsortiumOis(long[]);
method public void writeToParcel(android.os.Parcel, int);
field @NonNull public static final android.os.Parcelable.Creator<android.net.wifi.hotspot2.pps.HomeSp> CREATOR;
diff --git a/wifi/api/system-current.txt b/wifi/api/system-current.txt
index c3e573c311c2..fd45ebec80ef 100644
--- a/wifi/api/system-current.txt
+++ b/wifi/api/system-current.txt
@@ -292,6 +292,7 @@ package android.net.wifi {
method public int getBandwidth();
method @Nullable public android.net.MacAddress getBssid();
method public int getFrequency();
+ method public int getWifiStandard();
method public void writeToParcel(@NonNull android.os.Parcel, int);
field public static final int CHANNEL_WIDTH_160MHZ = 6; // 0x6
field public static final int CHANNEL_WIDTH_20MHZ = 2; // 0x2
@@ -331,6 +332,7 @@ package android.net.wifi {
field @Deprecated public static final int RANDOMIZATION_NONE = 0; // 0x0
field @Deprecated public static final int RANDOMIZATION_PERSISTENT = 1; // 0x1
field @Deprecated public static final int RECENT_FAILURE_AP_UNABLE_TO_HANDLE_NEW_STA = 17; // 0x11
+ field @Deprecated public static final int RECENT_FAILURE_MBO_OCE_DISCONNECT = 1001; // 0x3e9
field @Deprecated public static final int RECENT_FAILURE_NONE = 0; // 0x0
field @Deprecated public boolean allowAutojoin;
field @Deprecated public int carrierId;
@@ -506,7 +508,7 @@ package android.net.wifi {
field public static final int EASY_CONNECT_NETWORK_ROLE_AP = 1; // 0x1
field public static final int EASY_CONNECT_NETWORK_ROLE_STA = 0; // 0x0
field public static final String EXTRA_CHANGE_REASON = "changeReason";
- field public static final String EXTRA_LINK_PROPERTIES = "android.net.wifi.extra.LINK_PROPERTIES";
+ field @Deprecated public static final String EXTRA_LINK_PROPERTIES = "android.net.wifi.extra.LINK_PROPERTIES";
field public static final String EXTRA_MULTIPLE_NETWORKS_CHANGED = "multipleChanges";
field public static final String EXTRA_OSU_NETWORK = "android.net.wifi.extra.OSU_NETWORK";
field public static final String EXTRA_PREVIOUS_WIFI_AP_STATE = "previous_wifi_state";
diff --git a/wifi/java/android/net/wifi/ScanResult.java b/wifi/java/android/net/wifi/ScanResult.java
index 54ec1e1c7c0b..329ca37407de 100644
--- a/wifi/java/android/net/wifi/ScanResult.java
+++ b/wifi/java/android/net/wifi/ScanResult.java
@@ -587,12 +587,18 @@ public final class ScanResult implements Parcelable {
* 6 GHz band frequency of first channel in MHz
* @hide
*/
- public static final int BAND_6_GHZ_START_FREQ_MHZ = 5945;
+ public static final int BAND_6_GHZ_START_FREQ_MHZ = 5955;
/**
* 6 GHz band frequency of last channel in MHz
* @hide
*/
- public static final int BAND_6_GHZ_END_FREQ_MHZ = 7105;
+ public static final int BAND_6_GHZ_END_FREQ_MHZ = 7115;
+
+ /**
+ * 6 GHz band operating class 136 channel 2 center frequency in MHz
+ * @hide
+ */
+ public static final int BAND_6_GHZ_OP_CLASS_136_CH_2_FREQ_MHZ = 5935;
/**
* 60 GHz band first channel number
@@ -645,7 +651,10 @@ public final class ScanResult implements Parcelable {
* @hide
*/
public static boolean is6GHz(int freqMhz) {
- return freqMhz >= BAND_6_GHZ_START_FREQ_MHZ && freqMhz <= BAND_6_GHZ_END_FREQ_MHZ;
+ if (freqMhz == BAND_6_GHZ_OP_CLASS_136_CH_2_FREQ_MHZ) {
+ return true;
+ }
+ return (freqMhz >= BAND_6_GHZ_START_FREQ_MHZ && freqMhz <= BAND_6_GHZ_END_FREQ_MHZ);
}
/**
@@ -687,6 +696,9 @@ public final class ScanResult implements Parcelable {
}
if (band == WifiScanner.WIFI_BAND_6_GHZ) {
if (channel >= BAND_6_GHZ_FIRST_CH_NUM && channel <= BAND_6_GHZ_LAST_CH_NUM) {
+ if (channel == 2) {
+ return BAND_6_GHZ_OP_CLASS_136_CH_2_FREQ_MHZ;
+ }
return ((channel - BAND_6_GHZ_FIRST_CH_NUM) * 5) + BAND_6_GHZ_START_FREQ_MHZ;
} else {
return UNSPECIFIED;
@@ -711,6 +723,9 @@ public final class ScanResult implements Parcelable {
} else if (is5GHz(freqMhz)) {
return ((freqMhz - BAND_5_GHZ_START_FREQ_MHZ) / 5) + BAND_5_GHZ_FIRST_CH_NUM;
} else if (is6GHz(freqMhz)) {
+ if (freqMhz == BAND_6_GHZ_OP_CLASS_136_CH_2_FREQ_MHZ) {
+ return 2;
+ }
return ((freqMhz - BAND_6_GHZ_START_FREQ_MHZ) / 5) + BAND_6_GHZ_FIRST_CH_NUM;
}
diff --git a/wifi/java/android/net/wifi/SoftApInfo.java b/wifi/java/android/net/wifi/SoftApInfo.java
index 40981f7b0fb1..cf61f81e6d22 100644
--- a/wifi/java/android/net/wifi/SoftApInfo.java
+++ b/wifi/java/android/net/wifi/SoftApInfo.java
@@ -86,8 +86,6 @@ public final class SoftApInfo implements Parcelable {
*/
public static final int CHANNEL_WIDTH_160MHZ = 6;
-
-
/** The frequency which AP resides on. */
private int mFrequency = 0;
@@ -99,6 +97,11 @@ public final class SoftApInfo implements Parcelable {
private MacAddress mBssid;
/**
+ * The operational mode of the AP.
+ */
+ private @WifiAnnotations.WifiStandard int mWifiStandard = ScanResult.WIFI_STANDARD_UNKNOWN;
+
+ /**
* Get the frequency which AP resides on.
*/
public int getFrequency() {
@@ -163,6 +166,27 @@ public final class SoftApInfo implements Parcelable {
}
/**
+ * Set the operational mode of the AP.
+ *
+ * @param wifiStandard values from {@link ScanResult}'s {@code WIFI_STANDARD_}
+ * @hide
+ */
+ public void setWifiStandard(@WifiAnnotations.WifiStandard int wifiStandard) {
+ mWifiStandard = wifiStandard;
+ }
+
+ /**
+ * Get the operational mode of the AP.
+ * @return valid values from {@link ScanResult}'s {@code WIFI_STANDARD_}
+ */
+ public @WifiAnnotations.WifiStandard int getWifiStandard() {
+ if (!SdkLevelUtil.isAtLeastS()) {
+ throw new UnsupportedOperationException();
+ }
+ return mWifiStandard;
+ }
+
+ /**
* @hide
*/
public SoftApInfo(@Nullable SoftApInfo source) {
@@ -170,6 +194,7 @@ public final class SoftApInfo implements Parcelable {
mFrequency = source.mFrequency;
mBandwidth = source.mBandwidth;
mBssid = source.mBssid;
+ mWifiStandard = source.mWifiStandard;
}
}
@@ -191,6 +216,7 @@ public final class SoftApInfo implements Parcelable {
dest.writeInt(mFrequency);
dest.writeInt(mBandwidth);
dest.writeParcelable(mBssid, flags);
+ dest.writeInt(mWifiStandard);
}
@NonNull
@@ -201,6 +227,7 @@ public final class SoftApInfo implements Parcelable {
info.mFrequency = in.readInt();
info.mBandwidth = in.readInt();
info.mBssid = in.readParcelable(MacAddress.class.getClassLoader());
+ info.mWifiStandard = in.readInt();
return info;
}
@@ -215,8 +242,9 @@ public final class SoftApInfo implements Parcelable {
StringBuilder sbuf = new StringBuilder();
sbuf.append("SoftApInfo{");
sbuf.append("bandwidth= ").append(mBandwidth);
- sbuf.append(",frequency= ").append(mFrequency);
+ sbuf.append(", frequency= ").append(mFrequency);
if (mBssid != null) sbuf.append(",bssid=").append(mBssid.toString());
+ sbuf.append(", wifiStandard= ").append(mWifiStandard);
sbuf.append("}");
return sbuf.toString();
}
@@ -228,11 +256,12 @@ public final class SoftApInfo implements Parcelable {
SoftApInfo softApInfo = (SoftApInfo) o;
return mFrequency == softApInfo.mFrequency
&& mBandwidth == softApInfo.mBandwidth
- && Objects.equals(mBssid, softApInfo.mBssid);
+ && Objects.equals(mBssid, softApInfo.mBssid)
+ && mWifiStandard == softApInfo.mWifiStandard;
}
@Override
public int hashCode() {
- return Objects.hash(mFrequency, mBandwidth, mBssid);
+ return Objects.hash(mFrequency, mBandwidth, mBssid, mWifiStandard);
}
}
diff --git a/wifi/java/android/net/wifi/WifiConfiguration.java b/wifi/java/android/net/wifi/WifiConfiguration.java
index 02b7a42d3878..391be357e963 100644
--- a/wifi/java/android/net/wifi/WifiConfiguration.java
+++ b/wifi/java/android/net/wifi/WifiConfiguration.java
@@ -1206,12 +1206,18 @@ public class WifiConfiguration implements Parcelable {
/**
* @hide
- * The wall clock time of when |mRandomizedMacAddress| should be re-randomized in aggressive
- * randomization mode.
+ * The wall clock time of when |mRandomizedMacAddress| should be re-randomized in enhanced
+ * MAC randomization mode.
*/
public long randomizedMacExpirationTimeMs = 0;
/**
+ * The wall clock time of when |mRandomizedMacAddress| is last modified.
+ * @hide
+ */
+ public long randomizedMacLastModifiedTimeMs = 0;
+
+ /**
* @hide
* Checks if the given MAC address can be used for Connected Mac Randomization
* by verifying that it is non-null, unicast, locally assigned, and not default mac.
@@ -2060,7 +2066,8 @@ public class WifiConfiguration implements Parcelable {
@Retention(RetentionPolicy.SOURCE)
@IntDef(prefix = "RECENT_FAILURE_", value = {
RECENT_FAILURE_NONE,
- RECENT_FAILURE_AP_UNABLE_TO_HANDLE_NEW_STA})
+ RECENT_FAILURE_AP_UNABLE_TO_HANDLE_NEW_STA,
+ RECENT_FAILURE_MBO_OCE_DISCONNECT})
public @interface RecentFailureReason {}
/**
@@ -2078,6 +2085,13 @@ public class WifiConfiguration implements Parcelable {
public static final int RECENT_FAILURE_AP_UNABLE_TO_HANDLE_NEW_STA = 17;
/**
+ * This network recently disconnected as a result of MBO/OCE.
+ * @hide
+ */
+ @SystemApi
+ public static final int RECENT_FAILURE_MBO_OCE_DISCONNECT = 1001;
+
+ /**
* Get the failure reason for the most recent connection attempt, or
* {@link #RECENT_FAILURE_NONE} if there was no failure.
*
@@ -2281,6 +2295,9 @@ public class WifiConfiguration implements Parcelable {
sbuf.append(" randomizedMacExpirationTimeMs: ")
.append(randomizedMacExpirationTimeMs == 0 ? "<none>"
: logTimeOfDay(randomizedMacExpirationTimeMs)).append("\n");
+ sbuf.append(" randomizedMacLastModifiedTimeMs: ")
+ .append(randomizedMacLastModifiedTimeMs == 0 ? "<none>"
+ : logTimeOfDay(randomizedMacLastModifiedTimeMs)).append("\n");
sbuf.append(" KeyMgmt:");
for (int k = 0; k < this.allowedKeyManagement.size(); k++) {
if (this.allowedKeyManagement.get(k)) {
@@ -2837,6 +2854,7 @@ public class WifiConfiguration implements Parcelable {
mRandomizedMacAddress = source.mRandomizedMacAddress;
macRandomizationSetting = source.macRandomizationSetting;
randomizedMacExpirationTimeMs = source.randomizedMacExpirationTimeMs;
+ randomizedMacLastModifiedTimeMs = source.randomizedMacLastModifiedTimeMs;
requirePmf = source.requirePmf;
updateIdentifier = source.updateIdentifier;
carrierId = source.carrierId;
@@ -2911,6 +2929,7 @@ public class WifiConfiguration implements Parcelable {
dest.writeInt(macRandomizationSetting);
dest.writeInt(osu ? 1 : 0);
dest.writeLong(randomizedMacExpirationTimeMs);
+ dest.writeLong(randomizedMacLastModifiedTimeMs);
dest.writeInt(carrierId);
dest.writeString(mPasspointUniqueId);
}
@@ -2985,6 +3004,7 @@ public class WifiConfiguration implements Parcelable {
config.macRandomizationSetting = in.readInt();
config.osu = in.readInt() != 0;
config.randomizedMacExpirationTimeMs = in.readLong();
+ config.randomizedMacLastModifiedTimeMs = in.readLong();
config.carrierId = in.readInt();
config.mPasspointUniqueId = in.readString();
return config;
diff --git a/wifi/java/android/net/wifi/WifiManager.java b/wifi/java/android/net/wifi/WifiManager.java
index c76f4a63a777..2219bfcd6aab 100644
--- a/wifi/java/android/net/wifi/WifiManager.java
+++ b/wifi/java/android/net/wifi/WifiManager.java
@@ -1053,9 +1053,6 @@ public class WifiManager {
/**
* Broadcast intent action indicating that the link configuration changed on wifi.
- * <br />Included Extras:
- * <br />{@link #EXTRA_LINK_PROPERTIES}: {@link android.net.LinkProperties} object associated
- * with the Wi-Fi network.
* <br /> No permissions are required to listen to this broadcast.
* @hide
*/
@@ -1071,8 +1068,12 @@ public class WifiManager {
* Included in the {@link #ACTION_LINK_CONFIGURATION_CHANGED} broadcast.
*
* Retrieve with {@link android.content.Intent#getParcelableExtra(String)}.
+ *
+ * @deprecated this extra is no longer populated.
+ *
* @hide
*/
+ @Deprecated
@SystemApi
public static final String EXTRA_LINK_PROPERTIES = "android.net.wifi.extra.LINK_PROPERTIES";
diff --git a/wifi/java/android/net/wifi/hotspot2/pps/HomeSp.java b/wifi/java/android/net/wifi/hotspot2/pps/HomeSp.java
index 8f34579f6a5d..35a8ff6095e0 100644
--- a/wifi/java/android/net/wifi/hotspot2/pps/HomeSp.java
+++ b/wifi/java/android/net/wifi/hotspot2/pps/HomeSp.java
@@ -16,6 +16,7 @@
package android.net.wifi.hotspot2.pps;
+import android.annotation.Nullable;
import android.os.Parcel;
import android.os.Parcelable;
import android.text.TextUtils;
@@ -139,16 +140,26 @@ public final class HomeSp implements Parcelable {
* (MO) tree for more detail.
*/
private long[] mMatchAllOis = null;
+
/**
- * @hide
+ * Set a list of HomeOIs such that all OIs in the list must match an OI in the Roaming
+ * Consortium advertised by a hotspot operator. The list set by this API will have precedence
+ * over {@link #setMatchAnyOis(long[])}, meaning the list set in {@link #setMatchAnyOis(long[])}
+ * will only be used for matching if the list set by this API is null or empty.
+ *
+ * @param matchAllOis An array of longs containing the HomeOIs
*/
- public void setMatchAllOis(long[] matchAllOis) {
+ public void setMatchAllOis(@Nullable long[] matchAllOis) {
mMatchAllOis = matchAllOis;
}
+
/**
- * @hide
+ * Get the list of HomeOIs such that all OIs in the list must match an OI in the Roaming
+ * Consortium advertised by a hotspot operator.
+ *
+ * @return An array of longs containing the HomeOIs
*/
- public long[] getMatchAllOis() {
+ public @Nullable long[] getMatchAllOis() {
return mMatchAllOis;
}
@@ -159,23 +170,34 @@ public final class HomeSp implements Parcelable {
* of that Hotspot provider (e.g. successful authentication with such Hotspot
* is possible).
*
- * {@link #mMatchAllOIs} will have precedence over this one, meaning this list will
- * only be used for matching if {@link #mMatchAllOIs} is null or empty.
+ * The list set by {@link #setMatchAllOis(long[])} will have precedence over this one, meaning
+ * this list will only be used for matching if the list set by {@link #setMatchAllOis(long[])}
+ * is null or empty.
*
* Refer to HomeSP/HomeOIList subtree in PerProviderSubscription (PPS) Management Object
* (MO) tree for more detail.
*/
private long[] mMatchAnyOis = null;
+
/**
- * @hide
+ * Set a list of HomeOIs such that any OI in the list matches an OI in the Roaming Consortium
+ * advertised by a hotspot operator. The list set by {@link #setMatchAllOis(long[])}
+ * will have precedence over this API, meaning this list will only be used for matching if the
+ * list set by {@link #setMatchAllOis(long[])} is null or empty.
+ *
+ * @param matchAnyOis An array of longs containing the HomeOIs
*/
- public void setMatchAnyOis(long[] matchAnyOis) {
+ public void setMatchAnyOis(@Nullable long[] matchAnyOis) {
mMatchAnyOis = matchAnyOis;
}
+
/**
- * @hide
+ * Get a list of HomeOIs such that any OI in the list matches an OI in the Roaming Consortium
+ * advertised by a hotspot operator.
+ *
+ * @return An array of longs containing the HomeOIs
*/
- public long[] getMatchAnyOis() {
+ public @Nullable long[] getMatchAnyOis() {
return mMatchAnyOis;
}
@@ -186,16 +208,25 @@ public final class HomeSp implements Parcelable {
* operator merges between the providers.
*/
private String[] mOtherHomePartners = null;
+
/**
- * @hide
+ * Set the list of FQDN (Fully Qualified Domain Name) of other Home partner providers.
+ *
+ * @param otherHomePartners Array of Strings containing the FQDNs of other Home partner
+ * providers
*/
- public void setOtherHomePartners(String[] otherHomePartners) {
+ public void setOtherHomePartners(@Nullable String[] otherHomePartners) {
mOtherHomePartners = otherHomePartners;
}
+
/**
- * @hide
+ * Get the list of FQDN (Fully Qualified Domain Name) of other Home partner providers set in
+ * the profile.
+ *
+ * @return Array of Strings containing the FQDNs of other Home partner providers set in the
+ * profile
*/
- public String[] getOtherHomePartners() {
+ public @Nullable String[] getOtherHomePartners() {
return mOtherHomePartners;
}
diff --git a/wifi/tests/src/android/net/wifi/ScanResultTest.java b/wifi/tests/src/android/net/wifi/ScanResultTest.java
index 4a3586826de9..59b88a7f0c3f 100644
--- a/wifi/tests/src/android/net/wifi/ScanResultTest.java
+++ b/wifi/tests/src/android/net/wifi/ScanResultTest.java
@@ -103,9 +103,10 @@ public class ScanResultTest {
5845, WifiScanner.WIFI_BAND_5_GHZ, 169,
5865, WifiScanner.WIFI_BAND_5_GHZ, 173,
/* Now some 6GHz channels */
- 5945, WifiScanner.WIFI_BAND_6_GHZ, 1,
- 5960, WifiScanner.WIFI_BAND_6_GHZ, 4,
- 6100, WifiScanner.WIFI_BAND_6_GHZ, 32
+ 5955, WifiScanner.WIFI_BAND_6_GHZ, 1,
+ 5935, WifiScanner.WIFI_BAND_6_GHZ, 2,
+ 5970, WifiScanner.WIFI_BAND_6_GHZ, 4,
+ 6110, WifiScanner.WIFI_BAND_6_GHZ, 32
};
/**
diff --git a/wifi/tests/src/android/net/wifi/SoftApInfoTest.java b/wifi/tests/src/android/net/wifi/SoftApInfoTest.java
index 458a95494438..2821c35f9347 100644
--- a/wifi/tests/src/android/net/wifi/SoftApInfoTest.java
+++ b/wifi/tests/src/android/net/wifi/SoftApInfoTest.java
@@ -17,6 +17,7 @@
package android.net.wifi;
import android.net.MacAddress;
+import android.net.wifi.util.SdkLevelUtil;
import android.os.Parcel;
import static org.junit.Assert.assertEquals;
@@ -40,6 +41,7 @@ public class SoftApInfoTest {
info.setFrequency(2412);
info.setBandwidth(SoftApInfo.CHANNEL_WIDTH_20MHZ);
info.setBssid(MacAddress.fromString("aa:bb:cc:dd:ee:ff"));
+ info.setWifiStandard(ScanResult.WIFI_STANDARD_LEGACY);
SoftApInfo copiedInfo = new SoftApInfo(info);
@@ -57,6 +59,7 @@ public class SoftApInfoTest {
info.setFrequency(2412);
info.setBandwidth(SoftApInfo.CHANNEL_WIDTH_20MHZ);
info.setBssid(MacAddress.fromString("aa:bb:cc:dd:ee:ff"));
+ info.setWifiStandard(ScanResult.WIFI_STANDARD_LEGACY);
Parcel parcelW = Parcel.obtain();
info.writeToParcel(parcelW, 0);
@@ -72,4 +75,19 @@ public class SoftApInfoTest {
assertEquals(info.hashCode(), fromParcel.hashCode());
}
+
+ /**
+ * Verifies the initial value same as expected.
+ */
+ @Test
+ public void testInitialValue() throws Exception {
+ SoftApInfo info = new SoftApInfo();
+ assertEquals(info.getFrequency(), 0);
+ assertEquals(info.getBandwidth(), SoftApInfo.CHANNEL_WIDTH_INVALID);
+ if (SdkLevelUtil.isAtLeastS()) {
+ assertEquals(info.getBssid(), null);
+ assertEquals(info.getWifiStandard(), ScanResult.WIFI_STANDARD_UNKNOWN);
+ }
+ }
+
}