summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--Android.bp33
-rw-r--r--PREUPLOAD.cfg1
-rw-r--r--apct-tests/perftests/core/src/android/wm/RelayoutPerfTest.java2
-rw-r--r--apex/jobscheduler/service/java/com/android/server/job/controllers/ConnectivityController.java8
-rw-r--r--apex/media/framework/Android.bp2
-rw-r--r--apex/media/framework/java/android/media/MediaParser.java17
-rw-r--r--apex/permission/service/java/com/android/permission/persistence/RuntimePermissionsPersistenceImpl.java6
-rw-r--r--apex/sdkextensions/OWNERS1
-rw-r--r--apex/sdkextensions/framework/Android.bp1
-rw-r--r--apex/statsd/framework/java/android/app/StatsManager.java20
-rw-r--r--apex/statsd/tests/libstatspull/Android.bp56
-rw-r--r--apex/statsd/tests/libstatspull/AndroidManifest.xml30
-rw-r--r--apex/statsd/tests/libstatspull/TEST_MAPPING7
-rw-r--r--apex/statsd/tests/libstatspull/jni/stats_pull_helper.cpp88
-rw-r--r--apex/statsd/tests/libstatspull/protos/test_atoms.proto32
-rw-r--r--apex/statsd/tests/libstatspull/src/com/android/internal/os/statsd/StatsConfigUtils.java124
-rw-r--r--apex/statsd/tests/libstatspull/src/com/android/internal/os/statsd/libstats/LibStatsPullTests.java285
-rw-r--r--api/current.txt61
-rwxr-xr-xapi/system-current.txt220
-rw-r--r--api/test-current.txt184
-rw-r--r--api/test-lint-baseline.txt8
-rw-r--r--cmds/statsd/src/atoms.proto146
-rw-r--r--cmds/statsd/src/external/StatsPullerManager.cpp6
-rw-r--r--cmds/statsd/src/guardrail/StatsdStats.h6
-rw-r--r--cmds/statsd/src/metrics/metrics_manager_util.cpp5
-rw-r--r--cmds/statsd/src/stats_log_util.h4
-rw-r--r--cmds/telecom/src/com/android/commands/telecom/Telecom.java8
-rw-r--r--core/java/android/annotation/SystemApi.java47
-rw-r--r--core/java/android/app/ApplicationPackageManager.java26
-rw-r--r--core/java/android/app/INotificationManager.aidl4
-rw-r--r--core/java/android/app/ITransientNotificationCallback.aidl27
-rw-r--r--core/java/android/app/Notification.java86
-rw-r--r--core/java/android/content/Context.java3
-rw-r--r--core/java/android/content/pm/ApplicationInfo.java11
-rw-r--r--core/java/android/content/pm/PackageManager.java16
-rw-r--r--core/java/android/content/pm/UserInfo.java4
-rw-r--r--core/java/android/content/pm/parsing/PackageImpl.java1
-rw-r--r--core/java/android/hardware/camera2/CameraCharacteristics.java23
-rw-r--r--core/java/android/hardware/camera2/CameraDevice.java19
-rw-r--r--core/java/android/hardware/camera2/CameraManager.java196
-rw-r--r--core/java/android/hardware/camera2/impl/CameraMetadataNative.java46
-rw-r--r--core/java/android/hardware/camera2/params/MandatoryStreamCombination.java105
-rw-r--r--core/java/android/hardware/camera2/utils/CameraIdAndSessionConfiguration.java85
-rw-r--r--core/java/android/hardware/camera2/utils/ConcurrentCameraIdCombination.java91
-rw-r--r--core/java/android/hardware/display/BrightnessConfiguration.java12
-rw-r--r--core/java/android/hardware/location/ContextHubManager.java3
-rw-r--r--core/java/android/os/BatteryStatsManager.java2
-rw-r--r--core/java/android/os/HwParcel.java1
-rw-r--r--core/java/android/os/IVibratorService.aidl2
-rw-r--r--core/java/android/os/PowerManager.java2
-rw-r--r--core/java/android/os/SystemVibrator.java23
-rw-r--r--core/java/android/os/TelephonyServiceManager.java2
-rw-r--r--core/java/android/os/VibrationEffect.aidl4
-rw-r--r--core/java/android/os/VibrationEffect.java346
-rw-r--r--core/java/android/os/Vibrator.java143
-rw-r--r--core/java/android/os/connectivity/WifiActivityEnergyInfo.java132
-rw-r--r--core/java/android/os/incremental/IIncrementalManager.aidl4
-rw-r--r--core/java/android/os/incremental/IIncrementalManagerNative.aidl36
-rw-r--r--core/java/android/os/incremental/IncrementalFileStorages.java10
-rw-r--r--core/java/android/os/incremental/IncrementalFileSystemControlParcel.aidl7
-rw-r--r--core/java/android/os/incremental/IncrementalNewFileParams.aidl31
-rw-r--r--core/java/android/os/incremental/IncrementalSignature.aidl31
-rw-r--r--core/java/android/os/incremental/IncrementalStorage.java169
-rw-r--r--core/java/android/provider/Settings.java32
-rw-r--r--core/java/android/provider/Telephony.java45
-rw-r--r--core/java/android/security/net/config/WfaCertificateSource.java5
-rw-r--r--core/java/android/service/autofill/FillResponse.java2
-rw-r--r--core/java/android/service/autofill/IInlineSuggestionRenderService.aidl30
-rw-r--r--core/java/android/service/autofill/IInlineSuggestionUiCallback.aidl29
-rw-r--r--core/java/android/service/autofill/InlineSuggestionRenderService.java92
-rw-r--r--core/java/android/service/contentcapture/ContentCaptureService.java27
-rw-r--r--core/java/android/service/contentcapture/DataShareReadAdapter.java3
-rw-r--r--core/java/android/service/contentcapture/IDataShareCallback.aidl3
-rw-r--r--core/java/android/service/controls/TokenProvider.aidl7
-rw-r--r--core/java/android/service/quicksettings/Tile.java22
-rw-r--r--core/java/android/service/wallpaper/WallpaperService.java5
-rw-r--r--core/java/android/speech/tts/TextToSpeechService.java1
-rw-r--r--core/java/android/telephony/CellBroadcastIntents.java88
-rw-r--r--core/java/android/telephony/TelephonyRegistryManager.java21
-rw-r--r--core/java/android/util/CloseGuard.java4
-rw-r--r--core/java/android/view/IPinnedStackController.aidl5
-rw-r--r--core/java/android/view/IWindowSession.aidl6
-rw-r--r--core/java/android/view/ImeInsetsSourceConsumer.java4
-rw-r--r--core/java/android/view/InsetsAnimationControlCallbacks.java6
-rw-r--r--core/java/android/view/InsetsAnimationControlImpl.java8
-rw-r--r--core/java/android/view/InsetsController.java136
-rw-r--r--core/java/android/view/InsetsSourceConsumer.java9
-rw-r--r--core/java/android/view/KeyEvent.java14
-rw-r--r--core/java/android/view/View.java15
-rw-r--r--core/java/android/view/ViewRootImpl.java43
-rw-r--r--core/java/android/view/WindowManagerGlobal.java6
-rw-r--r--core/java/android/view/WindowlessWindowManager.java2
-rw-r--r--core/java/android/view/contentcapture/ContentCaptureManager.java24
-rw-r--r--core/java/android/view/contentcapture/IContentCaptureManager.aidl4
-rw-r--r--core/java/android/widget/ScrollBarDrawable.java2
-rw-r--r--core/java/android/widget/Toast.java212
-rw-r--r--core/java/com/android/internal/config/sysui/SystemUiDeviceConfigFlags.java6
-rw-r--r--core/java/com/android/internal/statusbar/IStatusBar.aidl12
-rw-r--r--core/java/com/android/internal/statusbar/IStatusBarService.aidl1
-rw-r--r--core/java/com/android/internal/util/LocationPermissionChecker.java (renamed from core/java/com/android/internal/util/ConnectivityUtil.java)96
-rwxr-xr-xcore/jni/android/graphics/Bitmap.cpp27
-rw-r--r--core/jni/android_content_res_ApkAssets.cpp2
-rw-r--r--core/jni/android_util_Binder.cpp5
-rw-r--r--core/jni/android_view_InputEventSender.cpp40
-rw-r--r--core/jni/android_view_KeyEvent.cpp83
-rw-r--r--core/jni/android_view_KeyEvent.h2
-rw-r--r--core/jni/android_view_MotionEvent.cpp244
-rw-r--r--core/jni/android_view_MotionEvent.h2
-rw-r--r--core/proto/android/server/windowmanagerservice.proto7
-rw-r--r--core/res/AndroidManifest.xml20
-rw-r--r--core/res/res/values-af/strings.xml2
-rw-r--r--core/res/res/values-am/strings.xml2
-rw-r--r--core/res/res/values-ar/strings.xml12
-rw-r--r--core/res/res/values-as/strings.xml2
-rw-r--r--core/res/res/values-az/strings.xml2
-rw-r--r--core/res/res/values-b+sr+Latn/strings.xml2
-rw-r--r--core/res/res/values-be/strings.xml2
-rw-r--r--core/res/res/values-bg/strings.xml2
-rw-r--r--core/res/res/values-bn/strings.xml2
-rw-r--r--core/res/res/values-bs/strings.xml2
-rw-r--r--core/res/res/values-ca/strings.xml2
-rw-r--r--core/res/res/values-cs/strings.xml2
-rw-r--r--core/res/res/values-da/strings.xml2
-rw-r--r--core/res/res/values-de/strings.xml2
-rw-r--r--core/res/res/values-el/strings.xml2
-rw-r--r--core/res/res/values-en-rAU/strings.xml2
-rw-r--r--core/res/res/values-en-rCA/strings.xml2
-rw-r--r--core/res/res/values-en-rGB/strings.xml2
-rw-r--r--core/res/res/values-en-rIN/strings.xml2
-rw-r--r--core/res/res/values-en-rXC/strings.xml2
-rw-r--r--core/res/res/values-es-rUS/strings.xml2
-rw-r--r--core/res/res/values-es/strings.xml2
-rw-r--r--core/res/res/values-et/strings.xml2
-rw-r--r--core/res/res/values-eu/strings.xml2
-rw-r--r--core/res/res/values-fa/strings.xml2
-rw-r--r--core/res/res/values-fi/strings.xml2
-rw-r--r--core/res/res/values-fr-rCA/strings.xml2
-rw-r--r--core/res/res/values-fr/strings.xml2
-rw-r--r--core/res/res/values-gl/strings.xml2
-rw-r--r--core/res/res/values-gu/strings.xml8
-rw-r--r--core/res/res/values-hi/strings.xml2
-rw-r--r--core/res/res/values-hr/strings.xml2
-rw-r--r--core/res/res/values-hu/strings.xml2
-rw-r--r--core/res/res/values-hy/strings.xml2
-rw-r--r--core/res/res/values-in/strings.xml2
-rw-r--r--core/res/res/values-is/strings.xml2
-rw-r--r--core/res/res/values-it/strings.xml2
-rw-r--r--core/res/res/values-iw/strings.xml8
-rw-r--r--core/res/res/values-ja/strings.xml2
-rw-r--r--core/res/res/values-ka/strings.xml2
-rw-r--r--core/res/res/values-kk/strings.xml2
-rw-r--r--core/res/res/values-km/strings.xml2
-rw-r--r--core/res/res/values-kn/strings.xml8
-rw-r--r--core/res/res/values-ko/strings.xml2
-rw-r--r--core/res/res/values-ky/strings.xml6
-rw-r--r--core/res/res/values-lo/strings.xml2
-rw-r--r--core/res/res/values-lt/strings.xml2
-rw-r--r--core/res/res/values-lv/strings.xml2
-rw-r--r--core/res/res/values-mk/strings.xml2
-rw-r--r--core/res/res/values-ml/strings.xml8
-rw-r--r--core/res/res/values-mn/strings.xml8
-rw-r--r--core/res/res/values-mr/strings.xml2
-rw-r--r--core/res/res/values-ms/strings.xml2
-rw-r--r--core/res/res/values-my/strings.xml2
-rw-r--r--core/res/res/values-nb/strings.xml2
-rw-r--r--core/res/res/values-ne/strings.xml8
-rw-r--r--core/res/res/values-nl/strings.xml2
-rw-r--r--core/res/res/values-or/strings.xml2
-rw-r--r--core/res/res/values-pa/strings.xml2
-rw-r--r--core/res/res/values-pl/strings.xml2
-rw-r--r--core/res/res/values-pt-rBR/strings.xml2
-rw-r--r--core/res/res/values-pt-rPT/strings.xml2
-rw-r--r--core/res/res/values-pt/strings.xml2
-rw-r--r--core/res/res/values-ro/strings.xml2
-rw-r--r--core/res/res/values-ru/strings.xml2
-rw-r--r--core/res/res/values-si/strings.xml2
-rw-r--r--core/res/res/values-sk/strings.xml2
-rw-r--r--core/res/res/values-sl/strings.xml2
-rw-r--r--core/res/res/values-sq/strings.xml2
-rw-r--r--core/res/res/values-sr/strings.xml2
-rw-r--r--core/res/res/values-sv/strings.xml2
-rw-r--r--core/res/res/values-sw/strings.xml2
-rw-r--r--core/res/res/values-ta/strings.xml2
-rw-r--r--core/res/res/values-te/strings.xml8
-rw-r--r--core/res/res/values-th/strings.xml2
-rw-r--r--core/res/res/values-tl/strings.xml2
-rw-r--r--core/res/res/values-tr/strings.xml2
-rw-r--r--core/res/res/values-uk/strings.xml2
-rw-r--r--core/res/res/values-ur/strings.xml8
-rw-r--r--core/res/res/values-uz/strings.xml2
-rw-r--r--core/res/res/values-vi/strings.xml2
-rw-r--r--core/res/res/values-zh-rCN/strings.xml8
-rw-r--r--core/res/res/values-zh-rHK/strings.xml2
-rw-r--r--core/res/res/values-zh-rTW/strings.xml2
-rw-r--r--core/res/res/values-zu/strings.xml2
-rw-r--r--core/tests/coretests/Android.bp1
-rw-r--r--core/tests/coretests/src/android/app/PullAtomMetadataTest.java10
-rw-r--r--core/tests/coretests/src/android/content/integrity/AtomicFormulaTest.java73
-rw-r--r--core/tests/coretests/src/android/content/integrity/CompoundFormulaTest.java58
-rw-r--r--core/tests/coretests/src/android/content/integrity/IntegrityUtilsTest.java26
-rw-r--r--core/tests/coretests/src/android/content/integrity/RuleTest.java51
-rw-r--r--core/tests/coretests/src/android/content/integrity/TestUtils.java51
-rw-r--r--core/tests/coretests/src/android/hardware/display/BrightnessConfigurationTest.java6
-rw-r--r--core/tests/coretests/src/android/provider/NameValueCacheTest.java232
-rw-r--r--core/tests/coretests/src/android/view/InsetsAnimationControlImplTest.java2
-rw-r--r--core/tests/coretests/src/android/view/InsetsControllerTest.java98
-rw-r--r--core/tests/coretests/src/android/view/KeyEventTest.java61
-rw-r--r--core/tests/hosttests/test-apps/DownloadManagerTestApp/src/com/android/frameworks/downloadmanagertests/DownloadManagerTestRunner.java2
-rw-r--r--core/tests/utiltests/src/com/android/internal/util/LocationPermissionCheckerTest.java (renamed from core/tests/utiltests/src/com/android/internal/util/ConnectivityUtilTest.java)57
-rw-r--r--data/etc/platform.xml4
-rw-r--r--graphics/java/android/graphics/Bitmap.java3
-rw-r--r--graphics/java/android/graphics/Canvas.java60
-rw-r--r--media/java/android/media/MediaFormat.java1
-rw-r--r--media/java/android/media/audiopolicy/AudioProductStrategy.java4
-rw-r--r--media/java/android/media/tv/ITvInputManager.aidl2
-rw-r--r--media/java/android/media/tv/ITvInputSession.aidl2
-rw-r--r--media/java/android/media/tv/ITvInputSessionWrapper.java8
-rw-r--r--media/java/android/media/tv/TvInputManager.java13
-rwxr-xr-xmedia/java/android/media/tv/TvInputService.java30
-rw-r--r--media/java/android/media/tv/TvRecordingClient.java28
-rw-r--r--media/jni/android_media_tv_Tuner.cpp180
-rw-r--r--native/graphics/jni/imagedecoder.cpp8
-rw-r--r--packages/CarSystemUI/TEST_MAPPING (renamed from packages/CarSystemUI/src/com/android/systemui/TEST_MAPPING)0
-rw-r--r--packages/CarSystemUI/res/values/config.xml1
-rw-r--r--packages/CarSystemUI/src/com/android/systemui/CarSystemUIBinder.java7
-rw-r--r--packages/Incremental/NativeAdbDataLoader/jni/com_android_incremental_nativeadb_DataLoaderService.cpp104
-rw-r--r--packages/SettingsLib/src/com/android/settingslib/wifi/AccessPoint.java2
-rw-r--r--packages/SettingsLib/src/com/android/settingslib/wifi/WifiTracker.java2
-rw-r--r--packages/SettingsLib/src/com/android/settingslib/wifi/WifiUtils.java8
-rw-r--r--packages/SettingsLib/tests/integ/src/com/android/settingslib/wifi/AccessPointTest.java2
-rw-r--r--packages/Shell/AndroidManifest.xml1
-rw-r--r--packages/SystemUI/TEST_MAPPING19
-rw-r--r--packages/SystemUI/plugin/src/com/android/systemui/plugins/qs/QSTile.java4
-rw-r--r--packages/SystemUI/res-product/values-ky/strings.xml24
-rw-r--r--packages/SystemUI/res/color/light_background.xml6
-rw-r--r--packages/SystemUI/res/color/light_foreground.xml6
-rw-r--r--packages/SystemUI/res/color/lock_background.xml6
-rw-r--r--packages/SystemUI/res/color/lock_foreground.xml6
-rw-r--r--packages/SystemUI/res/color/unknown_foreground.xml6
-rw-r--r--packages/SystemUI/res/drawable/control_background.xml32
-rw-r--r--packages/SystemUI/res/drawable/control_layer.xml22
-rw-r--r--packages/SystemUI/res/drawable/control_no_favorites_background.xml22
-rw-r--r--packages/SystemUI/res/drawable/ic_device_thermostat_gm2_24px.xml9
-rw-r--r--packages/SystemUI/res/drawable/ic_light_off_gm2_24px.xml19
-rw-r--r--packages/SystemUI/res/drawable/ic_lock_gm2_24px.xml9
-rw-r--r--packages/SystemUI/res/drawable/ic_lock_open_gm2_24px.xml12
-rw-r--r--packages/SystemUI/res/drawable/ic_more_vert.xml24
-rw-r--r--packages/SystemUI/res/drawable/ic_power_gm2_24px.xml9
-rw-r--r--packages/SystemUI/res/drawable/ic_power_off_gm2_24px.xml9
-rw-r--r--packages/SystemUI/res/drawable/ic_power_settings_new_gm2_24px.xml9
-rw-r--r--packages/SystemUI/res/drawable/ic_switches_gm2_24px.xml9
-rw-r--r--packages/SystemUI/res/drawable/ic_vacuum_gm2_24px.xml9
-rw-r--r--packages/SystemUI/res/drawable/ic_videocam_gm2_24px.xml9
-rw-r--r--packages/SystemUI/res/layout/controls_base_item.xml79
-rw-r--r--packages/SystemUI/res/layout/controls_no_favorites.xml18
-rw-r--r--packages/SystemUI/res/layout/controls_row.xml22
-rw-r--r--packages/SystemUI/res/layout/controls_with_favorites.xml35
-rw-r--r--packages/SystemUI/res/layout/global_actions_grid_v2.xml15
-rw-r--r--packages/SystemUI/res/values-af/strings.xml37
-rw-r--r--packages/SystemUI/res/values-am/strings.xml4
-rw-r--r--packages/SystemUI/res/values-ar/strings.xml37
-rw-r--r--packages/SystemUI/res/values-as/strings.xml37
-rw-r--r--packages/SystemUI/res/values-az/strings.xml37
-rw-r--r--packages/SystemUI/res/values-b+sr+Latn/strings.xml4
-rw-r--r--packages/SystemUI/res/values-be/strings.xml37
-rw-r--r--packages/SystemUI/res/values-bg/strings.xml4
-rw-r--r--packages/SystemUI/res/values-bn/strings.xml37
-rw-r--r--packages/SystemUI/res/values-bs/strings.xml4
-rw-r--r--packages/SystemUI/res/values-ca/strings.xml4
-rw-r--r--packages/SystemUI/res/values-cs/strings.xml4
-rw-r--r--packages/SystemUI/res/values-da/strings.xml4
-rw-r--r--packages/SystemUI/res/values-de/strings.xml37
-rw-r--r--packages/SystemUI/res/values-el/strings.xml4
-rw-r--r--packages/SystemUI/res/values-en-rAU/strings.xml4
-rw-r--r--packages/SystemUI/res/values-en-rCA/strings.xml4
-rw-r--r--packages/SystemUI/res/values-en-rGB/strings.xml4
-rw-r--r--packages/SystemUI/res/values-en-rIN/strings.xml4
-rw-r--r--packages/SystemUI/res/values-en-rXC/strings.xml4
-rw-r--r--packages/SystemUI/res/values-es-rUS/strings.xml4
-rw-r--r--packages/SystemUI/res/values-es/strings.xml4
-rw-r--r--packages/SystemUI/res/values-et/strings.xml37
-rw-r--r--packages/SystemUI/res/values-eu/strings.xml37
-rw-r--r--packages/SystemUI/res/values-fa/strings.xml37
-rw-r--r--packages/SystemUI/res/values-fi/strings.xml37
-rw-r--r--packages/SystemUI/res/values-fr-rCA/strings.xml4
-rw-r--r--packages/SystemUI/res/values-fr/strings.xml4
-rw-r--r--packages/SystemUI/res/values-gl/strings.xml4
-rw-r--r--packages/SystemUI/res/values-gu/strings.xml37
-rw-r--r--packages/SystemUI/res/values-hi/strings.xml37
-rw-r--r--packages/SystemUI/res/values-hr/strings.xml4
-rw-r--r--packages/SystemUI/res/values-hu/strings.xml4
-rw-r--r--packages/SystemUI/res/values-hy/strings.xml37
-rw-r--r--packages/SystemUI/res/values-in/strings.xml4
-rw-r--r--packages/SystemUI/res/values-is/strings.xml4
-rw-r--r--packages/SystemUI/res/values-it/strings.xml4
-rw-r--r--packages/SystemUI/res/values-iw/strings.xml37
-rw-r--r--packages/SystemUI/res/values-ja/strings.xml37
-rw-r--r--packages/SystemUI/res/values-ka/strings.xml37
-rw-r--r--packages/SystemUI/res/values-kk/strings.xml37
-rw-r--r--packages/SystemUI/res/values-km/strings.xml37
-rw-r--r--packages/SystemUI/res/values-kn/strings.xml37
-rw-r--r--packages/SystemUI/res/values-ko/strings.xml37
-rw-r--r--packages/SystemUI/res/values-ky/strings.xml41
-rw-r--r--packages/SystemUI/res/values-lo/strings.xml37
-rw-r--r--packages/SystemUI/res/values-lt/strings.xml4
-rw-r--r--packages/SystemUI/res/values-lv/strings.xml37
-rw-r--r--packages/SystemUI/res/values-mk/strings.xml4
-rw-r--r--packages/SystemUI/res/values-ml/strings.xml37
-rw-r--r--packages/SystemUI/res/values-mn/strings.xml37
-rw-r--r--packages/SystemUI/res/values-mr/strings.xml37
-rw-r--r--packages/SystemUI/res/values-ms/strings.xml37
-rw-r--r--packages/SystemUI/res/values-my/strings.xml37
-rw-r--r--packages/SystemUI/res/values-nb/strings.xml37
-rw-r--r--packages/SystemUI/res/values-ne/strings.xml37
-rw-r--r--packages/SystemUI/res/values-nl/strings.xml4
-rw-r--r--packages/SystemUI/res/values-or/strings.xml37
-rw-r--r--packages/SystemUI/res/values-pa/strings.xml37
-rw-r--r--packages/SystemUI/res/values-pl/strings.xml37
-rw-r--r--packages/SystemUI/res/values-pt-rBR/strings.xml4
-rw-r--r--packages/SystemUI/res/values-pt-rPT/strings.xml4
-rw-r--r--packages/SystemUI/res/values-pt/strings.xml4
-rw-r--r--packages/SystemUI/res/values-ro/strings.xml37
-rw-r--r--packages/SystemUI/res/values-ru/strings.xml37
-rw-r--r--packages/SystemUI/res/values-si/strings.xml37
-rw-r--r--packages/SystemUI/res/values-sk/strings.xml4
-rw-r--r--packages/SystemUI/res/values-sl/strings.xml37
-rw-r--r--packages/SystemUI/res/values-sq/strings.xml37
-rw-r--r--packages/SystemUI/res/values-sr/strings.xml4
-rw-r--r--packages/SystemUI/res/values-sv/strings.xml4
-rw-r--r--packages/SystemUI/res/values-sw/strings.xml37
-rw-r--r--packages/SystemUI/res/values-ta/strings.xml37
-rw-r--r--packages/SystemUI/res/values-te/strings.xml37
-rw-r--r--packages/SystemUI/res/values-television/config.xml1
-rw-r--r--packages/SystemUI/res/values-th/strings.xml37
-rw-r--r--packages/SystemUI/res/values-tl/strings.xml37
-rw-r--r--packages/SystemUI/res/values-tr/strings.xml37
-rw-r--r--packages/SystemUI/res/values-uk/strings.xml37
-rw-r--r--packages/SystemUI/res/values-ur/strings.xml37
-rw-r--r--packages/SystemUI/res/values-uz/strings.xml4
-rw-r--r--packages/SystemUI/res/values-vi/strings.xml37
-rw-r--r--packages/SystemUI/res/values-zh-rCN/strings.xml37
-rw-r--r--packages/SystemUI/res/values-zh-rHK/strings.xml37
-rw-r--r--packages/SystemUI/res/values-zh-rTW/strings.xml37
-rw-r--r--packages/SystemUI/res/values-zu/strings.xml4
-rw-r--r--packages/SystemUI/res/values/colors.xml12
-rw-r--r--packages/SystemUI/res/values/config.xml1
-rw-r--r--packages/SystemUI/res/values/dimens.xml7
-rw-r--r--packages/SystemUI/res/values/strings.xml6
-rw-r--r--packages/SystemUI/src/com/android/systemui/bubbles/Bubble.java54
-rw-r--r--packages/SystemUI/src/com/android/systemui/bubbles/BubbleController.java38
-rw-r--r--packages/SystemUI/src/com/android/systemui/bubbles/BubbleData.java16
-rw-r--r--packages/SystemUI/src/com/android/systemui/bubbles/BubbleStackView.java55
-rw-r--r--packages/SystemUI/src/com/android/systemui/bubbles/BubbleViewInfoTask.java2
-rw-r--r--packages/SystemUI/src/com/android/systemui/controls/ui/ControlViewHolder.kt400
-rw-r--r--packages/SystemUI/src/com/android/systemui/controls/ui/ControlWithState.kt31
-rw-r--r--packages/SystemUI/src/com/android/systemui/controls/ui/ControlsUiController.kt5
-rw-r--r--packages/SystemUI/src/com/android/systemui/controls/ui/ControlsUiControllerImpl.kt191
-rw-r--r--packages/SystemUI/src/com/android/systemui/dagger/SystemUIBinder.java7
-rw-r--r--packages/SystemUI/src/com/android/systemui/dagger/SystemUIModule.java10
-rw-r--r--packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsDialog.java28
-rw-r--r--packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java22
-rw-r--r--packages/SystemUI/src/com/android/systemui/log/LogBuffer.kt213
-rw-r--r--packages/SystemUI/src/com/android/systemui/log/LogLevel.kt31
-rw-r--r--packages/SystemUI/src/com/android/systemui/log/LogMessage.kt47
-rw-r--r--packages/SystemUI/src/com/android/systemui/log/LogMessageImpl.kt74
-rw-r--r--packages/SystemUI/src/com/android/systemui/log/LogcatEchoTracker.kt32
-rw-r--r--packages/SystemUI/src/com/android/systemui/log/LogcatEchoTrackerDebug.kt133
-rw-r--r--packages/SystemUI/src/com/android/systemui/log/LogcatEchoTrackerProd.kt30
-rw-r--r--packages/SystemUI/src/com/android/systemui/log/dagger/DozeLog.java33
-rw-r--r--packages/SystemUI/src/com/android/systemui/log/dagger/LogModule.java76
-rw-r--r--packages/SystemUI/src/com/android/systemui/log/dagger/NotificationLog.java33
-rw-r--r--packages/SystemUI/src/com/android/systemui/pip/PipBoundsHandler.java3
-rw-r--r--packages/SystemUI/src/com/android/systemui/pip/phone/PipManager.java7
-rw-r--r--packages/SystemUI/src/com/android/systemui/pip/phone/PipTouchGesture.java6
-rw-r--r--packages/SystemUI/src/com/android/systemui/pip/phone/PipTouchHandler.java31
-rw-r--r--packages/SystemUI/src/com/android/systemui/qs/external/CustomTile.java7
-rw-r--r--packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSTileBaseView.java43
-rw-r--r--packages/SystemUI/src/com/android/systemui/qs/tiles/BluetoothTile.java10
-rw-r--r--packages/SystemUI/src/com/android/systemui/qs/tiles/CastTile.java9
-rw-r--r--packages/SystemUI/src/com/android/systemui/qs/tiles/CellularTile.java12
-rw-r--r--packages/SystemUI/src/com/android/systemui/qs/tiles/DndTile.java2
-rw-r--r--packages/SystemUI/src/com/android/systemui/qs/tiles/FlashlightTile.java5
-rw-r--r--packages/SystemUI/src/com/android/systemui/qs/tiles/HotspotTile.java1
-rw-r--r--packages/SystemUI/src/com/android/systemui/qs/tiles/LocationTile.java11
-rw-r--r--packages/SystemUI/src/com/android/systemui/qs/tiles/WifiTile.java4
-rw-r--r--packages/SystemUI/src/com/android/systemui/qs/tiles/WorkModeTile.java5
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/CommandQueue.java66
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/ListEntry.java5
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotifCollection.java32
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/ShadeListBuilder.java138
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coalescer/GroupCoalescer.java29
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coalescer/GroupCoalescerLogger.kt62
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/listbuilder/ShadeListBuilderLogger.kt217
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/notifcollection/NotifCollectionLogger.kt62
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/provider/HighPriorityProvider.java4
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationConversationInfo.java16
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarFragment.java4
-rw-r--r--packages/SystemUI/src/com/android/systemui/toast/ToastUI.java212
-rw-r--r--packages/SystemUI/src/com/android/systemui/wm/SystemWindows.java4
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/bubbles/BubbleControllerTest.java52
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/bubbles/BubbleDataTest.java26
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/bubbles/BubbleTest.java42
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/pip/PipBoundsHandlerTest.java240
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/NotifCollectionTest.java14
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/ShadeListBuilderTest.java6
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/coalescer/GroupCoalescerTest.java11
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationConversationInfoTest.java29
-rw-r--r--packages/Tethering/src/android/net/dhcp/DhcpServerCallbacks.java5
-rw-r--r--packages/Tethering/src/android/net/ip/IpServer.java5
-rw-r--r--packages/Tethering/src/android/net/util/BaseNetdUnsolicitedEventListener.java5
-rw-r--r--services/autofill/java/com/android/server/autofill/AutofillManagerServiceImpl.java28
-rw-r--r--services/autofill/java/com/android/server/autofill/RemoteInlineSuggestionRenderService.java135
-rw-r--r--services/backup/java/com/android/server/backup/UserBackupManagerService.java2
-rw-r--r--services/backup/java/com/android/server/backup/restore/PerformUnifiedRestoreTask.java27
-rw-r--r--services/contentcapture/java/com/android/server/contentcapture/ContentCaptureManagerService.java269
-rw-r--r--services/core/java/com/android/server/ConnectivityService.java5
-rw-r--r--services/core/java/com/android/server/LocationManagerService.java9
-rw-r--r--services/core/java/com/android/server/NetworkManagementService.java5
-rw-r--r--services/core/java/com/android/server/ServiceWatcher.java26
-rw-r--r--services/core/java/com/android/server/VibratorService.java128
-rw-r--r--services/core/java/com/android/server/am/ActiveServices.java10
-rw-r--r--services/core/java/com/android/server/am/BatteryExternalStatsWorker.java64
-rw-r--r--services/core/java/com/android/server/am/OomAdjuster.java31
-rw-r--r--services/core/java/com/android/server/am/ProcessList.java72
-rw-r--r--services/core/java/com/android/server/appop/AppOpsService.java14
-rw-r--r--services/core/java/com/android/server/compat/PlatformCompat.java40
-rw-r--r--services/core/java/com/android/server/connectivity/NetdEventListenerService.java5
-rw-r--r--services/core/java/com/android/server/content/SyncManager.java4
-rw-r--r--services/core/java/com/android/server/display/BrightnessMappingStrategy.java8
-rw-r--r--services/core/java/com/android/server/incremental/IncrementalManagerService.java2
-rw-r--r--services/core/java/com/android/server/integrity/AppIntegrityManagerServiceImpl.java44
-rw-r--r--services/core/java/com/android/server/integrity/engine/RuleEvaluationEngine.java5
-rw-r--r--services/core/java/com/android/server/integrity/engine/RuleEvaluator.java49
-rw-r--r--services/core/java/com/android/server/integrity/model/IntegrityCheckResult.java58
-rw-r--r--services/core/java/com/android/server/location/LocationProviderProxy.java80
-rw-r--r--services/core/java/com/android/server/location/MockableLocationProvider.java3
-rw-r--r--services/core/java/com/android/server/location/gnss/GnssManagerService.java (renamed from services/core/java/com/android/server/GnssManagerService.java)20
-rw-r--r--services/core/java/com/android/server/media/MediaSessionRecord.java23
-rw-r--r--services/core/java/com/android/server/notification/NotificationDelegate.java8
-rwxr-xr-xservices/core/java/com/android/server/notification/NotificationManagerService.java195
-rw-r--r--services/core/java/com/android/server/notification/toast/CustomToastRecord.java83
-rw-r--r--services/core/java/com/android/server/notification/toast/TextToastRecord.java84
-rw-r--r--services/core/java/com/android/server/notification/toast/ToastRecord.java88
-rw-r--r--services/core/java/com/android/server/om/OverlayActorEnforcer.java8
-rw-r--r--services/core/java/com/android/server/om/TEST_MAPPING14
-rw-r--r--services/core/java/com/android/server/pm/AppsFilter.java24
-rw-r--r--services/core/java/com/android/server/pm/PackageInstallerSession.java320
-rw-r--r--services/core/java/com/android/server/pm/PackageManagerService.java2
-rw-r--r--services/core/java/com/android/server/pm/PackageManagerShellCommandDataLoader.java1
-rw-r--r--services/core/java/com/android/server/policy/PhoneWindowManager.java2
-rw-r--r--services/core/java/com/android/server/power/Notifier.java19
-rw-r--r--services/core/java/com/android/server/power/PowerManagerService.java6
-rw-r--r--services/core/java/com/android/server/power/WakeLockLog.java1355
-rw-r--r--services/core/java/com/android/server/stats/pull/ProcfsMemoryUtil.java15
-rw-r--r--services/core/java/com/android/server/stats/pull/StatsPullAtomService.java746
-rw-r--r--services/core/java/com/android/server/statusbar/StatusBarManagerInternal.java14
-rw-r--r--services/core/java/com/android/server/statusbar/StatusBarManagerService.java32
-rwxr-xr-xservices/core/java/com/android/server/tv/TvInputManagerService.java5
-rw-r--r--services/core/java/com/android/server/wm/ActivityStack.java45
-rw-r--r--services/core/java/com/android/server/wm/ActivityStarter.java4
-rw-r--r--services/core/java/com/android/server/wm/DisplayContent.java4
-rw-r--r--services/core/java/com/android/server/wm/DisplayPolicy.java15
-rw-r--r--services/core/java/com/android/server/wm/InputMonitor.java8
-rw-r--r--services/core/java/com/android/server/wm/InsetsSourceProvider.java2
-rw-r--r--services/core/java/com/android/server/wm/PinnedStackController.java25
-rw-r--r--services/core/java/com/android/server/wm/RecentTasks.java42
-rw-r--r--services/core/java/com/android/server/wm/SeamlessRotator.java4
-rw-r--r--services/core/java/com/android/server/wm/Session.java5
-rw-r--r--services/core/java/com/android/server/wm/Task.java22
-rw-r--r--services/core/java/com/android/server/wm/TaskSnapshotSurface.java8
-rw-r--r--services/core/java/com/android/server/wm/WindowManagerService.java10
-rw-r--r--services/core/java/com/android/server/wm/WindowState.java4
-rw-r--r--services/core/java/com/android/server/wm/WindowStateAnimator.java11
-rw-r--r--services/core/java/com/android/server/wm/WindowSurfaceController.java36
-rw-r--r--services/core/jni/Android.bp9
-rw-r--r--services/core/jni/com_android_server_VibratorService.cpp67
-rw-r--r--services/incremental/Android.bp2
-rw-r--r--services/incremental/BinderIncrementalService.cpp149
-rw-r--r--services/incremental/BinderIncrementalService.h63
-rw-r--r--services/incremental/IncrementalService.cpp307
-rw-r--r--services/incremental/IncrementalService.h66
-rw-r--r--services/incremental/ServiceWrappers.cpp31
-rw-r--r--services/incremental/ServiceWrappers.h135
-rw-r--r--services/incremental/path.cpp31
-rw-r--r--services/incremental/path.h14
-rw-r--r--services/incremental/test/IncrementalServiceTest.cpp161
-rw-r--r--services/java/com/android/server/SystemServer.java8
-rw-r--r--services/net/java/android/net/IpMemoryStore.java5
-rw-r--r--services/net/java/android/net/ip/IpClientUtil.java5
-rw-r--r--services/people/java/com/android/server/people/PeopleService.java25
-rw-r--r--services/people/java/com/android/server/people/SessionInfo.java5
-rw-r--r--services/people/java/com/android/server/people/data/AggregateEventHistoryImpl.java95
-rw-r--r--services/people/java/com/android/server/people/data/ContactsQueryHelper.java182
-rw-r--r--services/people/java/com/android/server/people/data/ConversationInfo.java372
-rw-r--r--services/people/java/com/android/server/people/data/ConversationStore.java109
-rw-r--r--services/people/java/com/android/server/people/data/DataManager.java559
-rw-r--r--services/people/java/com/android/server/people/data/Event.java195
-rw-r--r--services/people/java/com/android/server/people/data/EventHistory.java44
-rw-r--r--services/people/java/com/android/server/people/data/EventHistoryImpl.java88
-rw-r--r--services/people/java/com/android/server/people/data/EventIndex.java377
-rw-r--r--services/people/java/com/android/server/people/data/EventList.java103
-rw-r--r--services/people/java/com/android/server/people/data/EventStore.java114
-rw-r--r--services/people/java/com/android/server/people/data/PackageData.java145
-rw-r--r--services/people/java/com/android/server/people/data/UserData.java103
-rw-r--r--services/people/java/com/android/server/people/prediction/ConversationData.java55
-rw-r--r--services/people/java/com/android/server/people/prediction/ConversationPredictor.java69
-rw-r--r--services/tests/mockingservicestests/src/com/android/server/job/controllers/ConnectivityControllerTest.java46
-rw-r--r--services/tests/servicestests/AndroidManifest.xml2
-rw-r--r--services/tests/servicestests/src/com/android/server/WatchdogDiagnosticsTest.java2
-rw-r--r--services/tests/servicestests/src/com/android/server/backup/restore/PerformUnifiedRestoreTaskTest.java38
-rw-r--r--services/tests/servicestests/src/com/android/server/integrity/AppIntegrityManagerServiceImplTest.java43
-rw-r--r--services/tests/servicestests/src/com/android/server/integrity/engine/RuleEvaluationEngineTest.java213
-rw-r--r--services/tests/servicestests/src/com/android/server/integrity/engine/RuleEvaluatorTest.java78
-rw-r--r--services/tests/servicestests/src/com/android/server/integrity/model/IntegrityCheckResultTest.java79
-rw-r--r--services/tests/servicestests/src/com/android/server/location/gnss/GnssManagerServiceTest.java (renamed from services/tests/servicestests/src/com/android/server/GnssManagerServiceTest.java)20
-rw-r--r--services/tests/servicestests/src/com/android/server/people/data/AggregateEventHistoryImplTest.java131
-rw-r--r--services/tests/servicestests/src/com/android/server/people/data/ContactsQueryHelperTest.java187
-rw-r--r--services/tests/servicestests/src/com/android/server/people/data/ConversationInfoTest.java130
-rw-r--r--services/tests/servicestests/src/com/android/server/people/data/ConversationStoreTest.java153
-rw-r--r--services/tests/servicestests/src/com/android/server/people/data/DataManagerTest.java451
-rw-r--r--services/tests/servicestests/src/com/android/server/people/data/EventHistoryImplTest.java118
-rw-r--r--services/tests/servicestests/src/com/android/server/people/data/EventIndexTest.java176
-rw-r--r--services/tests/servicestests/src/com/android/server/people/data/EventListTest.java137
-rw-r--r--services/tests/servicestests/src/com/android/server/people/data/PackageDataTest.java122
-rw-r--r--services/tests/servicestests/src/com/android/server/people/data/TestUtils.java42
-rw-r--r--services/tests/servicestests/src/com/android/server/pm/AppsFilterTest.java31
-rw-r--r--services/tests/servicestests/src/com/android/server/pm/PackageInstallerSessionTest.java11
-rw-r--r--services/tests/servicestests/src/com/android/server/pm/PackageSettingBuilder.java11
-rw-r--r--services/tests/servicestests/src/com/android/server/pm/UserManagerServiceUserInfoTest.java19
-rw-r--r--services/tests/servicestests/src/com/android/server/power/WakeLockLogTest.java268
-rwxr-xr-xservices/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java40
-rw-r--r--services/tests/wmtests/src/com/android/server/wm/ActivityStarterTests.java14
-rw-r--r--services/tests/wmtests/src/com/android/server/wm/RecentTasksTest.java33
-rw-r--r--services/tests/wmtests/src/com/android/server/wm/TaskOrganizerTests.java52
-rwxr-xr-xstartop/scripts/app_startup/metrics/com.google.android.GoogleCamera25
-rwxr-xr-xstartop/scripts/app_startup/parse_metrics30
-rw-r--r--telecomm/java/android/telecom/Call.java33
-rw-r--r--telecomm/java/android/telecom/Connection.java11
-rw-r--r--telecomm/java/android/telecom/ConnectionService.java32
-rw-r--r--telecomm/java/android/telecom/InCallAdapter.java13
-rw-r--r--telecomm/java/com/android/internal/telecom/IConnectionService.aidl2
-rw-r--r--telecomm/java/com/android/internal/telecom/IInCallAdapter.aidl2
-rw-r--r--telephony/common/com/android/internal/telephony/SmsApplication.java30
-rw-r--r--telephony/common/com/android/internal/telephony/SmsNumberUtils.java3
-rw-r--r--telephony/common/com/android/internal/telephony/TelephonyPermissions.java5
-rw-r--r--telephony/common/com/android/internal/telephony/util/TelephonyUtils.java7
-rw-r--r--telephony/common/com/google/android/mms/util/DrmConvertSession.java30
-rw-r--r--telephony/java/android/telephony/Annotation.java52
-rw-r--r--telephony/java/android/telephony/CallAttributes.java6
-rw-r--r--telephony/java/android/telephony/CallForwardingInfo.aidl19
-rw-r--r--telephony/java/android/telephony/CallForwardingInfo.java307
-rw-r--r--telephony/java/android/telephony/CallQuality.java4
-rw-r--r--telephony/java/android/telephony/ImsManager.java10
-rw-r--r--telephony/java/android/telephony/PhoneCapability.java20
-rw-r--r--telephony/java/android/telephony/SimSlotCapability.java4
-rw-r--r--telephony/java/android/telephony/SmsCbMessage.java23
-rw-r--r--telephony/java/android/telephony/TelephonyManager.java210
-rw-r--r--telephony/java/android/telephony/data/ApnSetting.java7
-rw-r--r--telephony/java/android/telephony/ims/ImsException.java1
-rw-r--r--telephony/java/android/telephony/ims/ImsMmTelManager.java16
-rw-r--r--telephony/java/android/telephony/ims/ImsService.java5
-rw-r--r--telephony/java/android/telephony/ims/RcsContactUceCapability.java39
-rw-r--r--telephony/java/android/telephony/ims/RcsUceAdapter.java5
-rw-r--r--telephony/java/android/telephony/ims/feature/MmTelFeature.java12
-rw-r--r--telephony/java/android/telephony/ims/feature/RcsFeature.java6
-rw-r--r--telephony/java/android/telephony/ims/stub/ImsCallSessionImplBase.java7
-rw-r--r--telephony/java/android/telephony/ims/stub/RcsCapabilityExchange.java4
-rw-r--r--telephony/java/android/telephony/ims/stub/RcsPresenceExchangeImplBase.java4
-rw-r--r--telephony/java/android/telephony/ims/stub/RcsSipOptionsImplBase.java13
-rw-r--r--telephony/java/com/android/internal/telephony/ITelephony.aidl74
-rw-r--r--tests/FlickerTests/src/com/android/server/wm/flicker/ChangeAppRotationTest.java2
-rw-r--r--tests/FlickerTests/src/com/android/server/wm/flicker/CloseImeWindowToHomeTest.java3
-rw-r--r--tests/FlickerTests/src/com/android/server/wm/flicker/SeamlessAppRotationTest.java4
-rw-r--r--tests/HwAccelerationTest/src/com/android/test/hwui/AlphaLayersActivity.java22
-rw-r--r--tests/PlatformCompatGating/Android.bp1
-rw-r--r--tests/PlatformCompatGating/test-rules/src/android/compat/testing/PlatformCompatChangeRule.java7
-rw-r--r--wifi/java/android/net/wifi/WifiInfo.java92
-rw-r--r--wifi/java/android/net/wifi/WifiManager.java33
-rw-r--r--wifi/java/android/net/wifi/WifiScanner.java2
-rw-r--r--wifi/tests/README.md21
-rwxr-xr-xwifi/tests/runtests.sh25
-rw-r--r--wifi/tests/src/android/net/wifi/WifiInfoTest.java4
582 files changed, 18830 insertions, 4741 deletions
diff --git a/Android.bp b/Android.bp
index 9411eeca834c..df276a053dfc 100644
--- a/Android.bp
+++ b/Android.bp
@@ -191,12 +191,20 @@ filegroup {
path: "telephony/java",
}
+genrule {
+ name: "statslog-telephony-common-java-gen",
+ tools: ["stats-log-api-gen"],
+ cmd: "$(location stats-log-api-gen) --java $(out) --module telephony_common"
+ + " --javaPackage com.android.internal.telephony --javaClass TelephonyCommonStatsLog",
+ out: ["com/android/internal/telephony/TelephonyCommonStatsLog.java"],
+}
+
filegroup {
name: "framework-telephony-common-sources",
srcs: [
"telephony/common/**/*.java",
+ ":statslog-telephony-common-java-gen",
],
- path: "telephony/common",
}
filegroup {
@@ -265,7 +273,8 @@ filegroup {
// etc.
":framework-javastream-protos",
- ":framework-statslog-gen",
+ ":framework-statslog-gen", // StatsLogInternal.java
+ ":statslog-framework-java-gen", // FrameworkStatsLog.java
// telephony annotations
":framework-telephony-annotations",
@@ -533,7 +542,7 @@ java_library {
srcs: [":framework-all-sources"],
installable: false,
static_libs: [
- "exoplayer2-core",
+ "exoplayer2-extractor",
"android.hardware.wifi-V1.0-java-constants",
],
libs: ["icing-java-proto-lite"],
@@ -553,7 +562,7 @@ java_library {
"compat-changeid-annotation-processor",
],
static_libs: [
- "exoplayer2-core",
+ "exoplayer2-extractor",
"android.hardware.wifi-V1.0-java-constants",
]
}
@@ -604,6 +613,14 @@ genrule {
out: ["android/util/StatsLogInternal.java"],
}
+genrule {
+ name: "statslog-framework-java-gen",
+ tools: ["stats-log-api-gen"],
+ cmd: "$(location stats-log-api-gen) --java $(out) --module framework" +
+ " --javaPackage com.android.internal.util --javaClass FrameworkStatsLog --worksource",
+ out: ["com/android/internal/util/FrameworkStatsLog.java"],
+}
+
gensrcs {
name: "framework-javastream-protos",
depfile: true,
@@ -922,6 +939,8 @@ filegroup {
srcs: [
"core/java/android/os/incremental/IIncrementalManager.aidl",
"core/java/android/os/incremental/IIncrementalManagerNative.aidl",
+ "core/java/android/os/incremental/IncrementalNewFileParams.aidl",
+ "core/java/android/os/incremental/IncrementalSignature.aidl",
],
path: "core/java",
}
@@ -1267,7 +1286,11 @@ metalava_framework_docs_args = "--manifest $(location core/res/AndroidManifest.x
"--hide MissingPermission --hide BroadcastBehavior " +
"--hide HiddenSuperclass --hide DeprecationMismatch --hide UnavailableSymbol " +
"--hide SdkConstant --hide HiddenTypeParameter --hide Todo --hide Typo " +
- "--force-convert-to-warning-nullability-annotations +*:-android.*:+android.icu.*:-dalvik.*"
+ "--force-convert-to-warning-nullability-annotations +*:-android.*:+android.icu.*:-dalvik.* " +
+ "--api-lint-ignore-prefix android.icu. " +
+ "--api-lint-ignore-prefix java. " +
+ "--api-lint-ignore-prefix junit. " +
+ "--api-lint-ignore-prefix org. "
build = [
"StubLibraries.bp",
diff --git a/PREUPLOAD.cfg b/PREUPLOAD.cfg
index 4d1ac0ec218c..9abb308534df 100644
--- a/PREUPLOAD.cfg
+++ b/PREUPLOAD.cfg
@@ -8,6 +8,7 @@ clang_format = --commit ${PREUPLOAD_COMMIT} --style file --extensions c,h,cc,cpp
cmds/input/
core/jni/
libs/input/
+ services/core/jni/
[Hook Scripts]
checkstyle_hook = ${REPO_ROOT}/prebuilts/checkstyle/checkstyle.py --sha ${PREUPLOAD_COMMIT}
diff --git a/apct-tests/perftests/core/src/android/wm/RelayoutPerfTest.java b/apct-tests/perftests/core/src/android/wm/RelayoutPerfTest.java
index c62aad622f25..b6e39e14602a 100644
--- a/apct-tests/perftests/core/src/android/wm/RelayoutPerfTest.java
+++ b/apct-tests/perftests/core/src/android/wm/RelayoutPerfTest.java
@@ -150,7 +150,7 @@ public class RelayoutPerfTest extends WindowManagerPerfTestBase {
mViewVisibility.getAsInt(), mFlags, mFrameNumber, mOutFrame,
mOutContentInsets, mOutVisibleInsets, mOutStableInsets,
mOutBackDropFrame, mOutDisplayCutout, mOutMergedConfiguration,
- mOutSurfaceControl, mOutInsetsState, new Point());
+ mOutSurfaceControl, mOutInsetsState, new Point(), new SurfaceControl());
}
}
}
diff --git a/apex/jobscheduler/service/java/com/android/server/job/controllers/ConnectivityController.java b/apex/jobscheduler/service/java/com/android/server/job/controllers/ConnectivityController.java
index a0e83daf877d..bb94275fc409 100644
--- a/apex/jobscheduler/service/java/com/android/server/job/controllers/ConnectivityController.java
+++ b/apex/jobscheduler/service/java/com/android/server/job/controllers/ConnectivityController.java
@@ -144,14 +144,18 @@ public final class ConnectivityController extends RestrictingController implemen
public void startTrackingRestrictedJobLocked(JobStatus jobStatus) {
// Don't need to start tracking the job. If the job needed network, it would already be
// tracked.
- updateConstraintsSatisfied(jobStatus);
+ if (jobStatus.hasConnectivityConstraint()) {
+ updateConstraintsSatisfied(jobStatus);
+ }
}
@Override
public void stopTrackingRestrictedJobLocked(JobStatus jobStatus) {
// Shouldn't stop tracking the job here. If the job was tracked, it still needs network,
// even after being unrestricted.
- updateConstraintsSatisfied(jobStatus);
+ if (jobStatus.hasConnectivityConstraint()) {
+ updateConstraintsSatisfied(jobStatus);
+ }
}
/**
diff --git a/apex/media/framework/Android.bp b/apex/media/framework/Android.bp
index 18382a488428..2266d049d70a 100644
--- a/apex/media/framework/Android.bp
+++ b/apex/media/framework/Android.bp
@@ -50,7 +50,7 @@ java_library {
],
static_libs: [
- "exoplayer2-core"
+ "exoplayer2-extractor"
],
jarjar_rules: "jarjar_rules.txt",
diff --git a/apex/media/framework/java/android/media/MediaParser.java b/apex/media/framework/java/android/media/MediaParser.java
index ac66d1b7f4ad..d59270c6a51b 100644
--- a/apex/media/framework/java/android/media/MediaParser.java
+++ b/apex/media/framework/java/android/media/MediaParser.java
@@ -223,7 +223,7 @@ public final class MediaParser {
public static final class SeekPoint {
/** A {@link SeekPoint} whose time and byte offset are both set to 0. */
- public static final @NonNull SeekPoint START = new SeekPoint(0, 0);
+ @NonNull public static final SeekPoint START = new SeekPoint(0, 0);
/** The time of the seek point, in microseconds. */
public final long timeUs;
@@ -241,7 +241,8 @@ public final class MediaParser {
}
@Override
- public @NonNull String toString() {
+ @NonNull
+ public String toString() {
return "[timeUs=" + timeUs + ", position=" + position + "]";
}
@@ -414,7 +415,8 @@ public final class MediaParser {
* @return A new instance.
* @throws IllegalArgumentException If an invalid name is provided.
*/
- public static @NonNull MediaParser createByName(
+ @NonNull
+ public static MediaParser createByName(
@NonNull String name, @NonNull OutputConsumer outputConsumer) {
String[] nameAsArray = new String[] {name};
assertValidNames(nameAsArray);
@@ -431,7 +433,8 @@ public final class MediaParser {
* default array of names is used.
* @return A new instance.
*/
- public static @NonNull MediaParser create(
+ @NonNull
+ public static MediaParser create(
@NonNull OutputConsumer outputConsumer, @NonNull String... extractorNames) {
assertValidNames(extractorNames);
if (extractorNames.length == 0) {
@@ -448,7 +451,8 @@ public final class MediaParser {
*
* <p>TODO: List which properties are taken into account. E.g. MimeType.
*/
- public static @NonNull List<String> getExtractorNames(@NonNull MediaFormat mediaFormat) {
+ @NonNull
+ public static List<String> getExtractorNames(@NonNull MediaFormat mediaFormat) {
throw new UnsupportedOperationException();
}
@@ -479,7 +483,8 @@ public final class MediaParser {
* @return The name of the backing extractor implementation, or null if the backing extractor
* implementation has not yet been selected.
*/
- public @Nullable String getExtractorName() {
+ @Nullable
+ public String getExtractorName() {
return mExtractorName;
}
diff --git a/apex/permission/service/java/com/android/permission/persistence/RuntimePermissionsPersistenceImpl.java b/apex/permission/service/java/com/android/permission/persistence/RuntimePermissionsPersistenceImpl.java
index 1dbad456760c..90b1c4b6ff5f 100644
--- a/apex/permission/service/java/com/android/permission/persistence/RuntimePermissionsPersistenceImpl.java
+++ b/apex/permission/service/java/com/android/permission/persistence/RuntimePermissionsPersistenceImpl.java
@@ -19,6 +19,7 @@ package com.android.permission.persistence;
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.content.ApexContext;
+import android.content.pm.PackageManager;
import android.os.UserHandle;
import android.util.ArrayMap;
import android.util.AtomicFile;
@@ -242,9 +243,10 @@ public class RuntimePermissionsPersistenceImpl implements RuntimePermissionsPers
serializer.startTag(null, TAG_PERMISSION);
serializer.attribute(null, ATTRIBUTE_NAME, permissionState.getName());
serializer.attribute(null, ATTRIBUTE_GRANTED, Boolean.toString(
- permissionState.isGranted()));
+ permissionState.isGranted() && (permissionState.getFlags()
+ & PackageManager.FLAG_PERMISSION_ONE_TIME) == 0));
serializer.attribute(null, ATTRIBUTE_FLAGS, Integer.toHexString(
- permissionState.getFlags()));
+ permissionState.getFlags() & ~PackageManager.FLAG_PERMISSION_ONE_TIME));
serializer.endTag(null, TAG_PERMISSION);
}
}
diff --git a/apex/sdkextensions/OWNERS b/apex/sdkextensions/OWNERS
index feb274262bef..a6e55228596b 100644
--- a/apex/sdkextensions/OWNERS
+++ b/apex/sdkextensions/OWNERS
@@ -1 +1,2 @@
+dariofreni@google.com
hansson@google.com
diff --git a/apex/sdkextensions/framework/Android.bp b/apex/sdkextensions/framework/Android.bp
index dd174734df6d..245a96b99148 100644
--- a/apex/sdkextensions/framework/Android.bp
+++ b/apex/sdkextensions/framework/Android.bp
@@ -32,6 +32,7 @@ java_library {
libs: [ "framework-annotations-lib" ],
permitted_packages: [ "android.os.ext" ],
installable: true,
+ plugins: ["java_api_finder"],
visibility: [
"//frameworks/base/apex/sdkextensions",
"//frameworks/base/apex/sdkextensions/testing",
diff --git a/apex/statsd/framework/java/android/app/StatsManager.java b/apex/statsd/framework/java/android/app/StatsManager.java
index ad1ac95d667c..a1de330c300a 100644
--- a/apex/statsd/framework/java/android/app/StatsManager.java
+++ b/apex/statsd/framework/java/android/app/StatsManager.java
@@ -474,17 +474,6 @@ public final class StatsManager {
}
/**
- * Temp registration for while the migration is in progress.
- *
- * @hide
- */
- public void registerPullAtomCallback(int atomTag, @Nullable PullAtomMetadata metadata,
- @NonNull StatsPullAtomCallback callback,
- @NonNull @CallbackExecutor Executor executor) {
- registerPullAtomCallback(atomTag, metadata, executor, callback);
- }
-
- /**
* Registers a callback for an atom when that atom is to be pulled. The stats service will
* invoke pullData in the callback when the stats service determines that this atom needs to be
* pulled.
@@ -591,15 +580,6 @@ public final class StatsManager {
}
/**
- * Temp for while migrations are in progress.
- *
- * @hide
- */
- public static PullAtomMetadata.Builder newBuilder() {
- return new PullAtomMetadata.Builder();
- }
-
- /**
* Builder for PullAtomMetadata.
*/
public static class Builder {
diff --git a/apex/statsd/tests/libstatspull/Android.bp b/apex/statsd/tests/libstatspull/Android.bp
new file mode 100644
index 000000000000..e81396405d31
--- /dev/null
+++ b/apex/statsd/tests/libstatspull/Android.bp
@@ -0,0 +1,56 @@
+// 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.
+
+android_test {
+ name: "LibStatsPullTests",
+ static_libs: [
+ "androidx.test.rules",
+ "platformprotoslite",
+ "statsdprotolite",
+ "truth-prebuilt",
+ ],
+ libs: [
+ "android.test.runner.stubs",
+ "android.test.base.stubs",
+ ],
+ jni_libs: [
+ "libstatspull_testhelper",
+ ],
+ srcs: [
+ "src/**/*.java",
+ "protos/**/*.proto",
+ ],
+ test_suites: [
+ "general-tests",
+ ],
+ platform_apis: true,
+ privileged: true,
+ certificate: "platform",
+ compile_multilib: "both",
+}
+
+cc_library_shared {
+ name: "libstatspull_testhelper",
+ srcs: ["jni/stats_pull_helper.cpp"],
+ cflags: [
+ "-Wall",
+ "-Werror",
+ ],
+ shared_libs: [
+ "libbinder",
+ "libutils",
+ "libstatspull",
+ "libstatssocket",
+ ],
+} \ No newline at end of file
diff --git a/apex/statsd/tests/libstatspull/AndroidManifest.xml b/apex/statsd/tests/libstatspull/AndroidManifest.xml
new file mode 100644
index 000000000000..bffd400bdb2c
--- /dev/null
+++ b/apex/statsd/tests/libstatspull/AndroidManifest.xml
@@ -0,0 +1,30 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ -->
+
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+ package="com.android.internal.os.statsd.libstats" >
+
+
+ <uses-permission android:name="android.permission.DUMP" />
+ <uses-permission android:name="android.permission.PACKAGE_USAGE_STATS" />
+
+ <instrumentation android:name="androidx.test.runner.AndroidJUnitRunner"
+ android:targetPackage="com.android.internal.os.statsd.libstats"
+ android:label="Tests for libstatspull">
+ </instrumentation>
+</manifest>
+
diff --git a/apex/statsd/tests/libstatspull/TEST_MAPPING b/apex/statsd/tests/libstatspull/TEST_MAPPING
new file mode 100644
index 000000000000..5e1178cffc05
--- /dev/null
+++ b/apex/statsd/tests/libstatspull/TEST_MAPPING
@@ -0,0 +1,7 @@
+{
+ "presubmit" : [
+ {
+ "name" : "LibStatsPullTests"
+ }
+ ]
+} \ No newline at end of file
diff --git a/apex/statsd/tests/libstatspull/jni/stats_pull_helper.cpp b/apex/statsd/tests/libstatspull/jni/stats_pull_helper.cpp
new file mode 100644
index 000000000000..e4ab823f345a
--- /dev/null
+++ b/apex/statsd/tests/libstatspull/jni/stats_pull_helper.cpp
@@ -0,0 +1,88 @@
+/*
+ * 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.
+ */
+
+#include <binder/ProcessState.h>
+#include <jni.h>
+#include <log/log.h>
+#include <stats_event.h>
+#include <stats_pull_atom_callback.h>
+
+#include <chrono>
+#include <thread>
+
+using std::this_thread::sleep_for;
+using namespace android;
+
+namespace {
+static int32_t sAtomTag;
+static int32_t sPullReturnVal;
+static int64_t sLatencyMillis;
+static int32_t sAtomsPerPull;
+static int32_t sNumPulls = 0;
+
+static bool initialized = false;
+
+static void init() {
+ if (!initialized) {
+ initialized = true;
+ // Set up the binder
+ sp<ProcessState> ps(ProcessState::self());
+ ps->setThreadPoolMaxThreadCount(9);
+ ps->startThreadPool();
+ ps->giveThreadPoolName();
+ }
+}
+
+static status_pull_atom_return_t pullAtomCallback(int32_t atomTag, pulled_stats_event_list* data,
+ void* /*cookie*/) {
+ sNumPulls++;
+ sleep_for(std::chrono::milliseconds(sLatencyMillis));
+ for (int i = 0; i < sAtomsPerPull; i++) {
+ stats_event* event = add_stats_event_to_pull_data(data);
+ stats_event_set_atom_id(event, atomTag);
+ stats_event_write_int64(event, (int64_t) sNumPulls);
+ stats_event_build(event);
+ }
+ return sPullReturnVal;
+}
+
+extern "C"
+JNIEXPORT void JNICALL
+Java_com_android_internal_os_statsd_libstats_LibStatsPullTests_registerStatsPuller(
+ JNIEnv* /*env*/, jobject /* this */, jint atomTag, jlong timeoutNs, jlong coolDownNs,
+ jint pullRetVal, jlong latencyMillis, int atomsPerPull)
+{
+ init();
+ sAtomTag = atomTag;
+ sPullReturnVal = pullRetVal;
+ sLatencyMillis = latencyMillis;
+ sAtomsPerPull = atomsPerPull;
+ sNumPulls = 0;
+ pull_atom_metadata metadata = {.cool_down_ns = coolDownNs,
+ .timeout_ns = timeoutNs,
+ .additive_fields = nullptr,
+ .additive_fields_size = 0};
+ register_stats_pull_atom_callback(sAtomTag, &pullAtomCallback, &metadata, nullptr);
+}
+
+extern "C"
+JNIEXPORT void JNICALL
+Java_com_android_internal_os_statsd_libstats_LibStatsPullTests_unregisterStatsPuller(
+ JNIEnv* /*env*/, jobject /* this */, jint /*atomTag*/)
+{
+ unregister_stats_pull_atom_callback(sAtomTag);
+}
+} // namespace \ No newline at end of file
diff --git a/apex/statsd/tests/libstatspull/protos/test_atoms.proto b/apex/statsd/tests/libstatspull/protos/test_atoms.proto
new file mode 100644
index 000000000000..56c1b534a7ce
--- /dev/null
+++ b/apex/statsd/tests/libstatspull/protos/test_atoms.proto
@@ -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.
+ */
+syntax = "proto2";
+
+package com.android.internal.os.statsd.protos;
+
+option java_package = "com.android.internal.os.statsd.protos";
+option java_outer_classname = "TestAtoms";
+
+message PullCallbackAtomWrapper {
+ optional PullCallbackAtom pull_callback_atom = 150030;
+}
+
+message PullCallbackAtom {
+ optional int64 long_val = 1;
+}
+
+
+
diff --git a/apex/statsd/tests/libstatspull/src/com/android/internal/os/statsd/StatsConfigUtils.java b/apex/statsd/tests/libstatspull/src/com/android/internal/os/statsd/StatsConfigUtils.java
new file mode 100644
index 000000000000..d0d140092586
--- /dev/null
+++ b/apex/statsd/tests/libstatspull/src/com/android/internal/os/statsd/StatsConfigUtils.java
@@ -0,0 +1,124 @@
+/*
+ * 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.internal.os.statsd;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import android.app.StatsManager;
+import android.util.Log;
+
+import com.android.internal.os.StatsdConfigProto.AtomMatcher;
+import com.android.internal.os.StatsdConfigProto.FieldValueMatcher;
+import com.android.internal.os.StatsdConfigProto.SimpleAtomMatcher;
+import com.android.internal.os.StatsdConfigProto.StatsdConfig;
+import com.android.os.AtomsProto.AppBreadcrumbReported;
+import com.android.os.AtomsProto.Atom;
+import com.android.os.StatsLog.ConfigMetricsReport;
+import com.android.os.StatsLog.ConfigMetricsReportList;
+import com.android.os.StatsLog.GaugeBucketInfo;
+import com.android.os.StatsLog.GaugeMetricData;
+import com.android.os.StatsLog.StatsLogReport;
+import com.android.os.StatsLog.StatsdStatsReport;
+import com.android.os.StatsLog.StatsdStatsReport.ConfigStats;
+
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * Util class for constructing statsd configs.
+ */
+public class StatsConfigUtils {
+ public static final String TAG = "statsd.StatsConfigUtils";
+ public static final int SHORT_WAIT = 2_000; // 2 seconds.
+
+ /**
+ * @return An empty StatsdConfig in serialized proto format.
+ */
+ public static StatsdConfig.Builder getSimpleTestConfig(long configId) {
+ return StatsdConfig.newBuilder().setId(configId)
+ .addAllowedLogSource(StatsConfigUtils.class.getPackage().getName());
+ }
+
+
+ public static boolean verifyValidConfigExists(StatsManager statsManager, long configId) {
+ StatsdStatsReport report = null;
+ try {
+ report = StatsdStatsReport.parser().parseFrom(statsManager.getStatsMetadata());
+ } catch (Exception e) {
+ Log.e(TAG, "getMetadata failed", e);
+ }
+ assertThat(report).isNotNull();
+ boolean foundConfig = false;
+ for (ConfigStats configStats : report.getConfigStatsList()) {
+ if (configStats.getId() == configId && configStats.getIsValid()
+ && configStats.getDeletionTimeSec() == 0) {
+ foundConfig = true;
+ }
+ }
+ return foundConfig;
+ }
+
+ public static AtomMatcher getAppBreadcrumbMatcher(long id, int label) {
+ return AtomMatcher.newBuilder()
+ .setId(id)
+ .setSimpleAtomMatcher(
+ SimpleAtomMatcher.newBuilder()
+ .setAtomId(Atom.APP_BREADCRUMB_REPORTED_FIELD_NUMBER)
+ .addFieldValueMatcher(FieldValueMatcher.newBuilder()
+ .setField(AppBreadcrumbReported.LABEL_FIELD_NUMBER)
+ .setEqInt(label)
+ )
+ )
+ .build();
+ }
+
+ public static ConfigMetricsReport getConfigMetricsReport(StatsManager statsManager,
+ long configId) {
+ ConfigMetricsReportList reportList = null;
+ try {
+ reportList = ConfigMetricsReportList.parser()
+ .parseFrom(statsManager.getReports(configId));
+ } catch (Exception e) {
+ Log.e(TAG, "getData failed", e);
+ }
+ assertThat(reportList).isNotNull();
+ assertThat(reportList.getReportsCount()).isEqualTo(1);
+ ConfigMetricsReport report = reportList.getReports(0);
+ assertThat(report.getDumpReportReason())
+ .isEqualTo(ConfigMetricsReport.DumpReportReason.GET_DATA_CALLED);
+ return report;
+
+ }
+ public static List<Atom> getGaugeMetricDataList(ConfigMetricsReport report) {
+ List<Atom> data = new ArrayList<>();
+ for (StatsLogReport metric : report.getMetricsList()) {
+ for (GaugeMetricData gaugeMetricData : metric.getGaugeMetrics().getDataList()) {
+ for (GaugeBucketInfo bucketInfo : gaugeMetricData.getBucketInfoList()) {
+ for (Atom atom : bucketInfo.getAtomList()) {
+ data.add(atom);
+ }
+ }
+ }
+ }
+ return data;
+ }
+
+ public static List<Atom> getGaugeMetricDataList(StatsManager statsManager, long configId) {
+ ConfigMetricsReport report = getConfigMetricsReport(statsManager, configId);
+ return getGaugeMetricDataList(report);
+ }
+}
+
diff --git a/apex/statsd/tests/libstatspull/src/com/android/internal/os/statsd/libstats/LibStatsPullTests.java b/apex/statsd/tests/libstatspull/src/com/android/internal/os/statsd/libstats/LibStatsPullTests.java
new file mode 100644
index 000000000000..dbd636d2e95c
--- /dev/null
+++ b/apex/statsd/tests/libstatspull/src/com/android/internal/os/statsd/libstats/LibStatsPullTests.java
@@ -0,0 +1,285 @@
+/*
+ * 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.internal.os.statsd.libstats;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import android.app.StatsManager;
+import android.content.Context;
+import android.util.Log;
+import android.util.StatsLog;
+
+import androidx.test.InstrumentationRegistry;
+import androidx.test.filters.MediumTest;
+import androidx.test.runner.AndroidJUnit4;
+
+import com.android.internal.os.StatsdConfigProto.AtomMatcher;
+import com.android.internal.os.StatsdConfigProto.FieldFilter;
+import com.android.internal.os.StatsdConfigProto.GaugeMetric;
+import com.android.internal.os.StatsdConfigProto.SimpleAtomMatcher;
+import com.android.internal.os.StatsdConfigProto.StatsdConfig;
+import com.android.internal.os.StatsdConfigProto.TimeUnit;
+import com.android.internal.os.statsd.StatsConfigUtils;
+import com.android.internal.os.statsd.protos.TestAtoms;
+import com.android.os.AtomsProto.Atom;
+
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+import java.util.List;
+
+/**
+ * Test puller registration.
+ */
+@MediumTest
+@RunWith(AndroidJUnit4.class)
+public class LibStatsPullTests {
+ private static final String LOG_TAG = LibStatsPullTests.class.getSimpleName();
+ private static final int SHORT_SLEEP_MILLIS = 250;
+ private static final int LONG_SLEEP_MILLIS = 1_000;
+ private Context mContext;
+ private static final int PULL_ATOM_TAG = 150030;
+ private static final int APP_BREADCRUMB_LABEL = 3;
+ private static int sPullReturnValue;
+ private static long sConfigId;
+ private static long sPullLatencyMillis;
+ private static long sPullTimeoutNs;
+ private static long sCoolDownNs;
+ private static int sAtomsPerPull;
+
+ static {
+ System.loadLibrary("statspull_testhelper");
+ }
+
+ /**
+ * Setup the tests. Initialize shared data.
+ */
+ @Before
+ public void setup() {
+// Debug.waitForDebugger();
+ mContext = InstrumentationRegistry.getTargetContext();
+ assertThat(InstrumentationRegistry.getInstrumentation()).isNotNull();
+ sPullReturnValue = StatsManager.PULL_SUCCESS;
+ sPullLatencyMillis = 0;
+ sPullTimeoutNs = 10_000_000_000L;
+ sCoolDownNs = 1_000_000_000L;
+ sAtomsPerPull = 1;
+ }
+
+ /**
+ * Teardown the tests.
+ */
+ @After
+ public void tearDown() throws Exception {
+ unregisterStatsPuller(PULL_ATOM_TAG);
+ StatsManager statsManager = (StatsManager) mContext.getSystemService(
+ Context.STATS_MANAGER);
+ statsManager.removeConfig(sConfigId);
+ }
+
+ /**
+ * Tests adding a puller callback and that pulls complete successfully.
+ */
+ @Test
+ public void testPullAtomCallbackRegistration() throws Exception {
+ StatsManager statsManager = (StatsManager) mContext.getSystemService(
+ Context.STATS_MANAGER);
+ // Upload a config that captures that pulled atom.
+ createAndAddConfigToStatsd(statsManager);
+
+ // Add the puller.
+ registerStatsPuller(PULL_ATOM_TAG, sPullTimeoutNs, sCoolDownNs, sPullReturnValue,
+ sPullLatencyMillis, sAtomsPerPull);
+ Thread.sleep(SHORT_SLEEP_MILLIS);
+ StatsLog.logStart(APP_BREADCRUMB_LABEL);
+ // Let the current bucket finish.
+ Thread.sleep(LONG_SLEEP_MILLIS);
+ List<Atom> data = StatsConfigUtils.getGaugeMetricDataList(statsManager, sConfigId);
+ unregisterStatsPuller(PULL_ATOM_TAG);
+ assertThat(data.size()).isEqualTo(1);
+ TestAtoms.PullCallbackAtomWrapper atomWrapper = null;
+ try {
+ atomWrapper = TestAtoms.PullCallbackAtomWrapper.parser()
+ .parseFrom(data.get(0).toByteArray());
+ } catch (Exception e) {
+ Log.e(LOG_TAG, "Failed to parse primitive atoms");
+ }
+ assertThat(atomWrapper).isNotNull();
+ assertThat(atomWrapper.hasPullCallbackAtom()).isTrue();
+ TestAtoms.PullCallbackAtom atom =
+ atomWrapper.getPullCallbackAtom();
+ assertThat(atom.getLongVal()).isEqualTo(1);
+ }
+
+ /**
+ * Tests that a failed pull is skipped.
+ */
+ @Test
+ public void testPullAtomCallbackFailure() throws Exception {
+ StatsManager statsManager = (StatsManager) mContext.getSystemService(
+ Context.STATS_MANAGER);
+ createAndAddConfigToStatsd(statsManager);
+ sPullReturnValue = StatsManager.PULL_SKIP;
+ // Add the puller.
+ registerStatsPuller(PULL_ATOM_TAG, sPullTimeoutNs, sCoolDownNs, sPullReturnValue,
+ sPullLatencyMillis, sAtomsPerPull);
+ Thread.sleep(SHORT_SLEEP_MILLIS);
+ StatsLog.logStart(APP_BREADCRUMB_LABEL);
+ // Let the current bucket finish.
+ Thread.sleep(LONG_SLEEP_MILLIS);
+ List<Atom> data = StatsConfigUtils.getGaugeMetricDataList(statsManager, sConfigId);
+ unregisterStatsPuller(PULL_ATOM_TAG);
+ assertThat(data.size()).isEqualTo(0);
+ }
+
+ /**
+ * Tests that a pull that times out is skipped.
+ */
+ @Test
+ public void testPullAtomCallbackTimeout() throws Exception {
+ StatsManager statsManager = (StatsManager) mContext.getSystemService(
+ Context.STATS_MANAGER);
+ createAndAddConfigToStatsd(statsManager);
+ // The puller will sleep for 1.5 sec.
+ sPullLatencyMillis = 1_500;
+ // 1 second timeout
+ sPullTimeoutNs = 1_000_000_000;
+
+ // Add the puller.
+ registerStatsPuller(PULL_ATOM_TAG, sPullTimeoutNs, sCoolDownNs, sPullReturnValue,
+ sPullLatencyMillis, sAtomsPerPull);
+ Thread.sleep(SHORT_SLEEP_MILLIS);
+ StatsLog.logStart(APP_BREADCRUMB_LABEL);
+ // Let the current bucket finish and the pull timeout.
+ Thread.sleep(sPullLatencyMillis * 2);
+ List<Atom> data = StatsConfigUtils.getGaugeMetricDataList(statsManager, sConfigId);
+ unregisterStatsPuller(PULL_ATOM_TAG);
+ assertThat(data.size()).isEqualTo(0);
+ }
+
+ /**
+ * Tests that 2 pulls in quick succession use the cache instead of pulling again.
+ */
+ @Test
+ public void testPullAtomCallbackCache() throws Exception {
+ StatsManager statsManager = (StatsManager) mContext.getSystemService(
+ Context.STATS_MANAGER);
+ createAndAddConfigToStatsd(statsManager);
+
+ // Set the cooldown to 10 seconds
+ sCoolDownNs = 10_000_000_000L;
+ // Add the puller.
+ registerStatsPuller(PULL_ATOM_TAG, sPullTimeoutNs, sCoolDownNs, sPullReturnValue,
+ sPullLatencyMillis, sAtomsPerPull);
+
+ Thread.sleep(SHORT_SLEEP_MILLIS);
+ StatsLog.logStart(APP_BREADCRUMB_LABEL);
+ // Pull from cache.
+ StatsLog.logStart(APP_BREADCRUMB_LABEL);
+ Thread.sleep(LONG_SLEEP_MILLIS);
+ List<Atom> data = StatsConfigUtils.getGaugeMetricDataList(statsManager, sConfigId);
+ unregisterStatsPuller(PULL_ATOM_TAG);
+ assertThat(data.size()).isEqualTo(2);
+ for (int i = 0; i < data.size(); i++) {
+ TestAtoms.PullCallbackAtomWrapper atomWrapper = null;
+ try {
+ atomWrapper = TestAtoms.PullCallbackAtomWrapper.parser()
+ .parseFrom(data.get(i).toByteArray());
+ } catch (Exception e) {
+ Log.e(LOG_TAG, "Failed to parse primitive atoms");
+ }
+ assertThat(atomWrapper).isNotNull();
+ assertThat(atomWrapper.hasPullCallbackAtom()).isTrue();
+ TestAtoms.PullCallbackAtom atom =
+ atomWrapper.getPullCallbackAtom();
+ assertThat(atom.getLongVal()).isEqualTo(1);
+ }
+ }
+
+ /**
+ * Tests that a pull that returns 1000 stats events works properly.
+ */
+ @Test
+ public void testPullAtomCallbackStress() throws Exception {
+ StatsManager statsManager = (StatsManager) mContext.getSystemService(
+ Context.STATS_MANAGER);
+ // Upload a config that captures that pulled atom.
+ createAndAddConfigToStatsd(statsManager);
+ sAtomsPerPull = 1000;
+ // Add the puller.
+ registerStatsPuller(PULL_ATOM_TAG, sPullTimeoutNs, sCoolDownNs, sPullReturnValue,
+ sPullLatencyMillis, sAtomsPerPull);
+
+ Thread.sleep(SHORT_SLEEP_MILLIS);
+ StatsLog.logStart(APP_BREADCRUMB_LABEL);
+ // Let the current bucket finish.
+ Thread.sleep(LONG_SLEEP_MILLIS);
+ List<Atom> data = StatsConfigUtils.getGaugeMetricDataList(statsManager, sConfigId);
+ statsManager.unregisterPullAtomCallback(PULL_ATOM_TAG);
+ assertThat(data.size()).isEqualTo(sAtomsPerPull);
+
+ for (int i = 0; i < data.size(); i++) {
+ TestAtoms.PullCallbackAtomWrapper atomWrapper = null;
+ try {
+ atomWrapper = TestAtoms.PullCallbackAtomWrapper.parser()
+ .parseFrom(data.get(i).toByteArray());
+ } catch (Exception e) {
+ Log.e(LOG_TAG, "Failed to parse primitive atoms");
+ }
+ assertThat(atomWrapper).isNotNull();
+ assertThat(atomWrapper.hasPullCallbackAtom()).isTrue();
+ TestAtoms.PullCallbackAtom atom =
+ atomWrapper.getPullCallbackAtom();
+ assertThat(atom.getLongVal()).isEqualTo(1);
+ }
+ }
+
+ private void createAndAddConfigToStatsd(StatsManager statsManager) throws Exception {
+ sConfigId = System.currentTimeMillis();
+ long triggerMatcherId = sConfigId + 10;
+ long pullerMatcherId = sConfigId + 11;
+ long metricId = sConfigId + 100;
+ StatsdConfig config = StatsConfigUtils.getSimpleTestConfig(sConfigId)
+ .addAtomMatcher(
+ StatsConfigUtils.getAppBreadcrumbMatcher(triggerMatcherId,
+ APP_BREADCRUMB_LABEL))
+ .addAtomMatcher(AtomMatcher.newBuilder()
+ .setId(pullerMatcherId)
+ .setSimpleAtomMatcher(SimpleAtomMatcher.newBuilder()
+ .setAtomId(PULL_ATOM_TAG))
+ )
+ .addGaugeMetric(GaugeMetric.newBuilder()
+ .setId(metricId)
+ .setWhat(pullerMatcherId)
+ .setTriggerEvent(triggerMatcherId)
+ .setGaugeFieldsFilter(FieldFilter.newBuilder().setIncludeAll(true))
+ .setBucket(TimeUnit.CTS)
+ .setSamplingType(GaugeMetric.SamplingType.FIRST_N_SAMPLES)
+ .setMaxNumGaugeAtomsPerBucket(1000)
+ )
+ .build();
+ statsManager.addConfig(sConfigId, config.toByteArray());
+ assertThat(StatsConfigUtils.verifyValidConfigExists(statsManager, sConfigId)).isTrue();
+ }
+
+ private native void registerStatsPuller(int atomTag, long timeoutNs, long coolDownNs,
+ int pullReturnVal, long latencyMillis, int atomPerPull);
+
+ private native void unregisterStatsPuller(int atomTag);
+}
+
diff --git a/api/current.txt b/api/current.txt
index c09dad40e532..06271c89ec91 100644
--- a/api/current.txt
+++ b/api/current.txt
@@ -10173,6 +10173,7 @@ package android.content {
field public static final String STORAGE_STATS_SERVICE = "storagestats";
field public static final String SYSTEM_HEALTH_SERVICE = "systemhealth";
field public static final String TELECOM_SERVICE = "telecom";
+ field public static final String TELEPHONY_IMS_SERVICE = "telephony_ims";
field public static final String TELEPHONY_SERVICE = "phone";
field public static final String TELEPHONY_SUBSCRIPTION_SERVICE = "telephony_subscription_service";
field public static final String TEXT_CLASSIFICATION_SERVICE = "textclassification";
@@ -12016,6 +12017,7 @@ package android.content.pm {
field public static final String FEATURE_CAMERA_CAPABILITY_MANUAL_POST_PROCESSING = "android.hardware.camera.capability.manual_post_processing";
field public static final String FEATURE_CAMERA_CAPABILITY_MANUAL_SENSOR = "android.hardware.camera.capability.manual_sensor";
field public static final String FEATURE_CAMERA_CAPABILITY_RAW = "android.hardware.camera.capability.raw";
+ field public static final String FEATURE_CAMERA_CONCURRENT = "android.hardware.camera.concurrent";
field public static final String FEATURE_CAMERA_EXTERNAL = "android.hardware.camera.external";
field public static final String FEATURE_CAMERA_FLASH = "android.hardware.camera.flash";
field public static final String FEATURE_CAMERA_FRONT = "android.hardware.camera.front";
@@ -12073,6 +12075,7 @@ package android.content.pm {
field public static final String FEATURE_SENSOR_GYROSCOPE = "android.hardware.sensor.gyroscope";
field public static final String FEATURE_SENSOR_HEART_RATE = "android.hardware.sensor.heartrate";
field public static final String FEATURE_SENSOR_HEART_RATE_ECG = "android.hardware.sensor.heartrate.ecg";
+ field public static final String FEATURE_SENSOR_HINGE_ANGLE = "android.hardware.sensor.hinge_angle";
field public static final String FEATURE_SENSOR_LIGHT = "android.hardware.sensor.light";
field public static final String FEATURE_SENSOR_PROXIMITY = "android.hardware.sensor.proximity";
field public static final String FEATURE_SENSOR_RELATIVE_HUMIDITY = "android.hardware.sensor.relative_humidity";
@@ -14246,9 +14249,12 @@ package android.graphics {
method public int getWidth();
method public boolean isHardwareAccelerated();
method public boolean isOpaque();
- method public boolean quickReject(@NonNull android.graphics.RectF, @NonNull android.graphics.Canvas.EdgeType);
- method public boolean quickReject(@NonNull android.graphics.Path, @NonNull android.graphics.Canvas.EdgeType);
- method public boolean quickReject(float, float, float, float, @NonNull android.graphics.Canvas.EdgeType);
+ method @Deprecated public boolean quickReject(@NonNull android.graphics.RectF, @NonNull android.graphics.Canvas.EdgeType);
+ method public boolean quickReject(@NonNull android.graphics.RectF);
+ method @Deprecated public boolean quickReject(@NonNull android.graphics.Path, @NonNull android.graphics.Canvas.EdgeType);
+ method public boolean quickReject(@NonNull android.graphics.Path);
+ method @Deprecated public boolean quickReject(float, float, float, float, @NonNull android.graphics.Canvas.EdgeType);
+ method public boolean quickReject(float, float, float, float);
method public void restore();
method public void restoreToCount(int);
method public void rotate(float);
@@ -14273,9 +14279,9 @@ package android.graphics {
field public static final int ALL_SAVE_FLAG = 31; // 0x1f
}
- public enum Canvas.EdgeType {
- enum_constant public static final android.graphics.Canvas.EdgeType AA;
- enum_constant public static final android.graphics.Canvas.EdgeType BW;
+ @Deprecated public enum Canvas.EdgeType {
+ enum_constant @Deprecated public static final android.graphics.Canvas.EdgeType AA;
+ enum_constant @Deprecated public static final android.graphics.Canvas.EdgeType BW;
}
public enum Canvas.VertexMode {
@@ -17172,6 +17178,7 @@ package android.hardware.camera2 {
field @NonNull public static final android.hardware.camera2.CameraCharacteristics.Key<java.lang.Float> SCALER_AVAILABLE_MAX_DIGITAL_ZOOM;
field @NonNull public static final android.hardware.camera2.CameraCharacteristics.Key<int[]> SCALER_AVAILABLE_ROTATE_AND_CROP_MODES;
field @NonNull public static final android.hardware.camera2.CameraCharacteristics.Key<java.lang.Integer> SCALER_CROPPING_TYPE;
+ field @NonNull public static final android.hardware.camera2.CameraCharacteristics.Key<android.hardware.camera2.params.MandatoryStreamCombination[]> SCALER_MANDATORY_CONCURRENT_STREAM_COMBINATIONS;
field @NonNull public static final android.hardware.camera2.CameraCharacteristics.Key<android.hardware.camera2.params.MandatoryStreamCombination[]> SCALER_MANDATORY_STREAM_COMBINATIONS;
field @NonNull public static final android.hardware.camera2.CameraCharacteristics.Key<android.hardware.camera2.params.StreamConfigurationMap> SCALER_STREAM_CONFIGURATION_MAP;
field @NonNull public static final android.hardware.camera2.CameraCharacteristics.Key<int[]> SENSOR_AVAILABLE_TEST_PATTERN_MODES;
@@ -17261,6 +17268,8 @@ package android.hardware.camera2 {
public final class CameraManager {
method @NonNull public android.hardware.camera2.CameraCharacteristics getCameraCharacteristics(@NonNull String) throws android.hardware.camera2.CameraAccessException;
method @NonNull public String[] getCameraIdList() throws android.hardware.camera2.CameraAccessException;
+ method @NonNull public java.util.Set<java.util.Set<java.lang.String>> getConcurrentStreamingCameraIds() throws android.hardware.camera2.CameraAccessException;
+ method @RequiresPermission(android.Manifest.permission.CAMERA) public boolean isConcurrentSessionConfigurationSupported(@NonNull java.util.Map<java.lang.String,android.hardware.camera2.params.SessionConfiguration>) throws android.hardware.camera2.CameraAccessException;
method @RequiresPermission(android.Manifest.permission.CAMERA) public void openCamera(@NonNull String, @NonNull android.hardware.camera2.CameraDevice.StateCallback, @Nullable android.os.Handler) throws android.hardware.camera2.CameraAccessException;
method @RequiresPermission(android.Manifest.permission.CAMERA) public void openCamera(@NonNull String, @NonNull java.util.concurrent.Executor, @NonNull android.hardware.camera2.CameraDevice.StateCallback) throws android.hardware.camera2.CameraAccessException;
method public void registerAvailabilityCallback(@NonNull android.hardware.camera2.CameraManager.AvailabilityCallback, @Nullable android.os.Handler);
@@ -29194,6 +29203,7 @@ package android.media.tv {
method public void onAppPrivateCommand(@NonNull String, android.os.Bundle);
method public abstract void onRelease();
method public abstract void onStartRecording(@Nullable android.net.Uri);
+ method public void onStartRecording(@Nullable android.net.Uri, @NonNull android.os.Bundle);
method public abstract void onStopRecording();
method public abstract void onTune(android.net.Uri);
method public void onTune(android.net.Uri, android.os.Bundle);
@@ -29244,6 +29254,7 @@ package android.media.tv {
method public void release();
method public void sendAppPrivateCommand(@NonNull String, android.os.Bundle);
method public void startRecording(@Nullable android.net.Uri);
+ method public void startRecording(@Nullable android.net.Uri, @NonNull android.os.Bundle);
method public void stopRecording();
method public void tune(String, android.net.Uri);
method public void tune(String, android.net.Uri, android.os.Bundle);
@@ -36477,7 +36488,7 @@ package android.os {
method public boolean isSustainedPerformanceModeSupported();
method public boolean isWakeLockLevelSupported(int);
method public android.os.PowerManager.WakeLock newWakeLock(int, String);
- method public void reboot(@Nullable String);
+ method @RequiresPermission(android.Manifest.permission.REBOOT) public void reboot(@Nullable String);
method public void removeThermalStatusListener(@NonNull android.os.PowerManager.OnThermalStatusChangedListener);
field public static final int ACQUIRE_CAUSES_WAKEUP = 268435456; // 0x10000000
field public static final String ACTION_DEVICE_IDLE_MODE_CHANGED = "android.os.action.DEVICE_IDLE_MODE_CHANGED";
@@ -36919,6 +36930,7 @@ package android.os {
method public static android.os.VibrationEffect createWaveform(long[], int);
method public static android.os.VibrationEffect createWaveform(long[], int[], int);
method public int describeContents();
+ method @NonNull public static android.os.VibrationEffect.Composition startComposition();
field @NonNull public static final android.os.Parcelable.Creator<android.os.VibrationEffect> CREATOR;
field public static final int DEFAULT_AMPLITUDE = -1; // 0xffffffff
field public static final int EFFECT_CLICK = 0; // 0x0
@@ -36927,7 +36939,26 @@ package android.os {
field public static final int EFFECT_TICK = 2; // 0x2
}
+ public static class VibrationEffect.Composition {
+ ctor public VibrationEffect.Composition();
+ method @Nullable public android.os.VibrationEffect.Composition addPrimitive(int);
+ method @Nullable public android.os.VibrationEffect.Composition addPrimitive(int, @FloatRange(from=0.0f, to=1.0f) float);
+ method @Nullable public android.os.VibrationEffect.Composition addPrimitive(int, @FloatRange(from=0.0f, to=1.0f) float, @IntRange(from=0) int);
+ method @NonNull public android.os.VibrationEffect compose();
+ field public static final int PRIMITIVE_CLICK = 1; // 0x1
+ field public static final int PRIMITIVE_LIGHT_TICK = 7; // 0x7
+ field public static final int PRIMITIVE_QUICK_FALL = 6; // 0x6
+ field public static final int PRIMITIVE_QUICK_RISE = 4; // 0x4
+ field public static final int PRIMITIVE_SLOW_RISE = 5; // 0x5
+ field public static final int PRIMITIVE_SPIN = 3; // 0x3
+ field public static final int PRIMITIVE_THUD = 2; // 0x2
+ }
+
public abstract class Vibrator {
+ method @Nullable public Boolean areAllEffectsSupported(@NonNull int...);
+ method public boolean areAllPrimitivesSupported(@NonNull int...);
+ method @Nullable public boolean[] areEffectsSupported(@NonNull int...);
+ method @NonNull public boolean[] arePrimitivesSupported(@NonNull int...);
method @RequiresPermission(android.Manifest.permission.VIBRATE) public abstract void cancel();
method public abstract boolean hasAmplitudeControl();
method public abstract boolean hasVibrator();
@@ -39868,6 +39899,7 @@ package android.provider {
field public static final int MATCH_ONLY = 3; // 0x3
field public static final String MEDIA_IGNORE_FILENAME = ".nomedia";
field public static final String MEDIA_SCANNER_VOLUME = "volume";
+ field public static final String META_DATA_REVIEW_GALLERY_PREWARM_SERVICE = "android.media.review_gallery_prewarm_service";
field public static final String META_DATA_STILL_IMAGE_CAMERA_PREWARM_SERVICE = "android.media.still_image_camera_preview_service";
field public static final String QUERY_ARG_MATCH_FAVORITE = "android:query-arg-match-favorite";
field public static final String QUERY_ARG_MATCH_PENDING = "android:query-arg-match-pending";
@@ -43861,11 +43893,13 @@ package android.service.quicksettings {
method public android.graphics.drawable.Icon getIcon();
method public CharSequence getLabel();
method public int getState();
+ method @Nullable public CharSequence getStateDescription();
method @Nullable public CharSequence getSubtitle();
method public void setContentDescription(CharSequence);
method public void setIcon(android.graphics.drawable.Icon);
method public void setLabel(CharSequence);
method public void setState(int);
+ method public void setStateDescription(@Nullable CharSequence);
method public void setSubtitle(@Nullable CharSequence);
method public void updateTile();
method public void writeToParcel(android.os.Parcel, int);
@@ -45211,6 +45245,7 @@ package android.telecom {
method public void registerCallback(android.telecom.Call.Callback);
method public void registerCallback(android.telecom.Call.Callback, android.os.Handler);
method public void reject(boolean, String);
+ method public void reject(int);
method public void removeExtras(java.util.List<java.lang.String>);
method public void removeExtras(java.lang.String...);
method public void respondToRttRequest(int, boolean);
@@ -45226,6 +45261,8 @@ package android.telecom {
field public static final String EXTRA_LAST_EMERGENCY_CALLBACK_TIME_MILLIS = "android.telecom.extra.LAST_EMERGENCY_CALLBACK_TIME_MILLIS";
field public static final String EXTRA_SILENT_RINGING_REQUESTED = "android.telecom.extra.SILENT_RINGING_REQUESTED";
field public static final String EXTRA_SUGGESTED_PHONE_ACCOUNTS = "android.telecom.extra.SUGGESTED_PHONE_ACCOUNTS";
+ field public static final int REJECT_REASON_DECLINED = 1; // 0x1
+ field public static final int REJECT_REASON_UNWANTED = 2; // 0x2
field public static final int STATE_ACTIVE = 4; // 0x4
field public static final int STATE_AUDIO_PROCESSING = 12; // 0xc
field public static final int STATE_CONNECTING = 9; // 0x9
@@ -45493,6 +45530,7 @@ package android.telecom {
method public void onPostDialContinue(boolean);
method public void onPullExternalCall();
method public void onReject();
+ method public void onReject(int);
method public void onReject(String);
method public void onSeparate();
method public void onShowIncomingCallUi();
@@ -48252,13 +48290,20 @@ package android.telephony.ims {
public final class ImsException extends java.lang.Exception {
method public int getCode();
+ field public static final int CODE_ERROR_INVALID_SUBSCRIPTION = 3; // 0x3
field public static final int CODE_ERROR_SERVICE_UNAVAILABLE = 1; // 0x1
field public static final int CODE_ERROR_UNSPECIFIED = 0; // 0x0
field public static final int CODE_ERROR_UNSUPPORTED_OPERATION = 2; // 0x2
}
+ public class ImsManager {
+ method @NonNull public android.telephony.ims.ImsMmTelManager getImsMmTelManager(int);
+ field public static final String ACTION_WFC_IMS_REGISTRATION_ERROR = "android.telephony.ims.action.WFC_IMS_REGISTRATION_ERROR";
+ field public static final String EXTRA_WFC_REGISTRATION_FAILURE_MESSAGE = "android.telephony.ims.extra.WFC_REGISTRATION_FAILURE_MESSAGE";
+ field public static final String EXTRA_WFC_REGISTRATION_FAILURE_TITLE = "android.telephony.ims.extra.WFC_REGISTRATION_FAILURE_TITLE";
+ }
+
public class ImsMmTelManager implements android.telephony.ims.RegistrationManager {
- method @NonNull @RequiresPermission(anyOf={"android.permission.READ_PRIVILEGED_PHONE_STATE", android.Manifest.permission.READ_PRECISE_PHONE_STATE}) public static android.telephony.ims.ImsMmTelManager createForSubscriptionId(int);
method @RequiresPermission("android.permission.READ_PRIVILEGED_PHONE_STATE") public void getRegistrationState(@NonNull java.util.concurrent.Executor, @NonNull java.util.function.Consumer<java.lang.Integer>);
method @RequiresPermission(anyOf={"android.permission.READ_PRIVILEGED_PHONE_STATE", android.Manifest.permission.READ_PRECISE_PHONE_STATE}) public void getRegistrationTransportType(@NonNull java.util.concurrent.Executor, @NonNull java.util.function.Consumer<java.lang.Integer>);
method @RequiresPermission(anyOf={"android.permission.READ_PRIVILEGED_PHONE_STATE", android.Manifest.permission.READ_PRECISE_PHONE_STATE}) public int getVoWiFiModeSetting();
diff --git a/api/system-current.txt b/api/system-current.txt
index d1ff478a8590..23beedbd1b29 100755
--- a/api/system-current.txt
+++ b/api/system-current.txt
@@ -1796,7 +1796,6 @@ package android.content {
field public static final String STATUS_BAR_SERVICE = "statusbar";
field public static final String SYSTEM_CONFIG_SERVICE = "system_config";
field public static final String SYSTEM_UPDATE_SERVICE = "system_update";
- field public static final String TELEPHONY_IMS_SERVICE = "telephony_ims";
field public static final String TELEPHONY_REGISTRY_SERVICE = "telephony_registry";
field public static final String TETHERING_SERVICE = "tethering";
field public static final String VR_SERVICE = "vrmanager";
@@ -2520,7 +2519,7 @@ package android.hardware.display {
method @Nullable public android.hardware.display.BrightnessCorrection getCorrectionByPackageName(@NonNull String);
method public android.util.Pair<float[],float[]> getCurve();
method public float getShortTermModelLowerLuxMultiplier();
- method public long getShortTermModelTimeout();
+ method public long getShortTermModelTimeoutMillis();
method public float getShortTermModelUpperLuxMultiplier();
method public boolean shouldCollectColorSamples();
method public void writeToParcel(android.os.Parcel, int);
@@ -2537,7 +2536,7 @@ package android.hardware.display {
method public int getMaxCorrectionsByPackageName();
method @NonNull public android.hardware.display.BrightnessConfiguration.Builder setDescription(@Nullable String);
method @NonNull public android.hardware.display.BrightnessConfiguration.Builder setShortTermModelLowerLuxMultiplier(@FloatRange(from=0.0f) float);
- method @NonNull public android.hardware.display.BrightnessConfiguration.Builder setShortTermModelTimeout(long);
+ method @NonNull public android.hardware.display.BrightnessConfiguration.Builder setShortTermModelTimeoutMillis(long);
method @NonNull public android.hardware.display.BrightnessConfiguration.Builder setShortTermModelUpperLuxMultiplier(@FloatRange(from=0.0f) float);
method @NonNull public android.hardware.display.BrightnessConfiguration.Builder setShouldCollectColorSamples(boolean);
}
@@ -7572,12 +7571,12 @@ package android.net.wifi {
}
public class WifiInfo implements android.os.Parcelable {
- method @Nullable public String getAppPackageName();
- method public double getRxSuccessRate();
+ method public double getLostTxPacketsPerSecond();
+ method @Nullable public String getRequestingPackageName();
+ method public double getRetriedTxPacketsPerSecond();
method public int getScore();
- method public double getTxBadRate();
- method public double getTxRetriesRate();
- method public double getTxSuccessRate();
+ method public double getSuccessfulRxPacketsPerSecond();
+ method public double getSuccessfulTxPacketsPerSecond();
method public boolean isEphemeral();
method public boolean isOsuAp();
method public boolean isPasspointAp();
@@ -7605,19 +7604,17 @@ package android.net.wifi {
method @RequiresPermission(anyOf={android.Manifest.permission.NETWORK_SETTINGS, android.Manifest.permission.NETWORK_SETUP_WIZARD, android.Manifest.permission.NETWORK_STACK}) public void connect(int, @Nullable android.net.wifi.WifiManager.ActionListener);
method @Deprecated @RequiresPermission(anyOf={android.Manifest.permission.NETWORK_SETTINGS, android.Manifest.permission.NETWORK_SETUP_WIZARD, android.Manifest.permission.NETWORK_STACK}) public void disable(int, @Nullable android.net.wifi.WifiManager.ActionListener);
method @RequiresPermission(anyOf={android.Manifest.permission.NETWORK_SETTINGS, android.Manifest.permission.NETWORK_STACK}) public void disableEphemeralNetwork(@NonNull String);
- method @RequiresPermission(android.Manifest.permission.NETWORK_SETTINGS) public void enableVerboseLogging(int);
method @RequiresPermission(android.Manifest.permission.CONNECTIVITY_INTERNAL) public void factoryReset();
method @RequiresPermission(anyOf={android.Manifest.permission.NETWORK_SETTINGS, android.Manifest.permission.NETWORK_SETUP_WIZARD, android.Manifest.permission.NETWORK_STACK}) public void forget(int, @Nullable android.net.wifi.WifiManager.ActionListener);
method @NonNull @RequiresPermission(anyOf={android.Manifest.permission.NETWORK_SETTINGS, android.Manifest.permission.NETWORK_SETUP_WIZARD}) public java.util.List<android.util.Pair<android.net.wifi.WifiConfiguration,java.util.Map<java.lang.Integer,java.util.List<android.net.wifi.ScanResult>>>> getAllMatchingWifiConfigs(@NonNull java.util.List<android.net.wifi.ScanResult>);
method @Nullable @RequiresPermission(android.Manifest.permission.CONNECTIVITY_INTERNAL) public String getCountryCode();
- method @Nullable @RequiresPermission(android.Manifest.permission.ACCESS_WIFI_STATE) public android.net.Network getCurrentNetwork();
+ method @Nullable @RequiresPermission(anyOf={android.Manifest.permission.NETWORK_SETTINGS, android.Manifest.permission.NETWORK_SETUP_WIZARD}) public android.net.Network getCurrentNetwork();
method @NonNull @RequiresPermission(android.Manifest.permission.NETWORK_SETTINGS) public String[] getFactoryMacAddresses();
method @NonNull @RequiresPermission(anyOf={android.Manifest.permission.NETWORK_SETTINGS, android.Manifest.permission.NETWORK_SETUP_WIZARD}) public java.util.Map<android.net.wifi.hotspot2.OsuProvider,java.util.List<android.net.wifi.ScanResult>> getMatchingOsuProviders(@Nullable java.util.List<android.net.wifi.ScanResult>);
method @NonNull @RequiresPermission(anyOf={android.Manifest.permission.NETWORK_SETTINGS, android.Manifest.permission.NETWORK_SETUP_WIZARD}) public java.util.Map<android.net.wifi.hotspot2.OsuProvider,android.net.wifi.hotspot2.PasspointConfiguration> getMatchingPasspointConfigsForOsuProviders(@NonNull java.util.Set<android.net.wifi.hotspot2.OsuProvider>);
method @NonNull @RequiresPermission(allOf={android.Manifest.permission.ACCESS_FINE_LOCATION, android.Manifest.permission.ACCESS_WIFI_STATE}) public java.util.Map<android.net.wifi.WifiNetworkSuggestion,java.util.List<android.net.wifi.ScanResult>> getMatchingScanResults(@NonNull java.util.List<android.net.wifi.WifiNetworkSuggestion>, @Nullable java.util.List<android.net.wifi.ScanResult>);
method @RequiresPermission(allOf={android.Manifest.permission.ACCESS_FINE_LOCATION, android.Manifest.permission.ACCESS_WIFI_STATE, android.Manifest.permission.READ_WIFI_CREDENTIAL}) public java.util.List<android.net.wifi.WifiConfiguration> getPrivilegedConfiguredNetworks();
method @NonNull @RequiresPermission(android.Manifest.permission.ACCESS_WIFI_STATE) public android.net.wifi.SoftApConfiguration getSoftApConfiguration();
- method public int getVerboseLoggingLevel();
method @RequiresPermission(android.Manifest.permission.ACCESS_WIFI_STATE) public void getWifiActivityEnergyInfoAsync(@NonNull java.util.concurrent.Executor, @NonNull android.net.wifi.WifiManager.OnWifiActivityEnergyInfoListener);
method @Deprecated @Nullable @RequiresPermission(android.Manifest.permission.ACCESS_WIFI_STATE) public android.net.wifi.WifiConfiguration getWifiApConfiguration();
method @RequiresPermission(android.Manifest.permission.ACCESS_WIFI_STATE) public int getWifiApState();
@@ -7627,6 +7624,7 @@ package android.net.wifi {
method @Deprecated public boolean isDeviceToDeviceRttSupported();
method @RequiresPermission(android.Manifest.permission.NETWORK_SETTINGS) public boolean isDualModeSupported();
method public boolean isPortableHotspotSupported();
+ method public boolean isVerboseLoggingEnabled();
method @RequiresPermission(android.Manifest.permission.ACCESS_WIFI_STATE) public boolean isWifiApEnabled();
method public boolean isWifiScannerSupported();
method @RequiresPermission(android.Manifest.permission.NETWORK_SETTINGS) public void registerNetworkRequestMatchCallback(@NonNull java.util.concurrent.Executor, @NonNull android.net.wifi.WifiManager.NetworkRequestMatchCallback);
@@ -7635,7 +7633,7 @@ package android.net.wifi {
method @RequiresPermission(android.Manifest.permission.WIFI_UPDATE_USABILITY_STATS_SCORE) public void removeOnWifiUsabilityStatsListener(@NonNull android.net.wifi.WifiManager.OnWifiUsabilityStatsListener);
method @RequiresPermission(android.Manifest.permission.NETWORK_SETTINGS) public void restoreBackupData(@NonNull byte[]);
method @Nullable @RequiresPermission(android.Manifest.permission.NETWORK_SETTINGS) public android.net.wifi.SoftApConfiguration restoreSoftApBackupData(@NonNull byte[]);
- method @Deprecated @RequiresPermission(android.Manifest.permission.NETWORK_SETTINGS) public void restoreSupplicantBackupData(@NonNull byte[], @NonNull byte[]);
+ method @RequiresPermission(android.Manifest.permission.NETWORK_SETTINGS) public void restoreSupplicantBackupData(@NonNull byte[], @NonNull byte[]);
method @NonNull @RequiresPermission(android.Manifest.permission.NETWORK_SETTINGS) public byte[] retrieveBackupData();
method @NonNull @RequiresPermission(android.Manifest.permission.NETWORK_SETTINGS) public byte[] retrieveSoftApBackupData();
method @RequiresPermission(anyOf={android.Manifest.permission.NETWORK_SETTINGS, android.Manifest.permission.NETWORK_SETUP_WIZARD, android.Manifest.permission.NETWORK_STACK}) public void save(@NonNull android.net.wifi.WifiConfiguration, @Nullable android.net.wifi.WifiManager.ActionListener);
@@ -7643,6 +7641,7 @@ package android.net.wifi {
method @RequiresPermission(android.Manifest.permission.NETWORK_SETTINGS) public void setMacRandomizationSettingPasspointEnabled(@NonNull String, boolean);
method @RequiresPermission(android.Manifest.permission.NETWORK_SETTINGS) public void setMeteredOverridePasspoint(@NonNull String, int);
method @RequiresPermission(android.Manifest.permission.NETWORK_SETTINGS) public boolean setSoftApConfiguration(@NonNull android.net.wifi.SoftApConfiguration);
+ method @RequiresPermission(android.Manifest.permission.NETWORK_SETTINGS) public void setVerboseLoggingEnabled(boolean);
method @Deprecated @RequiresPermission(android.Manifest.permission.CHANGE_WIFI_STATE) public boolean setWifiApConfiguration(android.net.wifi.WifiConfiguration);
method @RequiresPermission(android.Manifest.permission.WIFI_UPDATE_USABILITY_STATS_SCORE) public boolean setWifiConnectedNetworkScorer(@NonNull java.util.concurrent.Executor, @NonNull android.net.wifi.WifiManager.WifiConnectedNetworkScorer);
method @RequiresPermission(anyOf={android.Manifest.permission.NETWORK_SETTINGS, android.Manifest.permission.NETWORK_SETUP_WIZARD}) public void startEasyConnectAsConfiguratorInitiator(@NonNull String, int, int, @NonNull java.util.concurrent.Executor, @NonNull android.net.wifi.EasyConnectStatusCallback);
@@ -8368,7 +8367,7 @@ package android.os {
method @NonNull public android.os.BatterySaverPolicyConfig.Builder setLocationMode(int);
}
- public class BatteryStatsManager {
+ public final class BatteryStatsManager {
method @NonNull @RequiresPermission(android.Manifest.permission.UPDATE_DEVICE_STATS) public android.os.connectivity.CellularBatteryStats getCellularBatteryStats();
method @NonNull @RequiresPermission(android.Manifest.permission.UPDATE_DEVICE_STATS) public android.os.connectivity.WifiBatteryStats getWifiBatteryStats();
method @RequiresPermission(android.Manifest.permission.UPDATE_DEVICE_STATS) public void reportFullWifiLockAcquiredFromSource(@NonNull android.os.WorkSource);
@@ -8607,9 +8606,6 @@ package android.os {
field public static final int STATUS_SUCCESS = 0; // 0x0
}
- @IntDef(prefix={"STATUS_"}, value={android.os.HwParcel.STATUS_SUCCESS}) @java.lang.annotation.Retention(java.lang.annotation.RetentionPolicy.SOURCE) public static @interface HwParcel.Status {
- }
-
public interface IHwBinder {
method public boolean linkToDeath(android.os.IHwBinder.DeathRecipient, long);
method public android.os.IHwInterface queryLocalInterface(String);
@@ -8860,7 +8856,7 @@ package android.os {
ctor public TelephonyServiceManager.ServiceNotFoundException(@NonNull String);
}
- public final class TelephonyServiceManager.ServiceRegisterer {
+ public static final class TelephonyServiceManager.ServiceRegisterer {
method @Nullable public android.os.IBinder get();
method @NonNull public android.os.IBinder getOrThrow() throws android.os.TelephonyServiceManager.ServiceNotFoundException;
method public void register(@NonNull android.os.IBinder);
@@ -9046,23 +9042,15 @@ package android.os.connectivity {
}
public final class WifiActivityEnergyInfo implements android.os.Parcelable {
- ctor public WifiActivityEnergyInfo(long, int, long, long, long, long);
+ ctor public WifiActivityEnergyInfo(long, int, @IntRange(from=0) long, @IntRange(from=0) long, @IntRange(from=0) long, @IntRange(from=0) long);
method public int describeContents();
- method public long getControllerEnergyUsedMicroJoules();
- method public long getControllerIdleDurationMillis();
- method public long getControllerRxDurationMillis();
- method public long getControllerScanDurationMillis();
- method public long getControllerTxDurationMillis();
+ method @IntRange(from=0) public long getControllerEnergyUsedMicroJoules();
+ method @IntRange(from=0) public long getControllerIdleDurationMillis();
+ method @IntRange(from=0) public long getControllerRxDurationMillis();
+ method @IntRange(from=0) public long getControllerScanDurationMillis();
+ method @IntRange(from=0) public long getControllerTxDurationMillis();
method public int getStackState();
method public long getTimeSinceBootMillis();
- method public boolean isValid();
- method public void setControllerEnergyUsedMicroJoules(long);
- method public void setControllerIdleDurationMillis(long);
- method public void setControllerRxDurationMillis(long);
- method public void setControllerScanDurationMillis(long);
- method public void setControllerTxDurationMillis(long);
- method public void setStackState(int);
- method public void setTimeSinceBootMillis(long);
method public void writeToParcel(@NonNull android.os.Parcel, int);
field @NonNull public static final android.os.Parcelable.Creator<android.os.connectivity.WifiActivityEnergyInfo> CREATOR;
field public static final int STACK_STATE_INVALID = 0; // 0x0
@@ -9715,6 +9703,7 @@ package android.provider {
field public static final String CMAS_SEVERITY = "cmas_severity";
field public static final String CMAS_URGENCY = "cmas_urgency";
field @NonNull public static final android.net.Uri CONTENT_URI;
+ field public static final String DATA_CODING_SCHEME = "dcs";
field public static final String DEFAULT_SORT_ORDER = "date DESC";
field public static final String DELIVERY_TIME = "date";
field public static final String ETWS_WARNING_TYPE = "etws_warning_type";
@@ -9722,9 +9711,11 @@ package android.provider {
field public static final String GEOMETRIES = "geometries";
field public static final String LAC = "lac";
field public static final String LANGUAGE_CODE = "language";
+ field public static final String LOCATION_CHECK_TIME = "location_check_time";
field public static final String MAXIMUM_WAIT_TIME = "maximum_wait_time";
field public static final String MESSAGE_BODY = "body";
field public static final String MESSAGE_BROADCASTED = "message_broadcasted";
+ field public static final String MESSAGE_DISPLAYED = "message_displayed";
field public static final String MESSAGE_FORMAT = "format";
field @NonNull @RequiresPermission(android.Manifest.permission.READ_CELL_BROADCASTS) public static final android.net.Uri MESSAGE_HISTORY_URI;
field public static final String MESSAGE_PRIORITY = "priority";
@@ -10058,6 +10049,13 @@ package android.service.autofill {
method @NonNull public android.service.autofill.Dataset.Builder setInlinePresentation(@NonNull android.view.autofill.AutofillId, @Nullable android.view.autofill.AutofillValue, @Nullable java.util.regex.Pattern, @NonNull android.service.autofill.InlinePresentation);
}
+ public abstract class InlineSuggestionRenderService extends android.app.Service {
+ ctor public InlineSuggestionRenderService();
+ method @Nullable public final android.os.IBinder onBind(@NonNull android.content.Intent);
+ method @Nullable public android.view.View onRenderSuggestion(@NonNull android.service.autofill.InlinePresentation, int, int);
+ field public static final String SERVICE_INTERFACE = "android.service.autofill.InlineSuggestionRenderService";
+ }
+
}
package android.service.autofill.augmented {
@@ -11048,6 +11046,27 @@ 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(int, int, @Nullable String, int);
+ method public int describeContents();
+ method @Nullable public String getNumber();
+ method public int getReason();
+ method public int getStatus();
+ method public int getTimeoutSeconds();
+ field @NonNull public static final android.os.Parcelable.Creator<android.telephony.CallForwardingInfo> CREATOR;
+ 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 STATUS_ACTIVE = 1; // 0x1
+ field public static final int STATUS_FDN_CHECK_FAILURE = 2; // 0x2
+ field public static final int STATUS_INACTIVE = 0; // 0x0
+ field public static final int STATUS_NOT_SUPPORTED = 4; // 0x4
+ field public static final int STATUS_UNKNOWN_ERROR = 3; // 0x3
+ }
+
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);
@@ -11158,7 +11177,7 @@ package android.telephony {
}
public class CellBroadcastIntents {
- method public static void sendOrderedBroadcastForBackgroundReceivers(@NonNull android.content.Context, @Nullable android.os.UserHandle, @NonNull android.content.Intent, @Nullable String, @Nullable String, @Nullable android.content.BroadcastReceiver, @Nullable android.os.Handler, int, @Nullable String, @Nullable android.os.Bundle);
+ method public static void sendSmsCbReceivedBroadcast(@NonNull android.content.Context, @Nullable android.os.UserHandle, @NonNull android.telephony.SmsCbMessage, @Nullable android.content.BroadcastReceiver, @Nullable android.os.Handler, int, int);
}
public abstract class CellBroadcastService extends android.app.Service {
@@ -12031,11 +12050,12 @@ package android.telephony {
}
public final class SmsCbMessage implements android.os.Parcelable {
- ctor public SmsCbMessage(int, int, int, @NonNull android.telephony.SmsCbLocation, int, @Nullable String, @Nullable String, int, @Nullable android.telephony.SmsCbEtwsInfo, @Nullable android.telephony.SmsCbCmasInfo, int, @Nullable java.util.List<android.telephony.CbGeoUtils.Geometry>, long, int, int);
+ ctor public SmsCbMessage(int, int, int, @NonNull android.telephony.SmsCbLocation, int, @Nullable String, int, @Nullable String, int, @Nullable android.telephony.SmsCbEtwsInfo, @Nullable android.telephony.SmsCbCmasInfo, int, @Nullable java.util.List<android.telephony.CbGeoUtils.Geometry>, long, int, int);
method @NonNull public static android.telephony.SmsCbMessage createFromCursor(@NonNull android.database.Cursor);
method public int describeContents();
method @Nullable public android.telephony.SmsCbCmasInfo getCmasWarningInfo();
method @NonNull public android.content.ContentValues getContentValues();
+ method public int getDataCodingScheme();
method @Nullable public android.telephony.SmsCbEtwsInfo getEtwsWarningInfo();
method public int getGeographicalScope();
method @NonNull public java.util.List<android.telephony.CbGeoUtils.Geometry> getGeometries();
@@ -12178,6 +12198,8 @@ package android.telephony {
method @Nullable @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public String getAidForAppType(int);
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 @NonNull @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public android.telephony.CallForwardingInfo getCallForwarding(int);
+ method @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public int getCallWaitingStatus();
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);
@@ -12268,6 +12290,8 @@ package android.telephony {
method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public boolean setAllowedNetworkTypes(long);
method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public boolean setAlwaysAllowMmsData(boolean);
method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public void setAlwaysReportSignalStrength(boolean);
+ method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public boolean setCallForwarding(@NonNull android.telephony.CallForwardingInfo);
+ method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public boolean setCallWaitingStatus(boolean);
method @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 boolean setCdmaRoamingMode(int);
@@ -12318,6 +12342,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_ACTIVE = 1; // 0x1
+ field public static final int CALL_WAITING_STATUS_INACTIVE = 2; // 0x2
+ 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 CARD_POWER_DOWN = 0; // 0x0
field public static final int CARD_POWER_UP = 1; // 0x1
field public static final int CARD_POWER_UP_PASS_THROUGH = 2; // 0x2
@@ -13022,21 +13050,18 @@ package android.telephony.ims {
}
public class ImsManager {
- method @NonNull public android.telephony.ims.ImsMmTelManager getImsMmTelManager(int);
method @NonNull public android.telephony.ims.ImsRcsManager getImsRcsManager(int);
field public static final String ACTION_FORBIDDEN_NO_SERVICE_AUTHORIZATION = "com.android.internal.intent.action.ACTION_FORBIDDEN_NO_SERVICE_AUTHORIZATION";
- field public static final String ACTION_WFC_IMS_REGISTRATION_ERROR = "android.telephony.ims.action.WFC_IMS_REGISTRATION_ERROR";
- field public static final String EXTRA_WFC_REGISTRATION_FAILURE_MESSAGE = "android.telephony.ims.extra.WFC_REGISTRATION_FAILURE_MESSAGE";
- field public static final String EXTRA_WFC_REGISTRATION_FAILURE_TITLE = "android.telephony.ims.extra.WFC_REGISTRATION_FAILURE_TITLE";
}
public class ImsMmTelManager implements android.telephony.ims.RegistrationManager {
+ method @Deprecated @NonNull @RequiresPermission(anyOf={android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE, android.Manifest.permission.READ_PRECISE_PHONE_STATE}) public static android.telephony.ims.ImsMmTelManager createForSubscriptionId(int);
method @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public void getFeatureState(@NonNull java.util.concurrent.Executor, @NonNull java.util.function.Consumer<java.lang.Integer>) throws android.telephony.ims.ImsException;
method @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public void getRegistrationState(@NonNull java.util.concurrent.Executor, @NonNull java.util.function.Consumer<java.lang.Integer>);
method @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public int getVoWiFiRoamingModeSetting();
- method @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public boolean isAvailable(@android.telephony.ims.feature.MmTelFeature.MmTelCapabilities.MmTelCapability int, int);
- method @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public boolean isCapable(@android.telephony.ims.feature.MmTelFeature.MmTelCapabilities.MmTelCapability int, int);
- method @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public void isSupported(@android.telephony.ims.feature.MmTelFeature.MmTelCapabilities.MmTelCapability int, int, @NonNull java.util.concurrent.Executor, @NonNull java.util.function.Consumer<java.lang.Boolean>) throws android.telephony.ims.ImsException;
+ method @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public boolean isAvailable(int, int);
+ method @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public boolean isCapable(int, int);
+ method @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public void isSupported(int, int, @NonNull java.util.concurrent.Executor, @NonNull java.util.function.Consumer<java.lang.Boolean>) throws android.telephony.ims.ImsException;
method @Deprecated @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public void registerImsRegistrationCallback(@NonNull java.util.concurrent.Executor, @NonNull android.telephony.ims.ImsMmTelManager.RegistrationCallback) throws android.telephony.ims.ImsException;
method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public void setAdvancedCallingSettingEnabled(boolean);
method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public void setRttCapabilitySetting(boolean);
@@ -13300,13 +13325,13 @@ package android.telephony.ims {
public class ProvisioningManager {
method @NonNull public static android.telephony.ims.ProvisioningManager createForSubscriptionId(int);
method @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) @WorkerThread public int getProvisioningIntValue(int);
- method @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) @WorkerThread public boolean getProvisioningStatusForCapability(@android.telephony.ims.feature.MmTelFeature.MmTelCapabilities.MmTelCapability int, int);
+ method @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) @WorkerThread public boolean getProvisioningStatusForCapability(int, int);
method @Nullable @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) @WorkerThread public String getProvisioningStringValue(int);
method @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) @WorkerThread public boolean getRcsProvisioningStatusForCapability(int);
method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public void notifyRcsAutoConfigurationReceived(@NonNull byte[], boolean);
method @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public void registerProvisioningChangedCallback(@NonNull java.util.concurrent.Executor, @NonNull android.telephony.ims.ProvisioningManager.Callback) throws android.telephony.ims.ImsException;
method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) @WorkerThread public int setProvisioningIntValue(int, int);
- method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) @WorkerThread public void setProvisioningStatusForCapability(@android.telephony.ims.feature.MmTelFeature.MmTelCapabilities.MmTelCapability int, int, boolean);
+ method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) @WorkerThread public void setProvisioningStatusForCapability(int, int, boolean);
method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) @WorkerThread public int setProvisioningStringValue(int, @NonNull String);
method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) @WorkerThread public void setRcsProvisioningStatusForCapability(int, boolean);
method @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public void unregisterProvisioningChangedCallback(@NonNull android.telephony.ims.ProvisioningManager.Callback);
@@ -13398,8 +13423,8 @@ package android.telephony.ims {
method public int describeContents();
method @NonNull public java.util.List<java.lang.String> getCapableExtensionTags();
method @NonNull public android.net.Uri getContactUri();
- method @Nullable public android.net.Uri getServiceUri(int);
- method public boolean isCapable(int);
+ method @Nullable public android.net.Uri getServiceUri(long);
+ method public boolean isCapable(long);
method public boolean isCapable(@NonNull String);
method public void writeToParcel(@NonNull android.os.Parcel, int);
field public static final int CAPABILITY_CALL_COMPOSER = 4194304; // 0x400000
@@ -13421,6 +13446,7 @@ package android.telephony.ims {
field public static final int CAPABILITY_IMAGE_SHARE = 256; // 0x100
field public static final int CAPABILITY_IP_VIDEO_CALL = 16384; // 0x4000
field public static final int CAPABILITY_IP_VOICE_CALL = 8192; // 0x2000
+ field public static final int CAPABILITY_MMTEL_CALL_COMPOSER = 1073741824; // 0x40000000
field public static final int CAPABILITY_PLUG_IN = 268435456; // 0x10000000
field public static final int CAPABILITY_POST_CALL = 8388608; // 0x800000
field public static final int CAPABILITY_RCS_VIDEO_CALL = 1048576; // 0x100000
@@ -13429,6 +13455,7 @@ package android.telephony.ims {
field public static final int CAPABILITY_SHARED_MAP = 16777216; // 0x1000000
field public static final int CAPABILITY_SHARED_SKETCH = 33554432; // 0x2000000
field public static final int CAPABILITY_SOCIAL_PRESENCE = 2048; // 0x800
+ field public static final int CAPABILITY_STANDALONE_CHAT_BOT = 536870912; // 0x20000000
field public static final int CAPABILITY_VIDEO_SHARE = 1024; // 0x400
field public static final int CAPABILITY_VIDEO_SHARE_DURING_CS_CALL = 512; // 0x200
field @NonNull public static final android.os.Parcelable.Creator<android.telephony.ims.RcsContactUceCapability> CREATOR;
@@ -13436,14 +13463,16 @@ package android.telephony.ims {
public static class RcsContactUceCapability.Builder {
ctor public RcsContactUceCapability.Builder(@NonNull android.net.Uri);
- method @NonNull public android.telephony.ims.RcsContactUceCapability.Builder add(int, @NonNull android.net.Uri);
- method @NonNull public android.telephony.ims.RcsContactUceCapability.Builder add(int);
+ method @NonNull public android.telephony.ims.RcsContactUceCapability.Builder add(long, @NonNull android.net.Uri);
+ method @NonNull public android.telephony.ims.RcsContactUceCapability.Builder add(long);
method @NonNull public android.telephony.ims.RcsContactUceCapability.Builder add(@NonNull String);
method @NonNull public android.telephony.ims.RcsContactUceCapability build();
}
public class RcsUceAdapter {
+ method @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public int getUcePublishState() throws android.telephony.ims.ImsException;
method @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public boolean isUceSettingEnabled() throws android.telephony.ims.ImsException;
+ method @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public void requestCapabilities(@NonNull java.util.concurrent.Executor, @NonNull java.util.List<android.net.Uri>, @NonNull android.telephony.ims.RcsUceAdapter.CapabilitiesCallback) throws android.telephony.ims.ImsException;
method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public void setUceSettingEnabled(boolean) throws android.telephony.ims.ImsException;
field public static final int ERROR_ALREADY_IN_QUEUE = 13; // 0xd
field public static final int ERROR_FORBIDDEN = 6; // 0x6
@@ -13465,13 +13494,19 @@ package android.telephony.ims {
field public static final int PUBLISH_STATE_VOLTE_PROVISION_ERROR = 3; // 0x3
}
+ public static class RcsUceAdapter.CapabilitiesCallback {
+ ctor public RcsUceAdapter.CapabilitiesCallback();
+ method public void onCapabilitiesReceived(@NonNull java.util.List<android.telephony.ims.RcsContactUceCapability>);
+ method public void onError(int);
+ }
+
}
package android.telephony.ims.feature {
public final class CapabilityChangeRequest implements android.os.Parcelable {
- method public void addCapabilitiesToDisableForTech(@android.telephony.ims.feature.MmTelFeature.MmTelCapabilities.MmTelCapability int, int);
- method public void addCapabilitiesToEnableForTech(@android.telephony.ims.feature.MmTelFeature.MmTelCapabilities.MmTelCapability int, int);
+ method public void addCapabilitiesToDisableForTech(int, int);
+ method public void addCapabilitiesToEnableForTech(int, int);
method public int describeContents();
method public java.util.List<android.telephony.ims.feature.CapabilityChangeRequest.CapabilityPair> getCapabilitiesToDisable();
method public java.util.List<android.telephony.ims.feature.CapabilityChangeRequest.CapabilityPair> getCapabilitiesToEnable();
@@ -13480,8 +13515,8 @@ package android.telephony.ims.feature {
}
public static class CapabilityChangeRequest.CapabilityPair {
- ctor public CapabilityChangeRequest.CapabilityPair(@android.telephony.ims.feature.MmTelFeature.MmTelCapabilities.MmTelCapability int, int);
- method @android.telephony.ims.feature.MmTelFeature.MmTelCapabilities.MmTelCapability public int getCapability();
+ ctor public CapabilityChangeRequest.CapabilityPair(int, int);
+ method public int getCapability();
method public int getRadioTech();
}
@@ -13526,10 +13561,10 @@ package android.telephony.ims.feature {
method public final void notifyVoiceMessageCountUpdate(int);
method public void onFeatureReady();
method public void onFeatureRemoved();
- method public boolean queryCapabilityConfiguration(@android.telephony.ims.feature.MmTelFeature.MmTelCapabilities.MmTelCapability int, int);
+ method public boolean queryCapabilityConfiguration(int, int);
method @NonNull public final android.telephony.ims.feature.MmTelFeature.MmTelCapabilities queryCapabilityStatus();
method public void setUiTtyMode(int, @Nullable android.os.Message);
- method @android.telephony.ims.feature.MmTelFeature.ProcessCallResult public int shouldProcessCall(@NonNull String[]);
+ method public int shouldProcessCall(@NonNull String[]);
field public static final String EXTRA_IS_UNKNOWN_CALL = "android.telephony.ims.feature.extra.IS_UNKNOWN_CALL";
field public static final String EXTRA_IS_USSD = "android.telephony.ims.feature.extra.IS_USSD";
field public static final int PROCESS_CALL_CSFB = 1; // 0x1
@@ -13539,21 +13574,17 @@ package android.telephony.ims.feature {
public static class MmTelFeature.MmTelCapabilities extends android.telephony.ims.feature.ImsFeature.Capabilities {
ctor public MmTelFeature.MmTelCapabilities();
ctor @Deprecated public MmTelFeature.MmTelCapabilities(android.telephony.ims.feature.ImsFeature.Capabilities);
- ctor public MmTelFeature.MmTelCapabilities(@android.telephony.ims.feature.MmTelFeature.MmTelCapabilities.MmTelCapability int);
- method public final void addCapabilities(@android.telephony.ims.feature.MmTelFeature.MmTelCapabilities.MmTelCapability int);
- method public final boolean isCapable(@android.telephony.ims.feature.MmTelFeature.MmTelCapabilities.MmTelCapability int);
- method public final void removeCapabilities(@android.telephony.ims.feature.MmTelFeature.MmTelCapabilities.MmTelCapability int);
- }
-
- @IntDef(flag=true, value={android.telephony.ims.feature.MmTelFeature.MmTelCapabilities.CAPABILITY_TYPE_VOICE, android.telephony.ims.feature.MmTelFeature.MmTelCapabilities.CAPABILITY_TYPE_VIDEO, android.telephony.ims.feature.MmTelFeature.MmTelCapabilities.CAPABILITY_TYPE_UT, android.telephony.ims.feature.MmTelFeature.MmTelCapabilities.CAPABILITY_TYPE_SMS}) @java.lang.annotation.Retention(java.lang.annotation.RetentionPolicy.SOURCE) public static @interface MmTelFeature.MmTelCapabilities.MmTelCapability {
- }
-
- @IntDef(flag=true, value={android.telephony.ims.feature.MmTelFeature.PROCESS_CALL_IMS, android.telephony.ims.feature.MmTelFeature.PROCESS_CALL_CSFB}) @java.lang.annotation.Retention(java.lang.annotation.RetentionPolicy.SOURCE) public static @interface MmTelFeature.ProcessCallResult {
+ ctor public MmTelFeature.MmTelCapabilities(int);
+ method public final void addCapabilities(int);
+ method public final boolean isCapable(int);
+ method public final void removeCapabilities(int);
}
public class RcsFeature extends android.telephony.ims.feature.ImsFeature {
ctor public RcsFeature();
method public void changeEnabledCapabilities(@NonNull android.telephony.ims.feature.CapabilityChangeRequest, @NonNull android.telephony.ims.feature.ImsFeature.CapabilityCallbackProxy);
+ method @NonNull public android.telephony.ims.stub.RcsSipOptionsImplBase getOptionsExchangeImpl();
+ method @NonNull public android.telephony.ims.stub.RcsPresenceExchangeImplBase getPresenceExchangeImpl();
method public final void notifyCapabilitiesStatusChanged(@NonNull android.telephony.ims.feature.RcsFeature.RcsImsCapabilities);
method public void onFeatureReady();
method public void onFeatureRemoved();
@@ -13746,6 +13777,71 @@ package android.telephony.ims.stub {
field public static final int INVALID_RESULT = -1; // 0xffffffff
}
+ public class RcsCapabilityExchange {
+ ctor public RcsCapabilityExchange();
+ method public final void onCommandUpdate(int, int) throws android.telephony.ims.ImsException;
+ field public static final int COMMAND_CODE_FETCH_ERROR = 4; // 0x4
+ field public static final int COMMAND_CODE_GENERIC_FAILURE = 2; // 0x2
+ field public static final int COMMAND_CODE_INSUFFICIENT_MEMORY = 6; // 0x6
+ field public static final int COMMAND_CODE_INVALID_PARAM = 3; // 0x3
+ field public static final int COMMAND_CODE_LOST_NETWORK_CONNECTION = 7; // 0x7
+ field public static final int COMMAND_CODE_NOT_FOUND = 9; // 0x9
+ field public static final int COMMAND_CODE_NOT_SUPPORTED = 8; // 0x8
+ field public static final int COMMAND_CODE_NO_CHANGE_IN_CAP = 11; // 0xb
+ field public static final int COMMAND_CODE_REQUEST_TIMEOUT = 5; // 0x5
+ field public static final int COMMAND_CODE_SERVICE_UNAVAILABLE = 10; // 0xa
+ field public static final int COMMAND_CODE_SERVICE_UNKNOWN = 0; // 0x0
+ field public static final int COMMAND_CODE_SUCCESS = 1; // 0x1
+ }
+
+ public class RcsPresenceExchangeImplBase extends android.telephony.ims.stub.RcsCapabilityExchange {
+ ctor public RcsPresenceExchangeImplBase();
+ method public final void onCapabilityRequestResponse(@NonNull java.util.List<android.telephony.ims.RcsContactUceCapability>, int) throws android.telephony.ims.ImsException;
+ method public final void onNetworkResponse(int, @NonNull String, int) throws android.telephony.ims.ImsException;
+ method public final void onNotifyUpdateCapabilites(int) throws android.telephony.ims.ImsException;
+ method public final void onUnpublish() throws android.telephony.ims.ImsException;
+ method public void requestCapabilities(@NonNull java.util.List<android.net.Uri>, int);
+ method public void updateCapabilities(@NonNull android.telephony.ims.RcsContactUceCapability, int);
+ field public static final int CAPABILITY_UPDATE_TRIGGER_ETAG_EXPIRED = 0; // 0x0
+ field public static final int CAPABILITY_UPDATE_TRIGGER_MOVE_TO_2G = 6; // 0x6
+ field public static final int CAPABILITY_UPDATE_TRIGGER_MOVE_TO_3G = 5; // 0x5
+ field public static final int CAPABILITY_UPDATE_TRIGGER_MOVE_TO_EHRPD = 3; // 0x3
+ field public static final int CAPABILITY_UPDATE_TRIGGER_MOVE_TO_HSPAPLUS = 4; // 0x4
+ field public static final int CAPABILITY_UPDATE_TRIGGER_MOVE_TO_IWLAN = 8; // 0x8
+ field public static final int CAPABILITY_UPDATE_TRIGGER_MOVE_TO_LTE_VOPS_DISABLED = 1; // 0x1
+ field public static final int CAPABILITY_UPDATE_TRIGGER_MOVE_TO_LTE_VOPS_ENABLED = 2; // 0x2
+ field public static final int CAPABILITY_UPDATE_TRIGGER_MOVE_TO_NR5G_VOPS_DISABLED = 10; // 0xa
+ field public static final int CAPABILITY_UPDATE_TRIGGER_MOVE_TO_NR5G_VOPS_ENABLED = 11; // 0xb
+ field public static final int CAPABILITY_UPDATE_TRIGGER_MOVE_TO_WLAN = 7; // 0x7
+ field public static final int CAPABILITY_UPDATE_TRIGGER_UNKNOWN = 9; // 0x9
+ field public static final int RESPONSE_FORBIDDEN = 3; // 0x3
+ field public static final int RESPONSE_NOT_AUTHORIZED_FOR_PRESENCE = 2; // 0x2
+ field public static final int RESPONSE_NOT_FOUND = 4; // 0x4
+ field public static final int RESPONSE_NOT_REGISTERED = 1; // 0x1
+ field public static final int RESPONSE_SIP_INTERVAL_TOO_SHORT = 7; // 0x7
+ field public static final int RESPONSE_SIP_REQUEST_TIMEOUT = 5; // 0x5
+ field public static final int RESPONSE_SIP_SERVICE_UNAVAILABLE = 8; // 0x8
+ field public static final int RESPONSE_SUBSCRIBE_GENERIC_FAILURE = -1; // 0xffffffff
+ field public static final int RESPONSE_SUBSCRIBE_TOO_LARGE = 6; // 0x6
+ field public static final int RESPONSE_SUCCESS = 0; // 0x0
+ }
+
+ public class RcsSipOptionsImplBase extends android.telephony.ims.stub.RcsCapabilityExchange {
+ ctor public RcsSipOptionsImplBase();
+ method public final void onCapabilityRequestResponse(int, @NonNull String, @Nullable android.telephony.ims.RcsContactUceCapability, int) throws android.telephony.ims.ImsException;
+ method public final void onRemoteCapabilityRequest(@NonNull android.net.Uri, @NonNull android.telephony.ims.RcsContactUceCapability, int) throws android.telephony.ims.ImsException;
+ method public void respondToCapabilityRequest(@NonNull String, @NonNull android.telephony.ims.RcsContactUceCapability, int);
+ method public void respondToCapabilityRequestWithError(@NonNull android.net.Uri, int, @NonNull String, int);
+ method public void sendCapabilityRequest(@NonNull android.net.Uri, @NonNull android.telephony.ims.RcsContactUceCapability, int);
+ field public static final int RESPONSE_BAD_REQUEST = 5; // 0x5
+ field public static final int RESPONSE_DOES_NOT_EXIST_ANYWHERE = 4; // 0x4
+ field public static final int RESPONSE_GENERIC_FAILURE = -1; // 0xffffffff
+ field public static final int RESPONSE_NOT_FOUND = 3; // 0x3
+ field public static final int RESPONSE_REQUEST_TIMEOUT = 2; // 0x2
+ field public static final int RESPONSE_SUCCESS = 0; // 0x0
+ field public static final int RESPONSE_TEMPORARILY_UNAVAILABLE = 1; // 0x1
+ }
+
}
package android.telephony.mbms {
diff --git a/api/test-current.txt b/api/test-current.txt
index 796495a6b21d..917e9383d1f4 100644
--- a/api/test-current.txt
+++ b/api/test-current.txt
@@ -777,7 +777,6 @@ package android.content {
field public static final String POWER_WHITELIST_MANAGER = "power_whitelist";
field public static final String ROLLBACK_SERVICE = "rollback";
field public static final String STATUS_BAR_SERVICE = "statusbar";
- field public static final String TELEPHONY_IMS_SERVICE = "telephony_ims";
field public static final String TEST_NETWORK_SERVICE = "test_network";
}
@@ -1092,7 +1091,7 @@ package android.hardware.display {
method @Nullable public android.hardware.display.BrightnessCorrection getCorrectionByPackageName(@NonNull String);
method public android.util.Pair<float[],float[]> getCurve();
method public float getShortTermModelLowerLuxMultiplier();
- method public long getShortTermModelTimeout();
+ method public long getShortTermModelTimeoutMillis();
method public float getShortTermModelUpperLuxMultiplier();
method public boolean shouldCollectColorSamples();
method public void writeToParcel(android.os.Parcel, int);
@@ -1109,7 +1108,7 @@ package android.hardware.display {
method public int getMaxCorrectionsByPackageName();
method @NonNull public android.hardware.display.BrightnessConfiguration.Builder setDescription(@Nullable String);
method @NonNull public android.hardware.display.BrightnessConfiguration.Builder setShortTermModelLowerLuxMultiplier(@FloatRange(from=0.0f) float);
- method @NonNull public android.hardware.display.BrightnessConfiguration.Builder setShortTermModelTimeout(long);
+ method @NonNull public android.hardware.display.BrightnessConfiguration.Builder setShortTermModelTimeoutMillis(long);
method @NonNull public android.hardware.display.BrightnessConfiguration.Builder setShortTermModelUpperLuxMultiplier(@FloatRange(from=0.0f) float);
method @NonNull public android.hardware.display.BrightnessConfiguration.Builder setShouldCollectColorSamples(boolean);
}
@@ -2219,9 +2218,6 @@ package android.os {
field public static final int STATUS_SUCCESS = 0; // 0x0
}
- @IntDef(prefix={"STATUS_"}, value={android.os.HwParcel.STATUS_SUCCESS}) @java.lang.annotation.Retention(java.lang.annotation.RetentionPolicy.SOURCE) public static @interface HwParcel.Status {
- }
-
public interface IHwBinder {
method public boolean linkToDeath(android.os.IHwBinder.DeathRecipient, long);
method public android.os.IHwInterface queryLocalInterface(String);
@@ -2850,6 +2846,7 @@ package android.provider {
field public static final String CMAS_SEVERITY = "cmas_severity";
field public static final String CMAS_URGENCY = "cmas_urgency";
field @NonNull public static final android.net.Uri CONTENT_URI;
+ field public static final String DATA_CODING_SCHEME = "dcs";
field public static final String DEFAULT_SORT_ORDER = "date DESC";
field public static final String DELIVERY_TIME = "date";
field public static final String ETWS_WARNING_TYPE = "etws_warning_type";
@@ -2857,9 +2854,11 @@ package android.provider {
field public static final String GEOMETRIES = "geometries";
field public static final String LAC = "lac";
field public static final String LANGUAGE_CODE = "language";
+ field public static final String LOCATION_CHECK_TIME = "location_check_time";
field public static final String MAXIMUM_WAIT_TIME = "maximum_wait_time";
field public static final String MESSAGE_BODY = "body";
field public static final String MESSAGE_BROADCASTED = "message_broadcasted";
+ field public static final String MESSAGE_DISPLAYED = "message_displayed";
field public static final String MESSAGE_FORMAT = "format";
field @NonNull @RequiresPermission(android.Manifest.permission.READ_CELL_BROADCASTS) public static final android.net.Uri MESSAGE_HISTORY_URI;
field public static final String MESSAGE_PRIORITY = "priority";
@@ -2994,6 +2993,13 @@ package android.service.autofill {
method public void apply(@NonNull android.service.autofill.ValueFinder, @NonNull android.widget.RemoteViews, int) throws java.lang.Exception;
}
+ public abstract class InlineSuggestionRenderService extends android.app.Service {
+ ctor public InlineSuggestionRenderService();
+ method @Nullable public final android.os.IBinder onBind(@NonNull android.content.Intent);
+ method @Nullable public android.view.View onRenderSuggestion(@NonNull android.service.autofill.InlinePresentation, int, int);
+ field public static final String SERVICE_INTERFACE = "android.service.autofill.InlineSuggestionRenderService";
+ }
+
public abstract class InternalOnClickAction implements android.service.autofill.OnClickAction android.os.Parcelable {
ctor public InternalOnClickAction();
method public abstract void onClick(@NonNull android.view.ViewGroup);
@@ -3808,21 +3814,18 @@ package android.telephony.ims {
}
public class ImsManager {
- method @NonNull public android.telephony.ims.ImsMmTelManager getImsMmTelManager(int);
method @NonNull public android.telephony.ims.ImsRcsManager getImsRcsManager(int);
field public static final String ACTION_FORBIDDEN_NO_SERVICE_AUTHORIZATION = "com.android.internal.intent.action.ACTION_FORBIDDEN_NO_SERVICE_AUTHORIZATION";
- field public static final String ACTION_WFC_IMS_REGISTRATION_ERROR = "android.telephony.ims.action.WFC_IMS_REGISTRATION_ERROR";
- field public static final String EXTRA_WFC_REGISTRATION_FAILURE_MESSAGE = "android.telephony.ims.extra.WFC_REGISTRATION_FAILURE_MESSAGE";
- field public static final String EXTRA_WFC_REGISTRATION_FAILURE_TITLE = "android.telephony.ims.extra.WFC_REGISTRATION_FAILURE_TITLE";
}
public class ImsMmTelManager implements android.telephony.ims.RegistrationManager {
+ method @Deprecated @NonNull @RequiresPermission(anyOf={"android.permission.READ_PRIVILEGED_PHONE_STATE", android.Manifest.permission.READ_PRECISE_PHONE_STATE}) public static android.telephony.ims.ImsMmTelManager createForSubscriptionId(int);
method @RequiresPermission("android.permission.READ_PRIVILEGED_PHONE_STATE") public void getFeatureState(@NonNull java.util.concurrent.Executor, @NonNull java.util.function.Consumer<java.lang.Integer>) throws android.telephony.ims.ImsException;
method @RequiresPermission("android.permission.READ_PRIVILEGED_PHONE_STATE") public void getRegistrationState(@NonNull java.util.concurrent.Executor, @NonNull java.util.function.Consumer<java.lang.Integer>);
method @RequiresPermission("android.permission.READ_PRIVILEGED_PHONE_STATE") public int getVoWiFiRoamingModeSetting();
- method @RequiresPermission("android.permission.READ_PRIVILEGED_PHONE_STATE") public boolean isAvailable(@android.telephony.ims.feature.MmTelFeature.MmTelCapabilities.MmTelCapability int, int);
- method @RequiresPermission("android.permission.READ_PRIVILEGED_PHONE_STATE") public boolean isCapable(@android.telephony.ims.feature.MmTelFeature.MmTelCapabilities.MmTelCapability int, int);
- method @RequiresPermission("android.permission.READ_PRIVILEGED_PHONE_STATE") public void isSupported(@android.telephony.ims.feature.MmTelFeature.MmTelCapabilities.MmTelCapability int, int, @NonNull java.util.concurrent.Executor, @NonNull java.util.function.Consumer<java.lang.Boolean>) throws android.telephony.ims.ImsException;
+ method @RequiresPermission("android.permission.READ_PRIVILEGED_PHONE_STATE") public boolean isAvailable(int, int);
+ method @RequiresPermission("android.permission.READ_PRIVILEGED_PHONE_STATE") public boolean isCapable(int, int);
+ method @RequiresPermission("android.permission.READ_PRIVILEGED_PHONE_STATE") public void isSupported(int, int, @NonNull java.util.concurrent.Executor, @NonNull java.util.function.Consumer<java.lang.Boolean>) throws android.telephony.ims.ImsException;
method @Deprecated @RequiresPermission("android.permission.READ_PRIVILEGED_PHONE_STATE") public void registerImsRegistrationCallback(@NonNull java.util.concurrent.Executor, @NonNull android.telephony.ims.ImsMmTelManager.RegistrationCallback) throws android.telephony.ims.ImsException;
method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public void setAdvancedCallingSettingEnabled(boolean);
method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public void setRttCapabilitySetting(boolean);
@@ -4082,13 +4085,13 @@ package android.telephony.ims {
public class ProvisioningManager {
method @NonNull public static android.telephony.ims.ProvisioningManager createForSubscriptionId(int);
method @RequiresPermission("android.permission.READ_PRIVILEGED_PHONE_STATE") @WorkerThread public int getProvisioningIntValue(int);
- method @RequiresPermission("android.permission.READ_PRIVILEGED_PHONE_STATE") @WorkerThread public boolean getProvisioningStatusForCapability(@android.telephony.ims.feature.MmTelFeature.MmTelCapabilities.MmTelCapability int, int);
+ method @RequiresPermission("android.permission.READ_PRIVILEGED_PHONE_STATE") @WorkerThread public boolean getProvisioningStatusForCapability(int, int);
method @Nullable @RequiresPermission("android.permission.READ_PRIVILEGED_PHONE_STATE") @WorkerThread public String getProvisioningStringValue(int);
method @RequiresPermission("android.permission.READ_PRIVILEGED_PHONE_STATE") @WorkerThread public boolean getRcsProvisioningStatusForCapability(int);
method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public void notifyRcsAutoConfigurationReceived(@NonNull byte[], boolean);
method @RequiresPermission("android.permission.READ_PRIVILEGED_PHONE_STATE") public void registerProvisioningChangedCallback(@NonNull java.util.concurrent.Executor, @NonNull android.telephony.ims.ProvisioningManager.Callback) throws android.telephony.ims.ImsException;
method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) @WorkerThread public int setProvisioningIntValue(int, int);
- method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) @WorkerThread public void setProvisioningStatusForCapability(@android.telephony.ims.feature.MmTelFeature.MmTelCapabilities.MmTelCapability int, int, boolean);
+ method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) @WorkerThread public void setProvisioningStatusForCapability(int, int, boolean);
method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) @WorkerThread public int setProvisioningStringValue(int, @NonNull String);
method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) @WorkerThread public void setRcsProvisioningStatusForCapability(int, boolean);
method @RequiresPermission("android.permission.READ_PRIVILEGED_PHONE_STATE") public void unregisterProvisioningChangedCallback(@NonNull android.telephony.ims.ProvisioningManager.Callback);
@@ -4176,8 +4179,60 @@ package android.telephony.ims {
method public void onProvisioningStringChanged(int, @NonNull String);
}
+ public final class RcsContactUceCapability implements android.os.Parcelable {
+ method public int describeContents();
+ method @NonNull public java.util.List<java.lang.String> getCapableExtensionTags();
+ method @NonNull public android.net.Uri getContactUri();
+ method @Nullable public android.net.Uri getServiceUri(long);
+ method public boolean isCapable(long);
+ method public boolean isCapable(@NonNull String);
+ method public void writeToParcel(@NonNull android.os.Parcel, int);
+ field public static final int CAPABILITY_CALL_COMPOSER = 4194304; // 0x400000
+ field public static final int CAPABILITY_CHAT_BOT = 67108864; // 0x4000000
+ field public static final int CAPABILITY_CHAT_BOT_ROLE = 134217728; // 0x8000000
+ field public static final int CAPABILITY_CHAT_SESSION = 2; // 0x2
+ field public static final int CAPABILITY_CHAT_SESSION_STORE_FORWARD = 4; // 0x4
+ field public static final int CAPABILITY_CHAT_STANDALONE = 1; // 0x1
+ field public static final int CAPABILITY_DISCOVERY_VIA_PRESENCE = 4096; // 0x1000
+ field public static final int CAPABILITY_FILE_TRANSFER = 8; // 0x8
+ field public static final int CAPABILITY_FILE_TRANSFER_HTTP = 64; // 0x40
+ field public static final int CAPABILITY_FILE_TRANSFER_SMS = 128; // 0x80
+ field public static final int CAPABILITY_FILE_TRANSFER_STORE_FORWARD = 32; // 0x20
+ field public static final int CAPABILITY_FILE_TRANSFER_THUMBNAIL = 16; // 0x10
+ field public static final int CAPABILITY_GEOLOCATION_PULL = 131072; // 0x20000
+ field public static final int CAPABILITY_GEOLOCATION_PULL_FILE_TRANSFER = 262144; // 0x40000
+ field public static final int CAPABILITY_GEOLOCATION_PUSH = 32768; // 0x8000
+ field public static final int CAPABILITY_GEOLOCATION_PUSH_SMS = 65536; // 0x10000
+ field public static final int CAPABILITY_IMAGE_SHARE = 256; // 0x100
+ field public static final int CAPABILITY_IP_VIDEO_CALL = 16384; // 0x4000
+ field public static final int CAPABILITY_IP_VOICE_CALL = 8192; // 0x2000
+ field public static final int CAPABILITY_MMTEL_CALL_COMPOSER = 1073741824; // 0x40000000
+ field public static final int CAPABILITY_PLUG_IN = 268435456; // 0x10000000
+ field public static final int CAPABILITY_POST_CALL = 8388608; // 0x800000
+ field public static final int CAPABILITY_RCS_VIDEO_CALL = 1048576; // 0x100000
+ field public static final int CAPABILITY_RCS_VIDEO_ONLY_CALL = 2097152; // 0x200000
+ field public static final int CAPABILITY_RCS_VOICE_CALL = 524288; // 0x80000
+ field public static final int CAPABILITY_SHARED_MAP = 16777216; // 0x1000000
+ field public static final int CAPABILITY_SHARED_SKETCH = 33554432; // 0x2000000
+ field public static final int CAPABILITY_SOCIAL_PRESENCE = 2048; // 0x800
+ field public static final int CAPABILITY_STANDALONE_CHAT_BOT = 536870912; // 0x20000000
+ field public static final int CAPABILITY_VIDEO_SHARE = 1024; // 0x400
+ field public static final int CAPABILITY_VIDEO_SHARE_DURING_CS_CALL = 512; // 0x200
+ field @NonNull public static final android.os.Parcelable.Creator<android.telephony.ims.RcsContactUceCapability> CREATOR;
+ }
+
+ public static class RcsContactUceCapability.Builder {
+ ctor public RcsContactUceCapability.Builder(@NonNull android.net.Uri);
+ method @NonNull public android.telephony.ims.RcsContactUceCapability.Builder add(long, @NonNull android.net.Uri);
+ method @NonNull public android.telephony.ims.RcsContactUceCapability.Builder add(long);
+ method @NonNull public android.telephony.ims.RcsContactUceCapability.Builder add(@NonNull String);
+ method @NonNull public android.telephony.ims.RcsContactUceCapability build();
+ }
+
public class RcsUceAdapter {
+ method @RequiresPermission("android.permission.READ_PRIVILEGED_PHONE_STATE") public int getUcePublishState() throws android.telephony.ims.ImsException;
method @RequiresPermission("android.permission.READ_PRIVILEGED_PHONE_STATE") public boolean isUceSettingEnabled() throws android.telephony.ims.ImsException;
+ method @RequiresPermission("android.permission.READ_PRIVILEGED_PHONE_STATE") public void requestCapabilities(@NonNull java.util.concurrent.Executor, @NonNull java.util.List<android.net.Uri>, @NonNull android.telephony.ims.RcsUceAdapter.CapabilitiesCallback) throws android.telephony.ims.ImsException;
method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public void setUceSettingEnabled(boolean) throws android.telephony.ims.ImsException;
field public static final int ERROR_ALREADY_IN_QUEUE = 13; // 0xd
field public static final int ERROR_FORBIDDEN = 6; // 0x6
@@ -4199,13 +4254,19 @@ package android.telephony.ims {
field public static final int PUBLISH_STATE_VOLTE_PROVISION_ERROR = 3; // 0x3
}
+ public static class RcsUceAdapter.CapabilitiesCallback {
+ ctor public RcsUceAdapter.CapabilitiesCallback();
+ method public void onCapabilitiesReceived(@NonNull java.util.List<android.telephony.ims.RcsContactUceCapability>);
+ method public void onError(int);
+ }
+
}
package android.telephony.ims.feature {
public final class CapabilityChangeRequest implements android.os.Parcelable {
- method public void addCapabilitiesToDisableForTech(@android.telephony.ims.feature.MmTelFeature.MmTelCapabilities.MmTelCapability int, int);
- method public void addCapabilitiesToEnableForTech(@android.telephony.ims.feature.MmTelFeature.MmTelCapabilities.MmTelCapability int, int);
+ method public void addCapabilitiesToDisableForTech(int, int);
+ method public void addCapabilitiesToEnableForTech(int, int);
method public int describeContents();
method public java.util.List<android.telephony.ims.feature.CapabilityChangeRequest.CapabilityPair> getCapabilitiesToDisable();
method public java.util.List<android.telephony.ims.feature.CapabilityChangeRequest.CapabilityPair> getCapabilitiesToEnable();
@@ -4214,8 +4275,8 @@ package android.telephony.ims.feature {
}
public static class CapabilityChangeRequest.CapabilityPair {
- ctor public CapabilityChangeRequest.CapabilityPair(@android.telephony.ims.feature.MmTelFeature.MmTelCapabilities.MmTelCapability int, int);
- method @android.telephony.ims.feature.MmTelFeature.MmTelCapabilities.MmTelCapability public int getCapability();
+ ctor public CapabilityChangeRequest.CapabilityPair(int, int);
+ method public int getCapability();
method public int getRadioTech();
}
@@ -4260,10 +4321,10 @@ package android.telephony.ims.feature {
method public final void notifyVoiceMessageCountUpdate(int);
method public void onFeatureReady();
method public void onFeatureRemoved();
- method public boolean queryCapabilityConfiguration(@android.telephony.ims.feature.MmTelFeature.MmTelCapabilities.MmTelCapability int, int);
+ method public boolean queryCapabilityConfiguration(int, int);
method @NonNull public final android.telephony.ims.feature.MmTelFeature.MmTelCapabilities queryCapabilityStatus();
method public void setUiTtyMode(int, @Nullable android.os.Message);
- method @android.telephony.ims.feature.MmTelFeature.ProcessCallResult public int shouldProcessCall(@NonNull String[]);
+ method public int shouldProcessCall(@NonNull String[]);
field public static final String EXTRA_IS_UNKNOWN_CALL = "android.telephony.ims.feature.extra.IS_UNKNOWN_CALL";
field public static final String EXTRA_IS_USSD = "android.telephony.ims.feature.extra.IS_USSD";
field public static final int PROCESS_CALL_CSFB = 1; // 0x1
@@ -4273,21 +4334,17 @@ package android.telephony.ims.feature {
public static class MmTelFeature.MmTelCapabilities extends android.telephony.ims.feature.ImsFeature.Capabilities {
ctor public MmTelFeature.MmTelCapabilities();
ctor @Deprecated public MmTelFeature.MmTelCapabilities(android.telephony.ims.feature.ImsFeature.Capabilities);
- ctor public MmTelFeature.MmTelCapabilities(@android.telephony.ims.feature.MmTelFeature.MmTelCapabilities.MmTelCapability int);
- method public final void addCapabilities(@android.telephony.ims.feature.MmTelFeature.MmTelCapabilities.MmTelCapability int);
- method public final boolean isCapable(@android.telephony.ims.feature.MmTelFeature.MmTelCapabilities.MmTelCapability int);
- method public final void removeCapabilities(@android.telephony.ims.feature.MmTelFeature.MmTelCapabilities.MmTelCapability int);
- }
-
- @IntDef(flag=true, value={android.telephony.ims.feature.MmTelFeature.MmTelCapabilities.CAPABILITY_TYPE_VOICE, android.telephony.ims.feature.MmTelFeature.MmTelCapabilities.CAPABILITY_TYPE_VIDEO, android.telephony.ims.feature.MmTelFeature.MmTelCapabilities.CAPABILITY_TYPE_UT, android.telephony.ims.feature.MmTelFeature.MmTelCapabilities.CAPABILITY_TYPE_SMS}) @java.lang.annotation.Retention(java.lang.annotation.RetentionPolicy.SOURCE) public static @interface MmTelFeature.MmTelCapabilities.MmTelCapability {
- }
-
- @IntDef(flag=true, value={android.telephony.ims.feature.MmTelFeature.PROCESS_CALL_IMS, android.telephony.ims.feature.MmTelFeature.PROCESS_CALL_CSFB}) @java.lang.annotation.Retention(java.lang.annotation.RetentionPolicy.SOURCE) public static @interface MmTelFeature.ProcessCallResult {
+ ctor public MmTelFeature.MmTelCapabilities(int);
+ method public final void addCapabilities(int);
+ method public final boolean isCapable(int);
+ method public final void removeCapabilities(int);
}
public class RcsFeature extends android.telephony.ims.feature.ImsFeature {
ctor public RcsFeature();
method public void changeEnabledCapabilities(@NonNull android.telephony.ims.feature.CapabilityChangeRequest, @NonNull android.telephony.ims.feature.ImsFeature.CapabilityCallbackProxy);
+ method @NonNull public android.telephony.ims.stub.RcsSipOptionsImplBase getOptionsExchangeImpl();
+ method @NonNull public android.telephony.ims.stub.RcsPresenceExchangeImplBase getPresenceExchangeImpl();
method public final void notifyCapabilitiesStatusChanged(@NonNull android.telephony.ims.feature.RcsFeature.RcsImsCapabilities);
method public void onFeatureReady();
method public void onFeatureRemoved();
@@ -4480,6 +4537,71 @@ package android.telephony.ims.stub {
field public static final int INVALID_RESULT = -1; // 0xffffffff
}
+ public class RcsCapabilityExchange {
+ ctor public RcsCapabilityExchange();
+ method public final void onCommandUpdate(int, int) throws android.telephony.ims.ImsException;
+ field public static final int COMMAND_CODE_FETCH_ERROR = 4; // 0x4
+ field public static final int COMMAND_CODE_GENERIC_FAILURE = 2; // 0x2
+ field public static final int COMMAND_CODE_INSUFFICIENT_MEMORY = 6; // 0x6
+ field public static final int COMMAND_CODE_INVALID_PARAM = 3; // 0x3
+ field public static final int COMMAND_CODE_LOST_NETWORK_CONNECTION = 7; // 0x7
+ field public static final int COMMAND_CODE_NOT_FOUND = 9; // 0x9
+ field public static final int COMMAND_CODE_NOT_SUPPORTED = 8; // 0x8
+ field public static final int COMMAND_CODE_NO_CHANGE_IN_CAP = 11; // 0xb
+ field public static final int COMMAND_CODE_REQUEST_TIMEOUT = 5; // 0x5
+ field public static final int COMMAND_CODE_SERVICE_UNAVAILABLE = 10; // 0xa
+ field public static final int COMMAND_CODE_SERVICE_UNKNOWN = 0; // 0x0
+ field public static final int COMMAND_CODE_SUCCESS = 1; // 0x1
+ }
+
+ public class RcsPresenceExchangeImplBase extends android.telephony.ims.stub.RcsCapabilityExchange {
+ ctor public RcsPresenceExchangeImplBase();
+ method public final void onCapabilityRequestResponse(@NonNull java.util.List<android.telephony.ims.RcsContactUceCapability>, int) throws android.telephony.ims.ImsException;
+ method public final void onNetworkResponse(int, @NonNull String, int) throws android.telephony.ims.ImsException;
+ method public final void onNotifyUpdateCapabilites(int) throws android.telephony.ims.ImsException;
+ method public final void onUnpublish() throws android.telephony.ims.ImsException;
+ method public void requestCapabilities(@NonNull java.util.List<android.net.Uri>, int);
+ method public void updateCapabilities(@NonNull android.telephony.ims.RcsContactUceCapability, int);
+ field public static final int CAPABILITY_UPDATE_TRIGGER_ETAG_EXPIRED = 0; // 0x0
+ field public static final int CAPABILITY_UPDATE_TRIGGER_MOVE_TO_2G = 6; // 0x6
+ field public static final int CAPABILITY_UPDATE_TRIGGER_MOVE_TO_3G = 5; // 0x5
+ field public static final int CAPABILITY_UPDATE_TRIGGER_MOVE_TO_EHRPD = 3; // 0x3
+ field public static final int CAPABILITY_UPDATE_TRIGGER_MOVE_TO_HSPAPLUS = 4; // 0x4
+ field public static final int CAPABILITY_UPDATE_TRIGGER_MOVE_TO_IWLAN = 8; // 0x8
+ field public static final int CAPABILITY_UPDATE_TRIGGER_MOVE_TO_LTE_VOPS_DISABLED = 1; // 0x1
+ field public static final int CAPABILITY_UPDATE_TRIGGER_MOVE_TO_LTE_VOPS_ENABLED = 2; // 0x2
+ field public static final int CAPABILITY_UPDATE_TRIGGER_MOVE_TO_NR5G_VOPS_DISABLED = 10; // 0xa
+ field public static final int CAPABILITY_UPDATE_TRIGGER_MOVE_TO_NR5G_VOPS_ENABLED = 11; // 0xb
+ field public static final int CAPABILITY_UPDATE_TRIGGER_MOVE_TO_WLAN = 7; // 0x7
+ field public static final int CAPABILITY_UPDATE_TRIGGER_UNKNOWN = 9; // 0x9
+ field public static final int RESPONSE_FORBIDDEN = 3; // 0x3
+ field public static final int RESPONSE_NOT_AUTHORIZED_FOR_PRESENCE = 2; // 0x2
+ field public static final int RESPONSE_NOT_FOUND = 4; // 0x4
+ field public static final int RESPONSE_NOT_REGISTERED = 1; // 0x1
+ field public static final int RESPONSE_SIP_INTERVAL_TOO_SHORT = 7; // 0x7
+ field public static final int RESPONSE_SIP_REQUEST_TIMEOUT = 5; // 0x5
+ field public static final int RESPONSE_SIP_SERVICE_UNAVAILABLE = 8; // 0x8
+ field public static final int RESPONSE_SUBSCRIBE_GENERIC_FAILURE = -1; // 0xffffffff
+ field public static final int RESPONSE_SUBSCRIBE_TOO_LARGE = 6; // 0x6
+ field public static final int RESPONSE_SUCCESS = 0; // 0x0
+ }
+
+ public class RcsSipOptionsImplBase extends android.telephony.ims.stub.RcsCapabilityExchange {
+ ctor public RcsSipOptionsImplBase();
+ method public final void onCapabilityRequestResponse(int, @NonNull String, @Nullable android.telephony.ims.RcsContactUceCapability, int) throws android.telephony.ims.ImsException;
+ method public final void onRemoteCapabilityRequest(@NonNull android.net.Uri, @NonNull android.telephony.ims.RcsContactUceCapability, int) throws android.telephony.ims.ImsException;
+ method public void respondToCapabilityRequest(@NonNull String, @NonNull android.telephony.ims.RcsContactUceCapability, int);
+ method public void respondToCapabilityRequestWithError(@NonNull android.net.Uri, int, @NonNull String, int);
+ method public void sendCapabilityRequest(@NonNull android.net.Uri, @NonNull android.telephony.ims.RcsContactUceCapability, int);
+ field public static final int RESPONSE_BAD_REQUEST = 5; // 0x5
+ field public static final int RESPONSE_DOES_NOT_EXIST_ANYWHERE = 4; // 0x4
+ field public static final int RESPONSE_GENERIC_FAILURE = -1; // 0xffffffff
+ field public static final int RESPONSE_NOT_FOUND = 3; // 0x3
+ field public static final int RESPONSE_REQUEST_TIMEOUT = 2; // 0x2
+ field public static final int RESPONSE_SUCCESS = 0; // 0x0
+ field public static final int RESPONSE_TEMPORARILY_UNAVAILABLE = 1; // 0x1
+ }
+
}
package android.telephony.mbms {
diff --git a/api/test-lint-baseline.txt b/api/test-lint-baseline.txt
index a9c18363d8b0..94db3464c8de 100644
--- a/api/test-lint-baseline.txt
+++ b/api/test-lint-baseline.txt
@@ -2451,14 +2451,6 @@ ProtectedMember: android.view.ViewGroup#resetResolvedDrawables():
-PublicTypedef: android.os.HwParcel.Status:
-
-PublicTypedef: android.telephony.ims.feature.MmTelFeature.MmTelCapabilities.MmTelCapability:
-
-PublicTypedef: android.telephony.ims.feature.MmTelFeature.ProcessCallResult:
-
-
-
RawAidl: android.telephony.mbms.vendor.MbmsDownloadServiceBase:
RawAidl: android.telephony.mbms.vendor.MbmsStreamingServiceBase:
diff --git a/cmds/statsd/src/atoms.proto b/cmds/statsd/src/atoms.proto
index d9d5be6e46ec..1fdc8af70f67 100644
--- a/cmds/statsd/src/atoms.proto
+++ b/cmds/statsd/src/atoms.proto
@@ -78,9 +78,9 @@ message Atom {
// Pushed atoms start at 2.
oneof pushed {
// For StatsLog reasons, 1 is illegal and will not work. Must start at 2.
- BleScanStateChanged ble_scan_state_changed = 2;
+ BleScanStateChanged ble_scan_state_changed = 2 [(module) = "bluetooth"];
ProcessStateChanged process_state_changed = 3;
- BleScanResultReceived ble_scan_result_received = 4;
+ BleScanResultReceived ble_scan_result_received = 4 [(module) = "bluetooth"];
SensorStateChanged sensor_state_changed = 5;
GpsScanStateChanged gps_scan_state_changed = 6;
SyncStateChanged sync_state_changed = 7;
@@ -137,14 +137,15 @@ message Atom {
DaveyOccurred davey_occurred = 58 [(allow_from_any_uid) = true];
OverlayStateChanged overlay_state_changed = 59;
ForegroundServiceStateChanged foreground_service_state_changed = 60;
- CallStateChanged call_state_changed = 61;
+ CallStateChanged call_state_changed = 61 [(module) = "telecom"];
KeyguardStateChanged keyguard_state_changed = 62 [(module) = "sysui"];
KeyguardBouncerStateChanged keyguard_bouncer_state_changed = 63 [(module) = "sysui"];
KeyguardBouncerPasswordEntered keyguard_bouncer_password_entered = 64 [(module) = "sysui"];
AppDied app_died = 65;
ResourceConfigurationChanged resource_configuration_changed = 66;
BluetoothEnabledStateChanged bluetooth_enabled_state_changed = 67;
- BluetoothConnectionStateChanged bluetooth_connection_state_changed = 68;
+ BluetoothConnectionStateChanged bluetooth_connection_state_changed =
+ 68 [(module) = "bluetooth"];
GpsSignalQualityChanged gps_signal_quality_changed = 69;
UsbConnectorStateChanged usb_connector_state_changed = 70;
SpeakerImpedanceReported speaker_impedance_reported = 71;
@@ -207,9 +208,12 @@ message Atom {
RescuePartyResetReported rescue_party_reset_reported = 122;
SignedConfigReported signed_config_reported = 123;
GnssNiEventReported gnss_ni_event_reported = 124;
- BluetoothLinkLayerConnectionEvent bluetooth_link_layer_connection_event = 125;
- BluetoothAclConnectionStateChanged bluetooth_acl_connection_state_changed = 126;
- BluetoothScoConnectionStateChanged bluetooth_sco_connection_state_changed = 127;
+ BluetoothLinkLayerConnectionEvent bluetooth_link_layer_connection_event =
+ 125 [(module) = "bluetooth"];
+ BluetoothAclConnectionStateChanged bluetooth_acl_connection_state_changed =
+ 126 [(module) = "bluetooth"];
+ BluetoothScoConnectionStateChanged bluetooth_sco_connection_state_changed =
+ 127 [(module) = "bluetooth"];
AppDowngraded app_downgraded = 128;
AppOptimizedAfterDowngraded app_optimized_after_downgraded = 129;
LowStorageStateChanged low_storage_state_changed = 130;
@@ -233,33 +237,51 @@ message Atom {
BiometricSystemHealthIssueDetected biometric_system_health_issue_detected = 148;
BubbleUIChanged bubble_ui_changed = 149 [(module) = "sysui"];
ScheduledJobConstraintChanged scheduled_job_constraint_changed = 150;
- BluetoothActiveDeviceChanged bluetooth_active_device_changed = 151;
- BluetoothA2dpPlaybackStateChanged bluetooth_a2dp_playback_state_changed = 152;
- BluetoothA2dpCodecConfigChanged bluetooth_a2dp_codec_config_changed = 153;
- BluetoothA2dpCodecCapabilityChanged bluetooth_a2dp_codec_capability_changed = 154;
- BluetoothA2dpAudioUnderrunReported bluetooth_a2dp_audio_underrun_reported = 155;
- BluetoothA2dpAudioOverrunReported bluetooth_a2dp_audio_overrun_reported = 156;
- BluetoothDeviceRssiReported bluetooth_device_rssi_reported = 157;
- BluetoothDeviceFailedContactCounterReported bluetooth_device_failed_contact_counter_reported = 158;
- BluetoothDeviceTxPowerLevelReported bluetooth_device_tx_power_level_reported = 159;
- BluetoothHciTimeoutReported bluetooth_hci_timeout_reported = 160;
- BluetoothQualityReportReported bluetooth_quality_report_reported = 161;
- BluetoothDeviceInfoReported bluetooth_device_info_reported = 162;
- BluetoothRemoteVersionInfoReported bluetooth_remote_version_info_reported = 163;
- BluetoothSdpAttributeReported bluetooth_sdp_attribute_reported = 164;
- BluetoothBondStateChanged bluetooth_bond_state_changed = 165;
- BluetoothClassicPairingEventReported bluetooth_classic_pairing_event_reported = 166;
- BluetoothSmpPairingEventReported bluetooth_smp_pairing_event_reported = 167;
+ BluetoothActiveDeviceChanged bluetooth_active_device_changed =
+ 151 [(module) = "bluetooth"];
+ BluetoothA2dpPlaybackStateChanged bluetooth_a2dp_playback_state_changed =
+ 152 [(module) = "bluetooth"];
+ BluetoothA2dpCodecConfigChanged bluetooth_a2dp_codec_config_changed =
+ 153 [(module) = "bluetooth"];
+ BluetoothA2dpCodecCapabilityChanged bluetooth_a2dp_codec_capability_changed =
+ 154 [(module) = "bluetooth"];
+ BluetoothA2dpAudioUnderrunReported bluetooth_a2dp_audio_underrun_reported =
+ 155 [(module) = "bluetooth"];
+ BluetoothA2dpAudioOverrunReported bluetooth_a2dp_audio_overrun_reported =
+ 156 [(module) = "bluetooth"];
+ BluetoothDeviceRssiReported bluetooth_device_rssi_reported =
+ 157 [(module) = "bluetooth"];
+ BluetoothDeviceFailedContactCounterReported
+ bluetooth_device_failed_contact_counter_reported = 158 [(module) = "bluetooth"];
+ BluetoothDeviceTxPowerLevelReported bluetooth_device_tx_power_level_reported =
+ 159 [(module) = "bluetooth"];
+ BluetoothHciTimeoutReported bluetooth_hci_timeout_reported =
+ 160 [(module) = "bluetooth"];
+ BluetoothQualityReportReported bluetooth_quality_report_reported =
+ 161 [(module) = "bluetooth"];
+ BluetoothDeviceInfoReported bluetooth_device_info_reported =
+ 162 [(module) = "bluetooth"];
+ BluetoothRemoteVersionInfoReported bluetooth_remote_version_info_reported =
+ 163 [(module) = "bluetooth"];
+ BluetoothSdpAttributeReported bluetooth_sdp_attribute_reported =
+ 164 [(module) = "bluetooth"];
+ BluetoothBondStateChanged bluetooth_bond_state_changed =
+ 165 [(module) = "bluetooth"];
+ BluetoothClassicPairingEventReported bluetooth_classic_pairing_event_reported =
+ 166 [(module) = "bluetooth"];
+ BluetoothSmpPairingEventReported bluetooth_smp_pairing_event_reported =
+ 167 [(module) = "bluetooth"];
ScreenTimeoutExtensionReported screen_timeout_extension_reported = 168;
ProcessStartTime process_start_time = 169;
PermissionGrantRequestResultReported permission_grant_request_result_reported =
170 [(module) = "permissioncontroller"];
BluetoothSocketConnectionStateChanged bluetooth_socket_connection_state_changed = 171;
- DeviceIdentifierAccessDenied device_identifier_access_denied = 172;
+ DeviceIdentifierAccessDenied device_identifier_access_denied =
+ 172 [(module) = "telephony_common"];
BubbleDeveloperErrorReported bubble_developer_error_reported = 173;
- AssistGestureStageReported assist_gesture_stage_reported = 174;
- AssistGestureFeedbackReported assist_gesture_feedback_reported = 175;
- AssistGestureProgressReported assist_gesture_progress_reported = 176;
+ AssistGestureStageReported assist_gesture_stage_reported = 174 [(module) = "sysui"];
+ AssistGestureFeedbackReported assist_gesture_feedback_reported = 175 [(module) = "sysui"];
+ AssistGestureProgressReported assist_gesture_progress_reported = 176 [(module) = "sysui"];
TouchGestureClassified touch_gesture_classified = 177;
HiddenApiUsed hidden_api_used = 178 [(allow_from_any_uid) = true];
StyleUIChanged style_ui_changed = 179 [(module) = "sysui"];
@@ -271,7 +293,8 @@ message Atom {
BiometricEnrolled biometric_enrolled = 184;
SystemServerWatchdogOccurred system_server_watchdog_occurred = 185;
TombStoneOccurred tomb_stone_occurred = 186;
- BluetoothClassOfDeviceReported bluetooth_class_of_device_reported = 187;
+ BluetoothClassOfDeviceReported bluetooth_class_of_device_reported =
+ 187 [(module) = "bluetooth"];
IntelligenceEventReported intelligence_event_reported =
188 [(module) = "intelligence"];
ThermalThrottlingSeverityStateChanged thermal_throttling_severity_state_changed = 189;
@@ -288,8 +311,8 @@ message Atom {
MediametricsNuPlayerReported mediametrics_nuplayer_reported = 199;
MediametricsRecorderReported mediametrics_recorder_reported = 200;
MediametricsDrmManagerReported mediametrics_drmmanager_reported = 201;
- CarPowerStateChanged car_power_state_changed = 203;
- GarageModeInfo garage_mode_info = 204;
+ CarPowerStateChanged car_power_state_changed = 203 [(module) = "car"];
+ GarageModeInfo garage_mode_info = 204 [(module) = "car"];
TestAtomReported test_atom_reported = 205 [(module) = "cts"];
ContentCaptureCallerMismatchReported content_capture_caller_mismatch_reported = 206;
ContentCaptureServiceEvents content_capture_service_events = 207;
@@ -324,7 +347,8 @@ message Atom {
AppCompatibilityChangeReported app_compatibility_change_reported =
228 [(allow_from_any_uid) = true];
PerfettoUploaded perfetto_uploaded = 229 [(module) = "perfetto"];
- VmsClientConnectionStateChanged vms_client_connection_state_changed = 230;
+ VmsClientConnectionStateChanged vms_client_connection_state_changed =
+ 230 [(module) = "car"];
MediaProviderScanEvent media_provider_scan_event = 233 [(module) = "mediaprovider"];
MediaProviderDeletionEvent media_provider_deletion_event = 234 [(module) = "mediaprovider"];
MediaProviderPermissionEvent media_provider_permission_event =
@@ -343,6 +367,10 @@ message Atom {
NotificationChannelModified notification_panel_modified = 246;
IntegrityCheckResultReported integrity_check_result_reported = 247;
IntegrityRulesPushed integrity_rules_pushed = 248;
+ CellBroadcastMessageReported cb_message_reported =
+ 249 [(module) = "cellbroadcast"];
+ CellBroadcastMessageError cb_message_error =
+ 250 [(module) = "cellbroadcast"];
}
// Pulled events will start at field 10000.
@@ -412,7 +440,7 @@ message Atom {
SurfaceflingerStatsGlobalInfo surfaceflinger_stats_global_info = 10062;
SurfaceflingerStatsLayerInfo surfaceflinger_stats_layer_info = 10063;
ProcessMemorySnapshot process_memory_snapshot = 10064;
- VmsClientStats vms_client_stats = 10065;
+ VmsClientStats vms_client_stats = 10065 [(module) = "car"];
NotificationRemoteViews notification_remote_views = 10066;
DangerousPermissionStateSampled dangerous_permission_state_sampled = 10067;
GraphicsStats graphics_stats = 10068;
@@ -8112,3 +8140,55 @@ message IntegrityRulesPushed {
// identify the rules.
optional string rule_version = 3;
}
+
+/**
+ * Logs when a cell broadcast message is received on the device.
+ *
+ * Logged from CellBroadcastService module:
+ * packages/modules/CellBroadcastService/src/com/android/cellbroadcastservice/
+ */
+message CellBroadcastMessageReported {
+ // The type of Cell Broadcast message
+ enum CbType {
+ UNKNOWN_TYPE = 0;
+ GSM = 1;
+ CDMA = 2;
+ CDMA_SPC = 3;
+ }
+
+ // GSM, CDMA, CDMA-SCP
+ optional CbType type = 1;
+}
+
+/**
+ * Logs when an error occurs while handling a cell broadcast message;
+ *
+ * Logged from CellBroadcastService module:
+ * packages/modules/CellBroadcastService/src/com/android/cellbroadcastservice/
+ */
+message CellBroadcastMessageError {
+ // The type of error raised when trying to handle a cell broadcast message
+ enum ErrorType {
+ UNKNOWN_TYPE = 0;
+ CDMA_DECODING_ERROR = 1;
+ CDMA_SCP_EMPTY = 2;
+ CDMA_SCP_HANDLING_ERROR = 3;
+ GSM_INVALID_HEADER_LENGTH = 4;
+ GSM_UNSUPPORTED_HEADER_MESSAGE_TYPE = 5;
+ GSM_UNSUPPORTED_HEADER_DATA_CODING_SCHEME = 6;
+ GSM_INVALID_PDU = 7;
+ GSM_INVALID_GEO_FENCING_DATA = 8;
+ GSM_UMTS_INVALID_WAC = 9;
+ FAILED_TO_INSERT_TO_DB = 10;
+ UNEXPECTED_GEOMETRY_FROM_FWK = 11;
+ UNEXPECTED_GSM_MESSAGE_TYPE_FROM_FWK = 12;
+ UNEXPECTED_CDMA_MESSAGE_TYPE_FROM_FWK = 13;
+ UNEXPECTED_CDMA_SCP_MESSAGE_TYPE_FROM_FWK = 14;
+ }
+
+ // What kind of error occurred
+ optional ErrorType type = 1;
+
+ // Exception message (or log message) associated with the error (max 1000 chars)
+ optional string exception_message = 2;
+}
diff --git a/cmds/statsd/src/external/StatsPullerManager.cpp b/cmds/statsd/src/external/StatsPullerManager.cpp
index 7708e300929e..85d1e38d6052 100644
--- a/cmds/statsd/src/external/StatsPullerManager.cpp
+++ b/cmds/statsd/src/external/StatsPullerManager.cpp
@@ -119,9 +119,9 @@ bool StatsPullerManager::PullLocked(int tagId, vector<shared_ptr<LogEvent>>* dat
}
bool StatsPullerManager::PullerForMatcherExists(int tagId) const {
- // Vendor pulled atoms might be registered after we parse the config.
- return isVendorPulledAtom(tagId) ||
- kAllPullAtomInfo.find({.atomTag = tagId}) != kAllPullAtomInfo.end();
+ // Pulled atoms might be registered after we parse the config, so just make sure the id is in
+ // an appropriate range.
+ return isVendorPulledAtom(tagId) || isPulledAtom(tagId);
}
void StatsPullerManager::updateAlarmLocked() {
diff --git a/cmds/statsd/src/guardrail/StatsdStats.h b/cmds/statsd/src/guardrail/StatsdStats.h
index 32827852ecad..df810aa03958 100644
--- a/cmds/statsd/src/guardrail/StatsdStats.h
+++ b/cmds/statsd/src/guardrail/StatsdStats.h
@@ -164,6 +164,12 @@ public:
// Maximum number of pushed atoms statsd stats will track above kMaxPushedAtomId.
static const int kMaxNonPlatformPushedAtoms = 100;
+ // Atom id that is the start of the pulled atoms.
+ static const int kPullAtomStartTag = 10000;
+
+ // Atom id that is the start of vendor atoms.
+ static const int kVendorAtomStartTag = 100000;
+
// Vendor pulled atom start id.
static const int32_t kVendorPulledAtomStartTag = 150000;
diff --git a/cmds/statsd/src/metrics/metrics_manager_util.cpp b/cmds/statsd/src/metrics/metrics_manager_util.cpp
index 73c121242523..17f62b056426 100644
--- a/cmds/statsd/src/metrics/metrics_manager_util.cpp
+++ b/cmds/statsd/src/metrics/metrics_manager_util.cpp
@@ -426,7 +426,6 @@ bool initMetrics(const ConfigKey& key, const StatsdConfig& config, const int64_t
config.event_metric_size() + config.gauge_metric_size() +
config.value_metric_size();
allMetricProducers.reserve(allMetricsCount);
- StatsPullerManager statsPullerManager;
// Construct map from metric id to metric activation index. The map will be used to determine
// the metric activation corresponding to a metric.
@@ -661,7 +660,7 @@ bool initMetrics(const ConfigKey& key, const StatsdConfig& config, const int64_t
return false;
}
int atomTagId = *(atomMatcher->getAtomIds().begin());
- int pullTagId = statsPullerManager.PullerForMatcherExists(atomTagId) ? atomTagId : -1;
+ int pullTagId = pullerManager->PullerForMatcherExists(atomTagId) ? atomTagId : -1;
int conditionIndex = -1;
if (metric.has_condition()) {
@@ -753,7 +752,7 @@ bool initMetrics(const ConfigKey& key, const StatsdConfig& config, const int64_t
return false;
}
int atomTagId = *(atomMatcher->getAtomIds().begin());
- int pullTagId = statsPullerManager.PullerForMatcherExists(atomTagId) ? atomTagId : -1;
+ int pullTagId = pullerManager->PullerForMatcherExists(atomTagId) ? atomTagId : -1;
int triggerTrackerIndex;
int triggerAtomId = -1;
diff --git a/cmds/statsd/src/stats_log_util.h b/cmds/statsd/src/stats_log_util.h
index f3e94331a23e..5fdf6e260247 100644
--- a/cmds/statsd/src/stats_log_util.h
+++ b/cmds/statsd/src/stats_log_util.h
@@ -99,6 +99,10 @@ inline bool isVendorPulledAtom(int atomId) {
return atomId >= StatsdStats::kVendorPulledAtomStartTag && atomId < StatsdStats::kMaxAtomTag;
}
+inline bool isPulledAtom(int atomId) {
+ return atomId >= StatsdStats::kPullAtomStartTag && atomId < StatsdStats::kVendorAtomStartTag;
+}
+
} // namespace statsd
} // namespace os
} // namespace android
diff --git a/cmds/telecom/src/com/android/commands/telecom/Telecom.java b/cmds/telecom/src/com/android/commands/telecom/Telecom.java
index 1987440106f5..97074050448e 100644
--- a/cmds/telecom/src/com/android/commands/telecom/Telecom.java
+++ b/cmds/telecom/src/com/android/commands/telecom/Telecom.java
@@ -366,12 +366,8 @@ public final class Telecom extends BaseCommand {
}
private void runGetMaxPhones() throws RemoteException {
- // This assumes the max number of SIMs is 2, which it currently is
- if (TelephonyManager.MULTISIM_ALLOWED == mTelephonyManager.isMultiSimSupported()) {
- System.out.println("2");
- } else {
- System.out.println("1");
- }
+ // how many logical modems can be potentially active simultaneously
+ System.out.println(mTelephonyManager.getSupportedModemCount());
}
private void runSetEmergencyPhoneAccountPackageFilter() throws RemoteException {
diff --git a/core/java/android/annotation/SystemApi.java b/core/java/android/annotation/SystemApi.java
index 2cb93e4f8cea..ecbfed96cefd 100644
--- a/core/java/android/annotation/SystemApi.java
+++ b/core/java/android/annotation/SystemApi.java
@@ -23,6 +23,7 @@ import static java.lang.annotation.ElementType.METHOD;
import static java.lang.annotation.ElementType.PACKAGE;
import static java.lang.annotation.ElementType.TYPE;
+import java.lang.annotation.Repeatable;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
@@ -40,39 +41,48 @@ import java.lang.annotation.Target;
*/
@Target({TYPE, FIELD, METHOD, CONSTRUCTOR, ANNOTATION_TYPE, PACKAGE})
@Retention(RetentionPolicy.RUNTIME)
+@Repeatable(SystemApi.Container.class) // TODO(b/146727827): make this non-repeatable
public @interface SystemApi {
enum Client {
/**
* Specifies that the intended clients of a SystemApi are privileged apps.
- * This is the default value for {@link #client}. This implies
- * MODULE_APPS and MODULE_LIBRARIES as well, which means that APIs will also
- * be available to module apps and jars.
+ * This is the default value for {@link #client}.
+ * TODO Update the javadoc according to the final spec
*/
PRIVILEGED_APPS,
/**
- * Specifies that the intended clients of a SystemApi are modules implemented
- * as apps, like the NetworkStack app. This implies MODULE_LIBRARIES as well,
- * which means that APIs will also be available to module jars.
+ * DO NOT USE. Use PRIVILEGED_APPS instead.
+ * (This would provide no further protection over PRIVILEGED_APPS; do not rely on it)
+ * @deprecated Use #PRIVILEGED_APPS instead
*/
+ @Deprecated
MODULE_APPS,
/**
* Specifies that the intended clients of a SystemApi are modules implemented
* as libraries, like the conscrypt.jar in the conscrypt APEX.
+ * TODO Update the javadoc according to the final spec
*/
- MODULE_LIBRARIES
- }
+ MODULE_LIBRARIES,
- enum Process {
/**
- * Specifies that the SystemAPI is available in every Java processes.
- * This is the default value for {@link #process}.
+ * Specifies that the system API is available only in the system server process.
+ * Use this to expose APIs from code loaded by the system server process <em>but</em>
+ * not in <pre>BOOTCLASSPATH</pre>.
+ * TODO(b/148177503) Update "services-stubs" and actually use it.
*/
+ SYSTEM_SERVER
+ }
+
+ /** @deprecated do not use */
+ @Deprecated
+ enum Process {
+ /** @deprecated do not use */
ALL,
/**
- * Specifies that the SystemAPI is available only in the system server process.
+ * @deprecated use Client#SYSTEM_SERVER instead
*/
SYSTEM_SERVER
}
@@ -83,7 +93,18 @@ public @interface SystemApi {
Client client() default android.annotation.SystemApi.Client.PRIVILEGED_APPS;
/**
- * The process(es) that this SystemAPI is available
+ * @deprecated use Client#SYSTEM_SERVER instead for system_server APIs
*/
+ @Deprecated
Process process() default android.annotation.SystemApi.Process.ALL;
+
+
+ /**
+ * Container for {@link SystemApi} that allows it to be applied repeatedly to types.
+ */
+ @Retention(RetentionPolicy.RUNTIME)
+ @Target(TYPE)
+ @interface Container {
+ SystemApi[] value();
+ }
}
diff --git a/core/java/android/app/ApplicationPackageManager.java b/core/java/android/app/ApplicationPackageManager.java
index 5a4d62010f11..c09aa1ff05a8 100644
--- a/core/java/android/app/ApplicationPackageManager.java
+++ b/core/java/android/app/ApplicationPackageManager.java
@@ -632,22 +632,22 @@ public class ApplicationPackageManager extends PackageManager {
private static final int SYS_FEATURE_CACHE_SIZE = 256;
private static final String CACHE_KEY_SYS_FEATURE_PROPERTY = "cache_key.has_system_feature";
- private class HasSystemFeatureQuery {
+ private class SystemFeatureQuery {
public final String name;
public final int version;
- public HasSystemFeatureQuery(String n, int v) {
+ public SystemFeatureQuery(String n, int v) {
name = n;
version = v;
}
@Override
public String toString() {
- return String.format("HasSystemFeatureQuery(name=\"%s\", version=%d)",
+ return String.format("SystemFeatureQuery(name=\"%s\", version=%d)",
name, version);
}
@Override
public boolean equals(Object o) {
- if (o instanceof HasSystemFeatureQuery) {
- HasSystemFeatureQuery r = (HasSystemFeatureQuery) o;
+ if (o instanceof SystemFeatureQuery) {
+ SystemFeatureQuery r = (SystemFeatureQuery) o;
return Objects.equals(name, r.name) && version == r.version;
} else {
return false;
@@ -655,32 +655,32 @@ public class ApplicationPackageManager extends PackageManager {
}
@Override
public int hashCode() {
- return Objects.hashCode(name) * 13 + version;
+ return Objects.hashCode(name) + version;
}
}
- private final PropertyInvalidatedCache<HasSystemFeatureQuery, Boolean> mHasSystemFeatureCache =
- new PropertyInvalidatedCache<>(
+ private final PropertyInvalidatedCache<SystemFeatureQuery, Boolean> mSysFeatureCache =
+ new PropertyInvalidatedCache<SystemFeatureQuery, Boolean>(
SYS_FEATURE_CACHE_SIZE,
CACHE_KEY_SYS_FEATURE_PROPERTY) {
@Override
- protected Boolean recompute(HasSystemFeatureQuery query) {
+ protected Boolean recompute(SystemFeatureQuery query) {
return hasSystemFeatureUncached(query.name, query.version);
}
};
@Override
public boolean hasSystemFeature(String name, int version) {
- return mHasSystemFeatureCache.query(new HasSystemFeatureQuery(name, version));
+ return mSysFeatureCache.query(new SystemFeatureQuery(name, version)).booleanValue();
}
/** @hide */
- public void disableHasSystemFeatureCache() {
- mHasSystemFeatureCache.disableLocal();
+ public void disableSysFeatureCache() {
+ mSysFeatureCache.disableLocal();
}
/** @hide */
- public static void invalidateHasSystemFeatureCache() {
+ public static void invalidateSysFeatureCache() {
PropertyInvalidatedCache.invalidateCache(CACHE_KEY_SYS_FEATURE_PROPERTY);
}
diff --git a/core/java/android/app/INotificationManager.aidl b/core/java/android/app/INotificationManager.aidl
index d665f336ec1d..4b1ba0278682 100644
--- a/core/java/android/app/INotificationManager.aidl
+++ b/core/java/android/app/INotificationManager.aidl
@@ -18,6 +18,7 @@
package android.app;
import android.app.ITransientNotification;
+import android.app.ITransientNotificationCallback;
import android.app.Notification;
import android.app.NotificationChannel;
import android.app.NotificationChannelGroup;
@@ -45,8 +46,7 @@ interface INotificationManager
void cancelAllNotifications(String pkg, int userId);
void clearData(String pkg, int uid, boolean fromApp);
- // TODO: Replace parameter (ITransientNotification callback) with (CharSequence text)
- void enqueueTextToast(String pkg, IBinder token, ITransientNotification callback, int duration, int displayId);
+ void enqueueTextToast(String pkg, IBinder token, CharSequence text, int duration, int displayId, @nullable ITransientNotificationCallback callback);
void enqueueToast(String pkg, IBinder token, ITransientNotification callback, int duration, int displayId);
void cancelToast(String pkg, IBinder token);
void finishToken(String pkg, IBinder token);
diff --git a/core/java/android/app/ITransientNotificationCallback.aidl b/core/java/android/app/ITransientNotificationCallback.aidl
new file mode 100644
index 000000000000..abe254f2d16a
--- /dev/null
+++ b/core/java/android/app/ITransientNotificationCallback.aidl
@@ -0,0 +1,27 @@
+/*
+ * Copyright 2020, The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.app;
+
+/**
+ * Callback object to be called when the associated toast is shown or hidden.
+ *
+ * @hide
+ */
+oneway interface ITransientNotificationCallback {
+ void onToastShown();
+ void onToastHidden();
+}
diff --git a/core/java/android/app/Notification.java b/core/java/android/app/Notification.java
index 1af275fedd74..35d26aba9094 100644
--- a/core/java/android/app/Notification.java
+++ b/core/java/android/app/Notification.java
@@ -17,6 +17,8 @@
package android.app;
import static android.annotation.Dimension.DP;
+import static android.graphics.drawable.Icon.TYPE_URI;
+import static android.graphics.drawable.Icon.TYPE_URI_ADAPTIVE_BITMAP;
import static com.android.internal.util.ContrastColorUtil.satisfiesTextContrast;
@@ -2504,6 +2506,14 @@ public class Notification implements Parcelable
}
}
}
+
+ if (mBubbleMetadata != null && mBubbleMetadata.getBubbleIcon() != null) {
+ final Icon icon = mBubbleMetadata.getBubbleIcon();
+ final int iconType = icon.getType();
+ if (iconType == TYPE_URI_ADAPTIVE_BITMAP || iconType == TYPE_URI) {
+ visitor.accept(icon.getUri());
+ }
+ }
}
/**
@@ -3559,8 +3569,16 @@ public class Notification implements Parcelable
* This field will be ignored by Launchers that don't support badging, don't show
* notification content, or don't show {@link android.content.pm.ShortcutManager shortcuts}.
*
+ * If this notification has {@link BubbleMetadata} attached that was created with
+ * {@link BubbleMetadata.Builder#createShortcutBubble(String)} a check will be performed
+ * to ensure the shortcutId supplied to bubble metadata matches the shortcutId set here,
+ * if one was set. If the shortcutId's were specified but do not match, an exception
+ * is thrown.
+ *
* @param shortcutId the {@link ShortcutInfo#getId() id} of the shortcut this notification
* supersedes
+ *
+ * @see BubbleMetadata.Builder#createShortcutBubble(String)
*/
@NonNull
public Builder setShortcutId(String shortcutId) {
@@ -5916,9 +5934,29 @@ public class Notification implements Parcelable
/**
* Combine all of the options that have been set and return a new {@link Notification}
* object.
+ *
+ * If this notification has {@link BubbleMetadata} attached that was created with
+ * {@link BubbleMetadata.Builder#createShortcutBubble(String)} a check will be performed
+ * to ensure the shortcutId supplied to bubble metadata matches the shortcutId set on the
+ * notification builder, if one was set. If the shortcutId's were specified but do not
+ * match, an exception is thrown here.
+ *
+ * @see BubbleMetadata.Builder#createShortcutBubble(String)
+ * @see #setShortcutId(String)
*/
@NonNull
public Notification build() {
+ // Check shortcut id matches
+ if (mN.mShortcutId != null
+ && mN.mBubbleMetadata != null
+ && mN.mBubbleMetadata.getShortcutId() != null
+ && !mN.mShortcutId.equals(mN.mBubbleMetadata.getShortcutId())) {
+ throw new IllegalArgumentException(
+ "Notification and BubbleMetadata shortcut id's don't match,"
+ + " notification: " + mN.mShortcutId
+ + " vs bubble: " + mN.mBubbleMetadata.getShortcutId());
+ }
+
// first, add any extras from the calling code
if (mUserExtras != null) {
mN.extras = getAllExtras();
@@ -8634,17 +8672,23 @@ public class Notification implements Parcelable
public static final int FLAG_AUTO_EXPAND_BUBBLE = 0x00000001;
/**
- * If set and the app posting the bubble is in the foreground, the bubble will
- * be posted <b>without</b> the associated notification in the notification shade.
+ * Indicates whether the notification associated with the bubble is being visually
+ * suppressed from the notification shade. When <code>true</code> the notification is
+ * hidden, when <code>false</code> the notification shows as normal.
*
- * <p>This flag has no effect if the app posting the bubble is not in the foreground.
- * The app is considered foreground if it is visible and on the screen, note that
- * a foreground service does not qualify.
- * </p>
+ * <p>Apps sending bubbles may set this flag so that the bubble is posted <b>without</b>
+ * the associated notification in the notification shade.</p>
*
- * <p>Generally this flag should only be set if the user has performed an action to request
- * or create a bubble, or if the user has seen the content in the notification and the
- * notification is no longer relevant.</p>
+ * <p>Apps sending bubbles can only apply this flag when the app is in the foreground,
+ * otherwise the flag is not respected. The app is considered foreground if it is visible
+ * and on the screen, note that a foreground service does not qualify.</p>
+ *
+ * <p>Generally this flag should only be set by the app if the user has performed an
+ * action to request or create a bubble, or if the user has seen the content in the
+ * notification and the notification is no longer relevant. </p>
+ *
+ * <p>The system will also update this flag with <code>true</code> to hide the notification
+ * from the user once the bubble has been expanded. </p>
*
* @hide
*/
@@ -8762,6 +8806,24 @@ public class Notification implements Parcelable
}
/**
+ * Indicates whether the notification associated with the bubble is being visually
+ * suppressed from the notification shade. When <code>true</code> the notification is
+ * hidden, when <code>false</code> the notification shows as normal.
+ *
+ * <p>Apps sending bubbles may set this flag so that the bubble is posted <b>without</b>
+ * the associated notification in the notification shade.</p>
+ *
+ * <p>Apps sending bubbles can only apply this flag when the app is in the foreground,
+ * otherwise the flag is not respected. The app is considered foreground if it is visible
+ * and on the screen, note that a foreground service does not qualify.</p>
+ *
+ * <p>Generally the app should only set this flag if the user has performed an
+ * action to request or create a bubble, or if the user has seen the content in the
+ * notification and the notification is no longer relevant. </p>
+ *
+ * <p>The system will update this flag with <code>true</code> to hide the notification
+ * from the user once the bubble has been expanded.</p>
+ *
* @return whether this bubble should suppress the notification when it is posted.
*
* @see BubbleMetadata.Builder#setSuppressNotification(boolean)
@@ -8900,6 +8962,12 @@ public class Notification implements Parcelable
if (icon == null) {
throw new IllegalArgumentException("Bubbles require non-null icon");
}
+ if (icon.getType() != TYPE_URI_ADAPTIVE_BITMAP
+ && icon.getType() != TYPE_URI) {
+ Log.w(TAG, "Bubbles work best with icons of TYPE_URI or "
+ + "TYPE_URI_ADAPTIVE_BITMAP. "
+ + "In the future, using an icon of this type will be required.");
+ }
mShortcutId = null;
mPendingIntent = intent;
mIcon = icon;
diff --git a/core/java/android/content/Context.java b/core/java/android/content/Context.java
index 3156c248fe96..a0d5c108869e 100644
--- a/core/java/android/content/Context.java
+++ b/core/java/android/content/Context.java
@@ -5032,10 +5032,7 @@ public abstract class Context {
/**
* Use with {@link #getSystemService(String)} to retrieve an
* {@link android.telephony.ims.ImsManager}.
- * @hide
*/
- @SystemApi
- @TestApi
public static final String TELEPHONY_IMS_SERVICE = "telephony_ims";
/**
diff --git a/core/java/android/content/pm/ApplicationInfo.java b/core/java/android/content/pm/ApplicationInfo.java
index 1aed9772700b..b998539dee9c 100644
--- a/core/java/android/content/pm/ApplicationInfo.java
+++ b/core/java/android/content/pm/ApplicationInfo.java
@@ -133,6 +133,13 @@ public class ApplicationInfo extends PackageItemInfo implements Parcelable {
public int fullBackupContent = 0;
/**
+ * <code>true</code> if the package is capable of presenting a unified interface representing
+ * multiple profiles.
+ * @hide
+ */
+ public boolean crossProfile;
+
+ /**
* The default extra UI options for activities in this application.
* Set from the {@link android.R.attr#uiOptions} attribute in the
* activity's manifest.
@@ -1382,6 +1389,7 @@ public class ApplicationInfo extends PackageItemInfo implements Parcelable {
pw.println(prefix + "fullBackupContent="
+ (fullBackupContent < 0 ? "false" : "true"));
}
+ pw.println("crossProfile=" + (crossProfile ? "true" : "false"));
if (networkSecurityConfigRes != 0) {
pw.println(prefix + "networkSecurityConfigRes=0x"
+ Integer.toHexString(networkSecurityConfigRes));
@@ -1586,6 +1594,7 @@ public class ApplicationInfo extends PackageItemInfo implements Parcelable {
uiOptions = orig.uiOptions;
backupAgentName = orig.backupAgentName;
fullBackupContent = orig.fullBackupContent;
+ crossProfile = orig.crossProfile;
networkSecurityConfigRes = orig.networkSecurityConfigRes;
category = orig.category;
targetSandboxVersion = orig.targetSandboxVersion;
@@ -1665,6 +1674,7 @@ public class ApplicationInfo extends PackageItemInfo implements Parcelable {
dest.writeInt(descriptionRes);
dest.writeInt(uiOptions);
dest.writeInt(fullBackupContent);
+ dest.writeBoolean(crossProfile);
dest.writeInt(networkSecurityConfigRes);
dest.writeInt(category);
dest.writeInt(targetSandboxVersion);
@@ -1741,6 +1751,7 @@ public class ApplicationInfo extends PackageItemInfo implements Parcelable {
descriptionRes = source.readInt();
uiOptions = source.readInt();
fullBackupContent = source.readInt();
+ crossProfile = source.readBoolean();
networkSecurityConfigRes = source.readInt();
category = source.readInt();
targetSandboxVersion = source.readInt();
diff --git a/core/java/android/content/pm/PackageManager.java b/core/java/android/content/pm/PackageManager.java
index 66a2b7a3ac66..b64c001ea6e2 100644
--- a/core/java/android/content/pm/PackageManager.java
+++ b/core/java/android/content/pm/PackageManager.java
@@ -1960,6 +1960,15 @@ public abstract class PackageManager {
/**
* Feature for {@link #getSystemAvailableFeatures} and
+ * {@link #hasSystemFeature}: The device's main front and back cameras can stream
+ * concurrently as described in {@link
+ * android.hardware.camera2.CameraManager#getConcurrentStreamingCameraIds()}
+ */
+ @SdkConstant(SdkConstantType.FEATURE)
+ public static final String FEATURE_CAMERA_CONCURRENT = "android.hardware.camera.concurrent";
+
+ /**
+ * Feature for {@link #getSystemAvailableFeatures} and
* {@link #hasSystemFeature}: The device is capable of communicating with
* consumer IR devices.
*/
@@ -2326,6 +2335,13 @@ public abstract class PackageManager {
/**
* Feature for {@link #getSystemAvailableFeatures} and
+ * {@link #hasSystemFeature}: The device includes a hinge angle sensor.
+ */
+ @SdkConstant(SdkConstantType.FEATURE)
+ public static final String FEATURE_SENSOR_HINGE_ANGLE = "android.hardware.sensor.hinge_angle";
+
+ /**
+ * Feature for {@link #getSystemAvailableFeatures} and
* {@link #hasSystemFeature}: The device supports high fidelity sensor processing
* capabilities.
*/
diff --git a/core/java/android/content/pm/UserInfo.java b/core/java/android/content/pm/UserInfo.java
index 008cfa5e9423..f1506645d10c 100644
--- a/core/java/android/content/pm/UserInfo.java
+++ b/core/java/android/content/pm/UserInfo.java
@@ -362,6 +362,10 @@ public class UserInfo implements Parcelable {
// Don't support switching to an ephemeral user with removal in progress.
return false;
}
+ if (preCreated) {
+ // Don't support switching to pre-created users until they become "real" users.
+ return false;
+ }
return !isProfile();
}
diff --git a/core/java/android/content/pm/parsing/PackageImpl.java b/core/java/android/content/pm/parsing/PackageImpl.java
index 9baf3258a230..fe8307c7c8cd 100644
--- a/core/java/android/content/pm/parsing/PackageImpl.java
+++ b/core/java/android/content/pm/parsing/PackageImpl.java
@@ -2382,6 +2382,7 @@ public final class PackageImpl implements ParsingPackage, ParsedPackage, Android
appInfo.uiOptions = uiOptions;
appInfo.volumeUuid = volumeUuid;
appInfo.zygotePreloadName = zygotePreloadName;
+ appInfo.crossProfile = isCrossProfile();
appInfo.setBaseCodePath(baseCodePath);
appInfo.setBaseResourcePath(baseCodePath);
diff --git a/core/java/android/hardware/camera2/CameraCharacteristics.java b/core/java/android/hardware/camera2/CameraCharacteristics.java
index dfc4f0f6586c..b3a1ee2f9b69 100644
--- a/core/java/android/hardware/camera2/CameraCharacteristics.java
+++ b/core/java/android/hardware/camera2/CameraCharacteristics.java
@@ -2875,7 +2875,28 @@ public final class CameraCharacteristics extends CameraMetadata<CameraCharacteri
@NonNull
public static final Key<int[]> SCALER_AVAILABLE_ROTATE_AND_CROP_MODES =
new Key<int[]>("android.scaler.availableRotateAndCropModes", int[].class);
-
+ /**
+ * <p>An array of mandatory concurrent stream combinations.
+ * This is an app-readable conversion of the concurrent mandatory stream combination
+ * {@link android.hardware.camera2.CameraDevice#createCaptureSession tables}.</p>
+ * <p>The array of
+ * {@link android.hardware.camera2.params.MandatoryStreamCombination combinations} is
+ * generated according to the documented
+ * {@link android.hardware.camera2.CameraDevice#createCaptureSession guideline} for each device
+ * which has its Id present in the set returned by
+ * {@link android.hardware.camera2.CameraManager#getConcurrentStreamingCameraIds}.
+ * Clients can use the array as a quick reference to find an appropriate camera stream
+ * combination.
+ * The mandatory stream combination array will be {@code null} in case the device is not a part
+ * of at least one set of combinations returned by
+ * {@link android.hardware.camera2.CameraManager#getConcurrentStreamingCameraIds}.</p>
+ * <p><b>Optional</b> - The value for this key may be {@code null} on some devices.</p>
+ */
+ @PublicKey
+ @NonNull
+ @SyntheticKey
+ public static final Key<android.hardware.camera2.params.MandatoryStreamCombination[]> SCALER_MANDATORY_CONCURRENT_STREAM_COMBINATIONS =
+ new Key<android.hardware.camera2.params.MandatoryStreamCombination[]>("android.scaler.mandatoryConcurrentStreamCombinations", android.hardware.camera2.params.MandatoryStreamCombination[].class);
/**
* <p>The area of the image sensor which corresponds to active pixels after any geometric
* distortion correction has been applied.</p>
diff --git a/core/java/android/hardware/camera2/CameraDevice.java b/core/java/android/hardware/camera2/CameraDevice.java
index cc066812ba1f..24d931154533 100644
--- a/core/java/android/hardware/camera2/CameraDevice.java
+++ b/core/java/android/hardware/camera2/CameraDevice.java
@@ -680,6 +680,25 @@ public abstract class CameraDevice implements AutoCloseable {
* </table><br>
* </p>
*
+ *<p>Devices capable of streaming concurrently with other devices as described by
+ * {@link android.hardware.camera2.CameraManager#getConcurrentStreamingCameraIds} have the
+ * following guaranteed streams (when streaming concurrently with other devices)</p>
+ *
+ * <table>
+ * <tr><th colspan="5">Concurrent stream guaranteed configurations</th></tr>
+ * <tr><th colspan="2" id="rb">Target 1</th><th colspan="2" id="rb">Target 2</th><th rowspan="2">Sample use case(s)</th> </tr>
+ * <tr><th>Type</th><th id="rb">Max size</th><th>Type</th><th id="rb">Max size</th> </tr>
+ * <tr> <td>{@code YUV}</td><td id="rb">{@code MAXIMUM}</td> <td colspan="2" id="rb"></td> <td>In-app video / image processing.</td> </tr>
+ * <tr> <td>{@code PRIV}</td><td id="rb">{@code MAXIMUM}</td> <td colspan="2" id="rb"></td> <td>In-app viewfinder analysis.</td> </tr>
+ * <tr> <td>{@code YUV}</td><td id="rb">{@code MAXIMUM}</td> <td>{@code YUV }</td><td id="rb">{@code MAXIMUM}</td> <td>In-app video / processing with preview.</td> </tr>
+ * <tr> <td>{@code YUV}</td><td id="rb">{@code MAXIMUM}</td> <td>{@code PRIV }</td><td id="rb">{@code MAXIMUM}</td> <td>In-app video / processing with preview.</td> </tr>
+ * <tr> <td>{@code PRIV}</td><td id="rb">{@code MAXIMUM}</td> <td>{@code PRIV }</td><td id="rb">{@code MAXIMUM}</td> <td>Standard Recording.</td> </tr>
+ * </table><br>
+ * </p>
+ *
+ * <p> For guaranteed concurrent stream configurations, MAXIMUM refers to the camera device's
+ * resolution for that format from {@link StreamConfigurationMap#getOutputSizes} or
+ * 720p(1280X720) whichever is lower. </p>
* <p>MONOCHROME-capability ({@link CameraCharacteristics#REQUEST_AVAILABLE_CAPABILITIES}
* includes {@link CameraMetadata#REQUEST_AVAILABLE_CAPABILITIES_MONOCHROME MONOCHROME}) devices
* supporting {@link android.graphics.ImageFormat#Y8 Y8} support substituting {@code YUV}
diff --git a/core/java/android/hardware/camera2/CameraManager.java b/core/java/android/hardware/camera2/CameraManager.java
index 55025f0411f9..9ee56a928d81 100644
--- a/core/java/android/hardware/camera2/CameraManager.java
+++ b/core/java/android/hardware/camera2/CameraManager.java
@@ -31,6 +31,9 @@ import android.hardware.camera2.impl.CameraDeviceImpl;
import android.hardware.camera2.impl.CameraMetadataNative;
import android.hardware.camera2.legacy.CameraDeviceUserShim;
import android.hardware.camera2.legacy.LegacyMetadataMapper;
+import android.hardware.camera2.params.SessionConfiguration;
+import android.hardware.camera2.utils.CameraIdAndSessionConfiguration;
+import android.hardware.camera2.utils.ConcurrentCameraIdCombination;
import android.os.Binder;
import android.os.DeadObjectException;
import android.os.Handler;
@@ -40,6 +43,7 @@ import android.os.ServiceManager;
import android.os.ServiceSpecificException;
import android.os.SystemProperties;
import android.util.ArrayMap;
+import android.util.ArraySet;
import android.util.Log;
import android.util.Size;
import android.view.Display;
@@ -48,6 +52,7 @@ import android.view.WindowManager;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Comparator;
+import java.util.Map;
import java.util.Set;
import java.util.concurrent.Executor;
import java.util.concurrent.Executors;
@@ -126,6 +131,66 @@ public final class CameraManager {
}
/**
+ * Return the list of combinations of currently connected camera devices identifiers, which
+ * support configuring camera device sessions concurrently.
+ *
+ * <p>The set of combinations may include camera devices that may be in use by other camera API
+ * clients.</p>
+ *
+ * <p>The set of combinations doesn't contain physical cameras that can only be used as
+ * part of a logical multi-camera device.</p>
+ *
+ * @return The set of combinations of currently connected camera devices, that may have
+ * sessions configured concurrently. The set of combinations will be empty if no such
+ * combinations are supported by the camera subsystem.
+ *
+ * @throws CameraAccessException if the camera device has been disconnected.
+ */
+ @NonNull
+ public Set<Set<String>> getConcurrentStreamingCameraIds() throws CameraAccessException {
+ return CameraManagerGlobal.get().getConcurrentStreamingCameraIds();
+ }
+
+ /**
+ * Checks whether the provided set of camera devices and their corresponding
+ * {@link SessionConfiguration} can be configured concurrently.
+ *
+ * <p>This method performs a runtime check of the given {@link SessionConfiguration} and camera
+ * id combinations. The result confirms whether or not the passed session configurations can be
+ * successfully used to create camera capture sessions concurrently, on the given camera
+ * devices using {@link CameraDevice#createCaptureSession(SessionConfiguration)}.
+ * </p>
+ *
+ * <p>The method can be called at any point before, during and after active capture sessions.
+ * It will not impact normal camera behavior in any way and must complete significantly
+ * faster than creating a regular or constrained capture session.</p>
+ *
+ * <p>Although this method is faster than creating a new capture session, it is not intended
+ * to be used for exploring the entire space of supported concurrent stream combinations. The
+ * available mandatory concurrent stream combinations may be obtained by querying
+ * {@link #getCameraCharacteristics} for the key
+ * SCALER_MANDATORY_CONCURRENT_STREAM_COMBINATIONS. </p>
+ *
+ * <p>Note that session parameters will be ignored and calls to
+ * {@link SessionConfiguration#setSessionParameters} are not required.</p>
+ *
+ * @return {@code true} if the given combination of session configurations and corresponding
+ * camera ids are concurrently supported by the camera sub-system,
+ * {@code false} otherwise.
+ *
+ * @throws IllegalArgumentException if the set of camera devices provided is not a subset of
+ * those returned by getConcurrentStreamingCameraIds()
+ * @throws CameraAccessException if one of the camera devices queried is no longer connected.
+ */
+ @RequiresPermission(android.Manifest.permission.CAMERA)
+ public boolean isConcurrentSessionConfigurationSupported(
+ @NonNull Map<String, SessionConfiguration> cameraIdAndSessionConfig)
+ throws CameraAccessException {
+ return CameraManagerGlobal.get().isConcurrentSessionConfigurationSupported(
+ cameraIdAndSessionConfig);
+ }
+
+ /**
* Register a callback to be notified about camera device availability.
*
* <p>Registering the same callback again will replace the handler with the
@@ -336,8 +401,10 @@ public final class CameraManager {
} catch (NumberFormatException e) {
Log.e(TAG, "Failed to parse camera Id " + cameraId + " to integer");
}
+ boolean hasConcurrentStreams =
+ CameraManagerGlobal.get().cameraIdHasConcurrentStreamsLocked(cameraId);
+ info.setHasMandatoryConcurrentStreams(hasConcurrentStreams);
info.setDisplaySize(displaySize);
-
characteristics = new CameraCharacteristics(info);
}
} catch (ServiceSpecificException e) {
@@ -964,6 +1031,9 @@ public final class CameraManager {
private final ArrayMap<String, ArrayList<String>> mUnavailablePhysicalDevices =
new ArrayMap<String, ArrayList<String>>();
+ private final Set<Set<String>> mConcurrentCameraIdCombinations =
+ new ArraySet<Set<String>>();
+
// Registered availablility callbacks and their executors
private final ArrayMap<AvailabilityCallback, Executor> mCallbackMap =
new ArrayMap<AvailabilityCallback, Executor>();
@@ -1068,7 +1138,22 @@ public final class CameraManager {
} catch (RemoteException e) {
// Camera service is now down, leave mCameraService as null
}
+
+ try {
+ ConcurrentCameraIdCombination[] cameraIdCombinations =
+ cameraService.getConcurrentStreamingCameraIds();
+ for (ConcurrentCameraIdCombination comb : cameraIdCombinations) {
+ mConcurrentCameraIdCombinations.add(comb.getConcurrentCameraIdCombination());
+ }
+ } catch (ServiceSpecificException e) {
+ // Unexpected failure
+ throw new IllegalStateException("Failed to get concurrent camera id combinations",
+ e);
+ } catch (RemoteException e) {
+ // Camera service died in all probability
+ }
}
+
private String[] extractCameraIdListLocked() {
String[] cameraIds = null;
int idCount = 0;
@@ -1089,6 +1174,31 @@ public final class CameraManager {
}
return cameraIds;
}
+
+ private Set<Set<String>> extractConcurrentCameraIdListLocked() {
+ Set<Set<String>> concurrentCameraIds = new ArraySet<Set<String>>();
+ for (Set<String> cameraIds : mConcurrentCameraIdCombinations) {
+ Set<String> extractedCameraIds = new ArraySet<String>();
+ for (String cameraId : cameraIds) {
+ // if the camera id status is NOT_PRESENT or ENUMERATING; skip the device.
+ // TODO: Would a device status NOT_PRESENT ever be in the map ? it gets removed
+ // in the callback anyway.
+ Integer status = mDeviceStatus.get(cameraId);
+ if (status == null) {
+ // camera id not present
+ continue;
+ }
+ if (status == ICameraServiceListener.STATUS_ENUMERATING
+ || status == ICameraServiceListener.STATUS_NOT_PRESENT) {
+ continue;
+ }
+ extractedCameraIds.add(cameraId);
+ }
+ concurrentCameraIds.add(extractedCameraIds);
+ }
+ return concurrentCameraIds;
+ }
+
private static void sortCameraIds(String[] cameraIds) {
// The sort logic must match the logic in
// libcameraservice/common/CameraProviderManager.cpp::getAPI1CompatibleCameraDeviceIds
@@ -1214,6 +1324,88 @@ public final class CameraManager {
return cameraIds;
}
+ public @NonNull Set<Set<String>> getConcurrentStreamingCameraIds() {
+ Set<Set<String>> concurrentStreamingCameraIds = null;
+ synchronized (mLock) {
+ // Try to make sure we have an up-to-date list of concurrent camera devices.
+ connectCameraServiceLocked();
+ concurrentStreamingCameraIds = extractConcurrentCameraIdListLocked();
+ }
+ // TODO: Some sort of sorting ?
+ return concurrentStreamingCameraIds;
+ }
+
+ public boolean isConcurrentSessionConfigurationSupported(
+ @NonNull Map<String, SessionConfiguration> cameraIdsAndSessionConfigurations)
+ throws CameraAccessException {
+
+ if (cameraIdsAndSessionConfigurations == null) {
+ throw new IllegalArgumentException("cameraIdsAndSessionConfigurations was null");
+ }
+
+ int size = cameraIdsAndSessionConfigurations.size();
+ if (size == 0) {
+ throw new IllegalArgumentException("camera id and session combination is empty");
+ }
+
+ synchronized (mLock) {
+ // Go through all the elements and check if the camera ids are valid at least /
+ // belong to one of the combinations returned by getConcurrentStreamingCameraIds()
+ boolean subsetFound = false;
+ for (Set<String> combination : mConcurrentCameraIdCombinations) {
+ if (combination.containsAll(cameraIdsAndSessionConfigurations.keySet())) {
+ subsetFound = true;
+ }
+ }
+ if (!subsetFound) {
+ throw new IllegalArgumentException(
+ "The set of camera ids provided is not a subset of"
+ + "getConcurrentStreamingCameraIds");
+ }
+ CameraIdAndSessionConfiguration [] cameraIdsAndConfigs =
+ new CameraIdAndSessionConfiguration[size];
+ int i = 0;
+ for (Map.Entry<String, SessionConfiguration> pair :
+ cameraIdsAndSessionConfigurations.entrySet()) {
+ cameraIdsAndConfigs[i] =
+ new CameraIdAndSessionConfiguration(pair.getKey(), pair.getValue());
+ i++;
+ }
+ try {
+ return mCameraService.isConcurrentSessionConfigurationSupported(
+ cameraIdsAndConfigs);
+ } catch (ServiceSpecificException e) {
+ throwAsPublicException(e);
+ } catch (RemoteException e) {
+ // Camera service died - act as if the camera was disconnected
+ throw new CameraAccessException(CameraAccessException.CAMERA_DISCONNECTED,
+ "Camera service is currently unavailable", e);
+ }
+ }
+
+ return false;
+ }
+
+ /**
+ * Helper function to find out if a camera id is in the set of combinations returned by
+ * getConcurrentStreamingCameraIds()
+ * @param cameraId the unique identifier of the camera device to query
+ * @return Whether the camera device was found in the set of combinations returned by
+ * getConcurrentStreamingCameraIds
+ */
+ public boolean cameraIdHasConcurrentStreamsLocked(String cameraId) {
+ if (!mDeviceStatus.containsKey(cameraId)) {
+ Log.e(TAG, "cameraIdHasConcurrentStreamsLocked called on non existing camera id");
+ return false;
+ }
+ for (Set<String> comb : mConcurrentCameraIdCombinations) {
+ if (comb.contains(cameraId)) {
+ return true;
+ }
+ }
+ return false;
+ }
+
public void setTorchMode(String cameraId, boolean enabled) throws CameraAccessException {
synchronized(mLock) {
@@ -1698,6 +1890,8 @@ public final class CameraManager {
cameraId);
}
+ mConcurrentCameraIdCombinations.clear();
+
scheduleCameraServiceReconnectionLocked();
}
}
diff --git a/core/java/android/hardware/camera2/impl/CameraMetadataNative.java b/core/java/android/hardware/camera2/impl/CameraMetadataNative.java
index 3ae3d786af2a..aefe66fe5dec 100644
--- a/core/java/android/hardware/camera2/impl/CameraMetadataNative.java
+++ b/core/java/android/hardware/camera2/impl/CameraMetadataNative.java
@@ -621,6 +621,16 @@ public class CameraMetadataNative implements Parcelable {
}
});
sGetCommandMap.put(
+ CameraCharacteristics.SCALER_MANDATORY_CONCURRENT_STREAM_COMBINATIONS.getNativeKey(),
+ new GetCommand() {
+ @Override
+ @SuppressWarnings("unchecked")
+ public <T> T getValue(CameraMetadataNative metadata, Key<T> key) {
+ return (T) metadata.getMandatoryConcurrentStreamCombinations();
+ }
+ });
+
+ sGetCommandMap.put(
CameraCharacteristics.CONTROL_MAX_REGIONS_AE.getNativeKey(), new GetCommand() {
@Override
@SuppressWarnings("unchecked")
@@ -1247,7 +1257,8 @@ public class CameraMetadataNative implements Parcelable {
return ret;
}
- private MandatoryStreamCombination[] getMandatoryStreamCombinations() {
+ private MandatoryStreamCombination[] getMandatoryStreamCombinationsHelper(
+ boolean getConcurrent) {
int[] capabilities = getBase(CameraCharacteristics.REQUEST_AVAILABLE_CAPABILITIES);
ArrayList<Integer> caps = new ArrayList<Integer>();
caps.ensureCapacity(capabilities.length);
@@ -1257,7 +1268,13 @@ public class CameraMetadataNative implements Parcelable {
int hwLevel = getBase(CameraCharacteristics.INFO_SUPPORTED_HARDWARE_LEVEL);
MandatoryStreamCombination.Builder build = new MandatoryStreamCombination.Builder(
mCameraId, hwLevel, mDisplaySize, caps, getStreamConfigurationMap());
- List<MandatoryStreamCombination> combs = build.getAvailableMandatoryStreamCombinations();
+
+ List<MandatoryStreamCombination> combs = null;
+ if (getConcurrent) {
+ combs = build.getAvailableMandatoryConcurrentStreamCombinations();
+ } else {
+ combs = build.getAvailableMandatoryStreamCombinations();
+ }
if ((combs != null) && (!combs.isEmpty())) {
MandatoryStreamCombination[] combArray = new MandatoryStreamCombination[combs.size()];
combArray = combs.toArray(combArray);
@@ -1267,6 +1284,17 @@ public class CameraMetadataNative implements Parcelable {
return null;
}
+ private MandatoryStreamCombination[] getMandatoryConcurrentStreamCombinations() {
+ if (!mHasMandatoryConcurrentStreams) {
+ return null;
+ }
+ return getMandatoryStreamCombinationsHelper(true);
+ }
+
+ private MandatoryStreamCombination[] getMandatoryStreamCombinations() {
+ return getMandatoryStreamCombinationsHelper(false);
+ }
+
private StreamConfigurationMap getStreamConfigurationMap() {
StreamConfiguration[] configurations = getBase(
CameraCharacteristics.SCALER_AVAILABLE_STREAM_CONFIGURATIONS);
@@ -1614,6 +1642,7 @@ public class CameraMetadataNative implements Parcelable {
}
private int mCameraId = -1;
+ private boolean mHasMandatoryConcurrentStreams = false;
private Size mDisplaySize = new Size(0, 0);
/**
@@ -1628,6 +1657,18 @@ public class CameraMetadataNative implements Parcelable {
}
/**
+ * Set the current camera Id.
+ *
+ * @param hasMandatoryConcurrentStreams whether the metadata advertises mandatory concurrent
+ * streams.
+ *
+ * @hide
+ */
+ public void setHasMandatoryConcurrentStreams(boolean hasMandatoryConcurrentStreams) {
+ mHasMandatoryConcurrentStreams = hasMandatoryConcurrentStreams;
+ }
+
+ /**
* Set the current display size.
*
* @param displaySize The current display size.
@@ -1682,6 +1723,7 @@ public class CameraMetadataNative implements Parcelable {
public void swap(CameraMetadataNative other) {
nativeSwap(other);
mCameraId = other.mCameraId;
+ mHasMandatoryConcurrentStreams = other.mHasMandatoryConcurrentStreams;
mDisplaySize = other.mDisplaySize;
}
diff --git a/core/java/android/hardware/camera2/params/MandatoryStreamCombination.java b/core/java/android/hardware/camera2/params/MandatoryStreamCombination.java
index 23f18a80caf8..41e1443d6866 100644
--- a/core/java/android/hardware/camera2/params/MandatoryStreamCombination.java
+++ b/core/java/android/hardware/camera2/params/MandatoryStreamCombination.java
@@ -16,30 +16,27 @@
package android.hardware.camera2.params;
-import static com.android.internal.util.Preconditions.*;
import static android.hardware.camera2.params.StreamConfigurationMap.checkArgumentFormat;
-import android.annotation.IntRange;
+import static com.android.internal.util.Preconditions.*;
+
import android.annotation.NonNull;
import android.annotation.Nullable;
-import android.content.Context;
import android.graphics.ImageFormat;
import android.graphics.ImageFormat.Format;
import android.hardware.camera2.CameraCharacteristics;
-import android.hardware.camera2.CameraCharacteristics.Key;
import android.hardware.camera2.CameraDevice;
import android.hardware.camera2.CameraManager;
import android.hardware.camera2.CameraMetadata;
import android.hardware.camera2.params.StreamConfigurationMap;
import android.hardware.camera2.utils.HashCodeHelpers;
-import android.graphics.PixelFormat;
import android.media.CamcorderProfile;
-import android.util.Size;
import android.util.Log;
import android.util.Pair;
+import android.util.Size;
-import java.util.Arrays;
import java.util.ArrayList;
+import java.util.Arrays;
import java.util.Collections;
import java.util.Comparator;
import java.util.HashMap;
@@ -200,7 +197,6 @@ public final class MandatoryStreamCombination {
mDescription = description;
mIsReprocessable = isReprocessable;
}
-
/**
* Get the mandatory stream combination description.
*
@@ -271,7 +267,7 @@ public final class MandatoryStreamCombination {
mStreamsInformation.hashCode());
}
- private static enum SizeThreshold { VGA, PREVIEW, RECORD, MAXIMUM }
+ private static enum SizeThreshold { VGA, PREVIEW, RECORD, MAXIMUM, s720p }
private static enum ReprocessType { NONE, PRIVATE, YUV }
private static final class StreamTemplate {
public int mFormat;
@@ -653,6 +649,27 @@ public final class MandatoryStreamCombination {
/*reprocessType*/ ReprocessType.YUV),
};
+ private static StreamCombinationTemplate sConcurrentStreamCombinations[] = {
+ new StreamCombinationTemplate(new StreamTemplate [] {
+ new StreamTemplate(ImageFormat.YUV_420_888, SizeThreshold.s720p) },
+ "In-app video / image processing"),
+ new StreamCombinationTemplate(new StreamTemplate [] {
+ new StreamTemplate(ImageFormat.PRIVATE, SizeThreshold.s720p) },
+ "preview / preview to GPU"),
+ new StreamCombinationTemplate(new StreamTemplate [] {
+ new StreamTemplate(ImageFormat.YUV_420_888, SizeThreshold.s720p),
+ new StreamTemplate(ImageFormat.YUV_420_888, SizeThreshold.s720p)},
+ "In-app video / image processing with preview"),
+ new StreamCombinationTemplate(new StreamTemplate [] {
+ new StreamTemplate(ImageFormat.PRIVATE, SizeThreshold.s720p),
+ new StreamTemplate(ImageFormat.YUV_420_888, SizeThreshold.s720p)},
+ "In-app video / image processing with preview"),
+ new StreamCombinationTemplate(new StreamTemplate [] {
+ new StreamTemplate(ImageFormat.PRIVATE, SizeThreshold.s720p),
+ new StreamTemplate(ImageFormat.PRIVATE, SizeThreshold.s720p)},
+ "Standard Recording"),
+ };
+
/**
* Helper builder class to generate a list of available mandatory stream combinations.
* @hide
@@ -687,6 +704,64 @@ public final class MandatoryStreamCombination {
}
/**
+ * Retrieve a list of all available mandatory concurrent stream combinations.
+ * This method should only be called for devices which are listed in combinations returned
+ * by CameraManager.getConcurrentStreamingCameraIds.
+ *
+ * @return a non-modifiable list of supported mandatory concurrent stream combinations.
+ */
+ public @NonNull List<MandatoryStreamCombination>
+ getAvailableMandatoryConcurrentStreamCombinations() {
+ // Since concurrent streaming support is optional, we mandate these stream
+ // combinations regardless of camera device capabilities.
+ if (!isColorOutputSupported()) {
+ Log.v(TAG, "Device is not backward compatible!");
+ throw new IllegalArgumentException("Camera device which is not BACKWARD_COMPATIBLE"
+ + " cannot have mandatory concurrent streams");
+ }
+ Size size720p = new Size(1280, 720);
+
+ ArrayList<MandatoryStreamCombination> availableConcurrentStreamCombinations =
+ new ArrayList<MandatoryStreamCombination>();
+ availableConcurrentStreamCombinations.ensureCapacity(
+ sConcurrentStreamCombinations.length);
+ for (StreamCombinationTemplate combTemplate : sConcurrentStreamCombinations) {
+ ArrayList<MandatoryStreamInformation> streamsInfo =
+ new ArrayList<MandatoryStreamInformation>();
+ streamsInfo.ensureCapacity(combTemplate.mStreamTemplates.length);
+ for (StreamTemplate template : combTemplate.mStreamTemplates) {
+ MandatoryStreamInformation streamInfo;
+ List<Size> sizes = new ArrayList<Size>();
+ Size sizeChosen =
+ getMinSize(size720p,
+ getMaxSize(mStreamConfigMap.getOutputSizes(template.mFormat)));
+ sizes.add(sizeChosen);
+ try {
+ streamInfo = new MandatoryStreamInformation(sizes, template.mFormat);
+ } catch (IllegalArgumentException e) {
+ String cause = "No available sizes found for format: " + template.mFormat
+ + " size threshold: " + template.mSizeThreshold + " combination: "
+ + combTemplate.mDescription;
+ throw new RuntimeException(cause, e);
+ }
+ streamsInfo.add(streamInfo);
+ }
+
+ MandatoryStreamCombination streamCombination;
+ try {
+ streamCombination = new MandatoryStreamCombination(streamsInfo,
+ combTemplate.mDescription, /*isReprocess*/false);
+ } catch (IllegalArgumentException e) {
+ String cause = "No stream information for mandatory combination: "
+ + combTemplate.mDescription;
+ throw new RuntimeException(cause, e);
+ }
+ availableConcurrentStreamCombinations.add(streamCombination);
+ }
+ return Collections.unmodifiableList(availableConcurrentStreamCombinations);
+ }
+
+ /**
* Retrieve a list of all available mandatory stream combinations.
*
* @return a non-modifiable list of supported mandatory stream combinations or
@@ -965,6 +1040,18 @@ public final class MandatoryStreamCombination {
}
/**
+ * Return the lower size
+ */
+ public static @Nullable Size getMinSize(Size a, Size b) {
+ if (a == null || b == null) {
+ throw new IllegalArgumentException("sizes was empty");
+ }
+ if (a.getWidth() * a.getHeight() < b.getHeight() * b.getWidth()) {
+ return a;
+ }
+ return b;
+ }
+ /**
* Get the largest size by area.
*
* @param sizes an array of sizes, must have at least 1 element
diff --git a/core/java/android/hardware/camera2/utils/CameraIdAndSessionConfiguration.java b/core/java/android/hardware/camera2/utils/CameraIdAndSessionConfiguration.java
new file mode 100644
index 000000000000..cdc037cbd020
--- /dev/null
+++ b/core/java/android/hardware/camera2/utils/CameraIdAndSessionConfiguration.java
@@ -0,0 +1,85 @@
+/*
+ * 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.camera2.utils;
+
+import android.annotation.NonNull;
+import android.hardware.camera2.params.SessionConfiguration;
+import android.os.Parcel;
+import android.os.Parcelable;
+
+/**
+ * CameraIdAndSessionConfiguration
+ *
+ * Includes the pair of a cameraId and its corresponding SessionConfiguration, to be used with
+ * ICameraService.isConcurrentSessionConfigurationSupported.
+ * @hide
+ */
+public class CameraIdAndSessionConfiguration implements Parcelable {
+
+ private String mCameraId;
+ private SessionConfiguration mSessionConfiguration;
+
+ public CameraIdAndSessionConfiguration(@NonNull String cameraId,
+ @NonNull SessionConfiguration sessionConfiguration) {
+ mCameraId = cameraId;
+ mSessionConfiguration = sessionConfiguration;
+ }
+
+ public static final @NonNull
+ Parcelable.Creator<CameraIdAndSessionConfiguration> CREATOR =
+ new Parcelable.Creator<CameraIdAndSessionConfiguration>() {
+ @Override
+ public CameraIdAndSessionConfiguration createFromParcel(Parcel in) {
+ return new CameraIdAndSessionConfiguration(in);
+ }
+
+ @Override
+ public CameraIdAndSessionConfiguration[] newArray(int size) {
+ return new CameraIdAndSessionConfiguration[size];
+ }
+ };
+
+ private CameraIdAndSessionConfiguration(Parcel in) {
+ readFromParcel(in);
+ }
+
+ @Override
+ public int describeContents() {
+ return 0;
+ }
+
+ @Override
+ public void writeToParcel(Parcel dest, int flags) {
+ dest.writeString(mCameraId);
+ mSessionConfiguration.writeToParcel(dest, flags);
+ }
+
+ /**
+ * helper for CREATOR
+ */
+ public void readFromParcel(Parcel in) {
+ mCameraId = in.readString();
+ mSessionConfiguration = SessionConfiguration.CREATOR.createFromParcel(in);
+ }
+
+ public @NonNull String getCameraId() {
+ return mCameraId;
+ }
+
+ public @NonNull SessionConfiguration getSessionConfiguration() {
+ return mSessionConfiguration;
+ }
+}
diff --git a/core/java/android/hardware/camera2/utils/ConcurrentCameraIdCombination.java b/core/java/android/hardware/camera2/utils/ConcurrentCameraIdCombination.java
new file mode 100644
index 000000000000..8f4d6365f05e
--- /dev/null
+++ b/core/java/android/hardware/camera2/utils/ConcurrentCameraIdCombination.java
@@ -0,0 +1,91 @@
+/*
+ * 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.camera2.utils;
+
+import android.annotation.NonNull;
+import android.os.Parcel;
+import android.os.Parcelable;
+
+import java.util.HashSet;
+import java.util.Set;
+
+/**
+ * ConcurrentCameraIdCombination
+ *
+ * Includes a list of camera ids that may have sessions configured concurrently.
+ * @hide
+ */
+public class ConcurrentCameraIdCombination implements Parcelable {
+
+ private Set<String> mConcurrentCameraIds = new HashSet<>();
+
+ public static final @NonNull
+ Parcelable.Creator<ConcurrentCameraIdCombination> CREATOR =
+ new Parcelable.Creator<ConcurrentCameraIdCombination>() {
+ @Override
+ public ConcurrentCameraIdCombination createFromParcel(Parcel in) {
+ return new ConcurrentCameraIdCombination(in);
+ }
+
+ @Override
+ public ConcurrentCameraIdCombination[] newArray(int size) {
+ return new ConcurrentCameraIdCombination[size];
+ }
+ };
+
+ private ConcurrentCameraIdCombination(Parcel in) {
+ readFromParcel(in);
+ }
+
+ @Override
+ public int describeContents() {
+ return 0;
+ }
+
+ @Override
+ public void writeToParcel(Parcel dest, int flags) {
+ dest.writeInt(mConcurrentCameraIds.size());
+ for (String cameraId : mConcurrentCameraIds) {
+ dest.writeString(cameraId);
+ }
+ }
+
+ /**
+ * helper for CREATOR
+ */
+ public void readFromParcel(Parcel in) {
+ mConcurrentCameraIds.clear();
+ int cameraCombinationSize = in.readInt();
+ if (cameraCombinationSize < 0) {
+ throw new RuntimeException("cameraCombinationSize " + cameraCombinationSize
+ + " should not be negative");
+ }
+ for (int i = 0; i < cameraCombinationSize; i++) {
+ String cameraId = in.readString();
+ if (cameraId == null) {
+ throw new RuntimeException("Failed to read camera id from Parcel");
+ }
+ mConcurrentCameraIds.add(cameraId);
+ }
+ }
+
+ /**
+ * Get this concurrent camera id combination.
+ */
+ public Set<String> getConcurrentCameraIdCombination() {
+ return mConcurrentCameraIds;
+ }
+}
diff --git a/core/java/android/hardware/display/BrightnessConfiguration.java b/core/java/android/hardware/display/BrightnessConfiguration.java
index 13122d249b23..6412a0ce7219 100644
--- a/core/java/android/hardware/display/BrightnessConfiguration.java
+++ b/core/java/android/hardware/display/BrightnessConfiguration.java
@@ -61,7 +61,7 @@ public final class BrightnessConfiguration implements Parcelable {
private static final String ATTR_MODEL_LOWER_BOUND = "model-lower-bound";
private static final String ATTR_MODEL_UPPER_BOUND = "model-upper-bound";
/**
- * Returned from {@link #getShortTermModelTimeout()} if no timeout has been set.
+ * Returned from {@link #getShortTermModelTimeoutMillis()} if no timeout has been set.
* In this case the device will use the default timeout available in the
* {@link BrightnessConfiguration} returned from
* {@link DisplayManager#getDefaultBrightnessConfiguration()}.
@@ -160,7 +160,7 @@ public final class BrightnessConfiguration implements Parcelable {
* {@link #getShortTermModelUpperLuxMultiplier()} to decide whether to keep any adjustment
* the user has made to adaptive brightness.
*/
- public long getShortTermModelTimeout() {
+ public long getShortTermModelTimeoutMillis() {
return mShortTermModelTimeout;
}
@@ -326,7 +326,7 @@ public final class BrightnessConfiguration implements Parcelable {
builder.setDescription(description);
final boolean shouldCollectColorSamples = in.readBoolean();
builder.setShouldCollectColorSamples(shouldCollectColorSamples);
- builder.setShortTermModelTimeout(in.readLong());
+ builder.setShortTermModelTimeoutMillis(in.readLong());
builder.setShortTermModelLowerLuxMultiplier(in.readFloat());
builder.setShortTermModelUpperLuxMultiplier(in.readFloat());
return builder.build();
@@ -487,7 +487,7 @@ public final class BrightnessConfiguration implements Parcelable {
builder.addCorrectionByCategory(category, correction);
}
builder.setShouldCollectColorSamples(shouldCollectColorSamples);
- builder.setShortTermModelTimeout(shortTermModelTimeout);
+ builder.setShortTermModelTimeoutMillis(shortTermModelTimeout);
builder.setShortTermModelLowerLuxMultiplier(shortTermModelLowerLuxMultiplier);
builder.setShortTermModelUpperLuxMultiplier(shortTermModelUpperLuxMultiplier);
return builder.build();
@@ -673,8 +673,8 @@ public final class BrightnessConfiguration implements Parcelable {
* adjustment the user has made to adaptive brightness.
*/
@NonNull
- public Builder setShortTermModelTimeout(long shortTermModelTimeout) {
- mShortTermModelTimeout = shortTermModelTimeout;
+ public Builder setShortTermModelTimeoutMillis(long shortTermModelTimeoutMillis) {
+ mShortTermModelTimeout = shortTermModelTimeoutMillis;
return this;
}
diff --git a/core/java/android/hardware/location/ContextHubManager.java b/core/java/android/hardware/location/ContextHubManager.java
index 1001f800df3c..db16d24e0af1 100644
--- a/core/java/android/hardware/location/ContextHubManager.java
+++ b/core/java/android/hardware/location/ContextHubManager.java
@@ -19,6 +19,7 @@ import android.annotation.CallbackExecutor;
import android.annotation.IntDef;
import android.annotation.NonNull;
import android.annotation.Nullable;
+import android.annotation.RequiresFeature;
import android.annotation.RequiresPermission;
import android.annotation.SuppressLint;
import android.annotation.SystemApi;
@@ -26,6 +27,7 @@ import android.annotation.SystemService;
import android.app.PendingIntent;
import android.content.Context;
import android.content.Intent;
+import android.content.pm.PackageManager;
import android.os.Handler;
import android.os.HandlerExecutor;
import android.os.Looper;
@@ -52,6 +54,7 @@ import java.util.concurrent.Executor;
*/
@SystemApi
@SystemService(Context.CONTEXTHUB_SERVICE)
+@RequiresFeature(PackageManager.FEATURE_CONTEXTHUB)
public final class ContextHubManager {
private static final String TAG = "ContextHubManager";
diff --git a/core/java/android/os/BatteryStatsManager.java b/core/java/android/os/BatteryStatsManager.java
index 152141edb52d..f2e16b46422f 100644
--- a/core/java/android/os/BatteryStatsManager.java
+++ b/core/java/android/os/BatteryStatsManager.java
@@ -42,7 +42,7 @@ import java.lang.annotation.RetentionPolicy;
*/
@SystemApi
@SystemService(Context.BATTERY_STATS_SERVICE)
-public class BatteryStatsManager {
+public final class BatteryStatsManager {
/**
* Wifi states.
*
diff --git a/core/java/android/os/HwParcel.java b/core/java/android/os/HwParcel.java
index fb36e6f6978f..228548ad7802 100644
--- a/core/java/android/os/HwParcel.java
+++ b/core/java/android/os/HwParcel.java
@@ -38,6 +38,7 @@ import java.util.Arrays;
public class HwParcel {
private static final String TAG = "HwParcel";
+ /** @hide */
@IntDef(prefix = { "STATUS_" }, value = {
STATUS_SUCCESS,
})
diff --git a/core/java/android/os/IVibratorService.aidl b/core/java/android/os/IVibratorService.aidl
index 416d69229536..e201e43b5586 100644
--- a/core/java/android/os/IVibratorService.aidl
+++ b/core/java/android/os/IVibratorService.aidl
@@ -24,6 +24,8 @@ interface IVibratorService
{
boolean hasVibrator();
boolean hasAmplitudeControl();
+ boolean[] areEffectsSupported(in int[] effectIds);
+ boolean[] arePrimitivesSupported(in int[] primitiveIds);
boolean setAlwaysOnEffect(int uid, String opPkg, int alwaysOnId, in VibrationEffect effect,
in VibrationAttributes attributes);
void vibrate(int uid, String opPkg, in VibrationEffect effect,
diff --git a/core/java/android/os/PowerManager.java b/core/java/android/os/PowerManager.java
index 6a80788c81cc..f3d3837bbed1 100644
--- a/core/java/android/os/PowerManager.java
+++ b/core/java/android/os/PowerManager.java
@@ -1371,6 +1371,7 @@ public final class PowerManager {
* @throws UnsupportedOperationException if userspace reboot was requested on a device that
* doesn't support it.
*/
+ @RequiresPermission(permission.REBOOT)
public void reboot(@Nullable String reason) {
if (REBOOT_USERSPACE.equals(reason) && !isRebootingUserspaceSupported()) {
throw new UnsupportedOperationException(
@@ -1390,6 +1391,7 @@ public final class PowerManager {
* </p>
* @hide
*/
+ @RequiresPermission(permission.REBOOT)
public void rebootSafeMode() {
try {
mService.rebootSafeMode(false, true);
diff --git a/core/java/android/os/SystemVibrator.java b/core/java/android/os/SystemVibrator.java
index 8050454a8ac3..faf4a36ff577 100644
--- a/core/java/android/os/SystemVibrator.java
+++ b/core/java/android/os/SystemVibrator.java
@@ -16,6 +16,7 @@
package android.os;
+import android.annotation.NonNull;
import android.compat.annotation.UnsupportedAppUsage;
import android.content.Context;
import android.media.AudioAttributes;
@@ -104,6 +105,28 @@ public class SystemVibrator extends Vibrator {
}
@Override
+ public boolean[] areEffectsSupported(@VibrationEffect.EffectType int... effectIds) {
+ try {
+ return mService.areEffectsSupported(effectIds);
+ } catch (RemoteException e) {
+ Log.w(TAG, "Failed to query effect support");
+ }
+ return new boolean[effectIds.length];
+ }
+
+ @Override
+ public boolean[] arePrimitivesSupported(
+ @NonNull @VibrationEffect.Composition.Primitive int... primitiveIds) {
+ try {
+ return mService.arePrimitivesSupported(primitiveIds);
+ } catch (RemoteException e) {
+ Log.w(TAG, "Failed to query effect support");
+ }
+ return new boolean[primitiveIds.length];
+ }
+
+
+ @Override
public void cancel() {
if (mService == null) {
return;
diff --git a/core/java/android/os/TelephonyServiceManager.java b/core/java/android/os/TelephonyServiceManager.java
index c67dedb4ac17..1128f4cb538c 100644
--- a/core/java/android/os/TelephonyServiceManager.java
+++ b/core/java/android/os/TelephonyServiceManager.java
@@ -39,7 +39,7 @@ public class TelephonyServiceManager {
/**
* A class that exposes the methods to register and obtain each system service.
*/
- public final class ServiceRegisterer {
+ public static final class ServiceRegisterer {
private final String mServiceName;
/**
diff --git a/core/java/android/os/VibrationEffect.aidl b/core/java/android/os/VibrationEffect.aidl
index dcc79d798c3d..89478fac2f1a 100644
--- a/core/java/android/os/VibrationEffect.aidl
+++ b/core/java/android/os/VibrationEffect.aidl
@@ -17,6 +17,4 @@
package android.os;
parcelable VibrationEffect;
-parcelable VibrationEffect.OneShotVibration;
-parcelable VibrationEffect.WaveformVibration;
-parcelable VibrationEffect.EffectVibration;
+parcelable VibrationEffect.Composition.PrimitiveEffect; \ No newline at end of file
diff --git a/core/java/android/os/VibrationEffect.java b/core/java/android/os/VibrationEffect.java
index 75b4724c7d26..2d218f4f2541 100644
--- a/core/java/android/os/VibrationEffect.java
+++ b/core/java/android/os/VibrationEffect.java
@@ -16,7 +16,9 @@
package android.os;
+import android.annotation.FloatRange;
import android.annotation.IntDef;
+import android.annotation.IntRange;
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.annotation.TestApi;
@@ -28,9 +30,14 @@ import android.hardware.vibrator.V1_3.Effect;
import android.net.Uri;
import android.util.MathUtils;
+import com.android.internal.util.Preconditions;
+
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
+import java.util.ArrayList;
import java.util.Arrays;
+import java.util.List;
+import java.util.Objects;
/**
* A VibrationEffect describes a haptic effect to be performed by a {@link Vibrator}.
@@ -41,6 +48,8 @@ public abstract class VibrationEffect implements Parcelable {
private static final int PARCEL_TOKEN_ONE_SHOT = 1;
private static final int PARCEL_TOKEN_WAVEFORM = 2;
private static final int PARCEL_TOKEN_EFFECT = 3;
+ private static final int PARCEL_TOKEN_COMPOSITION = 4;
+
/**
* The default vibration strength of the device.
@@ -359,6 +368,16 @@ public abstract class VibrationEffect implements Parcelable {
return null;
}
+ /**
+ * Start composing a haptic effect.
+ *
+ * @see VibrationEffect.Composition
+ */
+ @NonNull
+ public static VibrationEffect.Composition startComposition() {
+ return new VibrationEffect.Composition();
+ }
+
@Override
public int describeContents() {
return 0;
@@ -839,6 +858,331 @@ public abstract class VibrationEffect implements Parcelable {
};
}
+ /** @hide */
+ public static final class Composed extends VibrationEffect implements Parcelable {
+ private final ArrayList<Composition.PrimitiveEffect> mPrimitiveEffects;
+
+ /**
+ * @hide
+ */
+ @SuppressWarnings("unchecked")
+ public Composed(@NonNull Parcel in) {
+ this(in.readArrayList(Composed.class.getClassLoader()));
+ }
+
+ /**
+ * @hide
+ */
+ public Composed(List<Composition.PrimitiveEffect> effects) {
+ mPrimitiveEffects = new ArrayList<>(Objects.requireNonNull(effects));
+ }
+
+ /**
+ * @hide
+ */
+ @NonNull
+ public List<Composition.PrimitiveEffect> getPrimitiveEffects() {
+ return mPrimitiveEffects;
+ }
+
+ @Override
+ public long getDuration() {
+ return -1;
+ }
+
+
+ /**
+ * @hide
+ */
+ @Override
+ public void validate() {
+ for (Composition.PrimitiveEffect effect : mPrimitiveEffects) {
+ Composition.checkPrimitive(effect.id);
+ Preconditions.checkArgumentInRange(
+ effect.scale, 0.0f, 1.0f, "scale");
+ }
+ }
+
+ @Override
+ public void writeToParcel(@NonNull Parcel out, int flags) {
+ out.writeInt(PARCEL_TOKEN_COMPOSITION);
+ out.writeList(mPrimitiveEffects);
+ }
+
+ @Override
+ public int describeContents() {
+ return 0;
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ if (this == o) return true;
+ if (o == null || getClass() != o.getClass()) return false;
+ Composed composed = (Composed) o;
+ return mPrimitiveEffects.equals(composed.mPrimitiveEffects);
+ }
+
+ @Override
+ public int hashCode() {
+ return Objects.hash(mPrimitiveEffects);
+ }
+
+ @Override
+ public String toString() {
+ return "Composed{mPrimitiveEffects=" + mPrimitiveEffects + '}';
+ }
+
+ public static final @NonNull Parcelable.Creator<Composed> CREATOR =
+ new Parcelable.Creator<Composed>() {
+ @Override
+ public Composed createFromParcel(@NonNull Parcel in) {
+ // Skip the type token
+ in.readInt();
+ return new Composed(in);
+ }
+
+ @Override
+ @NonNull
+ public Composed[] newArray(int size) {
+ return new Composed[size];
+ }
+ };
+ }
+
+ /**
+ * A composition of haptic primitives that, when combined, create a single haptic effect.
+ *
+ * @see VibrationEffect#startComposition()
+ */
+ public static class Composition {
+ /** @hide */
+ @IntDef(prefix = { "PRIMITIVE_" }, value = {
+ PRIMITIVE_CLICK,
+ PRIMITIVE_THUD,
+ PRIMITIVE_SPIN,
+ PRIMITIVE_QUICK_RISE,
+ PRIMITIVE_SLOW_RISE,
+ PRIMITIVE_QUICK_FALL,
+ PRIMITIVE_LIGHT_TICK,
+ })
+ @Retention(RetentionPolicy.SOURCE)
+ public @interface Primitive {}
+
+ /**
+ * No haptic effect. Used to generate extended delays between primitives.
+ * @hide
+ */
+ public static final int PRIMITIVE_NOOP = 0;
+ /**
+ * This effect should produce a sharp, crisp click sensation.
+ */
+ public static final int PRIMITIVE_CLICK = 1;
+ /**
+ * A haptic effect that simulates downwards movement with gravity. Often
+ * followed by extra energy of hitting and reverberation to augment
+ * physicality.
+ */
+ public static final int PRIMITIVE_THUD = 2;
+ /**
+ * A haptic effect that simulates spinning momentum.
+ */
+ public static final int PRIMITIVE_SPIN = 3;
+ /**
+ * A haptic effect that simulates quick upward movement against gravity.
+ */
+ public static final int PRIMITIVE_QUICK_RISE = 4;
+ /**
+ * A haptic effect that simulates slow upward movement against gravity.
+ */
+ public static final int PRIMITIVE_SLOW_RISE = 5;
+ /**
+ * A haptic effect that simulates quick downwards movement with gravity.
+ */
+ public static final int PRIMITIVE_QUICK_FALL = 6;
+ /**
+ * This very short effect should produce a light crisp sensation intended
+ * to be used repetitively for dynamic feedback.
+ */
+ public static final int PRIMITIVE_LIGHT_TICK = 7;
+
+
+ private ArrayList<PrimitiveEffect> mEffects = new ArrayList<>();
+
+ /**
+ * Add a haptic primitive to the end of the current composition.
+ *
+ * Similar to {@link #addPrimitive(int, float, int)}, but with no delay and a
+ * default scale applied.
+ *
+ * @param primitiveId The primitive to add
+ *
+ * @return The {@link Composition} object to enable adding multiple primitives in one chain.
+ */
+ @Nullable
+ public Composition addPrimitive(@Primitive int primitiveId) {
+ addPrimitive(primitiveId, /*scale*/ 1.0f, /*delay*/ 0);
+ return this;
+ }
+
+ /**
+ * Add a haptic primitive to the end of the current composition.
+ *
+ * Similar to {@link #addPrimitive(int, float, int)}, but with no delay.
+ *
+ * @param primitiveId The primitive to add
+ * @param scale The scale to apply to the intensity of the primitive.
+ *
+ * @return The {@link Composition} object to enable adding multiple primitives in one chain.
+ */
+ @Nullable
+ public Composition addPrimitive(@Primitive int primitiveId,
+ @FloatRange(from = 0f, to = 1f) float scale) {
+ addPrimitive(primitiveId, scale, /*delay*/ 0);
+ return this;
+ }
+
+ /**
+ * Add a haptic primitive to the end of the current composition.
+ *
+ * @param primitiveId The primitive to add
+ * @param scale The scale to apply to the intensity of the primitive.
+ * @param delay The amount of time, in milliseconds, to wait before playing the prior
+ * primitive and this one
+ * @return The {@link Composition} object to enable adding multiple primitives in one chain.
+ */
+ @Nullable
+ public Composition addPrimitive(@Primitive int primitiveId,
+ @FloatRange(from = 0f, to = 1f) float scale, @IntRange(from = 0) int delay) {
+ mEffects.add(new PrimitiveEffect(checkPrimitive(primitiveId), scale, delay));
+ return this;
+ }
+
+ /**
+ * Compose all of the added primitives together into a single {@link VibrationEffect}.
+ *
+ * The {@link Composition} object is still valid after this call, so you can continue adding
+ * more primitives to it and generating more {@link VibrationEffect}s by calling this method
+ * again.
+ *
+ * @return The {@link VibrationEffect} resulting from the composition of the primitives.
+ */
+ @NonNull
+ public VibrationEffect compose() {
+ if (mEffects.isEmpty()) {
+ throw new IllegalStateException(
+ "Composition must have at least one element to compose.");
+ }
+ return new VibrationEffect.Composed(mEffects);
+ }
+
+ /**
+ * @throws IllegalArgumentException throws if the primitive ID is not within the valid range
+ * @hide
+ *
+ */
+ static int checkPrimitive(int primitiveId) {
+ Preconditions.checkArgumentInRange(primitiveId, PRIMITIVE_NOOP, PRIMITIVE_LIGHT_TICK,
+ "primitiveId");
+ return primitiveId;
+ }
+
+ /**
+ * Convert the primitive ID to a human readable string for debugging
+ * @param id The ID to convert
+ * @return The ID in a human readable format.
+ * @hide
+ */
+ public static String primitiveToString(@Primitive int id) {
+ switch (id) {
+ case PRIMITIVE_NOOP:
+ return "PRIMITIVE_NOOP";
+ case PRIMITIVE_CLICK:
+ return "PRIMITIVE_CLICK";
+ case PRIMITIVE_THUD:
+ return "PRIMITIVE_THUD";
+ case PRIMITIVE_SPIN:
+ return "PRIMITIVE_SPIN";
+ case PRIMITIVE_QUICK_RISE:
+ return "PRIMITIVE_QUICK_RISE";
+ case PRIMITIVE_SLOW_RISE:
+ return "PRIMITIVE_SLOW_RISE";
+ case PRIMITIVE_QUICK_FALL:
+ return "PRIMITIVE_QUICK_FALL";
+ case PRIMITIVE_LIGHT_TICK:
+ return "PRIMITIVE_LIGHT_TICK";
+
+ default:
+ return Integer.toString(id);
+
+ }
+ }
+
+
+ /**
+ * @hide
+ */
+ public static class PrimitiveEffect implements Parcelable {
+ public int id;
+ public float scale;
+ public int delay;
+
+ PrimitiveEffect(int id, float scale, int delay) {
+ this.id = id;
+ this.scale = scale;
+ this.delay = delay;
+ }
+
+ @Override
+ public void writeToParcel(Parcel dest, int flags) {
+ dest.writeInt(id);
+ dest.writeFloat(scale);
+ dest.writeInt(delay);
+ }
+
+ @Override
+ public int describeContents() {
+ return 0;
+ }
+
+ @Override
+ public String toString() {
+ return "PrimitiveEffect{"
+ + "id=" + primitiveToString(id)
+ + ", scale=" + scale
+ + ", delay=" + delay
+ + '}';
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ if (this == o) return true;
+ if (o == null || getClass() != o.getClass()) return false;
+ PrimitiveEffect that = (PrimitiveEffect) o;
+ return id == that.id
+ && Float.compare(that.scale, scale) == 0
+ && delay == that.delay;
+ }
+
+ @Override
+ public int hashCode() {
+ return Objects.hash(id, scale, delay);
+ }
+
+
+ public static final @NonNull Parcelable.Creator<PrimitiveEffect> CREATOR =
+ new Parcelable.Creator<PrimitiveEffect>() {
+ @Override
+ public PrimitiveEffect createFromParcel(Parcel in) {
+ return new PrimitiveEffect(in.readInt(), in.readFloat(), in.readInt());
+ }
+ @Override
+ public PrimitiveEffect[] newArray(int size) {
+ return new PrimitiveEffect[size];
+ }
+ };
+ }
+ }
+
public static final @NonNull Parcelable.Creator<VibrationEffect> CREATOR =
new Parcelable.Creator<VibrationEffect>() {
@Override
@@ -850,6 +1194,8 @@ public abstract class VibrationEffect implements Parcelable {
return new Waveform(in);
} else if (token == PARCEL_TOKEN_EFFECT) {
return new Prebaked(in);
+ } else if (token == PARCEL_TOKEN_COMPOSITION) {
+ return new Composed(in);
} else {
throw new IllegalStateException(
"Unexpected vibration event type token in parcel.");
diff --git a/core/java/android/os/Vibrator.java b/core/java/android/os/Vibrator.java
index ae75f3d0d7e6..f055c60e6a41 100644
--- a/core/java/android/os/Vibrator.java
+++ b/core/java/android/os/Vibrator.java
@@ -17,6 +17,7 @@
package android.os;
import android.annotation.IntDef;
+import android.annotation.NonNull;
import android.annotation.Nullable;
import android.annotation.RequiresPermission;
import android.annotation.SystemService;
@@ -41,37 +42,42 @@ public abstract class Vibrator {
/**
* Vibration intensity: no vibrations.
+ *
* @hide
*/
public static final int VIBRATION_INTENSITY_OFF = 0;
/**
* Vibration intensity: low.
+ *
* @hide
*/
public static final int VIBRATION_INTENSITY_LOW = 1;
/**
* Vibration intensity: medium.
+ *
* @hide
*/
public static final int VIBRATION_INTENSITY_MEDIUM = 2;
/**
* Vibration intensity: high.
+ *
* @hide
*/
public static final int VIBRATION_INTENSITY_HIGH = 3;
/** @hide */
@Retention(RetentionPolicy.SOURCE)
- @IntDef(prefix = { "VIBRATION_INTENSITY_" }, value = {
- VIBRATION_INTENSITY_OFF,
- VIBRATION_INTENSITY_LOW,
- VIBRATION_INTENSITY_MEDIUM,
- VIBRATION_INTENSITY_HIGH
+ @IntDef(prefix = {"VIBRATION_INTENSITY_"}, value = {
+ VIBRATION_INTENSITY_OFF,
+ VIBRATION_INTENSITY_LOW,
+ VIBRATION_INTENSITY_MEDIUM,
+ VIBRATION_INTENSITY_HIGH
})
- public @interface VibrationIntensity{}
+ public @interface VibrationIntensity {
+ }
private final String mPackageName;
// The default vibration intensity level for haptic feedback.
@@ -117,6 +123,7 @@ public abstract class Vibrator {
/**
* Get the default vibration intensity for haptic feedback.
+ *
* @hide
*/
public int getDefaultHapticFeedbackIntensity() {
@@ -125,13 +132,16 @@ public abstract class Vibrator {
/**
* Get the default vibration intensity for notifications.
+ *
* @hide
*/
public int getDefaultNotificationVibrationIntensity() {
return mDefaultNotificationVibrationIntensity;
}
- /** Get the default vibration intensity for ringtones.
+ /**
+ * Get the default vibration intensity for ringtones.
+ *
* @hide
*/
public int getDefaultRingVibrationIntensity() {
@@ -156,11 +166,12 @@ public abstract class Vibrator {
* Configure an always-on haptics effect.
*
* @param alwaysOnId The board-specific always-on ID to configure.
- * @param effect Vibration effect to assign to always-on id. Passing null will disable it.
+ * @param effect Vibration effect to assign to always-on id. Passing null will disable it.
* @param attributes {@link AudioAttributes} corresponding to the vibration. For example,
- * specify {@link AudioAttributes#USAGE_ALARM} for alarm vibrations or
- * {@link AudioAttributes#USAGE_NOTIFICATION_RINGTONE} for
- * vibrations associated with incoming calls. May only be null when effect is null.
+ * specify {@link AudioAttributes#USAGE_ALARM} for alarm vibrations or
+ * {@link AudioAttributes#USAGE_NOTIFICATION_RINGTONE} for
+ * vibrations associated with incoming calls. May only be null when effect is
+ * null.
* @hide
*/
@RequiresPermission(android.Manifest.permission.VIBRATE_ALWAYS_ON)
@@ -183,7 +194,6 @@ public abstract class Vibrator {
* Vibrate constantly for the specified period of time.
*
* @param milliseconds The number of milliseconds to vibrate.
- *
* @deprecated Use {@link #vibrate(VibrationEffect)} instead.
*/
@Deprecated
@@ -196,11 +206,10 @@ public abstract class Vibrator {
* Vibrate constantly for the specified period of time.
*
* @param milliseconds The number of milliseconds to vibrate.
- * @param attributes {@link AudioAttributes} corresponding to the vibration. For example,
- * specify {@link AudioAttributes#USAGE_ALARM} for alarm vibrations or
- * {@link AudioAttributes#USAGE_NOTIFICATION_RINGTONE} for
- * vibrations associated with incoming calls.
- *
+ * @param attributes {@link AudioAttributes} corresponding to the vibration. For example,
+ * specify {@link AudioAttributes#USAGE_ALARM} for alarm vibrations or
+ * {@link AudioAttributes#USAGE_NOTIFICATION_RINGTONE} for
+ * vibrations associated with incoming calls.
* @deprecated Use {@link #vibrate(VibrationEffect, AudioAttributes)} instead.
*/
@Deprecated
@@ -231,9 +240,8 @@ public abstract class Vibrator {
* </p>
*
* @param pattern an array of longs of times for which to turn the vibrator on or off.
- * @param repeat the index into pattern at which to repeat, or -1 if
- * you don't want to repeat.
- *
+ * @param repeat the index into pattern at which to repeat, or -1 if
+ * you don't want to repeat.
* @deprecated Use {@link #vibrate(VibrationEffect)} instead.
*/
@Deprecated
@@ -256,14 +264,13 @@ public abstract class Vibrator {
* to start the repeat, or -1 to disable repeating.
* </p>
*
- * @param pattern an array of longs of times for which to turn the vibrator on or off.
- * @param repeat the index into pattern at which to repeat, or -1 if
- * you don't want to repeat.
+ * @param pattern an array of longs of times for which to turn the vibrator on or off.
+ * @param repeat the index into pattern at which to repeat, or -1 if
+ * you don't want to repeat.
* @param attributes {@link AudioAttributes} corresponding to the vibration. For example,
- * specify {@link AudioAttributes#USAGE_ALARM} for alarm vibrations or
- * {@link AudioAttributes#USAGE_NOTIFICATION_RINGTONE} for
- * vibrations associated with incoming calls.
- *
+ * specify {@link AudioAttributes#USAGE_ALARM} for alarm vibrations or
+ * {@link AudioAttributes#USAGE_NOTIFICATION_RINGTONE} for
+ * vibrations associated with incoming calls.
* @deprecated Use {@link #vibrate(VibrationEffect, AudioAttributes)} instead.
*/
@Deprecated
@@ -295,8 +302,9 @@ public abstract class Vibrator {
}
/**
- * Like {@link #vibrate(int, String, VibrationEffect, AudioAttributes)}, but allows the
+ * Like {@link #vibrate(VibrationEffect, AudioAttributes)}, but allows the
* caller to specify the vibration is owned by someone else and set reason for vibration.
+ *
* @hide
*/
@RequiresPermission(android.Manifest.permission.VIBRATE)
@@ -304,6 +312,85 @@ public abstract class Vibrator {
String reason, AudioAttributes attributes);
/**
+ * Query whether the vibrator supports the given effects.
+ *
+ * If the returned array is {@code null}, the hardware doesn't support querying its supported
+ * effects. It may support any or all effects, but there's no way to programmatically know
+ * whether a {@link #vibrate} call will be successful.
+ *
+ * If the returned array is non-null, then it will be the same length as the query array and
+ * the value at a given index will contain whether the effect at that same index in the
+ * querying array is supported or not.
+ *
+ * @param effectIds Which effects to query for.
+ * @return Whether the effects are supported. Null when the hardware doesn't tell us what it
+ * supports.
+ */
+ @Nullable
+ public boolean[] areEffectsSupported(
+ @NonNull @VibrationEffect.EffectType int... effectIds) {
+ return new boolean[effectIds.length];
+ }
+
+ /**
+ * Query whether the vibrator supports all of the given effects.
+ *
+ * If the result is {@code null}, the hardware doesn't support querying its supported
+ * effects. It may support any or all effects, but there's no way to programmatically know
+ * whether a {@link #vibrate} call will be successful.
+ *
+ * If the returned array is non-null, then it will return whether all of the effects are
+ * supported by the hardware.
+ *
+ * @param effectIds Which effects to query for.
+ * @return Whether the effects are supported. {@code null} when the hardware doesn't tell us
+ * what it supports.
+ */
+ @Nullable
+ public Boolean areAllEffectsSupported(
+ @NonNull @VibrationEffect.EffectType int... effectIds) {
+ for (boolean supported : areEffectsSupported(effectIds)) {
+ if (!supported) {
+ return false;
+ }
+ }
+ return true;
+ }
+
+
+ /**
+ * Query whether the vibrator supports the given primitives.
+ *
+ * The returned array will be the same length as the query array and the value at a given index
+ * will contain whether the effect at that same index in the querying array is supported or
+ * not.
+ *
+ * @param primitiveIds Which primitives to query for.
+ * @return Whether the primitives are supported.
+ */
+ @NonNull
+ public boolean[] arePrimitivesSupported(
+ @NonNull @VibrationEffect.Composition.Primitive int... primitiveIds) {
+ return new boolean[primitiveIds.length];
+ }
+
+ /**
+ * Query whether the vibrator supports all of the given primitives.
+ *
+ * @param primitiveIds Which primitives to query for.
+ * @return Whether primitives effects are supported.
+ */
+ public boolean areAllPrimitivesSupported(
+ @NonNull @VibrationEffect.Composition.Primitive int... primitiveIds) {
+ for (boolean supported : arePrimitivesSupported(primitiveIds)) {
+ if (!supported) {
+ return false;
+ }
+ }
+ return true;
+ }
+
+ /**
* Turn the vibrator off.
*/
@RequiresPermission(android.Manifest.permission.VIBRATE)
diff --git a/core/java/android/os/connectivity/WifiActivityEnergyInfo.java b/core/java/android/os/connectivity/WifiActivityEnergyInfo.java
index 664b6c87d339..016cc2f3cdad 100644
--- a/core/java/android/os/connectivity/WifiActivityEnergyInfo.java
+++ b/core/java/android/os/connectivity/WifiActivityEnergyInfo.java
@@ -16,7 +16,9 @@
package android.os.connectivity;
+import android.annotation.ElapsedRealtimeLong;
import android.annotation.IntDef;
+import android.annotation.IntRange;
import android.annotation.NonNull;
import android.annotation.SystemApi;
import android.app.ActivityThread;
@@ -37,14 +39,20 @@ import java.lang.annotation.RetentionPolicy;
*/
@SystemApi
public final class WifiActivityEnergyInfo implements Parcelable {
- private long mTimeSinceBootMillis;
+ @ElapsedRealtimeLong
+ private final long mTimeSinceBootMillis;
@StackState
- private int mStackState;
- private long mControllerTxDurationMillis;
- private long mControllerRxDurationMillis;
- private long mControllerScanDurationMillis;
- private long mControllerIdleDurationMillis;
- private long mControllerEnergyUsedMicroJoules;
+ private final int mStackState;
+ @IntRange(from = 0)
+ private final long mControllerTxDurationMillis;
+ @IntRange(from = 0)
+ private final long mControllerRxDurationMillis;
+ @IntRange(from = 0)
+ private final long mControllerScanDurationMillis;
+ @IntRange(from = 0)
+ private final long mControllerIdleDurationMillis;
+ @IntRange(from = 0)
+ private final long mControllerEnergyUsedMicroJoules;
/** @hide */
@Retention(RetentionPolicy.SOURCE)
@@ -67,7 +75,7 @@ public final class WifiActivityEnergyInfo implements Parcelable {
/**
* Constructor.
*
- * @param timeSinceBootMillis the time since boot, in milliseconds.
+ * @param timeSinceBootMillis the elapsed real time since boot, in milliseconds.
* @param stackState The current state of the Wifi Stack. One of {@link #STACK_STATE_INVALID},
* {@link #STACK_STATE_STATE_ACTIVE}, {@link #STACK_STATE_STATE_SCANNING},
* or {@link #STACK_STATE_STATE_IDLE}.
@@ -78,23 +86,27 @@ public final class WifiActivityEnergyInfo implements Parcelable {
* receiving.
*/
public WifiActivityEnergyInfo(
- long timeSinceBootMillis,
+ @ElapsedRealtimeLong long timeSinceBootMillis,
@StackState int stackState,
- long txDurationMillis,
- long rxDurationMillis,
- long scanDurationMillis,
- long idleDurationMillis) {
- mTimeSinceBootMillis = timeSinceBootMillis;
- mStackState = stackState;
- mControllerTxDurationMillis = txDurationMillis;
- mControllerRxDurationMillis = rxDurationMillis;
- mControllerScanDurationMillis = scanDurationMillis;
- mControllerIdleDurationMillis = idleDurationMillis;
-
+ @IntRange(from = 0) long txDurationMillis,
+ @IntRange(from = 0) long rxDurationMillis,
+ @IntRange(from = 0) long scanDurationMillis,
+ @IntRange(from = 0) long idleDurationMillis) {
+
+ this(timeSinceBootMillis,
+ stackState,
+ txDurationMillis,
+ rxDurationMillis,
+ scanDurationMillis,
+ idleDurationMillis,
+ calculateEnergyMicroJoules(txDurationMillis, rxDurationMillis, idleDurationMillis));
+ }
+
+ private static long calculateEnergyMicroJoules(
+ long txDurationMillis, long rxDurationMillis, long idleDurationMillis) {
final Context context = ActivityThread.currentActivityThread().getSystemContext();
if (context == null) {
- mControllerEnergyUsedMicroJoules = 0L;
- return;
+ return 0L;
}
// Calculate energy used using PowerProfile.
PowerProfile powerProfile = new PowerProfile(context);
@@ -106,10 +118,28 @@ public final class WifiActivityEnergyInfo implements Parcelable {
PowerProfile.POWER_WIFI_CONTROLLER_TX);
final double voltage = powerProfile.getAveragePower(
PowerProfile.POWER_WIFI_CONTROLLER_OPERATING_VOLTAGE) / 1000.0;
- final long energyUsedMicroJoules = (long) ((mControllerTxDurationMillis * txCurrent
- + mControllerRxDurationMillis * rxCurrent
- + mControllerIdleDurationMillis * rxIdleCurrent)
+
+ return (long) ((txDurationMillis * txCurrent
+ + rxDurationMillis * rxCurrent
+ + idleDurationMillis * rxIdleCurrent)
* voltage);
+ }
+
+ /** @hide */
+ public WifiActivityEnergyInfo(
+ @ElapsedRealtimeLong long timeSinceBootMillis,
+ @StackState int stackState,
+ @IntRange(from = 0) long txDurationMillis,
+ @IntRange(from = 0) long rxDurationMillis,
+ @IntRange(from = 0) long scanDurationMillis,
+ @IntRange(from = 0) long idleDurationMillis,
+ @IntRange(from = 0) long energyUsedMicroJoules) {
+ mTimeSinceBootMillis = timeSinceBootMillis;
+ mStackState = stackState;
+ mControllerTxDurationMillis = txDurationMillis;
+ mControllerRxDurationMillis = rxDurationMillis;
+ mControllerScanDurationMillis = scanDurationMillis;
+ mControllerIdleDurationMillis = idleDurationMillis;
mControllerEnergyUsedMicroJoules = energyUsedMicroJoules;
}
@@ -158,16 +188,12 @@ public final class WifiActivityEnergyInfo implements Parcelable {
return 0;
}
- /** Get the timestamp (milliseconds since boot) of record creation. */
+ /** Get the timestamp (elapsed real time milliseconds since boot) of record creation. */
+ @ElapsedRealtimeLong
public long getTimeSinceBootMillis() {
return mTimeSinceBootMillis;
}
- /** Set the timestamp (milliseconds since boot) of record creation. */
- public void setTimeSinceBootMillis(long timeSinceBootMillis) {
- mTimeSinceBootMillis = timeSinceBootMillis;
- }
-
/**
* Get the Wifi stack reported state. One of {@link #STACK_STATE_INVALID},
* {@link #STACK_STATE_STATE_ACTIVE}, {@link #STACK_STATE_STATE_SCANNING},
@@ -178,66 +204,40 @@ public final class WifiActivityEnergyInfo implements Parcelable {
return mStackState;
}
- /**
- * Set the Wifi stack reported state. One of {@link #STACK_STATE_INVALID},
- * {@link #STACK_STATE_STATE_ACTIVE}, {@link #STACK_STATE_STATE_SCANNING},
- * {@link #STACK_STATE_STATE_IDLE}.
- */
- public void setStackState(@StackState int stackState) {
- mStackState = stackState;
- }
-
/** Get the Wifi transmission duration, in milliseconds. */
+ @IntRange(from = 0)
public long getControllerTxDurationMillis() {
return mControllerTxDurationMillis;
}
- /** Set the Wifi transmission duration, in milliseconds. */
- public void setControllerTxDurationMillis(long controllerTxDurationMillis) {
- mControllerTxDurationMillis = controllerTxDurationMillis;
- }
-
/** Get the Wifi receive duration, in milliseconds. */
+ @IntRange(from = 0)
public long getControllerRxDurationMillis() {
return mControllerRxDurationMillis;
}
- /** Set the Wifi receive duration, in milliseconds. */
- public void setControllerRxDurationMillis(long controllerRxDurationMillis) {
- mControllerRxDurationMillis = controllerRxDurationMillis;
- }
-
/** Get the Wifi scan duration, in milliseconds. */
+ @IntRange(from = 0)
public long getControllerScanDurationMillis() {
return mControllerScanDurationMillis;
}
- /** Set the Wifi scan duration, in milliseconds. */
- public void setControllerScanDurationMillis(long controllerScanDurationMillis) {
- mControllerScanDurationMillis = controllerScanDurationMillis;
- }
-
/** Get the Wifi idle duration, in milliseconds. */
+ @IntRange(from = 0)
public long getControllerIdleDurationMillis() {
return mControllerIdleDurationMillis;
}
- /** Set the Wifi idle duration, in milliseconds. */
- public void setControllerIdleDurationMillis(long controllerIdleDurationMillis) {
- mControllerIdleDurationMillis = controllerIdleDurationMillis;
- }
-
/** Get the energy consumed by Wifi, in microjoules. */
+ @IntRange(from = 0)
public long getControllerEnergyUsedMicroJoules() {
return mControllerEnergyUsedMicroJoules;
}
- /** Set the energy consumed by Wifi, in microjoules. */
- public void setControllerEnergyUsedMicroJoules(long controllerEnergyUsedMicroJoules) {
- mControllerEnergyUsedMicroJoules = controllerEnergyUsedMicroJoules;
- }
-
- /** Returns true if the record is valid, false otherwise. */
+ /**
+ * Returns true if the record is valid, false otherwise.
+ * @hide
+ */
public boolean isValid() {
return mControllerTxDurationMillis >= 0
&& mControllerRxDurationMillis >= 0
diff --git a/core/java/android/os/incremental/IIncrementalManager.aidl b/core/java/android/os/incremental/IIncrementalManager.aidl
index 17a310a5beb0..b415bc02fbcc 100644
--- a/core/java/android/os/incremental/IIncrementalManager.aidl
+++ b/core/java/android/os/incremental/IIncrementalManager.aidl
@@ -33,5 +33,7 @@ interface IIncrementalManager {
boolean startDataLoader(int mountId);
void showHealthBlockedUI(int mountId);
void destroyDataLoader(int mountId);
- void newFileForDataLoader(int mountId, long inode, in byte[] metadata);
+
+ // fileId is a 16 byte long identifier.
+ void newFileForDataLoader(int mountId, in byte[] fileId, in byte[] metadata);
}
diff --git a/core/java/android/os/incremental/IIncrementalManagerNative.aidl b/core/java/android/os/incremental/IIncrementalManagerNative.aidl
index 14215b1ea84d..2b6cd1478da8 100644
--- a/core/java/android/os/incremental/IIncrementalManagerNative.aidl
+++ b/core/java/android/os/incremental/IIncrementalManagerNative.aidl
@@ -17,6 +17,7 @@
package android.os.incremental;
import android.content.pm.DataLoaderParamsParcel;
+import android.os.incremental.IncrementalNewFileParams;
/** @hide */
interface IIncrementalManagerNative {
@@ -40,7 +41,7 @@ interface IIncrementalManagerNative {
*/
const int BIND_TEMPORARY = 0;
const int BIND_PERMANENT = 1;
- int makeBindMount(int storageId, in @utf8InCpp String pathUnderStorage, in @utf8InCpp String targetFullPath, int bindType);
+ int makeBindMount(int storageId, in @utf8InCpp String sourcePath, in @utf8InCpp String targetFullPath, int bindType);
/**
* Deletes an existing bind mount on a path under a storage. Returns 0 on success, and -errno on failure.
@@ -48,49 +49,50 @@ interface IIncrementalManagerNative {
int deleteBindMount(int storageId, in @utf8InCpp String targetFullPath);
/**
- * Creates a directory under a storage. The target directory is specified by its relative path under the storage.
+ * Creates a directory under a storage. The target directory is specified by its path.
*/
- int makeDirectory(int storageId, in @utf8InCpp String pathUnderStorage);
+ int makeDirectory(int storageId, in @utf8InCpp String path);
/**
- * Recursively creates a directory under a storage. The target directory is specified by its relative path under the storage.
+ * Recursively creates a directory under a storage. The target directory is specified by its path.
* All the parent directories of the target directory will be created if they do not exist already.
*/
- int makeDirectories(int storageId, in @utf8InCpp String pathUnderStorage);
+ int makeDirectories(int storageId, in @utf8InCpp String path);
/**
- * Creates a file under a storage, specifying its name, size and metadata.
+ * Creates a file under a storage.
*/
- int makeFile(int storageId, in @utf8InCpp String pathUnderStorage, long size, in byte[] metadata);
+ int makeFile(int storageId, in @utf8InCpp String path, in IncrementalNewFileParams params);
/**
* Creates a file under a storage. Content of the file is from a range inside another file.
- * Both files are specified by relative paths under storage.
+ * Both files are specified by their paths.
*/
- int makeFileFromRange(int storageId, in @utf8InCpp String targetPathUnderStorage, in @utf8InCpp String sourcePathUnderStorage, long start, long end);
+ int makeFileFromRange(int storageId, in @utf8InCpp String targetPath, in @utf8InCpp String sourcePath, long start, long end);
/**
* Creates a hard link between two files in two storage instances.
- * Source and dest specified by parent storage IDs and their relative paths under the storage.
+ * Source and dest specified by parent storage IDs and their paths.
* The source and dest storage instances should be in the same fs mount.
* Note: destStorageId can be the same as sourceStorageId.
*/
- int makeLink(int sourceStorageId, in @utf8InCpp String sourcePathUnderStorage, int destStorageId, in @utf8InCpp String destPathUnderStorage);
+ int makeLink(int sourceStorageId, in @utf8InCpp String sourcePath, int destStorageId, in @utf8InCpp String destPath);
/**
- * Deletes a hard link in a storage, specified by the relative path of the link target under storage.
+ * Deletes a hard link in a storage, specified by its path.
*/
- int unlink(int storageId, in @utf8InCpp String pathUnderStorage);
+ int unlink(int storageId, in @utf8InCpp String path);
/**
- * Checks if a file's certain range is loaded. File is specified by relative file path under storage.
+ * Checks if a file's certain range is loaded. File is specified by its path.
*/
- boolean isFileRangeLoaded(int storageId, in @utf8InCpp String pathUnderStorage, long start, long end);
+ boolean isFileRangeLoaded(int storageId, in @utf8InCpp String path, long start, long end);
/**
- * Reads the metadata of a file. File is specified by relative path under storage.
+ * Reads the metadata of a file. File is specified by either its path or 16 byte id.
*/
- byte[] getFileMetadata(int storageId, in @utf8InCpp String pathUnderStorage);
+ byte[] getMetadataByPath(int storageId, in @utf8InCpp String path);
+ byte[] getMetadataById(int storageId, in byte[] fileId);
/**
* Starts loading data for a storage.
diff --git a/core/java/android/os/incremental/IncrementalFileStorages.java b/core/java/android/os/incremental/IncrementalFileStorages.java
index 63335a08f003..a0bfc1bf56b8 100644
--- a/core/java/android/os/incremental/IncrementalFileStorages.java
+++ b/core/java/android/os/incremental/IncrementalFileStorages.java
@@ -134,8 +134,8 @@ public final class IncrementalFileStorages {
}
if (!new File(mDefaultDir, apk.getName()).exists()) {
- mDefaultStorage.makeFile(apk.getName(), apk.getSize(),
- apk.getMetadata());
+ mDefaultStorage.makeFile(apk.getName(), apk.getSize(), null,
+ apk.getMetadata(), 0, null, null, null);
}
// Assuming APK files are already named properly, e.g., "base.apk"
mDefaultStorage.makeLink(apk.getName(), mApkStorage, apk.getName());
@@ -167,7 +167,8 @@ public final class IncrementalFileStorages {
current += '/';
}
String libFilePath = current + Paths.get(lib.getName()).getFileName();
- mDefaultStorage.makeFile(libFilePath, lib.getSize(), lib.getMetadata());
+ mDefaultStorage.makeFile(libFilePath, lib.getSize(), null, lib.getMetadata(), 0, null, null,
+ null);
mDefaultStorage.makeLink(libFilePath, mApkStorage, libFilePath);
}
@@ -183,7 +184,8 @@ public final class IncrementalFileStorages {
IncrementalManager.CREATE_MODE_CREATE
| IncrementalManager.CREATE_MODE_TEMPORARY_BIND);
}
- mDefaultStorage.makeFile(obb.getName(), obb.getSize(), obb.getMetadata());
+ mDefaultStorage.makeFile(obb.getName(), obb.getSize(), null, obb.getMetadata(), 0, null,
+ null, null);
mDefaultStorage.makeLink(obb.getName(), mObbStorage, obb.getName());
}
diff --git a/core/java/android/os/incremental/IncrementalFileSystemControlParcel.aidl b/core/java/android/os/incremental/IncrementalFileSystemControlParcel.aidl
index 0ae353d2741f..6018ad1efc4a 100644
--- a/core/java/android/os/incremental/IncrementalFileSystemControlParcel.aidl
+++ b/core/java/android/os/incremental/IncrementalFileSystemControlParcel.aidl
@@ -17,11 +17,12 @@
package android.os.incremental;
/**
- * Wraps two file descriptors that Incremental Service uses to communicate
+ * Wraps the file descriptors Incremental Service uses to communicate
* with Incremental FileSystem.
* @hide
*/
parcelable IncrementalFileSystemControlParcel {
- @nullable ParcelFileDescriptor cmd;
- @nullable ParcelFileDescriptor log;
+ ParcelFileDescriptor cmd;
+ ParcelFileDescriptor pendingReads;
+ ParcelFileDescriptor log;
}
diff --git a/core/java/android/os/incremental/IncrementalNewFileParams.aidl b/core/java/android/os/incremental/IncrementalNewFileParams.aidl
new file mode 100644
index 000000000000..182732cebdf1
--- /dev/null
+++ b/core/java/android/os/incremental/IncrementalNewFileParams.aidl
@@ -0,0 +1,31 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.os.incremental;
+
+import android.os.incremental.IncrementalSignature;
+
+/**
+ * All the parameters to create a new file on IncFS
+ * FileId is a 16 byte-long identifier.
+ * @hide
+ */
+parcelable IncrementalNewFileParams {
+ long size;
+ byte[] fileId;
+ byte[] metadata;
+ @nullable IncrementalSignature signature;
+}
diff --git a/core/java/android/os/incremental/IncrementalSignature.aidl b/core/java/android/os/incremental/IncrementalSignature.aidl
new file mode 100644
index 000000000000..729e8e5556a8
--- /dev/null
+++ b/core/java/android/os/incremental/IncrementalSignature.aidl
@@ -0,0 +1,31 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.os.incremental;
+
+/** {@hide} */
+parcelable IncrementalSignature {
+ /*
+ * Stable AIDL doesn't support constants, but here's the possible values
+ * const int HASH_ALGO_NONE = 0;
+ * const int HASH_ALGO_SHA256 = 1;
+ */
+
+ int hashAlgorithm = 0;
+ byte[] rootHash;
+ byte[] additionalData;
+ byte[] signature;
+}
diff --git a/core/java/android/os/incremental/IncrementalStorage.java b/core/java/android/os/incremental/IncrementalStorage.java
index 275086832c51..91dda0899a63 100644
--- a/core/java/android/os/incremental/IncrementalStorage.java
+++ b/core/java/android/os/incremental/IncrementalStorage.java
@@ -22,6 +22,8 @@ import android.os.RemoteException;
import java.io.File;
import java.io.IOException;
+import java.nio.ByteBuffer;
+import java.util.UUID;
/**
* Provides operations on an Incremental File System directory, using IncrementalServiceNative.
@@ -64,14 +66,14 @@ public final class IncrementalStorage {
* Temporarily bind-mounts a subdir under the current storage directory to a target directory.
* The bind-mount will NOT be preserved between device reboots.
*
- * @param sourcePathUnderStorage Source path as a relative path under current storage
- * directory.
- * @param targetPath Absolute path to the target directory.
+ * @param sourcePath Source path as a relative path under current storage
+ * directory.
+ * @param targetPath Absolute path to the target directory.
*/
- public void bind(@NonNull String sourcePathUnderStorage, @NonNull String targetPath)
+ public void bind(@NonNull String sourcePath, @NonNull String targetPath)
throws IOException {
try {
- int res = mService.makeBindMount(mId, sourcePathUnderStorage, targetPath,
+ int res = mService.makeBindMount(mId, sourcePath, targetPath,
IIncrementalManagerNative.BIND_TEMPORARY);
if (res < 0) {
throw new IOException("bind() failed with errno " + -res);
@@ -96,13 +98,13 @@ public final class IncrementalStorage {
* Permanently bind-mounts a subdir under the current storage directory to a target directory.
* The bind-mount WILL be preserved between device reboots.
*
- * @param sourcePathUnderStorage Relative path under the current storage directory.
- * @param targetPath Absolute path to the target directory.
+ * @param sourcePath Relative path under the current storage directory.
+ * @param targetPath Absolute path to the target directory.
*/
- public void bindPermanent(@NonNull String sourcePathUnderStorage, @NonNull String targetPath)
+ public void bindPermanent(@NonNull String sourcePath, @NonNull String targetPath)
throws IOException {
try {
- int res = mService.makeBindMount(mId, sourcePathUnderStorage, targetPath,
+ int res = mService.makeBindMount(mId, sourcePath, targetPath,
IIncrementalManagerNative.BIND_PERMANENT);
if (res < 0) {
throw new IOException("bind() permanent failed with errno " + -res);
@@ -131,11 +133,11 @@ public final class IncrementalStorage {
/**
* Creates a sub-directory under the current storage directory.
*
- * @param pathUnderStorage Relative path of the sub-directory, e.g., "subdir"
+ * @param path Relative path of the sub-directory, e.g., "subdir"
*/
- public void makeDirectory(@NonNull String pathUnderStorage) throws IOException {
+ public void makeDirectory(@NonNull String path) throws IOException {
try {
- int res = mService.makeDirectory(mId, pathUnderStorage);
+ int res = mService.makeDirectory(mId, path);
if (res < 0) {
throw new IOException("makeDirectory() failed with errno " + -res);
}
@@ -148,11 +150,11 @@ public final class IncrementalStorage {
* Creates a sub-directory under the current storage directory. If its parent dirs do not exist,
* create the parent dirs as well.
*
- * @param pathUnderStorage Relative path of the sub-directory, e.g., "subdir/subsubdir"
+ * @param path Full path.
*/
- public void makeDirectories(@NonNull String pathUnderStorage) throws IOException {
+ public void makeDirectories(@NonNull String path) throws IOException {
try {
- int res = mService.makeDirectories(mId, pathUnderStorage);
+ int res = mService.makeDirectories(mId, path);
if (res < 0) {
throw new IOException("makeDirectory() failed with errno " + -res);
}
@@ -164,15 +166,27 @@ public final class IncrementalStorage {
/**
* Creates a file under the current storage directory.
*
- * @param pathUnderStorage Relative path of the new file.
+ * @param path Relative path of the new file.
* @param size Size of the new file in bytes.
* @param metadata Metadata bytes.
*/
- public void makeFile(@NonNull String pathUnderStorage, long size,
- @Nullable byte[] metadata) throws IOException {
+ public void makeFile(@NonNull String path, long size, @Nullable UUID id,
+ @Nullable byte[] metadata, int hashAlgorithm, @Nullable byte[] rootHash,
+ @Nullable byte[] additionalData, @Nullable byte[] signature) throws IOException {
try {
- int res = mService.makeFile(mId, pathUnderStorage, size, metadata);
- if (res < 0) {
+ final IncrementalNewFileParams params = new IncrementalNewFileParams();
+ params.size = size;
+ params.metadata = metadata;
+ params.fileId = idToBytes(id);
+ if (hashAlgorithm != 0 || signature != null) {
+ params.signature = new IncrementalSignature();
+ params.signature.hashAlgorithm = hashAlgorithm;
+ params.signature.rootHash = rootHash;
+ params.signature.additionalData = additionalData;
+ params.signature.signature = signature;
+ }
+ int res = mService.makeFile(mId, path, params);
+ if (res != 0) {
throw new IOException("makeFile() failed with errno " + -res);
}
} catch (RemoteException e) {
@@ -184,15 +198,15 @@ public final class IncrementalStorage {
* Creates a file in Incremental storage. The content of the file is mapped from a range inside
* a source file in the same storage.
*
- * @param destRelativePath Target relative path under storage.
- * @param sourceRelativePath Source relative path under storage.
+ * @param destPath Target full path.
+ * @param sourcePath Source full path.
* @param rangeStart Starting offset (in bytes) in the source file.
* @param rangeEnd Ending offset (in bytes) in the source file.
*/
- public void makeFileFromRange(@NonNull String destRelativePath,
- @NonNull String sourceRelativePath, long rangeStart, long rangeEnd) throws IOException {
+ public void makeFileFromRange(@NonNull String destPath,
+ @NonNull String sourcePath, long rangeStart, long rangeEnd) throws IOException {
try {
- int res = mService.makeFileFromRange(mId, destRelativePath, sourceRelativePath,
+ int res = mService.makeFileFromRange(mId, destPath, sourcePath,
rangeStart, rangeEnd);
if (res < 0) {
throw new IOException("makeFileFromRange() failed, errno " + -res);
@@ -206,15 +220,15 @@ public final class IncrementalStorage {
* Creates a hard-link between two paths, which can be under different storages but in the same
* Incremental File System.
*
- * @param sourcePathUnderStorage The relative path of the source.
- * @param destStorage The target storage of the link target.
- * @param destPathUnderStorage The relative path of the target.
+ * @param sourcePath The absolute path of the source.
+ * @param destStorage The target storage of the link target.
+ * @param destPath The absolute path of the target.
*/
- public void makeLink(@NonNull String sourcePathUnderStorage, IncrementalStorage destStorage,
- @NonNull String destPathUnderStorage) throws IOException {
+ public void makeLink(@NonNull String sourcePath, IncrementalStorage destStorage,
+ @NonNull String destPath) throws IOException {
try {
- int res = mService.makeLink(mId, sourcePathUnderStorage, destStorage.getId(),
- destPathUnderStorage);
+ int res = mService.makeLink(mId, sourcePath, destStorage.getId(),
+ destPath);
if (res < 0) {
throw new IOException("makeLink() failed with errno " + -res);
}
@@ -226,11 +240,11 @@ public final class IncrementalStorage {
/**
* Deletes a hard-link under the current storage directory.
*
- * @param pathUnderStorage The relative path of the target.
+ * @param path The absolute path of the target.
*/
- public void unlink(@NonNull String pathUnderStorage) throws IOException {
+ public void unlink(@NonNull String path) throws IOException {
try {
- int res = mService.unlink(mId, pathUnderStorage);
+ int res = mService.unlink(mId, path);
if (res < 0) {
throw new IOException("unlink() failed with errno " + -res);
}
@@ -242,13 +256,14 @@ public final class IncrementalStorage {
/**
* Rename an old file name to a new file name under the current storage directory.
*
- * @param sourcePathUnderStorage Old file path as a relative path to the storage directory.
- * @param destPathUnderStorage New file path as a relative path to the storage directory.
+ * @param sourcepath Old file path as a full path to the storage directory.
+ * @param destpath New file path as a full path to the storage directory.
*/
- public void moveFile(@NonNull String sourcePathUnderStorage,
- @NonNull String destPathUnderStorage) throws IOException {
+ public void moveFile(@NonNull String sourcepath,
+ @NonNull String destpath) throws IOException {
+ //TODO(zyy): implement using rename(2) when confirmed that IncFS supports it.
try {
- int res = mService.makeLink(mId, sourcePathUnderStorage, mId, destPathUnderStorage);
+ int res = mService.makeLink(mId, sourcepath, mId, destpath);
if (res < 0) {
throw new IOException("moveFile() failed at makeLink(), errno " + -res);
}
@@ -256,7 +271,7 @@ public final class IncrementalStorage {
e.rethrowFromSystemServer();
}
try {
- mService.unlink(mId, sourcePathUnderStorage);
+ mService.unlink(mId, sourcepath);
} catch (RemoteException ignored) {
}
}
@@ -274,7 +289,7 @@ public final class IncrementalStorage {
throw new IOException("moveDir() requires that destination dir already exists.");
}
try {
- int res = mService.makeBindMount(mId, "", destPath,
+ int res = mService.makeBindMount(mId, sourcePath, destPath,
IIncrementalManagerNative.BIND_PERMANENT);
if (res < 0) {
throw new IOException("moveDir() failed at making bind mount, errno " + -res);
@@ -291,24 +306,24 @@ public final class IncrementalStorage {
/**
* Checks whether a file under the current storage directory is fully loaded.
*
- * @param pathUnderStorage The relative path of the file.
+ * @param path The relative path of the file.
* @return True if the file is fully loaded.
*/
- public boolean isFileFullyLoaded(@NonNull String pathUnderStorage) {
- return isFileRangeLoaded(pathUnderStorage, 0, -1);
+ public boolean isFileFullyLoaded(@NonNull String path) {
+ return isFileRangeLoaded(path, 0, -1);
}
/**
* Checks whether a range in a file if loaded.
*
- * @param pathUnderStorage The relative path of the file.
+ * @param path The relative path of the file.
* @param start The starting offset of the range.
* @param end The ending offset of the range.
* @return True if the file is fully loaded.
*/
- public boolean isFileRangeLoaded(@NonNull String pathUnderStorage, long start, long end) {
+ public boolean isFileRangeLoaded(@NonNull String path, long start, long end) {
try {
- return mService.isFileRangeLoaded(mId, pathUnderStorage, start, end);
+ return mService.isFileRangeLoaded(mId, path, start, end);
} catch (RemoteException e) {
e.rethrowFromSystemServer();
return false;
@@ -318,13 +333,65 @@ public final class IncrementalStorage {
/**
* Returns the metadata object of an IncFs File.
*
- * @param pathUnderStorage The relative path of the file.
+ * @param path The relative path of the file.
+ * @return Byte array that contains metadata bytes.
+ */
+ @Nullable
+ public byte[] getFileMetadata(@NonNull String path) {
+ try {
+ return mService.getMetadataByPath(mId, path);
+ } catch (RemoteException e) {
+ e.rethrowFromSystemServer();
+ return null;
+ }
+ }
+
+ private static final int UUID_BYTE_SIZE = 16;
+
+ /**
+ * Converts UUID to a byte array usable for Incremental API calls
+ *
+ * @param id The id to convert
+ * @return Byte array that contains the same ID.
+ */
+ public static byte[] idToBytes(UUID id) {
+ if (id == null) {
+ return null;
+ }
+ final ByteBuffer buf = ByteBuffer.wrap(new byte[UUID_BYTE_SIZE]);
+ buf.putLong(id.getMostSignificantBits());
+ buf.putLong(id.getLeastSignificantBits());
+ return buf.array();
+ }
+
+ /**
+ * Converts UUID from a byte array usable for Incremental API calls
+ *
+ * @param bytes The id in byte array format, 16 bytes long
+ * @return UUID constructed from the byte array.
+ */
+ public static UUID bytesToId(byte[] bytes) {
+ if (bytes.length != UUID_BYTE_SIZE) {
+ throw new IllegalArgumentException("Expected array of size " + UUID_BYTE_SIZE
+ + ", got " + bytes.length);
+ }
+ final ByteBuffer buf = ByteBuffer.wrap(bytes);
+ long msb = buf.getLong();
+ long lsb = buf.getLong();
+ return new UUID(msb, lsb);
+ }
+
+ /**
+ * Returns the metadata object of an IncFs File.
+ *
+ * @param id The file id.
* @return Byte array that contains metadata bytes.
*/
@Nullable
- public byte[] getFileMetadata(@NonNull String pathUnderStorage) {
+ public byte[] getFileMetadata(@NonNull UUID id) {
try {
- return mService.getFileMetadata(mId, pathUnderStorage);
+ final byte[] rawId = idToBytes(id);
+ return mService.getMetadataById(mId, rawId);
} catch (RemoteException e) {
e.rethrowFromSystemServer();
return null;
diff --git a/core/java/android/provider/Settings.java b/core/java/android/provider/Settings.java
index a62412068fda..3e0b46b6d8e8 100644
--- a/core/java/android/provider/Settings.java
+++ b/core/java/android/provider/Settings.java
@@ -2721,6 +2721,7 @@ public final class Settings {
}
}
});
+ currentGeneration = generation;
}
}
if (mGenerationTracker != null && currentGeneration ==
@@ -2801,14 +2802,7 @@ public final class Settings {
}
mValues.clear();
} else {
- boolean prefixCached = false;
- int size = mValues.size();
- for (int i = 0; i < size; ++i) {
- if (mValues.keyAt(i).startsWith(prefix)) {
- prefixCached = true;
- break;
- }
- }
+ boolean prefixCached = mValues.containsKey(prefix);
if (prefixCached) {
if (!names.isEmpty()) {
for (String name : names) {
@@ -2817,9 +2811,11 @@ public final class Settings {
}
}
} else {
- for (int i = 0; i < size; ++i) {
+ for (int i = 0; i < mValues.size(); ++i) {
String key = mValues.keyAt(i);
- if (key.startsWith(prefix)) {
+ // Explicitly exclude the prefix as it is only there to
+ // signal that the prefix has been cached.
+ if (key.startsWith(prefix) && !key.equals(prefix)) {
keyValues.put(key, mValues.get(key));
}
}
@@ -2907,12 +2903,15 @@ public final class Settings {
}
}
});
+ currentGeneration = generation;
}
}
if (mGenerationTracker != null && currentGeneration
== mGenerationTracker.getCurrentGeneration()) {
// cache the complete list of flags for the namespace
mValues.putAll(flagsToValues);
+ // Adding the prefix as a signal that the prefix is cached.
+ mValues.put(prefix, null);
}
}
return keyValues;
@@ -14104,7 +14103,7 @@ public final class Settings {
* @hide
*/
@RequiresPermission(Manifest.permission.READ_DEVICE_CONFIG)
- static Map<String, String> getStrings(@NonNull ContentResolver resolver,
+ public static Map<String, String> getStrings(@NonNull ContentResolver resolver,
@NonNull String namespace, @NonNull List<String> names) {
List<String> compositeNames = new ArrayList<>(names.size());
for (String name : names) {
@@ -14163,8 +14162,9 @@ public final class Settings {
* @hide
*/
@RequiresPermission(Manifest.permission.WRITE_DEVICE_CONFIG)
- static boolean setStrings(@NonNull ContentResolver resolver, @NonNull String namespace,
- @NonNull Map<String, String> keyValues) throws DeviceConfig.BadConfigException {
+ public static boolean setStrings(@NonNull ContentResolver resolver,
+ @NonNull String namespace, @NonNull Map<String, String> keyValues)
+ throws DeviceConfig.BadConfigException {
HashMap<String, String> compositeKeyValueMap = new HashMap<>(keyValues.keySet().size());
for (Map.Entry<String, String> entry : keyValues.entrySet()) {
compositeKeyValueMap.put(
@@ -14241,6 +14241,12 @@ public final class Settings {
}
}
+ /** @hide */
+ public static void clearProviderForTest() {
+ sProviderHolder.clearProviderForTest();
+ sNameValueCache.clearGenerationTrackerForTest();
+ }
+
private static String createCompositeName(@NonNull String namespace, @NonNull String name) {
Preconditions.checkNotNull(namespace);
Preconditions.checkNotNull(name);
diff --git a/core/java/android/provider/Telephony.java b/core/java/android/provider/Telephony.java
index 9da584ea1487..b6f5138a6582 100644
--- a/core/java/android/provider/Telephony.java
+++ b/core/java/android/provider/Telephony.java
@@ -1296,17 +1296,6 @@ public final class Telephony {
"android.provider.action.EXTERNAL_PROVIDER_CHANGE";
/**
- * Same as {@link #ACTION_DEFAULT_SMS_PACKAGE_CHANGED} but it's implicit (e.g. sent to
- * all apps) and requires
- * {@link android.Manifest.permission#MONITOR_DEFAULT_SMS_PACKAGE} to receive.
- *
- * @hide
- */
- @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
- public static final String ACTION_DEFAULT_SMS_PACKAGE_CHANGED_INTERNAL =
- "android.provider.action.DEFAULT_SMS_PACKAGE_CHANGED_INTERNAL";
-
- /**
* Broadcast action: When SMS-MMS db is being created. If file-based encryption is
* supported, this broadcast indicates creation of the db in credential-encrypted
* storage. A boolean is specified in {@link #EXTRA_IS_INITIAL_CREATE} to indicate if
@@ -4038,15 +4027,6 @@ public final class Telephony {
@Retention(RetentionPolicy.SOURCE)
public @interface EditStatus {}
- /** @hide */
- @IntDef({
- SKIP_464XLAT_DEFAULT,
- SKIP_464XLAT_DISABLE,
- SKIP_464XLAT_ENABLE,
- })
- @Retention(RetentionPolicy.SOURCE)
- public @interface Skip464XlatStatus {}
-
/**
* Compat framework change ID for the APN db read permission change.
*
@@ -4297,6 +4277,15 @@ public final class Telephony {
public static final String LANGUAGE_CODE = "language";
/**
+ * Dats coding scheme of the message.
+ * <p>
+ * The data coding scheme (dcs) value defined in 3GPP TS 23.038 section 4
+ * </p>
+ * <P>Type: INTEGER</P>
+ */
+ public static final String DATA_CODING_SCHEME = "dcs";
+
+ /**
* Message body.
* <P>Type: TEXT</P>
*/
@@ -4384,18 +4373,32 @@ public final class Telephony {
public static final String DEFAULT_SORT_ORDER = DELIVERY_TIME + " DESC";
/**
- * The timestamp in millisecond of when the device received the message.
+ * The timestamp in millisecond, reported by {@link System#currentTimeMillis()}, when the
+ * device received the message.
* <P>Type: BIGINT</P>
*/
public static final String RECEIVED_TIME = "received_time";
/**
+ * The timestamp in millisecond, reported by {@link System#currentTimeMillis()}, when
+ * location was checked last time. Note this is only applicable to geo-targeting message.
+ * For non geo-targeting message. the field will be set to -1.
+ * <P>Type: BIGINT</P>
+ */
+ public static final String LOCATION_CHECK_TIME = "location_check_time";
+ /**
* Indicates that whether the message has been broadcasted to the application.
* <P>Type: BOOLEAN</P>
*/
public static final String MESSAGE_BROADCASTED = "message_broadcasted";
/**
+ * Indicates that whether the message has been displayed to the user.
+ * <P>Type: BOOLEAN</P>
+ */
+ public static final String MESSAGE_DISPLAYED = "message_displayed";
+
+ /**
* The Warning Area Coordinates Elements. This element is used for geo-fencing purpose.
*
* The geometry and its coordinates are separated vertical bar, the first item is the
diff --git a/core/java/android/security/net/config/WfaCertificateSource.java b/core/java/android/security/net/config/WfaCertificateSource.java
index f212ef8bf447..545e4b0926b1 100644
--- a/core/java/android/security/net/config/WfaCertificateSource.java
+++ b/core/java/android/security/net/config/WfaCertificateSource.java
@@ -23,12 +23,15 @@ import java.io.File;
* @hide
*/
public final class WfaCertificateSource extends DirectoryCertificateSource {
+ private static final String CACERTS_WFA_PATH =
+ "/apex/com.android.wifi/etc/security/cacerts_wfa";
+
private static class NoPreloadHolder {
private static final WfaCertificateSource INSTANCE = new WfaCertificateSource();
}
private WfaCertificateSource() {
- super(new File(System.getenv("ANDROID_ROOT") + "/etc/security/cacerts_wfa"));
+ super(new File(CACERTS_WFA_PATH));
}
public static WfaCertificateSource getInstance() {
diff --git a/core/java/android/service/autofill/FillResponse.java b/core/java/android/service/autofill/FillResponse.java
index 9333dbd1e1d5..36f3a7899700 100644
--- a/core/java/android/service/autofill/FillResponse.java
+++ b/core/java/android/service/autofill/FillResponse.java
@@ -748,8 +748,8 @@ public final class FillResponse implements Parcelable {
parcel.writeParcelableArray(mFieldClassificationIds, flags);
parcel.writeInt(mFlags);
parcel.writeIntArray(mCancelIds);
- parcel.writeInt(mRequestId);
parcel.writeParcelable(mInlineActions, flags);
+ parcel.writeInt(mRequestId);
}
public static final @android.annotation.NonNull Parcelable.Creator<FillResponse> CREATOR =
diff --git a/core/java/android/service/autofill/IInlineSuggestionRenderService.aidl b/core/java/android/service/autofill/IInlineSuggestionRenderService.aidl
new file mode 100644
index 000000000000..decdcf586ee1
--- /dev/null
+++ b/core/java/android/service/autofill/IInlineSuggestionRenderService.aidl
@@ -0,0 +1,30 @@
+/*
+ * 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.service.autofill;
+
+import android.service.autofill.IInlineSuggestionUiCallback;
+import android.service.autofill.InlinePresentation;
+
+/**
+ * Interface from system to the inline suggestion render service.
+ *
+ * @hide
+ */
+oneway interface IInlineSuggestionRenderService {
+ void renderSuggestion(in IInlineSuggestionUiCallback callback, in InlinePresentation presentation,
+ int width, int height);
+}
diff --git a/core/java/android/service/autofill/IInlineSuggestionUiCallback.aidl b/core/java/android/service/autofill/IInlineSuggestionUiCallback.aidl
new file mode 100644
index 000000000000..a55a2ced0b89
--- /dev/null
+++ b/core/java/android/service/autofill/IInlineSuggestionUiCallback.aidl
@@ -0,0 +1,29 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.service.autofill;
+
+import android.view.SurfaceControl;
+
+/**
+ * Interface to receive events from inline suggestions.
+ *
+ * @hide
+ */
+oneway interface IInlineSuggestionUiCallback {
+ void autofill();
+ void onContent(in SurfaceControl surface);
+}
diff --git a/core/java/android/service/autofill/InlineSuggestionRenderService.java b/core/java/android/service/autofill/InlineSuggestionRenderService.java
new file mode 100644
index 000000000000..2593aab1eb63
--- /dev/null
+++ b/core/java/android/service/autofill/InlineSuggestionRenderService.java
@@ -0,0 +1,92 @@
+/*
+ * 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.service.autofill;
+
+import static com.android.internal.util.function.pooled.PooledLambda.obtainMessage;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.annotation.SystemApi;
+import android.annotation.TestApi;
+import android.app.Service;
+import android.app.slice.Slice;
+import android.content.Intent;
+import android.os.Handler;
+import android.os.IBinder;
+import android.os.Looper;
+import android.util.Log;
+import android.view.View;
+
+/**
+ * A service that renders an inline presentation given the {@link InlinePresentation} containing
+ * a {@link Slice} built using the {@link androidx.autofill.AutofillSliceBuilder}.
+ *
+ * {@hide}
+ */
+@SystemApi
+@TestApi
+public abstract class InlineSuggestionRenderService extends Service {
+
+ private static final String TAG = "InlineSuggestionRenderService";
+
+ /**
+ * The {@link Intent} that must be declared as handled by the service.
+ *
+ * <p>To be supported, the service must also require the
+ * {@link android.Manifest.permission#BIND_INLINE_SUGGESTION_RENDER_SERVICE} permission so
+ * that other applications can not abuse it.
+ */
+ public static final String SERVICE_INTERFACE =
+ "android.service.autofill.InlineSuggestionRenderService";
+
+ private final Handler mHandler = new Handler(Looper.getMainLooper(), null, true);
+
+ private void handleRenderSuggestion(IInlineSuggestionUiCallback callback,
+ InlinePresentation presentation, int width, int height) {
+ //TODO(b/146453086): implementation in ExtService
+ }
+
+ @Override
+ @Nullable
+ public final IBinder onBind(@NonNull Intent intent) {
+ if (SERVICE_INTERFACE.equals(intent.getAction())) {
+ return new IInlineSuggestionRenderService.Stub() {
+ @Override
+ public void renderSuggestion(@NonNull IInlineSuggestionUiCallback callback,
+ @NonNull InlinePresentation presentation, int width, int height) {
+ mHandler.sendMessage(obtainMessage(
+ InlineSuggestionRenderService::handleRenderSuggestion,
+ InlineSuggestionRenderService.this, callback, presentation,
+ width, height));
+ }
+ }.asBinder();
+ }
+
+ Log.w(TAG, "Tried to bind to wrong intent (should be " + SERVICE_INTERFACE + ": " + intent);
+ return null;
+ }
+
+ /**
+ * Renders the slice into a view.
+ */
+ @Nullable
+ public View onRenderSuggestion(@NonNull InlinePresentation presentation,
+ int width, int height) {
+ Log.e(TAG, "service implementation (" + getClass() + " does not implement "
+ + "onRenderSuggestion()");
+ return null;
+ }
+}
diff --git a/core/java/android/service/contentcapture/ContentCaptureService.java b/core/java/android/service/contentcapture/ContentCaptureService.java
index 36e2d1f6b251..ac2532dcea7d 100644
--- a/core/java/android/service/contentcapture/ContentCaptureService.java
+++ b/core/java/android/service/contentcapture/ContentCaptureService.java
@@ -544,11 +544,14 @@ public abstract class ContentCaptureService extends Service {
Preconditions.checkNotNull(adapter);
Preconditions.checkNotNull(executor);
- DataShareReadAdapterDelegate delegate =
- new DataShareReadAdapterDelegate(executor, adapter);
+ ICancellationSignal cancellationSignalTransport =
+ CancellationSignal.createTransport();
+
+ DataShareReadAdapterDelegate delegate = new DataShareReadAdapterDelegate(
+ executor, cancellationSignalTransport, adapter);
try {
- callback.accept(delegate);
+ callback.accept(cancellationSignalTransport, delegate);
} catch (RemoteException e) {
Slog.e(TAG, "Failed to accept data sharing", e);
}
@@ -655,12 +658,16 @@ public abstract class ContentCaptureService extends Service {
private final Object mLock = new Object();
private final WeakReference<DataShareReadAdapter> mAdapterReference;
private final WeakReference<Executor> mExecutorReference;
+ private final WeakReference<ICancellationSignal> mCancellationSignalReference;
- DataShareReadAdapterDelegate(Executor executor, DataShareReadAdapter adapter) {
+ DataShareReadAdapterDelegate(Executor executor,
+ ICancellationSignal cancellationSignalTransport, DataShareReadAdapter adapter) {
Preconditions.checkNotNull(executor);
+ Preconditions.checkNotNull(cancellationSignalTransport);
Preconditions.checkNotNull(adapter);
mExecutorReference = new WeakReference<>(executor);
+ mCancellationSignalReference = new WeakReference<>(cancellationSignalTransport);
mAdapterReference = new WeakReference<>(adapter);
}
@@ -668,7 +675,17 @@ public abstract class ContentCaptureService extends Service {
public void start(ParcelFileDescriptor fd, ICancellationSignal remoteCancellationSignal)
throws RemoteException {
synchronized (mLock) {
- CancellationSignal cancellationSignal = new CancellationSignal();
+ ICancellationSignal serverControlledCancellationSignal =
+ mCancellationSignalReference.get();
+
+ if (serverControlledCancellationSignal == null) {
+ Slog.w(TAG, "Can't execute onStart(), reference to cancellation signal has "
+ + "been GC'ed");
+ return;
+ }
+
+ CancellationSignal cancellationSignal =
+ CancellationSignal.fromTransport(serverControlledCancellationSignal);
cancellationSignal.setRemote(remoteCancellationSignal);
executeAdapterMethodLocked(
diff --git a/core/java/android/service/contentcapture/DataShareReadAdapter.java b/core/java/android/service/contentcapture/DataShareReadAdapter.java
index d9350ba5d774..ca6820110ea9 100644
--- a/core/java/android/service/contentcapture/DataShareReadAdapter.java
+++ b/core/java/android/service/contentcapture/DataShareReadAdapter.java
@@ -40,8 +40,7 @@ public interface DataShareReadAdapter {
void onStart(@NonNull ParcelFileDescriptor fd, @NonNull CancellationSignal cancellationSignal);
/**
- * Signals that the session failed to start or terminated unsuccessfully (e.g. due to a
- * timeout).
+ * Signals that the session failed to start or terminated unsuccessfully.
**/
void onError(int errorCode);
}
diff --git a/core/java/android/service/contentcapture/IDataShareCallback.aidl b/core/java/android/service/contentcapture/IDataShareCallback.aidl
index c1aa1bb7dcb5..d972adadb53c 100644
--- a/core/java/android/service/contentcapture/IDataShareCallback.aidl
+++ b/core/java/android/service/contentcapture/IDataShareCallback.aidl
@@ -16,10 +16,11 @@
package android.service.contentcapture;
+import android.os.ICancellationSignal;
import android.service.contentcapture.IDataShareReadAdapter;
/** @hide */
oneway interface IDataShareCallback {
- void accept(in IDataShareReadAdapter adapter);
+ void accept(in ICancellationSignal cancellationSignal, in IDataShareReadAdapter adapter);
void reject();
}
diff --git a/core/java/android/service/controls/TokenProvider.aidl b/core/java/android/service/controls/TokenProvider.aidl
new file mode 100644
index 000000000000..8f4b7953f659
--- /dev/null
+++ b/core/java/android/service/controls/TokenProvider.aidl
@@ -0,0 +1,7 @@
+package android.service.controls;
+
+/** @hide */
+interface TokenProvider {
+ void setAuthToken(String token);
+ String getAccountName();
+} \ No newline at end of file
diff --git a/core/java/android/service/quicksettings/Tile.java b/core/java/android/service/quicksettings/Tile.java
index 79c21521fe2d..40c0ac00a5a9 100644
--- a/core/java/android/service/quicksettings/Tile.java
+++ b/core/java/android/service/quicksettings/Tile.java
@@ -65,6 +65,7 @@ public final class Tile implements Parcelable {
private CharSequence mLabel;
private CharSequence mSubtitle;
private CharSequence mContentDescription;
+ private CharSequence mStateDescription;
// Default to inactive until clients of the new API can update.
private int mState = STATE_INACTIVE;
@@ -177,6 +178,14 @@ public final class Tile implements Parcelable {
}
/**
+ * Gets the current state description for the tile.
+ */
+ @Nullable
+ public CharSequence getStateDescription() {
+ return mStateDescription;
+ }
+
+ /**
* Sets the current content description for the tile.
*
* Does not take effect until {@link #updateTile()} is called.
@@ -187,6 +196,17 @@ public final class Tile implements Parcelable {
this.mContentDescription = contentDescription;
}
+ /**
+ * Sets the current state description for the tile.
+ *
+ * Does not take effect until {@link #updateTile()} is called.
+ *
+ * @param stateDescription New state description to use.
+ */
+ public void setStateDescription(@Nullable CharSequence stateDescription) {
+ this.mStateDescription = stateDescription;
+ }
+
@Override
public int describeContents() {
return 0;
@@ -215,6 +235,7 @@ public final class Tile implements Parcelable {
TextUtils.writeToParcel(mLabel, dest, flags);
TextUtils.writeToParcel(mSubtitle, dest, flags);
TextUtils.writeToParcel(mContentDescription, dest, flags);
+ TextUtils.writeToParcel(mStateDescription, dest, flags);
}
private void readFromParcel(Parcel source) {
@@ -227,6 +248,7 @@ public final class Tile implements Parcelable {
mLabel = TextUtils.CHAR_SEQUENCE_CREATOR.createFromParcel(source);
mSubtitle = TextUtils.CHAR_SEQUENCE_CREATOR.createFromParcel(source);
mContentDescription = TextUtils.CHAR_SEQUENCE_CREATOR.createFromParcel(source);
+ mStateDescription = TextUtils.CHAR_SEQUENCE_CREATOR.createFromParcel(source);
}
public static final @android.annotation.NonNull Creator<Tile> CREATOR = new Creator<Tile>() {
diff --git a/core/java/android/service/wallpaper/WallpaperService.java b/core/java/android/service/wallpaper/WallpaperService.java
index dd78c78654c3..e9285cc7931d 100644
--- a/core/java/android/service/wallpaper/WallpaperService.java
+++ b/core/java/android/service/wallpaper/WallpaperService.java
@@ -223,6 +223,9 @@ public abstract class WallpaperService extends Service {
SurfaceControl mSurfaceControl = new SurfaceControl();
+ // Unused relayout out-param
+ SurfaceControl mTmpSurfaceControl = new SurfaceControl();
+
final BaseSurfaceHolder mSurfaceHolder = new BaseSurfaceHolder() {
{
mRequestedFormat = PixelFormat.RGBX_8888;
@@ -902,7 +905,7 @@ public abstract class WallpaperService extends Service {
View.VISIBLE, 0, -1, mWinFrame, mContentInsets,
mVisibleInsets, mStableInsets, mBackdropFrame,
mDisplayCutout, mMergedConfiguration, mSurfaceControl,
- mInsetsState, mSurfaceSize);
+ mInsetsState, mSurfaceSize, mTmpSurfaceControl);
if (mSurfaceControl.isValid()) {
mSurfaceHolder.mSurface.copyFrom(mSurfaceControl);
mSurfaceControl.release();
diff --git a/core/java/android/speech/tts/TextToSpeechService.java b/core/java/android/speech/tts/TextToSpeechService.java
index 7815864ddfe9..26c831428f1c 100644
--- a/core/java/android/speech/tts/TextToSpeechService.java
+++ b/core/java/android/speech/tts/TextToSpeechService.java
@@ -1259,6 +1259,7 @@ public abstract class TextToSpeechService extends Service {
@Override
public IBinder onBind(Intent intent) {
if (TextToSpeech.Engine.INTENT_ACTION_TTS_SERVICE.equals(intent.getAction())) {
+ Binder.allowBlocking(mBinder.asBinder());
return mBinder;
}
return null;
diff --git a/core/java/android/telephony/CellBroadcastIntents.java b/core/java/android/telephony/CellBroadcastIntents.java
index 8446253c6302..2e0810835a52 100644
--- a/core/java/android/telephony/CellBroadcastIntents.java
+++ b/core/java/android/telephony/CellBroadcastIntents.java
@@ -16,22 +16,23 @@
package android.telephony;
+import android.Manifest;
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.annotation.SystemApi;
+import android.app.AppOpsManager;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
-import android.content.pm.PackageManager;
-import android.os.Bundle;
import android.os.Handler;
import android.os.UserHandle;
+import android.provider.Telephony;
/**
* A static helper class used to send Intents with prepopulated flags.
* <p>
- * This is intended to be used by the CellBroadcastService and will throw a security exception if
- * used from a UID besides the network stack UID.
+ * This is intended to be used by the CellBroadcastService and does nothing if the caller does not
+ * have permission to broadcast {@link Telephony.Sms.Intents.SMS_CB_RECEIVED_ACTION}.
*
* @hide
*/
@@ -39,6 +40,8 @@ import android.os.UserHandle;
public class CellBroadcastIntents {
private static final String LOG_TAG = "CellBroadcastIntents";
+ private static final String EXTRA_MESSAGE = "message";
+
/**
* @hide
*/
@@ -46,50 +49,71 @@ public class CellBroadcastIntents {
}
/**
- * Returns an intent which can be received by background BroadcastReceivers. This is only
- * intended to be used by the CellBroadcastService and will throw a security exception if called
- * from another UID.
+ * Broadcasts an SMS_CB_RECEIVED_ACTION intent which can be received by background
+ * BroadcastReceivers. This is only intended to be used by the CellBroadcastService and will
+ * do nothing if the caller does not have permission to broadcast
+ * {@link Telephony.Sms.Intents.SMS_CB_RECEIVED_ACTION}.
*
* @param context The context from which to send the broadcast
* @param user The user from which to send the broadcast
- * @param intent The Intent to broadcast; all receivers matching this Intent will
- * receive the broadcast.
- * @param receiverPermission String naming a permissions that a receiver must hold in order to
- * receive your broadcast. If null, no permission is required.
- * @param receiverAppOp The app op associated with the broadcast. If null, no appOp is
- * required. If both receiverAppOp and receiverPermission are
- * non-null, a receiver must have both of them to receive the
- * broadcast
+ * @param smsCbMessage The SmsCbMessage to include with the intent
* @param resultReceiver Your own BroadcastReceiver to treat as the final receiver of the
* broadcast.
* @param scheduler A custom Handler with which to schedule the resultReceiver
* callback; if null it will be scheduled in the Context's main
* thread.
* @param initialCode An initial value for the result code. Often Activity.RESULT_OK.
- * @param initialData An initial value for the result data. Often null.
- * @param initialExtras An initial value for the result extras. Often null.
+ * @param slotIndex The slot index to include in the intent
*/
- public static void sendOrderedBroadcastForBackgroundReceivers(@NonNull Context context,
- @Nullable UserHandle user, @NonNull Intent intent, @Nullable String receiverPermission,
- @Nullable String receiverAppOp, @Nullable BroadcastReceiver resultReceiver,
- @Nullable Handler scheduler, int initialCode, @Nullable String initialData,
- @Nullable Bundle initialExtras) {
- int status = context.checkCallingOrSelfPermission(
- "android.permission.GRANT_RUNTIME_PERMISSIONS_TO_TELEPHONY_DEFAULTS");
- if (status == PackageManager.PERMISSION_DENIED) {
- throw new SecurityException(
- "Caller does not have permission to send broadcast for background receivers");
- }
- Intent backgroundIntent = new Intent(intent);
+ public static void sendSmsCbReceivedBroadcast(@NonNull Context context,
+ @Nullable UserHandle user, @NonNull SmsCbMessage smsCbMessage,
+ @Nullable BroadcastReceiver resultReceiver, @Nullable Handler scheduler,
+ int initialCode, int slotIndex) {
+ Intent backgroundIntent = new Intent(Telephony.Sms.Intents.SMS_CB_RECEIVED_ACTION);
+ backgroundIntent.putExtra(EXTRA_MESSAGE, smsCbMessage);
backgroundIntent.setFlags(Intent.FLAG_RECEIVER_INCLUDE_BACKGROUND);
+ putPhoneIdAndSubIdExtra(context, backgroundIntent, slotIndex);
+
+ String receiverPermission = Manifest.permission.RECEIVE_SMS;
+ String receiverAppOp = AppOpsManager.OPSTR_RECEIVE_SMS;
if (user != null) {
context.createContextAsUser(user, 0).sendOrderedBroadcast(backgroundIntent,
receiverPermission, receiverAppOp, resultReceiver, scheduler, initialCode,
- initialData, initialExtras);
+ null, null);
} else {
context.sendOrderedBroadcast(backgroundIntent, receiverPermission,
- receiverAppOp, resultReceiver, scheduler, initialCode, initialData,
- initialExtras);
+ receiverAppOp, resultReceiver, scheduler, initialCode, null, null);
+ }
+ }
+
+ /**
+ * Put the phone ID and sub ID into an intent as extras.
+ */
+ private static void putPhoneIdAndSubIdExtra(Context context, Intent intent, int phoneId) {
+ int subId = getSubIdForPhone(context, phoneId);
+ if (subId != SubscriptionManager.INVALID_SUBSCRIPTION_ID) {
+ intent.putExtra("subscription", subId);
+ intent.putExtra(SubscriptionManager.EXTRA_SUBSCRIPTION_INDEX, subId);
+ }
+ intent.putExtra("phone", phoneId);
+ intent.putExtra(SubscriptionManager.EXTRA_SLOT_INDEX, phoneId);
+ }
+
+ /**
+ * Get the subscription ID for a phone ID, or INVALID_SUBSCRIPTION_ID if the phone does not
+ * have an active sub
+ * @param phoneId the phoneId to use
+ * @return the associated sub id
+ */
+ private static int getSubIdForPhone(Context context, int phoneId) {
+ SubscriptionManager subMan =
+ (SubscriptionManager) context.getSystemService(
+ Context.TELEPHONY_SUBSCRIPTION_SERVICE);
+ int[] subIds = subMan.getSubscriptionIds(phoneId);
+ if (subIds != null) {
+ return subIds[0];
+ } else {
+ return SubscriptionManager.INVALID_SUBSCRIPTION_ID;
}
}
}
diff --git a/core/java/android/telephony/TelephonyRegistryManager.java b/core/java/android/telephony/TelephonyRegistryManager.java
index 6787c46c046e..4024db1e16c4 100644
--- a/core/java/android/telephony/TelephonyRegistryManager.java
+++ b/core/java/android/telephony/TelephonyRegistryManager.java
@@ -20,8 +20,12 @@ import android.annotation.Nullable;
import android.annotation.RequiresPermission;
import android.annotation.SystemApi;
import android.annotation.TestApi;
+import android.compat.Compatibility;
+import android.compat.annotation.ChangeId;
+import android.compat.annotation.EnabledAfter;
import android.content.Context;
import android.os.Binder;
+import android.os.Build;
import android.os.RemoteException;
import android.os.ServiceManager;
import android.telephony.Annotation.ApnType;
@@ -199,6 +203,13 @@ public class TelephonyRegistryManager {
}
/**
+ * To check the SDK version for {@link #listenForSubscriber}.
+ */
+ @ChangeId
+ @EnabledAfter(targetSdkVersion = Build.VERSION_CODES.P)
+ private static final long LISTEN_CODE_CHANGE = 147600208L;
+
+ /**
* Listen for incoming subscriptions
* @param subId Subscription ID
* @param pkg Package name
@@ -210,6 +221,16 @@ public class TelephonyRegistryManager {
public void listenForSubscriber(int subId, @NonNull String pkg, @NonNull String featureId,
@NonNull PhoneStateListener listener, int events, boolean notifyNow) {
try {
+ // subId from PhoneStateListener is deprecated Q on forward, use the subId from
+ // TelephonyManager instance. Keep using subId from PhoneStateListener for pre-Q.
+ if (Compatibility.isChangeEnabled(LISTEN_CODE_CHANGE)) {
+ // Since mSubId in PhoneStateListener is deprecated from Q on forward, this is
+ // the only place to set mSubId and its for "informational" only.
+ listener.mSubId = (events == PhoneStateListener.LISTEN_NONE)
+ ? SubscriptionManager.INVALID_SUBSCRIPTION_ID : subId;
+ } else if (listener.mSubId != null) {
+ subId = listener.mSubId;
+ }
sRegistry.listenForSubscriber(
subId, pkg, featureId, listener.callback, events, notifyNow);
} catch (RemoteException e) {
diff --git a/core/java/android/util/CloseGuard.java b/core/java/android/util/CloseGuard.java
index 6ac769623bff..ba504a3b4167 100644
--- a/core/java/android/util/CloseGuard.java
+++ b/core/java/android/util/CloseGuard.java
@@ -26,7 +26,7 @@ import android.annotation.NonNull;
* A simple example: <pre> {@code
* class Foo {
*
- * private final CloseGuard guard = CloseGuard.get();
+ * private final CloseGuard guard = new CloseGuard();
*
* ...
*
@@ -64,7 +64,7 @@ import android.annotation.NonNull;
* be deferred. For example: <pre> {@code
* class Bar {
*
- * private final CloseGuard guard = CloseGuard.get();
+ * private final CloseGuard guard = new CloseGuard();
*
* ...
*
diff --git a/core/java/android/view/IPinnedStackController.aidl b/core/java/android/view/IPinnedStackController.aidl
index 00edb3a29e87..cb82f168d991 100644
--- a/core/java/android/view/IPinnedStackController.aidl
+++ b/core/java/android/view/IPinnedStackController.aidl
@@ -53,9 +53,4 @@ interface IPinnedStackController {
* {@param bounds} here is the final destination bounds.
*/
void resetBoundsAnimation(in Rect bounds);
-
- /**
- * Reports the current default and movement bounds to controller.
- */
- void reportBounds(in Rect defaultBounds, in Rect movementBounds);
}
diff --git a/core/java/android/view/IWindowSession.aidl b/core/java/android/view/IWindowSession.aidl
index e3446e1f7b57..1677357dedbe 100644
--- a/core/java/android/view/IWindowSession.aidl
+++ b/core/java/android/view/IWindowSession.aidl
@@ -92,6 +92,9 @@ interface IWindowSession {
* @param outSurface Object in which is placed the new display surface.
* @param insetsState The current insets state in the system.
* @param outSurfaceSize The width and height of the surface control
+ * @param outBlastSurfaceControl A BLAST SurfaceControl allocated by the WindowManager
+ * the SurfaceControl willl be managed by the client side, but the WindowManager
+ * may use it as a deferTransaction barrier.
*
* @return int Result flags: {@link WindowManagerGlobal#RELAYOUT_SHOW_FOCUS},
* {@link WindowManagerGlobal#RELAYOUT_FIRST_TIME}.
@@ -103,7 +106,8 @@ interface IWindowSession {
out Rect outBackdropFrame,
out DisplayCutout.ParcelableWrapper displayCutout,
out MergedConfiguration outMergedConfiguration, out SurfaceControl outSurfaceControl,
- out InsetsState insetsState, out Point outSurfaceSize);
+ out InsetsState insetsState, out Point outSurfaceSize,
+ out SurfaceControl outBlastSurfaceControl);
/*
* Notify the window manager that an application is relaunching and
diff --git a/core/java/android/view/ImeInsetsSourceConsumer.java b/core/java/android/view/ImeInsetsSourceConsumer.java
index d0694375df53..2e377518f6e0 100644
--- a/core/java/android/view/ImeInsetsSourceConsumer.java
+++ b/core/java/android/view/ImeInsetsSourceConsumer.java
@@ -87,7 +87,7 @@ public final class ImeInsetsSourceConsumer extends InsetsSourceConsumer {
* @return @see {@link android.view.InsetsSourceConsumer.ShowResult}.
*/
@Override
- @ShowResult int requestShow(boolean fromIme) {
+ public @ShowResult int requestShow(boolean fromIme) {
// TODO: ResultReceiver for IME.
// TODO: Set mShowOnNextImeRender to automatically show IME and guard it with a flag.
if (fromIme) {
@@ -95,7 +95,7 @@ public final class ImeInsetsSourceConsumer extends InsetsSourceConsumer {
}
return getImm().requestImeShow(null /* resultReceiver */)
- ? ShowResult.SHOW_DELAYED : ShowResult.SHOW_FAILED;
+ ? ShowResult.IME_SHOW_DELAYED : ShowResult.IME_SHOW_FAILED;
}
/**
diff --git a/core/java/android/view/InsetsAnimationControlCallbacks.java b/core/java/android/view/InsetsAnimationControlCallbacks.java
index 27edb0b69bdd..0645c98e994a 100644
--- a/core/java/android/view/InsetsAnimationControlCallbacks.java
+++ b/core/java/android/view/InsetsAnimationControlCallbacks.java
@@ -52,12 +52,6 @@ public interface InsetsAnimationControlCallbacks {
void notifyFinished(InsetsAnimationControlImpl controller, boolean shown);
/**
- * Get the description of the insets state.
- * @return {@link InsetsState} for adjusting corresponding {@link InsetsSource}.
- */
- InsetsState getState();
-
- /**
* Apply the new params to the surface.
* @param params The {@link android.view.SyncRtSurfaceTransactionApplier.SurfaceParams} to
* apply.
diff --git a/core/java/android/view/InsetsAnimationControlImpl.java b/core/java/android/view/InsetsAnimationControlImpl.java
index 405eccd56e3c..e863aa06ed26 100644
--- a/core/java/android/view/InsetsAnimationControlImpl.java
+++ b/core/java/android/view/InsetsAnimationControlImpl.java
@@ -191,13 +191,7 @@ public class InsetsAnimationControlImpl implements WindowInsetsAnimationControll
if (mCancelled) {
return;
}
- InsetsState state = new InsetsState(mController.getState());
- for (int i = mControls.size() - 1; i >= 0; i--) {
- InsetsSourceControl control = mControls.valueAt(i);
- state.getSource(control.getType()).setVisible(shown);
- }
- Insets insets = getInsetsFromState(state, mFrame, null /* typeSideMap */);
- setInsetsAndAlpha(insets, 1f /* alpha */, 1f /* fraction */);
+ setInsetsAndAlpha(shown ? mShownInsets : mHiddenInsets, 1f /* alpha */, 1f /* fraction */);
mFinished = true;
mShownOnFinish = shown;
}
diff --git a/core/java/android/view/InsetsController.java b/core/java/android/view/InsetsController.java
index c6e383539a82..6f76497572d7 100644
--- a/core/java/android/view/InsetsController.java
+++ b/core/java/android/view/InsetsController.java
@@ -19,6 +19,7 @@ package android.view;
import static android.view.InsetsState.ITYPE_IME;
import static android.view.InsetsState.toPublicType;
import static android.view.WindowInsets.Type.all;
+import static android.view.WindowInsets.Type.ime;
import static android.view.WindowManager.LayoutParams.PRIVATE_FLAG_APPEARANCE_CONTROLLED;
import static android.view.WindowManager.LayoutParams.PRIVATE_FLAG_BEHAVIOR_CONTROLLED;
@@ -31,6 +32,7 @@ import android.annotation.NonNull;
import android.annotation.Nullable;
import android.graphics.Insets;
import android.graphics.Rect;
+import android.os.Handler;
import android.os.RemoteException;
import android.util.ArraySet;
import android.util.Log;
@@ -55,6 +57,7 @@ import java.io.PrintWriter;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.util.ArrayList;
+import java.util.function.BiFunction;
/**
* Implements {@link WindowInsetsController} on the client.
@@ -64,6 +67,7 @@ public class InsetsController implements WindowInsetsController, InsetsAnimation
private static final int ANIMATION_DURATION_SHOW_MS = 275;
private static final int ANIMATION_DURATION_HIDE_MS = 340;
+ private static final int PENDING_CONTROL_TIMEOUT_MS = 2000;
static final Interpolator INTERPOLATOR = new PathInterpolator(0.4f, 0f, 0.2f, 1f);
@@ -235,12 +239,36 @@ public class InsetsController implements WindowInsetsController, InsetsAnimation
DefaultAnimationControlListener(boolean show) {
super(show);
}
-
+
@Override
protected void setStartingAnimation(boolean startingAnimation) {
mStartingAnimation = startingAnimation;
}
}
+ /**
+ * Represents a control request that we had to defer because we are waiting for the IME to
+ * process our show request.
+ */
+ private static class PendingControlRequest {
+
+ PendingControlRequest(@InsetsType int types, WindowInsetsAnimationControlListener listener,
+ long durationMs, Interpolator interpolator, @AnimationType int animationType,
+ @LayoutInsetsDuringAnimation int layoutInsetsDuringAnimation) {
+ this.types = types;
+ this.listener = listener;
+ this.durationMs = durationMs;
+ this.interpolator = interpolator;
+ this.animationType = animationType;
+ this.layoutInsetsDuringAnimation = layoutInsetsDuringAnimation;
+ }
+
+ final @InsetsType int types;
+ final WindowInsetsAnimationControlListener listener;
+ final long durationMs;
+ final Interpolator interpolator;
+ final @AnimationType int animationType;
+ final @LayoutInsetsDuringAnimation int layoutInsetsDuringAnimation;
+ }
private final String TAG = "InsetsControllerImpl";
@@ -248,8 +276,10 @@ public class InsetsController implements WindowInsetsController, InsetsAnimation
private final InsetsState mTmpState = new InsetsState();
private final Rect mFrame = new Rect();
+ private final BiFunction<InsetsController, Integer, InsetsSourceConsumer> mConsumerCreator;
private final SparseArray<InsetsSourceConsumer> mSourceConsumers = new SparseArray<>();
private final ViewRootImpl mViewRoot;
+ private final Handler mHandler;
private final SparseArray<InsetsSourceControl> mTmpControlArray = new SparseArray<>();
private final ArrayList<RunningAnimation> mRunningAnimations = new ArrayList<>();
@@ -263,7 +293,8 @@ public class InsetsController implements WindowInsetsController, InsetsAnimation
private final Rect mLastLegacyContentInsets = new Rect();
private final Rect mLastLegacyStableInsets = new Rect();
- private int mPendingTypesToShow;
+ /** Pending control request that is waiting on IME to be ready to be shown */
+ private PendingControlRequest mPendingImeControlRequest;
private int mLastLegacySoftInputMode;
private int mLastLegacySystemUiFlags;
@@ -271,8 +302,26 @@ public class InsetsController implements WindowInsetsController, InsetsAnimation
private SyncRtSurfaceTransactionApplier mApplier;
+ private Runnable mPendingControlTimeout = this::abortPendingImeControlRequest;
+
public InsetsController(ViewRootImpl viewRoot) {
+ this(viewRoot, (controller, type) -> {
+ if (type == ITYPE_IME) {
+ return new ImeInsetsSourceConsumer(controller.mState, Transaction::new, controller);
+ } else {
+ return new InsetsSourceConsumer(type, controller.mState, Transaction::new,
+ controller);
+ }
+ }, viewRoot.mHandler);
+ }
+
+ @VisibleForTesting
+ public InsetsController(ViewRootImpl viewRoot,
+ BiFunction<InsetsController, Integer, InsetsSourceConsumer> consumerCreator,
+ Handler handler) {
mViewRoot = viewRoot;
+ mConsumerCreator = consumerCreator;
+ mHandler = handler;
mAnimCallback = () -> {
mAnimCallbackScheduled = false;
if (mRunningAnimations.isEmpty()) {
@@ -393,7 +442,21 @@ public class InsetsController implements WindowInsetsController, InsetsAnimation
show(types, false /* fromIme */);
}
- void show(@InsetsType int types, boolean fromIme) {
+ @VisibleForTesting
+ public void show(@InsetsType int types, boolean fromIme) {
+
+ // Handle pending request ready in case there was one set.
+ if (fromIme && mPendingImeControlRequest != null) {
+ PendingControlRequest pendingRequest = mPendingImeControlRequest;
+ mPendingImeControlRequest = null;
+ mHandler.removeCallbacks(mPendingControlTimeout);
+ controlAnimationUnchecked(pendingRequest.types, pendingRequest.listener, mFrame,
+ true /* fromIme */, pendingRequest.durationMs, pendingRequest.interpolator,
+ false /* fade */, pendingRequest.animationType,
+ pendingRequest.layoutInsetsDuringAnimation);
+ return;
+ }
+
// TODO: Support a ResultReceiver for IME.
// TODO(b/123718661): Make show() work for multi-session IME.
int typesReady = 0;
@@ -463,6 +526,7 @@ public class InsetsController implements WindowInsetsController, InsetsAnimation
@LayoutInsetsDuringAnimation int layoutInsetsDuringAnimation) {
if (types == 0) {
// nothing to animate.
+ listener.onCancelled();
return;
}
cancelExistingControllers(types);
@@ -471,19 +535,18 @@ public class InsetsController implements WindowInsetsController, InsetsAnimation
final SparseArray<InsetsSourceControl> controls = new SparseArray<>();
Pair<Integer, Boolean> typesReadyPair = collectSourceControls(
- fromIme, internalTypes, controls, listener);
+ fromIme, internalTypes, controls);
int typesReady = typesReadyPair.first;
- boolean isReady = typesReadyPair.second;
- if (!isReady) {
- // IME isn't ready, all requested types would be shown once IME is ready.
- mPendingTypesToShow = typesReady;
- // TODO: listener for pending types.
+ boolean imeReady = typesReadyPair.second;
+ if (!imeReady) {
+ // IME isn't ready, all requested types will be animated once IME is ready
+ abortPendingImeControlRequest();
+ mPendingImeControlRequest = new PendingControlRequest(types, listener, durationMs,
+ interpolator, animationType, layoutInsetsDuringAnimation);
+ mHandler.postDelayed(mPendingControlTimeout, PENDING_CONTROL_TIMEOUT_MS);
return;
}
- // pending types from previous request.
- typesReady = collectPendingTypes(typesReady);
-
if (typesReady == 0) {
listener.onCancelled();
return;
@@ -496,13 +559,12 @@ public class InsetsController implements WindowInsetsController, InsetsAnimation
}
/**
- * @return Pair of (types ready to animate, is ready to animate).
+ * @return Pair of (types ready to animate, IME ready to animate).
*/
private Pair<Integer, Boolean> collectSourceControls(boolean fromIme,
- ArraySet<Integer> internalTypes, SparseArray<InsetsSourceControl> controls,
- WindowInsetsAnimationControlListener listener) {
+ ArraySet<Integer> internalTypes, SparseArray<InsetsSourceControl> controls) {
int typesReady = 0;
- boolean isReady = true;
+ boolean imeReady = true;
for (int i = internalTypes.size() - 1; i >= 0; i--) {
InsetsSourceConsumer consumer = getSourceConsumer(internalTypes.valueAt(i));
boolean setVisible = !consumer.isRequestedVisible();
@@ -512,16 +574,12 @@ public class InsetsController implements WindowInsetsController, InsetsAnimation
case ShowResult.SHOW_IMMEDIATELY:
typesReady |= InsetsState.toPublicType(consumer.getType());
break;
- case ShowResult.SHOW_DELAYED:
- isReady = false;
+ case ShowResult.IME_SHOW_DELAYED:
+ imeReady = false;
break;
- case ShowResult.SHOW_FAILED:
+ case ShowResult.IME_SHOW_FAILED:
// IME cannot be shown (since it didn't have focus), proceed
// with animation of other types.
- if (mPendingTypesToShow != 0) {
- // remove IME from pending because view no longer has focus.
- mPendingTypesToShow &= ~InsetsState.toPublicType(ITYPE_IME);
- }
break;
}
} else {
@@ -538,13 +596,7 @@ public class InsetsController implements WindowInsetsController, InsetsAnimation
controls.put(consumer.getType(), control);
}
}
- return new Pair<>(typesReady, isReady);
- }
-
- private int collectPendingTypes(@InsetsType int typesReady) {
- typesReady |= mPendingTypesToShow;
- mPendingTypesToShow = 0;
- return typesReady;
+ return new Pair<>(typesReady, imeReady);
}
private @LayoutInsetsDuringAnimation int getLayoutInsetsDuringAnimationMode(
@@ -577,6 +629,17 @@ public class InsetsController implements WindowInsetsController, InsetsAnimation
cancelAnimation(control, true /* invokeCallback */);
}
}
+ if ((types & ime()) != 0) {
+ abortPendingImeControlRequest();
+ }
+ }
+
+ private void abortPendingImeControlRequest() {
+ if (mPendingImeControlRequest != null) {
+ mPendingImeControlRequest.listener.onCancelled();
+ mPendingImeControlRequest = null;
+ mHandler.removeCallbacks(mPendingControlTimeout);
+ }
}
@VisibleForTesting
@@ -608,6 +671,9 @@ public class InsetsController implements WindowInsetsController, InsetsAnimation
cancelAnimation(control, true /* invokeCallback */);
}
}
+ if (consumer.getType() == ITYPE_IME) {
+ abortPendingImeControlRequest();
+ }
}
private void cancelAnimation(InsetsAnimationControlImpl control, boolean invokeCallback) {
@@ -635,7 +701,7 @@ public class InsetsController implements WindowInsetsController, InsetsAnimation
if (controller != null) {
return controller;
}
- controller = createConsumerOfType(type);
+ controller = mConsumerCreator.apply(this, type);
mSourceConsumers.put(type, controller);
return controller;
}
@@ -696,14 +762,6 @@ public class InsetsController implements WindowInsetsController, InsetsAnimation
return ANIMATION_TYPE_NONE;
}
- private InsetsSourceConsumer createConsumerOfType(int type) {
- if (type == ITYPE_IME) {
- return new ImeInsetsSourceConsumer(mState, Transaction::new, this);
- } else {
- return new InsetsSourceConsumer(type, mState, Transaction::new, this);
- }
- }
-
/**
* Sends the local visibility state back to window manager.
*/
diff --git a/core/java/android/view/InsetsSourceConsumer.java b/core/java/android/view/InsetsSourceConsumer.java
index 35a82b8bfd54..ddfd38c0320b 100644
--- a/core/java/android/view/InsetsSourceConsumer.java
+++ b/core/java/android/view/InsetsSourceConsumer.java
@@ -36,7 +36,7 @@ import java.util.function.Supplier;
public class InsetsSourceConsumer {
@Retention(RetentionPolicy.SOURCE)
- @IntDef(value = {ShowResult.SHOW_IMMEDIATELY, ShowResult.SHOW_DELAYED, ShowResult.SHOW_FAILED})
+ @IntDef(value = {ShowResult.SHOW_IMMEDIATELY, ShowResult.IME_SHOW_DELAYED, ShowResult.IME_SHOW_FAILED})
@interface ShowResult {
/**
* Window type is ready to be shown, will be shown immidiately.
@@ -46,12 +46,12 @@ public class InsetsSourceConsumer {
* Result will be delayed. Window needs to be prepared or request is not from controller.
* Request will be delegated to controller and may or may not be shown.
*/
- int SHOW_DELAYED = 1;
+ int IME_SHOW_DELAYED = 1;
/**
* Window will not be shown because one of the conditions couldn't be met.
* (e.g. in IME's case, when no editor is focused.)
*/
- int SHOW_FAILED = 2;
+ int IME_SHOW_FAILED = 2;
}
protected final InsetsController mController;
@@ -155,7 +155,8 @@ public class InsetsSourceConsumer {
* {@link android.inputmethodservice.InputMethodService}).
* @return @see {@link ShowResult}.
*/
- @ShowResult int requestShow(boolean fromController) {
+ @VisibleForTesting
+ public @ShowResult int requestShow(boolean fromController) {
return ShowResult.SHOW_IMMEDIATELY;
}
diff --git a/core/java/android/view/KeyEvent.java b/core/java/android/view/KeyEvent.java
index c638717b13c0..c91096e5aa25 100644
--- a/core/java/android/view/KeyEvent.java
+++ b/core/java/android/view/KeyEvent.java
@@ -19,6 +19,7 @@ package android.view;
import static android.view.Display.INVALID_DISPLAY;
import android.annotation.NonNull;
+import android.annotation.Nullable;
import android.annotation.TestApi;
import android.compat.annotation.UnsupportedAppUsage;
import android.os.Build;
@@ -1269,6 +1270,7 @@ public class KeyEvent extends InputEvent implements Parcelable {
@UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P, trackingBug = 115609023)
private int mSource;
private int mDisplayId;
+ private @Nullable byte[] mHmac;
@UnsupportedAppUsage
private int mMetaState;
@UnsupportedAppUsage
@@ -1546,6 +1548,7 @@ public class KeyEvent extends InputEvent implements Parcelable {
mDeviceId = origEvent.mDeviceId;
mSource = origEvent.mSource;
mDisplayId = origEvent.mDisplayId;
+ mHmac = origEvent.mHmac == null ? null : origEvent.mHmac.clone();
mScanCode = origEvent.mScanCode;
mFlags = origEvent.mFlags;
mCharacters = origEvent.mCharacters;
@@ -1573,6 +1576,7 @@ public class KeyEvent extends InputEvent implements Parcelable {
mDeviceId = origEvent.mDeviceId;
mSource = origEvent.mSource;
mDisplayId = origEvent.mDisplayId;
+ mHmac = null; // Don't copy HMAC, it will be invalid because eventTime is changing
mScanCode = origEvent.mScanCode;
mFlags = origEvent.mFlags;
mCharacters = origEvent.mCharacters;
@@ -1600,7 +1604,8 @@ public class KeyEvent extends InputEvent implements Parcelable {
*/
public static KeyEvent obtain(long downTime, long eventTime, int action,
int code, int repeat, int metaState,
- int deviceId, int scancode, int flags, int source, int displayId, String characters) {
+ int deviceId, int scancode, int flags, int source, int displayId, @Nullable byte[] hmac,
+ String characters) {
KeyEvent ev = obtain();
ev.mDownTime = downTime;
ev.mEventTime = eventTime;
@@ -1613,6 +1618,7 @@ public class KeyEvent extends InputEvent implements Parcelable {
ev.mFlags = flags;
ev.mSource = source;
ev.mDisplayId = displayId;
+ ev.mHmac = hmac;
ev.mCharacters = characters;
return ev;
}
@@ -1627,7 +1633,7 @@ public class KeyEvent extends InputEvent implements Parcelable {
int code, int repeat, int metaState,
int deviceId, int scancode, int flags, int source, String characters) {
return obtain(downTime, eventTime, action, code, repeat, metaState, deviceId, scancode,
- flags, source, INVALID_DISPLAY, characters);
+ flags, source, INVALID_DISPLAY, null /* hmac */, characters);
}
/**
@@ -1650,6 +1656,7 @@ public class KeyEvent extends InputEvent implements Parcelable {
ev.mFlags = other.mFlags;
ev.mSource = other.mSource;
ev.mDisplayId = other.mDisplayId;
+ ev.mHmac = other.mHmac == null ? null : other.mHmac.clone();
ev.mCharacters = other.mCharacters;
return ev;
}
@@ -1738,6 +1745,7 @@ public class KeyEvent extends InputEvent implements Parcelable {
mDeviceId = origEvent.mDeviceId;
mSource = origEvent.mSource;
mDisplayId = origEvent.mDisplayId;
+ mHmac = null; // Don't copy the hmac, it will be invalid since action is changing
mScanCode = origEvent.mScanCode;
mFlags = origEvent.mFlags;
// Don't copy mCharacters, since one way or the other we'll lose it
@@ -3091,6 +3099,7 @@ public class KeyEvent extends InputEvent implements Parcelable {
mDeviceId = in.readInt();
mSource = in.readInt();
mDisplayId = in.readInt();
+ mHmac = in.createByteArray();
mAction = in.readInt();
mKeyCode = in.readInt();
mRepeatCount = in.readInt();
@@ -3109,6 +3118,7 @@ public class KeyEvent extends InputEvent implements Parcelable {
out.writeInt(mDeviceId);
out.writeInt(mSource);
out.writeInt(mDisplayId);
+ out.writeByteArray(mHmac);
out.writeInt(mAction);
out.writeInt(mKeyCode);
out.writeInt(mRepeatCount);
diff --git a/core/java/android/view/View.java b/core/java/android/view/View.java
index c5f4faf2f462..5e59143b3866 100644
--- a/core/java/android/view/View.java
+++ b/core/java/android/view/View.java
@@ -22,10 +22,6 @@ import static android.util.StatsLog.TOUCH_GESTURE_CLASSIFIED__CLASSIFICATION__LO
import static android.util.StatsLog.TOUCH_GESTURE_CLASSIFIED__CLASSIFICATION__SINGLE_TAP;
import static android.util.StatsLog.TOUCH_GESTURE_CLASSIFIED__CLASSIFICATION__UNKNOWN_CLASSIFICATION;
import static android.view.ViewRootImpl.NEW_INSETS_MODE_FULL;
-import static android.view.WindowInsets.Type.ime;
-import static android.view.WindowInsets.Type.systemBars;
-import static android.view.WindowManager.LayoutParams.SOFT_INPUT_ADJUST_RESIZE;
-import static android.view.WindowManager.LayoutParams.SOFT_INPUT_MASK_ADJUST;
import static android.view.accessibility.AccessibilityEvent.CONTENT_CHANGE_TYPE_UNDEFINED;
import static java.lang.Math.max;
@@ -146,7 +142,6 @@ import android.widget.FrameLayout;
import android.widget.ScrollBarDrawable;
import com.android.internal.R;
-import com.android.internal.policy.DecorView;
import com.android.internal.view.TooltipPopup;
import com.android.internal.view.menu.MenuBuilder;
import com.android.internal.widget.ScrollBarUtils;
@@ -21856,7 +21851,7 @@ public class View implements Drawable.Callback, KeyEvent.Callback,
if (!concatMatrix &&
(parentFlags & (ViewGroup.FLAG_SUPPORT_STATIC_TRANSFORMATIONS |
ViewGroup.FLAG_CLIP_CHILDREN)) == ViewGroup.FLAG_CLIP_CHILDREN &&
- canvas.quickReject(mLeft, mTop, mRight, mBottom, Canvas.EdgeType.BW) &&
+ canvas.quickReject(mLeft, mTop, mRight, mBottom) &&
(mPrivateFlags & PFLAG_DRAW_ANIMATION) == 0) {
mPrivateFlags2 |= PFLAG2_VIEW_QUICK_REJECTED;
return more;
@@ -22140,6 +22135,7 @@ public class View implements Drawable.Callback, KeyEvent.Callback,
* 4. Draw children
* 5. If necessary, draw the fading edges and restore layers
* 6. Draw decorations (scrollbars for instance)
+ * 7. If necessary, draw the default focus highlight
*/
// Step 1, draw the background, if needed
@@ -22346,6 +22342,9 @@ public class View implements Drawable.Callback, KeyEvent.Callback,
// Step 6, draw decorations (foreground, scrollbars)
onDrawForeground(canvas);
+ // Step 7, draw the default focus highlight
+ drawDefaultFocusHighlight(canvas);
+
if (isShowingLayoutBounds()) {
debugDrawFocus(canvas);
}
@@ -23241,11 +23240,11 @@ public class View implements Drawable.Callback, KeyEvent.Callback,
}
/**
- * Draw the default focus highlight onto the canvas.
+ * Draw the default focus highlight onto the canvas if there is one and this view is focused.
* @param canvas the canvas where we're drawing the highlight.
*/
private void drawDefaultFocusHighlight(Canvas canvas) {
- if (mDefaultFocusHighlight != null) {
+ if (mDefaultFocusHighlight != null && isFocused()) {
if (mDefaultFocusHighlightSizeChanged) {
mDefaultFocusHighlightSizeChanged = false;
final int l = mScrollX;
diff --git a/core/java/android/view/ViewRootImpl.java b/core/java/android/view/ViewRootImpl.java
index 7a93dcc9e4ec..841c43fb1392 100644
--- a/core/java/android/view/ViewRootImpl.java
+++ b/core/java/android/view/ViewRootImpl.java
@@ -507,7 +507,7 @@ public final class ViewRootImpl implements ViewParent,
@UnsupportedAppUsage
public final Surface mSurface = new Surface();
private final SurfaceControl mSurfaceControl = new SurfaceControl();
- private SurfaceControl mBlastSurfaceControl;
+ private SurfaceControl mBlastSurfaceControl = new SurfaceControl();
private BLASTBufferQueue mBlastBufferQueue;
@@ -636,6 +636,7 @@ public final class ViewRootImpl implements ViewParent,
InputEventConsistencyVerifier.isInstrumentationEnabled() ?
new InputEventConsistencyVerifier(this, 0) : null;
+ private final InsetsController mInsetsController;
private final ImeFocusController mImeFocusController;
/**
@@ -646,7 +647,6 @@ public final class ViewRootImpl implements ViewParent,
return mImeFocusController;
}
- private final InsetsController mInsetsController = new InsetsController(this);
private final GestureExclusionTracker mGestureExclusionTracker = new GestureExclusionTracker();
@@ -705,6 +705,7 @@ public final class ViewRootImpl implements ViewParent,
mFallbackEventHandler = new PhoneFallbackEventHandler(context);
mChoreographer = Choreographer.getInstance();
mDisplayManager = (DisplayManager)context.getSystemService(Context.DISPLAY_SERVICE);
+ mInsetsController = new InsetsController(this);
String processorOverrideName = context.getResources().getString(
R.string.config_inputEventCompatProcessorOverrideClassName);
@@ -1690,23 +1691,17 @@ public final class ViewRootImpl implements ViewParent,
.build();
setBoundsLayerCrop();
mTransaction.show(mBoundsLayer).apply();
- }
- return mBoundsLayer;
+ }
+ return mBoundsLayer;
}
Surface getOrCreateBLASTSurface(int width, int height) {
if (mSurfaceControl == null || !mSurfaceControl.isValid()) {
return null;
}
- if (mBlastSurfaceControl == null) {
- mBlastSurfaceControl = new SurfaceControl.Builder(mSurfaceSession)
- .setParent(mSurfaceControl)
- .setName("BLAST")
- .setBLASTLayer()
- .build();
+ if ((mBlastBufferQueue != null) && mBlastSurfaceControl.isValid()) {
mBlastBufferQueue = new BLASTBufferQueue(
mBlastSurfaceControl, width, height);
-
}
mBlastBufferQueue.update(mBlastSurfaceControl, width, height);
@@ -3683,32 +3678,29 @@ public final class ViewRootImpl implements ViewParent,
Trace.traceBegin(Trace.TRACE_TAG_VIEW, "draw");
boolean usingAsyncReport = false;
+ boolean reportNextDraw = mReportNextDraw; // Capture the original value
if (mAttachInfo.mThreadedRenderer != null && mAttachInfo.mThreadedRenderer.isEnabled()) {
ArrayList<Runnable> commitCallbacks = mAttachInfo.mTreeObserver
.captureFrameCommitCallbacks();
- if (mReportNextDraw) {
- usingAsyncReport = true;
+ final boolean needFrameCompleteCallback = mNextDrawUseBLASTSyncTransaction ||
+ (commitCallbacks != null && commitCallbacks.size() > 0) ||
+ mReportNextDraw;
+ usingAsyncReport = mReportNextDraw;
+ if (needFrameCompleteCallback) {
final Handler handler = mAttachInfo.mHandler;
mAttachInfo.mThreadedRenderer.setFrameCompleteCallback((long frameNr) ->
handler.postAtFrontOfQueue(() -> {
finishBLASTSync();
- // TODO: Use the frame number
- pendingDrawFinished();
+ if (reportNextDraw) {
+ // TODO: Use the frame number
+ pendingDrawFinished();
+ }
if (commitCallbacks != null) {
for (int i = 0; i < commitCallbacks.size(); i++) {
commitCallbacks.get(i).run();
}
}
}));
- } else if (commitCallbacks != null && commitCallbacks.size() > 0) {
- final Handler handler = mAttachInfo.mHandler;
- mAttachInfo.mThreadedRenderer.setFrameCompleteCallback((long frameNr) ->
- handler.postAtFrontOfQueue(() -> {
- finishBLASTSync();
- for (int i = 0; i < commitCallbacks.size(); i++) {
- commitCallbacks.get(i).run();
- }
- }));
}
}
@@ -7344,7 +7336,8 @@ public final class ViewRootImpl implements ViewParent,
insetsPending ? WindowManagerGlobal.RELAYOUT_INSETS_PENDING : 0, frameNumber,
mTmpFrame, mPendingContentInsets, mPendingVisibleInsets,
mPendingStableInsets, mPendingBackDropFrame, mPendingDisplayCutout,
- mPendingMergedConfiguration, mSurfaceControl, mTempInsets, mSurfaceSize);
+ mPendingMergedConfiguration, mSurfaceControl, mTempInsets, mSurfaceSize,
+ mBlastSurfaceControl);
if (mSurfaceControl.isValid()) {
if (!WindowManagerGlobal.USE_BLAST_ADAPTER) {
mSurface.copyFrom(mSurfaceControl);
diff --git a/core/java/android/view/WindowManagerGlobal.java b/core/java/android/view/WindowManagerGlobal.java
index d39c3c0a5cc6..f03c4e731283 100644
--- a/core/java/android/view/WindowManagerGlobal.java
+++ b/core/java/android/view/WindowManagerGlobal.java
@@ -29,7 +29,6 @@ import android.os.IBinder;
import android.os.RemoteException;
import android.os.ServiceManager;
import android.os.SystemProperties;
-import android.provider.DeviceConfig;
import android.util.AndroidRuntimeException;
import android.util.ArraySet;
import android.util.Log;
@@ -63,10 +62,7 @@ public final class WindowManagerGlobal {
* This flag controls whether ViewRootImpl will utilize the Blast Adapter
* to send buffer updates to SurfaceFlinger
*/
- public static final boolean USE_BLAST_ADAPTER =
- SystemProperties.getBoolean(String.join(".", "persist.device_config",
- DeviceConfig.NAMESPACE_WINDOW_MANAGER_NATIVE_BOOT,
- WM_USE_BLAST_ADAPTER_FLAG), false);
+ public static final boolean USE_BLAST_ADAPTER = false;
/**
* The user is navigating with keys (not the touch screen), so
diff --git a/core/java/android/view/WindowlessWindowManager.java b/core/java/android/view/WindowlessWindowManager.java
index 9f2784889cab..91778aaf51fd 100644
--- a/core/java/android/view/WindowlessWindowManager.java
+++ b/core/java/android/view/WindowlessWindowManager.java
@@ -161,7 +161,7 @@ public class WindowlessWindowManager implements IWindowSession {
Rect outStableInsets, Rect outBackdropFrame,
DisplayCutout.ParcelableWrapper cutout, MergedConfiguration mergedConfiguration,
SurfaceControl outSurfaceControl, InsetsState outInsetsState,
- Point outSurfaceSize) {
+ Point outSurfaceSize, SurfaceControl outBLASTSurfaceControl) {
State state = null;
synchronized (this) {
state = mStateForWindow.get(window.asBinder());
diff --git a/core/java/android/view/contentcapture/ContentCaptureManager.java b/core/java/android/view/contentcapture/ContentCaptureManager.java
index 81c83834098c..cede3b5cf9fe 100644
--- a/core/java/android/view/contentcapture/ContentCaptureManager.java
+++ b/core/java/android/view/contentcapture/ContentCaptureManager.java
@@ -35,6 +35,7 @@ import android.os.Binder;
import android.os.CancellationSignal;
import android.os.Handler;
import android.os.IBinder;
+import android.os.ICancellationSignal;
import android.os.Looper;
import android.os.ParcelFileDescriptor;
import android.os.RemoteException;
@@ -655,9 +656,12 @@ public final class ContentCaptureManager {
Preconditions.checkNotNull(dataShareWriteAdapter);
Preconditions.checkNotNull(executor);
+ ICancellationSignal cancellationSignalTransport = CancellationSignal.createTransport();
+
try {
- mService.shareData(request,
- new DataShareAdapterDelegate(executor, dataShareWriteAdapter));
+ mService.shareData(request, cancellationSignalTransport,
+ new DataShareAdapterDelegate(executor,
+ cancellationSignalTransport, dataShareWriteAdapter));
} catch (RemoteException e) {
e.rethrowFromSystemServer();
}
@@ -715,20 +719,30 @@ public final class ContentCaptureManager {
private final WeakReference<DataShareWriteAdapter> mAdapterReference;
private final WeakReference<Executor> mExecutorReference;
+ private final WeakReference<ICancellationSignal> mCancellationSignal;
- private DataShareAdapterDelegate(Executor executor, DataShareWriteAdapter adapter) {
+ private DataShareAdapterDelegate(Executor executor,
+ ICancellationSignal cancellationSignalTransport, DataShareWriteAdapter adapter) {
Preconditions.checkNotNull(executor);
+ Preconditions.checkNotNull(cancellationSignalTransport);
Preconditions.checkNotNull(adapter);
mExecutorReference = new WeakReference<>(executor);
mAdapterReference = new WeakReference<>(adapter);
+ mCancellationSignal = new WeakReference<>(cancellationSignalTransport);
}
@Override
public void write(ParcelFileDescriptor destination)
throws RemoteException {
- // TODO(b/148264965): implement this.
- CancellationSignal cancellationSignal = new CancellationSignal();
+ ICancellationSignal cancellationSignalTransport = mCancellationSignal.get();
+ if (cancellationSignalTransport == null) {
+ Slog.w(TAG, "Can't execute write(), reference to cancellation signal has been "
+ + "GC'ed");
+ }
+ CancellationSignal cancellationSignal =
+ CancellationSignal.fromTransport(cancellationSignalTransport);
+
executeAdapterMethodLocked(adapter -> adapter.onWrite(destination, cancellationSignal),
"onWrite");
}
diff --git a/core/java/android/view/contentcapture/IContentCaptureManager.aidl b/core/java/android/view/contentcapture/IContentCaptureManager.aidl
index e8d85ac69907..5217e68eac50 100644
--- a/core/java/android/view/contentcapture/IContentCaptureManager.aidl
+++ b/core/java/android/view/contentcapture/IContentCaptureManager.aidl
@@ -23,6 +23,7 @@ import android.view.contentcapture.DataRemovalRequest;
import android.view.contentcapture.DataShareRequest;
import android.view.contentcapture.IDataShareWriteAdapter;
import android.os.IBinder;
+import android.os.ICancellationSignal;
import com.android.internal.os.IResultReceiver;
@@ -68,7 +69,8 @@ oneway interface IContentCaptureManager {
/**
* Requests sharing of a binary data with the content capture service.
*/
- void shareData(in DataShareRequest request, in IDataShareWriteAdapter adapter);
+ void shareData(in DataShareRequest request, in ICancellationSignal cancellationSignal,
+ in IDataShareWriteAdapter adapter);
/**
* Returns whether the content capture feature is enabled for the calling user.
diff --git a/core/java/android/widget/ScrollBarDrawable.java b/core/java/android/widget/ScrollBarDrawable.java
index 80e17b5e8217..c2d4596e17dc 100644
--- a/core/java/android/widget/ScrollBarDrawable.java
+++ b/core/java/android/widget/ScrollBarDrawable.java
@@ -138,7 +138,7 @@ public class ScrollBarDrawable extends Drawable implements Drawable.Callback {
}
final Rect r = getBounds();
- if (canvas.quickReject(r.left, r.top, r.right, r.bottom, Canvas.EdgeType.AA)) {
+ if (canvas.quickReject(r.left, r.top, r.right, r.bottom)) {
return;
}
diff --git a/core/java/android/widget/Toast.java b/core/java/android/widget/Toast.java
index 42d7892eeffb..c8a7c079994c 100644
--- a/core/java/android/widget/Toast.java
+++ b/core/java/android/widget/Toast.java
@@ -17,6 +17,7 @@
package android.widget;
import static com.android.internal.util.Preconditions.checkNotNull;
+import static com.android.internal.util.Preconditions.checkState;
import android.annotation.IntDef;
import android.annotation.NonNull;
@@ -24,6 +25,9 @@ import android.annotation.Nullable;
import android.annotation.StringRes;
import android.app.INotificationManager;
import android.app.ITransientNotification;
+import android.app.ITransientNotificationCallback;
+import android.compat.Compatibility;
+import android.compat.annotation.ChangeId;
import android.compat.annotation.UnsupportedAppUsage;
import android.content.Context;
import android.content.res.Configuration;
@@ -105,15 +109,45 @@ public class Toast {
*/
public static final int LENGTH_LONG = 1;
+ /**
+ * Text toasts will be rendered by SystemUI instead of in-app, so apps can't circumvent
+ * background custom toast restrictions.
+ *
+ * TODO(b/144152069): Add @EnabledAfter(Q) to target R+ after assessing impact on dogfood
+ */
+ @ChangeId
+ // @EnabledAfter(targetSdkVersion = Build.VERSION_CODES.Q)
+ private static final long CHANGE_TEXT_TOASTS_IN_THE_SYSTEM = 147798919L;
+
+
private final Binder mToken;
private final Context mContext;
+ private final Handler mHandler;
@UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P)
final TN mTN;
@UnsupportedAppUsage
int mDuration;
- View mNextView;
- // TODO(b/128611929): Remove this and check for null view when toast creation is in the system
- boolean mIsCustomToast = false;
+
+ /**
+ * This is also passed to {@link TN} object, where it's also accessed with itself as its own
+ * lock.
+ */
+ @GuardedBy("mCallbacks")
+ private final List<Callback> mCallbacks;
+
+ /**
+ * View to be displayed, in case this is a custom toast (e.g. not created with {@link
+ * #makeText(Context, int, int)} or its variants).
+ */
+ @Nullable
+ private View mNextView;
+
+ /**
+ * Text to be shown, in case this is NOT a custom toast (e.g. created with {@link
+ * #makeText(Context, int, int)} or its variants).
+ */
+ @Nullable
+ private CharSequence mText;
/**
* Construct an empty Toast object. You must call {@link #setView} before you
@@ -133,19 +167,34 @@ public class Toast {
public Toast(@NonNull Context context, @Nullable Looper looper) {
mContext = context;
mToken = new Binder();
- mTN = new TN(context.getPackageName(), mToken, looper);
+ looper = getLooper(looper);
+ mHandler = new Handler(looper);
+ mCallbacks = new ArrayList<>();
+ mTN = new TN(context.getPackageName(), mToken, mCallbacks, looper);
mTN.mY = context.getResources().getDimensionPixelSize(
com.android.internal.R.dimen.toast_y_offset);
mTN.mGravity = context.getResources().getInteger(
com.android.internal.R.integer.config_toastDefaultGravity);
}
+ private Looper getLooper(@Nullable Looper looper) {
+ if (looper != null) {
+ return looper;
+ }
+ return checkNotNull(Looper.myLooper(),
+ "Can't toast on a thread that has not called Looper.prepare()");
+ }
+
/**
* Show the view for the specified duration.
*/
public void show() {
- if (mNextView == null) {
- throw new RuntimeException("setView must have been called");
+ if (Compatibility.isChangeEnabled(CHANGE_TEXT_TOASTS_IN_THE_SYSTEM)) {
+ checkState(mNextView != null || mText != null, "You must either set a text or a view");
+ } else {
+ if (mNextView == null) {
+ throw new RuntimeException("setView must have been called");
+ }
}
INotificationManager service = getService();
@@ -155,10 +204,18 @@ public class Toast {
final int displayId = mContext.getDisplayId();
try {
- if (mIsCustomToast) {
- service.enqueueToast(pkg, mToken, tn, mDuration, displayId);
+ if (Compatibility.isChangeEnabled(CHANGE_TEXT_TOASTS_IN_THE_SYSTEM)) {
+ if (mNextView != null) {
+ // It's a custom toast
+ service.enqueueToast(pkg, mToken, tn, mDuration, displayId);
+ } else {
+ // It's a text toast
+ ITransientNotificationCallback callback =
+ new CallbackBinder(mCallbacks, mHandler);
+ service.enqueueTextToast(pkg, mToken, mText, mDuration, displayId, callback);
+ }
} else {
- service.enqueueTextToast(pkg, mToken, tn, mDuration, displayId);
+ service.enqueueToast(pkg, mToken, tn, mDuration, displayId);
}
} catch (RemoteException e) {
// Empty
@@ -171,7 +228,16 @@ public class Toast {
* after the appropriate duration.
*/
public void cancel() {
- mTN.cancel();
+ if (Compatibility.isChangeEnabled(CHANGE_TEXT_TOASTS_IN_THE_SYSTEM)
+ && mNextView == null) {
+ try {
+ getService().cancelToast(mContext.getOpPackageName(), mToken);
+ } catch (RemoteException e) {
+ // Empty
+ }
+ } else {
+ mTN.cancel();
+ }
}
/**
@@ -187,7 +253,6 @@ public class Toast {
*/
@Deprecated
public void setView(View view) {
- mIsCustomToast = true;
mNextView = view;
}
@@ -203,7 +268,6 @@ public class Toast {
* will not have custom toast views displayed.
*/
public View getView() {
- mIsCustomToast = true;
return mNextView;
}
@@ -298,8 +362,8 @@ public class Toast {
*/
public void addCallback(@NonNull Callback callback) {
checkNotNull(callback);
- synchronized (mTN.mCallbacks) {
- mTN.mCallbacks.add(callback);
+ synchronized (mCallbacks) {
+ mCallbacks.add(callback);
}
}
@@ -307,8 +371,8 @@ public class Toast {
* Removes a callback previously added with {@link #addCallback(Callback)}.
*/
public void removeCallback(@NonNull Callback callback) {
- synchronized (mTN.mCallbacks) {
- mTN.mCallbacks.remove(callback);
+ synchronized (mCallbacks) {
+ mCallbacks.remove(callback);
}
}
@@ -338,22 +402,30 @@ public class Toast {
/**
* Make a standard toast to display using the specified looper.
* If looper is null, Looper.myLooper() is used.
+ *
* @hide
*/
public static Toast makeText(@NonNull Context context, @Nullable Looper looper,
@NonNull CharSequence text, @Duration int duration) {
- Toast result = new Toast(context, looper);
-
- LayoutInflater inflate = (LayoutInflater)
- context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
- View v = inflate.inflate(com.android.internal.R.layout.transient_notification, null);
- TextView tv = (TextView)v.findViewById(com.android.internal.R.id.message);
- tv.setText(text);
-
- result.mNextView = v;
- result.mDuration = duration;
-
- return result;
+ if (Compatibility.isChangeEnabled(CHANGE_TEXT_TOASTS_IN_THE_SYSTEM)) {
+ Toast result = new Toast(context, looper);
+ result.mText = text;
+ result.mDuration = duration;
+ return result;
+ } else {
+ Toast result = new Toast(context, looper);
+
+ LayoutInflater inflate = (LayoutInflater)
+ context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
+ View v = inflate.inflate(com.android.internal.R.layout.transient_notification, null);
+ TextView tv = (TextView) v.findViewById(com.android.internal.R.id.message);
+ tv.setText(text);
+
+ result.mNextView = v;
+ result.mDuration = duration;
+
+ return result;
+ }
}
/**
@@ -385,14 +457,23 @@ public class Toast {
* @param s The new text for the Toast.
*/
public void setText(CharSequence s) {
- if (mNextView == null) {
- throw new RuntimeException("This Toast was not created with Toast.makeText()");
- }
- TextView tv = mNextView.findViewById(com.android.internal.R.id.message);
- if (tv == null) {
- throw new RuntimeException("This Toast was not created with Toast.makeText()");
+ if (Compatibility.isChangeEnabled(CHANGE_TEXT_TOASTS_IN_THE_SYSTEM)) {
+ if (mNextView != null) {
+ throw new IllegalStateException(
+ "Text provided for custom toast, remove previous setView() calls if you "
+ + "want a text toast instead.");
+ }
+ mText = s;
+ } else {
+ if (mNextView == null) {
+ throw new RuntimeException("This Toast was not created with Toast.makeText()");
+ }
+ TextView tv = mNextView.findViewById(com.android.internal.R.id.message);
+ if (tv == null) {
+ throw new RuntimeException("This Toast was not created with Toast.makeText()");
+ }
+ tv.setText(s);
}
- tv.setText(s);
}
// =======================================================================================
@@ -442,12 +523,18 @@ public class Toast {
final Binder mToken;
@GuardedBy("mCallbacks")
- private final List<Callback> mCallbacks = new ArrayList<>();
+ private final List<Callback> mCallbacks;
static final long SHORT_DURATION_TIMEOUT = 4000;
static final long LONG_DURATION_TIMEOUT = 7000;
- TN(String packageName, Binder token, @Nullable Looper looper) {
+ /**
+ * Creates a {@link ITransientNotification} object.
+ *
+ * The parameter {@code callbacks} is not copied and is accessed with itself as its own
+ * lock.
+ */
+ TN(String packageName, Binder token, List<Callback> callbacks, @Nullable Looper looper) {
// XXX This should be changed to use a Dialog, with a Theme.Toast
// defined that sets up the layout params appropriately.
final WindowManager.LayoutParams params = mParams;
@@ -464,15 +551,8 @@ public class Toast {
mPackageName = packageName;
mToken = token;
+ mCallbacks = callbacks;
- if (looper == null) {
- // Use Looper.myLooper() if looper is not specified.
- looper = Looper.myLooper();
- if (looper == null) {
- throw new RuntimeException(
- "Can't toast on a thread that has not called Looper.prepare()");
- }
- }
mHandler = new Handler(looper, null) {
@Override
public void handleMessage(Message msg) {
@@ -655,4 +735,46 @@ public class Toast {
*/
public void onToastHidden() {}
}
+
+ private static class CallbackBinder extends ITransientNotificationCallback.Stub {
+ private final Handler mHandler;
+
+ @GuardedBy("mCallbacks")
+ private final List<Callback> mCallbacks;
+
+ /**
+ * Creates a {@link ITransientNotificationCallback} object.
+ *
+ * The parameter {@code callbacks} is not copied and is accessed with itself as its own
+ * lock.
+ */
+ private CallbackBinder(List<Callback> callbacks, Handler handler) {
+ mCallbacks = callbacks;
+ mHandler = handler;
+ }
+
+ @Override
+ public void onToastShown() {
+ mHandler.post(() -> {
+ for (Callback callback : getCallbacks()) {
+ callback.onToastShown();
+ }
+ });
+ }
+
+ @Override
+ public void onToastHidden() {
+ mHandler.post(() -> {
+ for (Callback callback : getCallbacks()) {
+ callback.onToastHidden();
+ }
+ });
+ }
+
+ private List<Callback> getCallbacks() {
+ synchronized (mCallbacks) {
+ return new ArrayList<>(mCallbacks);
+ }
+ }
+ }
}
diff --git a/core/java/com/android/internal/config/sysui/SystemUiDeviceConfigFlags.java b/core/java/com/android/internal/config/sysui/SystemUiDeviceConfigFlags.java
index de64573d1e24..b232efc8f46c 100644
--- a/core/java/com/android/internal/config/sysui/SystemUiDeviceConfigFlags.java
+++ b/core/java/com/android/internal/config/sysui/SystemUiDeviceConfigFlags.java
@@ -364,6 +364,12 @@ public final class SystemUiDeviceConfigFlags {
*/
public static final String NAV_BAR_HANDLE_FORCE_OPAQUE = "nav_bar_handle_force_opaque";
+ /**
+ * (boolean) Whether to force the Nav Bar handle to remain visible over the lockscreen.
+ */
+ public static final String NAV_BAR_HANDLE_SHOW_OVER_LOCKSCREEN =
+ "nav_bar_handle_show_over_lockscreen";
+
private SystemUiDeviceConfigFlags() {
}
}
diff --git a/core/java/com/android/internal/statusbar/IStatusBar.aidl b/core/java/com/android/internal/statusbar/IStatusBar.aidl
index aa0ac37b007a..a2736333383e 100644
--- a/core/java/com/android/internal/statusbar/IStatusBar.aidl
+++ b/core/java/com/android/internal/statusbar/IStatusBar.aidl
@@ -16,6 +16,7 @@
package com.android.internal.statusbar;
+import android.app.ITransientNotificationCallback;
import android.content.ComponentName;
import android.graphics.Rect;
import android.hardware.biometrics.IBiometricServiceReceiverInternal;
@@ -198,4 +199,15 @@ oneway interface IStatusBar
* Dismiss the warning that the device is about to go to sleep due to user inactivity.
*/
void dismissInattentiveSleepWarning(boolean animated);
+
+ /**
+ * Displays a text toast.
+ */
+ void showToast(String packageName, IBinder token, CharSequence text, IBinder windowToken,
+ int duration, @nullable ITransientNotificationCallback callback);
+
+ /**
+ * Cancels toast with token {@code token} in {@code packageName}.
+ */
+ void hideToast(String packageName, IBinder token);
}
diff --git a/core/java/com/android/internal/statusbar/IStatusBarService.aidl b/core/java/com/android/internal/statusbar/IStatusBarService.aidl
index a9f7b8455807..7622883326a0 100644
--- a/core/java/com/android/internal/statusbar/IStatusBarService.aidl
+++ b/core/java/com/android/internal/statusbar/IStatusBarService.aidl
@@ -78,6 +78,7 @@ interface IStatusBarService
in int notificationLocation, boolean modifiedBeforeSending);
void onNotificationSettingsViewed(String key);
void onNotificationBubbleChanged(String key, boolean isBubble);
+ void onBubbleNotificationSuppressionChanged(String key, boolean isSuppressed);
void grantInlineReplyUriPermission(String key, in Uri uri, in UserHandle user, String packageName);
void clearInlineReplyUriPermissions(String key);
diff --git a/core/java/com/android/internal/util/ConnectivityUtil.java b/core/java/com/android/internal/util/LocationPermissionChecker.java
index 799352b9ec15..2db0e9979d94 100644
--- a/core/java/com/android/internal/util/ConnectivityUtil.java
+++ b/core/java/com/android/internal/util/LocationPermissionChecker.java
@@ -33,28 +33,59 @@ import com.android.internal.annotations.VisibleForTesting;
/**
- * Utility methods for common functionality using by different networks.
+ * The methods used for location permission and location mode checking.
*
* @hide
*/
-public class ConnectivityUtil {
+public class LocationPermissionChecker {
- private static final String TAG = "ConnectivityUtil";
+ private static final String TAG = "LocationPermissionChecker";
private final Context mContext;
- private final AppOpsManager mAppOps;
+ private final AppOpsManager mAppOpsManager;
private final UserManager mUserManager;
+ private final LocationManager mLocationManager;
- public ConnectivityUtil(Context context) {
+ public LocationPermissionChecker(Context context) {
mContext = context;
- mAppOps = (AppOpsManager) mContext.getSystemService(Context.APP_OPS_SERVICE);
+ mAppOpsManager = (AppOpsManager) mContext.getSystemService(Context.APP_OPS_SERVICE);
mUserManager = (UserManager) mContext.getSystemService(Context.USER_SERVICE);
+ mLocationManager =
+ (LocationManager) context.getSystemService(Context.LOCATION_SERVICE);
}
/**
- * API to determine if the caller has fine/coarse location permission (depending on
- * config/targetSDK level) and the location mode is enabled for the user. SecurityException is
- * thrown if the caller has no permission or the location mode is disabled.
+ * Check location permission granted by the caller.
+ *
+ * This API check if the location mode enabled for the caller and the caller has
+ * ACCESS_COARSE_LOCATION permission is targetSDK<29, otherwise, has ACCESS_FINE_LOCATION.
+ *
+ * @param pkgName package name of the application requesting access
+ * @param featureId The feature in the package
+ * @param uid The uid of the package
+ * @param message A message describing why the permission was checked. Only needed if this is
+ * not inside of a two-way binder call from the data receiver
+ *
+ * @return {@code true} returns if the caller has location permission and the location mode is
+ * enabled.
+ */
+ public boolean checkLocationPermission(String pkgName, @Nullable String featureId,
+ int uid, @Nullable String message) {
+ try {
+ enforceLocationPermission(pkgName, featureId, uid, message);
+ return true;
+ } catch (SecurityException e) {
+ return false;
+ }
+ }
+
+ /**
+ * Enforce the caller has location permission.
+ *
+ * This API determines if the location mode enabled for the caller and the caller has
+ * ACCESS_COARSE_LOCATION permission is targetSDK<29, otherwise, has ACCESS_FINE_LOCATION.
+ * SecurityException is thrown if the caller has no permission or the location mode is disabled.
+ *
* @param pkgName package name of the application requesting access
* @param featureId The feature in the package
* @param uid The uid of the package
@@ -62,31 +93,21 @@ public class ConnectivityUtil {
* not inside of a two-way binder call from the data receiver
*/
public void enforceLocationPermission(String pkgName, @Nullable String featureId, int uid,
- @Nullable String message)
- throws SecurityException {
+ @Nullable String message) throws SecurityException {
+
checkPackage(uid, pkgName);
// Location mode must be enabled
if (!isLocationModeEnabled()) {
- // Location mode is disabled, scan results cannot be returned
throw new SecurityException("Location mode is disabled for the device");
}
// LocationAccess by App: caller must have Coarse/Fine Location permission to have access to
// location information.
- boolean canAppPackageUseLocation = checkCallersLocationPermission(pkgName, featureId,
- uid, /* coarseForTargetSdkLessThanQ */ true, message);
-
- // If neither caller or app has location access, there is no need to check
- // any other permissions. Deny access to scan results.
- if (!canAppPackageUseLocation) {
+ if (!checkCallersLocationPermission(pkgName, featureId,
+ uid, /* coarseForTargetSdkLessThanQ */ true, message)) {
throw new SecurityException("UID " + uid + " has no location permission");
}
- // If the User or profile is current, permission is granted
- // Otherwise, uid must have INTERACT_ACROSS_USERS_FULL permission.
- if (!isCurrentProfile(uid) && !checkInteractAcrossUsersFull(uid)) {
- throw new SecurityException("UID " + uid + " profile not permitted");
- }
}
/**
@@ -104,6 +125,7 @@ public class ConnectivityUtil {
*/
public boolean checkCallersLocationPermission(String pkgName, @Nullable String featureId,
int uid, boolean coarseForTargetSdkLessThanQ, @Nullable String message) {
+
boolean isTargetSdkLessThanQ = isTargetSdkLessThan(pkgName, Build.VERSION_CODES.Q, uid);
String permissionType = Manifest.permission.ACCESS_FINE_LOCATION;
@@ -111,8 +133,7 @@ public class ConnectivityUtil {
// Having FINE permission implies having COARSE permission (but not the reverse)
permissionType = Manifest.permission.ACCESS_COARSE_LOCATION;
}
- if (getUidPermission(permissionType, uid)
- == PackageManager.PERMISSION_DENIED) {
+ if (getUidPermission(permissionType, uid) == PackageManager.PERMISSION_DENIED) {
return false;
}
@@ -134,10 +155,8 @@ public class ConnectivityUtil {
* Retrieves a handle to LocationManager (if not already done) and check if location is enabled.
*/
public boolean isLocationModeEnabled() {
- LocationManager locationManager =
- (LocationManager) mContext.getSystemService(Context.LOCATION_SERVICE);
try {
- return locationManager.isLocationEnabledForUser(UserHandle.of(
+ return mLocationManager.isLocationEnabledForUser(UserHandle.of(
getCurrentUser()));
} catch (Exception e) {
Log.e(TAG, "Failure to get location mode via API, falling back to settings", e);
@@ -166,27 +185,16 @@ public class ConnectivityUtil {
private boolean noteAppOpAllowed(String op, String pkgName, @Nullable String featureId,
int uid, @Nullable String message) {
- return mAppOps.noteOp(op, uid, pkgName, featureId, message) == AppOpsManager.MODE_ALLOWED;
+ return mAppOpsManager.noteOp(op, uid, pkgName, featureId, message)
+ == AppOpsManager.MODE_ALLOWED;
}
- private void checkPackage(int uid, String pkgName) throws SecurityException {
+ private void checkPackage(int uid, String pkgName)
+ throws SecurityException {
if (pkgName == null) {
throw new SecurityException("Checking UID " + uid + " but Package Name is Null");
}
- mAppOps.checkPackage(uid, pkgName);
- }
-
- private boolean isCurrentProfile(int uid) {
- UserHandle currentUser = UserHandle.of(getCurrentUser());
- UserHandle callingUser = UserHandle.getUserHandleForUid(uid);
- return currentUser.equals(callingUser)
- || mUserManager.isSameProfileGroup(currentUser, callingUser);
- }
-
- private boolean checkInteractAcrossUsersFull(int uid) {
- return getUidPermission(
- android.Manifest.permission.INTERACT_ACROSS_USERS_FULL, uid)
- == PackageManager.PERMISSION_GRANTED;
+ mAppOpsManager.checkPackage(uid, pkgName);
}
@VisibleForTesting
diff --git a/core/jni/android/graphics/Bitmap.cpp b/core/jni/android/graphics/Bitmap.cpp
index eb7d432b559b..30e914de45c2 100755
--- a/core/jni/android/graphics/Bitmap.cpp
+++ b/core/jni/android/graphics/Bitmap.cpp
@@ -34,7 +34,6 @@
#include <string>
#define DEBUG_PARCEL 0
-#define ASHMEM_BITMAP_MIN_SIZE (128 * (1 << 10))
static jclass gBitmap_class;
static jfieldID gBitmap_nativePtr;
@@ -587,7 +586,6 @@ static jobject Bitmap_createFromParcel(JNIEnv* env, jobject, jobject parcel) {
android::Parcel* p = android::parcelForJavaObject(env, parcel);
- const bool isMutable = p->readInt32() != 0;
const SkColorType colorType = (SkColorType)p->readInt32();
const SkAlphaType alphaType = (SkAlphaType)p->readInt32();
const uint32_t colorSpaceSize = p->readUint32();
@@ -636,11 +634,10 @@ static jobject Bitmap_createFromParcel(JNIEnv* env, jobject, jobject parcel) {
// Map the bitmap in place from the ashmem region if possible otherwise copy.
sk_sp<Bitmap> nativeBitmap;
- if (blob.fd() >= 0 && (blob.isMutable() || !isMutable) && (size >= ASHMEM_BITMAP_MIN_SIZE)) {
+ if (blob.fd() >= 0 && !blob.isMutable()) {
#if DEBUG_PARCEL
- ALOGD("Bitmap.createFromParcel: mapped contents of %s bitmap from %s blob "
+ ALOGD("Bitmap.createFromParcel: mapped contents of bitmap from %s blob "
"(fds %s)",
- isMutable ? "mutable" : "immutable",
blob.isMutable() ? "mutable" : "immutable",
p->allowFds() ? "allowed" : "forbidden");
#endif
@@ -657,7 +654,7 @@ static jobject Bitmap_createFromParcel(JNIEnv* env, jobject, jobject parcel) {
// Map the pixels in place and take ownership of the ashmem region. We must also respect the
// rowBytes value already set on the bitmap instead of attempting to compute our own.
nativeBitmap = Bitmap::createFrom(bitmap->info(), bitmap->rowBytes(), dupFd,
- const_cast<void*>(blob.data()), size, !isMutable);
+ const_cast<void*>(blob.data()), size, true);
if (!nativeBitmap) {
close(dupFd);
blob.release();
@@ -695,7 +692,7 @@ static jobject Bitmap_createFromParcel(JNIEnv* env, jobject, jobject parcel) {
}
return createBitmap(env, nativeBitmap.release(),
- getPremulBitmapCreateFlags(isMutable), NULL, NULL, density);
+ getPremulBitmapCreateFlags(false), NULL, NULL, density);
#else
doThrowRE(env, "Cannot use parcels outside of Android");
return NULL;
@@ -703,9 +700,7 @@ static jobject Bitmap_createFromParcel(JNIEnv* env, jobject, jobject parcel) {
}
static jboolean Bitmap_writeToParcel(JNIEnv* env, jobject,
- jlong bitmapHandle,
- jboolean isMutable, jint density,
- jobject parcel) {
+ jlong bitmapHandle, jint density, jobject parcel) {
#ifdef __ANDROID__ // Layoutlib does not support parcel
if (parcel == NULL) {
SkDebugf("------- writeToParcel null parcel\n");
@@ -718,7 +713,6 @@ static jboolean Bitmap_writeToParcel(JNIEnv* env, jobject,
auto bitmapWrapper = reinterpret_cast<BitmapWrapper*>(bitmapHandle);
bitmapWrapper->getSkBitmap(&bitmap);
- p->writeInt32(isMutable);
p->writeInt32(bitmap.colorType());
p->writeInt32(bitmap.alphaType());
SkColorSpace* colorSpace = bitmap.colorSpace();
@@ -745,7 +739,7 @@ static jboolean Bitmap_writeToParcel(JNIEnv* env, jobject,
// Transfer the underlying ashmem region if we have one and it's immutable.
android::status_t status;
int fd = bitmapWrapper->bitmap().getAshmemFd();
- if (fd >= 0 && !isMutable && p->allowFds()) {
+ if (fd >= 0 && p->allowFds()) {
#if DEBUG_PARCEL
ALOGD("Bitmap.writeToParcel: transferring immutable bitmap's ashmem fd as "
"immutable blob (fds %s)",
@@ -761,17 +755,14 @@ static jboolean Bitmap_writeToParcel(JNIEnv* env, jobject,
}
// Copy the bitmap to a new blob.
- bool mutableCopy = isMutable;
#if DEBUG_PARCEL
- ALOGD("Bitmap.writeToParcel: copying %s bitmap into new %s blob (fds %s)",
- isMutable ? "mutable" : "immutable",
- mutableCopy ? "mutable" : "immutable",
+ ALOGD("Bitmap.writeToParcel: copying bitmap into new blob (fds %s)",
p->allowFds() ? "allowed" : "forbidden");
#endif
size_t size = bitmap.computeByteSize();
android::Parcel::WritableBlob blob;
- status = p->writeBlob(size, mutableCopy, &blob);
+ status = p->writeBlob(size, false, &blob);
if (status) {
doThrowRE(env, "Could not copy bitmap to parcel blob.");
return JNI_FALSE;
@@ -1109,7 +1100,7 @@ static const JNINativeMethod gBitmapMethods[] = {
{ "nativeCreateFromParcel",
"(Landroid/os/Parcel;)Landroid/graphics/Bitmap;",
(void*)Bitmap_createFromParcel },
- { "nativeWriteToParcel", "(JZILandroid/os/Parcel;)Z",
+ { "nativeWriteToParcel", "(JILandroid/os/Parcel;)Z",
(void*)Bitmap_writeToParcel },
{ "nativeExtractAlpha", "(JJ[I)Landroid/graphics/Bitmap;",
(void*)Bitmap_extractAlpha },
diff --git a/core/jni/android_content_res_ApkAssets.cpp b/core/jni/android_content_res_ApkAssets.cpp
index 5d13cf82141b..b55dc68af558 100644
--- a/core/jni/android_content_res_ApkAssets.cpp
+++ b/core/jni/android_content_res_ApkAssets.cpp
@@ -221,7 +221,7 @@ static jobject NativeGetOverlayableInfo(JNIEnv* env, jclass /*clazz*/, jlong ptr
return nullptr;
}
- jstring actor_string = env->NewStringUTF(actor->first.c_str());
+ jstring actor_string = env->NewStringUTF(actor->second.c_str());
if (env->ExceptionCheck() || actor_string == nullptr) {
jniThrowException(env, "java/io/IOException", "Error reading overlayable from APK");
return 0;
diff --git a/core/jni/android_util_Binder.cpp b/core/jni/android_util_Binder.cpp
index fb8e633fec12..e77c25efb1d4 100644
--- a/core/jni/android_util_Binder.cpp
+++ b/core/jni/android_util_Binder.cpp
@@ -537,9 +537,10 @@ public:
LOGDEATH("Receiving binderDied() on JavaDeathRecipient %p\n", this);
if (mObject != NULL) {
JNIEnv* env = javavm_to_jnienv(mVM);
- jobject jBinderProxy = javaObjectForIBinder(env, who.promote());
+ ScopedLocalRef<jobject> jBinderProxy(env, javaObjectForIBinder(env, who.promote()));
env->CallStaticVoidMethod(gBinderProxyOffsets.mClass,
- gBinderProxyOffsets.mSendDeathNotice, mObject, jBinderProxy);
+ gBinderProxyOffsets.mSendDeathNotice, mObject,
+ jBinderProxy.get());
if (env->ExceptionCheck()) {
jthrowable excep = env->ExceptionOccurred();
report_exception(env, excep,
diff --git a/core/jni/android_view_InputEventSender.cpp b/core/jni/android_view_InputEventSender.cpp
index f90d1cf27d7c..84acf9ac6989 100644
--- a/core/jni/android_view_InputEventSender.cpp
+++ b/core/jni/android_view_InputEventSender.cpp
@@ -113,10 +113,13 @@ status_t NativeInputEventSender::sendKeyEvent(uint32_t seq, const KeyEvent* even
}
uint32_t publishedSeq = mNextPublishedSeq++;
- status_t status = mInputPublisher.publishKeyEvent(publishedSeq,
- event->getDeviceId(), event->getSource(), event->getDisplayId(), event->getAction(),
- event->getFlags(), event->getKeyCode(), event->getScanCode(), event->getMetaState(),
- event->getRepeatCount(), event->getDownTime(), event->getEventTime());
+ status_t status =
+ mInputPublisher.publishKeyEvent(publishedSeq, event->getDeviceId(), event->getSource(),
+ event->getDisplayId(), event->getHmac(),
+ event->getAction(), event->getFlags(),
+ event->getKeyCode(), event->getScanCode(),
+ event->getMetaState(), event->getRepeatCount(),
+ event->getDownTime(), event->getEventTime());
if (status) {
ALOGW("Failed to send key event on channel '%s'. status=%d",
getInputChannelName().c_str(), status);
@@ -134,17 +137,24 @@ status_t NativeInputEventSender::sendMotionEvent(uint32_t seq, const MotionEvent
uint32_t publishedSeq;
for (size_t i = 0; i <= event->getHistorySize(); i++) {
publishedSeq = mNextPublishedSeq++;
- status_t status = mInputPublisher.publishMotionEvent(publishedSeq,
- event->getDeviceId(), event->getSource(), event->getDisplayId(),
- event->getAction(), event->getActionButton(), event->getFlags(),
- event->getEdgeFlags(), event->getMetaState(), event->getButtonState(),
- event->getClassification(),
- event->getXOffset(), event->getYOffset(),
- event->getXPrecision(), event->getYPrecision(),
- event->getRawXCursorPosition(), event->getRawYCursorPosition(),
- event->getDownTime(), event->getHistoricalEventTime(i),
- event->getPointerCount(), event->getPointerProperties(),
- event->getHistoricalRawPointerCoords(0, i));
+ status_t status =
+ mInputPublisher.publishMotionEvent(publishedSeq, event->getDeviceId(),
+ event->getSource(), event->getDisplayId(),
+ event->getHmac(), event->getAction(),
+ event->getActionButton(), event->getFlags(),
+ event->getEdgeFlags(), event->getMetaState(),
+ event->getButtonState(),
+ event->getClassification(), event->getXScale(),
+ event->getYScale(), event->getXOffset(),
+ event->getYOffset(), event->getXPrecision(),
+ event->getYPrecision(),
+ event->getRawXCursorPosition(),
+ event->getRawYCursorPosition(),
+ event->getDownTime(),
+ event->getHistoricalEventTime(i),
+ event->getPointerCount(),
+ event->getPointerProperties(),
+ event->getHistoricalRawPointerCoords(0, i));
if (status) {
ALOGW("Failed to send motion event sample on channel '%s'. status=%d",
getInputChannelName().c_str(), status);
diff --git a/core/jni/android_view_KeyEvent.cpp b/core/jni/android_view_KeyEvent.cpp
index f0107723a43e..57979bd9b546 100644
--- a/core/jni/android_view_KeyEvent.cpp
+++ b/core/jni/android_view_KeyEvent.cpp
@@ -20,15 +20,53 @@
#include <android_runtime/AndroidRuntime.h>
#include <android_runtime/Log.h>
-#include <utils/Log.h>
#include <input/Input.h>
+#include <nativehelper/ScopedPrimitiveArray.h>
#include <nativehelper/ScopedUtfChars.h>
+#include <utils/Log.h>
+#include <optional>
#include "android_view_KeyEvent.h"
#include "core_jni_helpers.h"
namespace android {
+/**
+ * Convert an std::array of bytes into a Java object.
+ */
+template <size_t N>
+static ScopedLocalRef<jbyteArray> toJbyteArray(JNIEnv* env, const std::array<uint8_t, N>& data) {
+ ScopedLocalRef<jbyteArray> array(env, env->NewByteArray(N));
+ if (array.get() == nullptr) {
+ jniThrowException(env, "java/lang/OutOfMemoryError", nullptr);
+ return array;
+ }
+ static_assert(sizeof(char) == sizeof(uint8_t));
+ env->SetByteArrayRegion(array.get(), 0, N, reinterpret_cast<const signed char*>(data.data()));
+ return array;
+}
+
+/**
+ * Convert a Java object into an std::array of bytes of size N.
+ * If the object is null, or the length is unexpected, return std::nullopt.
+ */
+template <size_t N>
+static std::optional<std::array<uint8_t, N>> fromJobject(JNIEnv* env, jobject object) {
+ if (object == nullptr) {
+ return std::nullopt;
+ }
+ jbyteArray javaArray = reinterpret_cast<jbyteArray>(object);
+ ScopedByteArrayRO bytes(env, javaArray);
+ if (bytes.size() != N) {
+ ALOGE("Could not initialize array from java object, expected length %zu but got %zu", N,
+ bytes.size());
+ return std::nullopt;
+ }
+ std::array<uint8_t, N> array;
+ std::move(bytes.get(), bytes.get() + N, array.begin());
+ return array;
+}
+
// ----------------------------------------------------------------------------
static struct {
@@ -40,6 +78,7 @@ static struct {
jfieldID mDeviceId;
jfieldID mSource;
jfieldID mDisplayId;
+ jfieldID mHmac;
jfieldID mMetaState;
jfieldID mAction;
jfieldID mKeyCode;
@@ -54,20 +93,16 @@ static struct {
// ----------------------------------------------------------------------------
jobject android_view_KeyEvent_fromNative(JNIEnv* env, const KeyEvent* event) {
- jobject eventObj = env->CallStaticObjectMethod(gKeyEventClassInfo.clazz,
- gKeyEventClassInfo.obtain,
- nanoseconds_to_milliseconds(event->getDownTime()),
- nanoseconds_to_milliseconds(event->getEventTime()),
- event->getAction(),
- event->getKeyCode(),
- event->getRepeatCount(),
- event->getMetaState(),
- event->getDeviceId(),
- event->getScanCode(),
- event->getFlags(),
- event->getSource(),
- event->getDisplayId(),
- NULL);
+ ScopedLocalRef<jbyteArray> hmac = toJbyteArray(env, event->getHmac());
+ jobject eventObj =
+ env->CallStaticObjectMethod(gKeyEventClassInfo.clazz, gKeyEventClassInfo.obtain,
+ nanoseconds_to_milliseconds(event->getDownTime()),
+ nanoseconds_to_milliseconds(event->getEventTime()),
+ event->getAction(), event->getKeyCode(),
+ event->getRepeatCount(), event->getMetaState(),
+ event->getDeviceId(), event->getScanCode(),
+ event->getFlags(), event->getSource(),
+ event->getDisplayId(), hmac.get(), NULL);
if (env->ExceptionCheck()) {
ALOGE("An exception occurred while obtaining a key event.");
LOGE_EX(env);
@@ -82,6 +117,11 @@ status_t android_view_KeyEvent_toNative(JNIEnv* env, jobject eventObj,
jint deviceId = env->GetIntField(eventObj, gKeyEventClassInfo.mDeviceId);
jint source = env->GetIntField(eventObj, gKeyEventClassInfo.mSource);
jint displayId = env->GetIntField(eventObj, gKeyEventClassInfo.mDisplayId);
+ jobject hmacObj = env->GetObjectField(eventObj, gKeyEventClassInfo.mHmac);
+ std::optional<std::array<uint8_t, 32>> hmac = fromJobject<32>(env, hmacObj);
+ if (!hmac) {
+ hmac = INVALID_HMAC;
+ }
jint metaState = env->GetIntField(eventObj, gKeyEventClassInfo.mMetaState);
jint action = env->GetIntField(eventObj, gKeyEventClassInfo.mAction);
jint keyCode = env->GetIntField(eventObj, gKeyEventClassInfo.mKeyCode);
@@ -91,10 +131,9 @@ status_t android_view_KeyEvent_toNative(JNIEnv* env, jobject eventObj,
jlong downTime = env->GetLongField(eventObj, gKeyEventClassInfo.mDownTime);
jlong eventTime = env->GetLongField(eventObj, gKeyEventClassInfo.mEventTime);
- event->initialize(deviceId, source, displayId, action, flags, keyCode, scanCode, metaState,
- repeatCount,
- milliseconds_to_nanoseconds(downTime),
- milliseconds_to_nanoseconds(eventTime));
+ event->initialize(deviceId, source, displayId, *hmac, action, flags, keyCode, scanCode,
+ metaState, repeatCount, milliseconds_to_nanoseconds(downTime),
+ milliseconds_to_nanoseconds(eventTime));
return OK;
}
@@ -134,8 +173,9 @@ int register_android_view_KeyEvent(JNIEnv* env) {
jclass clazz = FindClassOrDie(env, "android/view/KeyEvent");
gKeyEventClassInfo.clazz = MakeGlobalRefOrDie(env, clazz);
- gKeyEventClassInfo.obtain = GetStaticMethodIDOrDie(env, gKeyEventClassInfo.clazz,
- "obtain", "(JJIIIIIIIIILjava/lang/String;)Landroid/view/KeyEvent;");
+ gKeyEventClassInfo.obtain =
+ GetStaticMethodIDOrDie(env, gKeyEventClassInfo.clazz, "obtain",
+ "(JJIIIIIIIII[BLjava/lang/String;)Landroid/view/KeyEvent;");
gKeyEventClassInfo.recycle = GetMethodIDOrDie(env, gKeyEventClassInfo.clazz,
"recycle", "()V");
@@ -143,6 +183,7 @@ int register_android_view_KeyEvent(JNIEnv* env) {
gKeyEventClassInfo.mSource = GetFieldIDOrDie(env, gKeyEventClassInfo.clazz, "mSource", "I");
gKeyEventClassInfo.mDisplayId = GetFieldIDOrDie(env, gKeyEventClassInfo.clazz, "mDisplayId",
"I");
+ gKeyEventClassInfo.mHmac = GetFieldIDOrDie(env, gKeyEventClassInfo.clazz, "mHmac", "[B");
gKeyEventClassInfo.mMetaState = GetFieldIDOrDie(env, gKeyEventClassInfo.clazz, "mMetaState",
"I");
gKeyEventClassInfo.mAction = GetFieldIDOrDie(env, gKeyEventClassInfo.clazz, "mAction", "I");
diff --git a/core/jni/android_view_KeyEvent.h b/core/jni/android_view_KeyEvent.h
index 586eb2f2a139..dab6bb75c91e 100644
--- a/core/jni/android_view_KeyEvent.h
+++ b/core/jni/android_view_KeyEvent.h
@@ -42,4 +42,4 @@ extern status_t android_view_KeyEvent_recycle(JNIEnv* env, jobject eventObj);
} // namespace android
-#endif // _ANDROID_OS_KEYEVENT_H
+#endif // _ANDROID_VIEW_KEYEVENT_H
diff --git a/core/jni/android_view_MotionEvent.cpp b/core/jni/android_view_MotionEvent.cpp
index feb9fe39e0a8..3335fb23cfb7 100644
--- a/core/jni/android_view_MotionEvent.cpp
+++ b/core/jni/android_view_MotionEvent.cpp
@@ -330,14 +330,12 @@ static void pointerPropertiesFromNative(JNIEnv* env, const PointerProperties* po
// ----------------------------------------------------------------------------
-static jlong android_view_MotionEvent_nativeInitialize(JNIEnv* env, jclass clazz,
- jlong nativePtr,
- jint deviceId, jint source, jint displayId, jint action, jint flags, jint edgeFlags,
- jint metaState, jint buttonState, jint classification,
- jfloat xOffset, jfloat yOffset, jfloat xPrecision, jfloat yPrecision,
- jlong downTimeNanos, jlong eventTimeNanos,
- jint pointerCount, jobjectArray pointerPropertiesObjArray,
- jobjectArray pointerCoordsObjArray) {
+static jlong android_view_MotionEvent_nativeInitialize(
+ JNIEnv* env, jclass clazz, jlong nativePtr, jint deviceId, jint source, jint displayId,
+ jint action, jint flags, jint edgeFlags, jint metaState, jint buttonState,
+ jint classification, jfloat xOffset, jfloat yOffset, jfloat xPrecision, jfloat yPrecision,
+ jlong downTimeNanos, jlong eventTimeNanos, jint pointerCount,
+ jobjectArray pointerPropertiesObjArray, jobjectArray pointerCoordsObjArray) {
if (!validatePointerCount(env, pointerCount)
|| !validatePointerPropertiesArray(env, pointerPropertiesObjArray, pointerCount)
|| !validatePointerCoordsObjArray(env, pointerCoordsObjArray, pointerCount)) {
@@ -371,11 +369,12 @@ static jlong android_view_MotionEvent_nativeInitialize(JNIEnv* env, jclass clazz
env->DeleteLocalRef(pointerCoordsObj);
}
- event->initialize(deviceId, source, displayId, action, 0, flags, edgeFlags, metaState,
- buttonState, static_cast<MotionClassification>(classification),
- xOffset, yOffset, xPrecision, yPrecision,
- AMOTION_EVENT_INVALID_CURSOR_POSITION, AMOTION_EVENT_INVALID_CURSOR_POSITION,
- downTimeNanos, eventTimeNanos, pointerCount, pointerProperties, rawPointerCoords);
+ event->initialize(deviceId, source, displayId, INVALID_HMAC, action, 0, flags, edgeFlags,
+ metaState, buttonState, static_cast<MotionClassification>(classification),
+ 1 /*xScale*/, 1 /*yScale*/, xOffset, yOffset, xPrecision, yPrecision,
+ AMOTION_EVENT_INVALID_CURSOR_POSITION, AMOTION_EVENT_INVALID_CURSOR_POSITION,
+ downTimeNanos, eventTimeNanos, pointerCount, pointerProperties,
+ rawPointerCoords);
return reinterpret_cast<jlong>(event);
@@ -757,155 +756,76 @@ static void android_view_MotionEvent_nativeScale(jlong nativePtr, jfloat scale)
// ----------------------------------------------------------------------------
static const JNINativeMethod gMotionEventMethods[] = {
- /* name, signature, funcPtr */
- { "nativeInitialize",
- "(JIIIIIIIIIFFFFJJI[Landroid/view/MotionEvent$PointerProperties;"
- "[Landroid/view/MotionEvent$PointerCoords;)J",
- (void*)android_view_MotionEvent_nativeInitialize },
- { "nativeDispose",
- "(J)V",
- (void*)android_view_MotionEvent_nativeDispose },
- { "nativeAddBatch",
- "(JJ[Landroid/view/MotionEvent$PointerCoords;I)V",
- (void*)android_view_MotionEvent_nativeAddBatch },
- { "nativeReadFromParcel",
- "(JLandroid/os/Parcel;)J",
- (void*)android_view_MotionEvent_nativeReadFromParcel },
- { "nativeWriteToParcel",
- "(JLandroid/os/Parcel;)V",
- (void*)android_view_MotionEvent_nativeWriteToParcel },
- { "nativeAxisToString", "(I)Ljava/lang/String;",
- (void*)android_view_MotionEvent_nativeAxisToString },
- { "nativeAxisFromString", "(Ljava/lang/String;)I",
- (void*)android_view_MotionEvent_nativeAxisFromString },
- { "nativeGetPointerProperties",
- "(JILandroid/view/MotionEvent$PointerProperties;)V",
- (void*)android_view_MotionEvent_nativeGetPointerProperties },
- { "nativeGetPointerCoords",
- "(JIILandroid/view/MotionEvent$PointerCoords;)V",
- (void*)android_view_MotionEvent_nativeGetPointerCoords },
-
- // --------------- @FastNative ----------------------
- { "nativeGetPointerId",
- "(JI)I",
- (void*)android_view_MotionEvent_nativeGetPointerId },
- { "nativeGetToolType",
- "(JI)I",
- (void*)android_view_MotionEvent_nativeGetToolType },
- { "nativeGetEventTimeNanos",
- "(JI)J",
- (void*)android_view_MotionEvent_nativeGetEventTimeNanos },
- { "nativeGetRawAxisValue",
- "(JIII)F",
- (void*)android_view_MotionEvent_nativeGetRawAxisValue },
- { "nativeGetAxisValue",
- "(JIII)F",
- (void*)android_view_MotionEvent_nativeGetAxisValue },
- { "nativeTransform",
- "(JLandroid/graphics/Matrix;)V",
- (void*)android_view_MotionEvent_nativeTransform },
-
- // --------------- @CriticalNative ------------------
-
- { "nativeCopy",
- "(JJZ)J",
- (void*)android_view_MotionEvent_nativeCopy },
- { "nativeGetDeviceId",
- "(J)I",
- (void*)android_view_MotionEvent_nativeGetDeviceId },
- { "nativeGetSource",
- "(J)I",
- (void*)android_view_MotionEvent_nativeGetSource },
- { "nativeSetSource",
- "(JI)V",
- (void*)android_view_MotionEvent_nativeSetSource },
- { "nativeGetDisplayId",
- "(J)I",
- (void*)android_view_MotionEvent_nativeGetDisplayId },
- { "nativeSetDisplayId",
- "(JI)V",
- (void*)android_view_MotionEvent_nativeSetDisplayId },
- { "nativeGetAction",
- "(J)I",
- (void*)android_view_MotionEvent_nativeGetAction },
- { "nativeSetAction",
- "(JI)V",
- (void*)android_view_MotionEvent_nativeSetAction },
- { "nativeGetActionButton",
- "(J)I",
- (void*)android_view_MotionEvent_nativeGetActionButton},
- { "nativeSetActionButton",
- "(JI)V",
- (void*)android_view_MotionEvent_nativeSetActionButton},
- { "nativeIsTouchEvent",
- "(J)Z",
- (void*)android_view_MotionEvent_nativeIsTouchEvent },
- { "nativeGetFlags",
- "(J)I",
- (void*)android_view_MotionEvent_nativeGetFlags },
- { "nativeSetFlags",
- "(JI)V",
- (void*)android_view_MotionEvent_nativeSetFlags },
- { "nativeGetEdgeFlags",
- "(J)I",
- (void*)android_view_MotionEvent_nativeGetEdgeFlags },
- { "nativeSetEdgeFlags",
- "(JI)V",
- (void*)android_view_MotionEvent_nativeSetEdgeFlags },
- { "nativeGetMetaState",
- "(J)I",
- (void*)android_view_MotionEvent_nativeGetMetaState },
- { "nativeGetButtonState",
- "(J)I",
- (void*)android_view_MotionEvent_nativeGetButtonState },
- { "nativeSetButtonState",
- "(JI)V",
- (void*)android_view_MotionEvent_nativeSetButtonState },
- { "nativeGetClassification",
- "(J)I",
- (void*)android_view_MotionEvent_nativeGetClassification },
- { "nativeOffsetLocation",
- "(JFF)V",
- (void*)android_view_MotionEvent_nativeOffsetLocation },
- { "nativeGetXOffset",
- "(J)F",
- (void*)android_view_MotionEvent_nativeGetXOffset },
- { "nativeGetYOffset",
- "(J)F",
- (void*)android_view_MotionEvent_nativeGetYOffset },
- { "nativeGetXPrecision",
- "(J)F",
- (void*)android_view_MotionEvent_nativeGetXPrecision },
- { "nativeGetYPrecision",
- "(J)F",
- (void*)android_view_MotionEvent_nativeGetYPrecision },
- { "nativeGetXCursorPosition",
- "(J)F",
- (void*)android_view_MotionEvent_nativeGetXCursorPosition },
- { "nativeGetYCursorPosition",
- "(J)F",
- (void*)android_view_MotionEvent_nativeGetYCursorPosition },
- { "nativeSetCursorPosition",
- "(JFF)V",
- (void*)android_view_MotionEvent_nativeSetCursorPosition },
- { "nativeGetDownTimeNanos",
- "(J)J",
- (void*)android_view_MotionEvent_nativeGetDownTimeNanos },
- { "nativeSetDownTimeNanos",
- "(JJ)V",
- (void*)android_view_MotionEvent_nativeSetDownTimeNanos },
- { "nativeGetPointerCount",
- "(J)I",
- (void*)android_view_MotionEvent_nativeGetPointerCount },
- { "nativeFindPointerIndex",
- "(JI)I",
- (void*)android_view_MotionEvent_nativeFindPointerIndex },
- { "nativeGetHistorySize",
- "(J)I",
- (void*)android_view_MotionEvent_nativeGetHistorySize },
- { "nativeScale",
- "(JF)V",
- (void*)android_view_MotionEvent_nativeScale },
+ /* name, signature, funcPtr */
+ {"nativeInitialize",
+ "(JIIIIIIIIIFFFFJJI[Landroid/view/MotionEvent$PointerProperties;"
+ "[Landroid/view/MotionEvent$PointerCoords;)J",
+ (void*)android_view_MotionEvent_nativeInitialize},
+ {"nativeDispose", "(J)V", (void*)android_view_MotionEvent_nativeDispose},
+ {"nativeAddBatch", "(JJ[Landroid/view/MotionEvent$PointerCoords;I)V",
+ (void*)android_view_MotionEvent_nativeAddBatch},
+ {"nativeReadFromParcel", "(JLandroid/os/Parcel;)J",
+ (void*)android_view_MotionEvent_nativeReadFromParcel},
+ {"nativeWriteToParcel", "(JLandroid/os/Parcel;)V",
+ (void*)android_view_MotionEvent_nativeWriteToParcel},
+ {"nativeAxisToString", "(I)Ljava/lang/String;",
+ (void*)android_view_MotionEvent_nativeAxisToString},
+ {"nativeAxisFromString", "(Ljava/lang/String;)I",
+ (void*)android_view_MotionEvent_nativeAxisFromString},
+ {"nativeGetPointerProperties", "(JILandroid/view/MotionEvent$PointerProperties;)V",
+ (void*)android_view_MotionEvent_nativeGetPointerProperties},
+ {"nativeGetPointerCoords", "(JIILandroid/view/MotionEvent$PointerCoords;)V",
+ (void*)android_view_MotionEvent_nativeGetPointerCoords},
+
+ // --------------- @FastNative ----------------------
+ {"nativeGetPointerId", "(JI)I", (void*)android_view_MotionEvent_nativeGetPointerId},
+ {"nativeGetToolType", "(JI)I", (void*)android_view_MotionEvent_nativeGetToolType},
+ {"nativeGetEventTimeNanos", "(JI)J",
+ (void*)android_view_MotionEvent_nativeGetEventTimeNanos},
+ {"nativeGetRawAxisValue", "(JIII)F", (void*)android_view_MotionEvent_nativeGetRawAxisValue},
+ {"nativeGetAxisValue", "(JIII)F", (void*)android_view_MotionEvent_nativeGetAxisValue},
+ {"nativeTransform", "(JLandroid/graphics/Matrix;)V",
+ (void*)android_view_MotionEvent_nativeTransform},
+
+ // --------------- @CriticalNative ------------------
+
+ {"nativeCopy", "(JJZ)J", (void*)android_view_MotionEvent_nativeCopy},
+ {"nativeGetDeviceId", "(J)I", (void*)android_view_MotionEvent_nativeGetDeviceId},
+ {"nativeGetSource", "(J)I", (void*)android_view_MotionEvent_nativeGetSource},
+ {"nativeSetSource", "(JI)V", (void*)android_view_MotionEvent_nativeSetSource},
+ {"nativeGetDisplayId", "(J)I", (void*)android_view_MotionEvent_nativeGetDisplayId},
+ {"nativeSetDisplayId", "(JI)V", (void*)android_view_MotionEvent_nativeSetDisplayId},
+ {"nativeGetAction", "(J)I", (void*)android_view_MotionEvent_nativeGetAction},
+ {"nativeSetAction", "(JI)V", (void*)android_view_MotionEvent_nativeSetAction},
+ {"nativeGetActionButton", "(J)I", (void*)android_view_MotionEvent_nativeGetActionButton},
+ {"nativeSetActionButton", "(JI)V", (void*)android_view_MotionEvent_nativeSetActionButton},
+ {"nativeIsTouchEvent", "(J)Z", (void*)android_view_MotionEvent_nativeIsTouchEvent},
+ {"nativeGetFlags", "(J)I", (void*)android_view_MotionEvent_nativeGetFlags},
+ {"nativeSetFlags", "(JI)V", (void*)android_view_MotionEvent_nativeSetFlags},
+ {"nativeGetEdgeFlags", "(J)I", (void*)android_view_MotionEvent_nativeGetEdgeFlags},
+ {"nativeSetEdgeFlags", "(JI)V", (void*)android_view_MotionEvent_nativeSetEdgeFlags},
+ {"nativeGetMetaState", "(J)I", (void*)android_view_MotionEvent_nativeGetMetaState},
+ {"nativeGetButtonState", "(J)I", (void*)android_view_MotionEvent_nativeGetButtonState},
+ {"nativeSetButtonState", "(JI)V", (void*)android_view_MotionEvent_nativeSetButtonState},
+ {"nativeGetClassification", "(J)I",
+ (void*)android_view_MotionEvent_nativeGetClassification},
+ {"nativeOffsetLocation", "(JFF)V", (void*)android_view_MotionEvent_nativeOffsetLocation},
+ {"nativeGetXOffset", "(J)F", (void*)android_view_MotionEvent_nativeGetXOffset},
+ {"nativeGetYOffset", "(J)F", (void*)android_view_MotionEvent_nativeGetYOffset},
+ {"nativeGetXPrecision", "(J)F", (void*)android_view_MotionEvent_nativeGetXPrecision},
+ {"nativeGetYPrecision", "(J)F", (void*)android_view_MotionEvent_nativeGetYPrecision},
+ {"nativeGetXCursorPosition", "(J)F",
+ (void*)android_view_MotionEvent_nativeGetXCursorPosition},
+ {"nativeGetYCursorPosition", "(J)F",
+ (void*)android_view_MotionEvent_nativeGetYCursorPosition},
+ {"nativeSetCursorPosition", "(JFF)V",
+ (void*)android_view_MotionEvent_nativeSetCursorPosition},
+ {"nativeGetDownTimeNanos", "(J)J", (void*)android_view_MotionEvent_nativeGetDownTimeNanos},
+ {"nativeSetDownTimeNanos", "(JJ)V", (void*)android_view_MotionEvent_nativeSetDownTimeNanos},
+ {"nativeGetPointerCount", "(J)I", (void*)android_view_MotionEvent_nativeGetPointerCount},
+ {"nativeFindPointerIndex", "(JI)I", (void*)android_view_MotionEvent_nativeFindPointerIndex},
+ {"nativeGetHistorySize", "(J)I", (void*)android_view_MotionEvent_nativeGetHistorySize},
+ {"nativeScale", "(JF)V", (void*)android_view_MotionEvent_nativeScale},
};
int register_android_view_MotionEvent(JNIEnv* env) {
diff --git a/core/jni/android_view_MotionEvent.h b/core/jni/android_view_MotionEvent.h
index 0cf1fb2f2203..9ce4bf367688 100644
--- a/core/jni/android_view_MotionEvent.h
+++ b/core/jni/android_view_MotionEvent.h
@@ -38,4 +38,4 @@ extern status_t android_view_MotionEvent_recycle(JNIEnv* env, jobject eventObj);
} // namespace android
-#endif // _ANDROID_OS_KEYEVENT_H
+#endif // _ANDROID_VIEW_MOTIONEVENT_H
diff --git a/core/proto/android/server/windowmanagerservice.proto b/core/proto/android/server/windowmanagerservice.proto
index 047307d3ee2c..c0743e58b5c5 100644
--- a/core/proto/android/server/windowmanagerservice.proto
+++ b/core/proto/android/server/windowmanagerservice.proto
@@ -147,7 +147,8 @@ message DisplayContentProto {
optional int32 id = 2;
repeated StackProto stacks = 3;
optional DockedStackDividerControllerProto docked_stack_divider_controller = 4;
- optional PinnedStackControllerProto pinned_stack_controller = 5;
+ // Will be removed soon.
+ optional PinnedStackControllerProto pinned_stack_controller = 5 [deprecated=true];
/* non app windows */
repeated WindowTokenProto above_app_windows = 6;
repeated WindowTokenProto below_app_windows = 7;
@@ -184,8 +185,8 @@ message DockedStackDividerControllerProto {
message PinnedStackControllerProto {
option (.android.msg_privacy).dest = DEST_AUTOMATIC;
- optional .android.graphics.RectProto default_bounds = 1;
- optional .android.graphics.RectProto movement_bounds = 2;
+ optional .android.graphics.RectProto default_bounds = 1 [deprecated=true];
+ optional .android.graphics.RectProto movement_bounds = 2 [deprecated=true];
}
/* represents TaskStack */
diff --git a/core/res/AndroidManifest.xml b/core/res/AndroidManifest.xml
index 020a8352ad07..4d319dfbe525 100644
--- a/core/res/AndroidManifest.xml
+++ b/core/res/AndroidManifest.xml
@@ -3339,6 +3339,13 @@
<permission android:name="android.permission.BIND_AUTOFILL_FIELD_CLASSIFICATION_SERVICE"
android:protectionLevel="signature" />
+ <!-- Must be required by an {@link android.service.autofill.InlineSuggestionRenderService}
+ to ensure that only the system can bind to it.
+ @hide This is not a third-party API (intended for OEMs and system apps).
+ -->
+ <permission android:name="android.permission.BIND_INLINE_SUGGESTION_RENDER_SERVICE"
+ android:protectionLevel="signature" />
+
<!-- Must be required by a android.service.textclassifier.TextClassifierService,
to ensure that only the system can bind to it.
@SystemApi @hide This is not a third-party API (intended for OEMs and system apps).
@@ -4849,6 +4856,19 @@
<permission android:name="android.permission.ACCESS_SHARED_LIBRARIES"
android:protectionLevel="signature|installer" />
+ <!-- Allows an app to log compat change usage.
+ @hide <p>Not for use by third-party applications.</p> -->
+ <permission android:name="android.permission.LOG_COMPAT_CHANGE"
+ android:protectionLevel="signature" />
+ <!-- Allows an app to read compat change config.
+ @hide <p>Not for use by third-party applications.</p> -->
+ <permission android:name="android.permission.READ_COMPAT_CHANGE_CONFIG"
+ android:protectionLevel="signature" />
+ <!-- Allows an app to override compat change config.
+ @hide <p>Not for use by third-party applications.</p> -->
+ <permission android:name="android.permission.OVERRIDE_COMPAT_CHANGE_CONFIG"
+ android:protectionLevel="signature" />
+
<!-- Allows input events to be monitored. Very dangerous! @hide -->
<permission android:name="android.permission.MONITOR_INPUT"
android:protectionLevel="signature" />
diff --git a/core/res/res/values-af/strings.xml b/core/res/res/values-af/strings.xml
index de8f55bb8036..c34a485bc7a9 100644
--- a/core/res/res/values-af/strings.xml
+++ b/core/res/res/values-af/strings.xml
@@ -1613,6 +1613,8 @@
<string name="kg_failed_attempts_almost_at_login" product="default" msgid="5270861875006378092">"Jy het jou ontsluitpatroon <xliff:g id="NUMBER_0">%1$d</xliff:g> keer verkeerdelik geteken. Na nog <xliff:g id="NUMBER_1">%2$d</xliff:g> onsuksesvolle pogings, sal jy gevra word om jou foon te ontsluit deur middel van \'n e-posrekening.\n\n Probeer weer oor <xliff:g id="NUMBER_2">%3$d</xliff:g> sekondes."</string>
<string name="kg_text_message_separator" product="default" msgid="4503708889934976866">" — "</string>
<string name="kg_reordering_delete_drop_target_text" msgid="2034358143731750914">"Verwyder"</string>
+ <!-- no translation found for allow_while_in_use_permission_in_fgs (4101339676785053656) -->
+ <skip />
<string name="safe_media_volume_warning" product="default" msgid="3751676824423049994">"Verhoog volume bo aanbevole vlak?\n\nOm lang tydperke teen hoë volume te luister, kan jou gehoor beskadig."</string>
<string name="accessibility_shortcut_warning_dialog_title" msgid="4017995837692622933">"Gebruik toeganklikheidkortpad?"</string>
<string name="accessibility_shortcut_toogle_warning" msgid="8306239551412868396">"Wanneer die kortpad aan is, sal \'n toeganklikheidkenmerk begin word as albei volumeknoppies 3 sekondes lank gedruk word.\n\n Bestaande toeganklikheidkenmerk:\n <xliff:g id="SERVICE_NAME">%1$s</xliff:g>\n\n Jy kan die kenmerk in Instellings &gt; Toeganklikheid verander."</string>
diff --git a/core/res/res/values-am/strings.xml b/core/res/res/values-am/strings.xml
index ec9abfe3e67f..23e6ccdbd87f 100644
--- a/core/res/res/values-am/strings.xml
+++ b/core/res/res/values-am/strings.xml
@@ -1613,6 +1613,8 @@
<string name="kg_failed_attempts_almost_at_login" product="default" msgid="5270861875006378092">"የመክፈቻ ስርዓተ ጥለቱን <xliff:g id="NUMBER_0">%1$d</xliff:g> ጊዜ በትክክል አልሳሉትም። ከ<xliff:g id="NUMBER_1">%2$d</xliff:g> ተጨማሪ ያልተሳኩ ሙከራዎች በኋላ የኢሜይል መለያ ተጠቅመው ስልክዎን እንዲከፍቱ ይጠየቃሉ።\n\nእባክዎ ከ<xliff:g id="NUMBER_2">%3$d</xliff:g> ሰከንዶች በኋላ እንደገና ይሞክሩ።"</string>
<string name="kg_text_message_separator" product="default" msgid="4503708889934976866">" — "</string>
<string name="kg_reordering_delete_drop_target_text" msgid="2034358143731750914">"አስወግድ"</string>
+ <!-- no translation found for allow_while_in_use_permission_in_fgs (4101339676785053656) -->
+ <skip />
<string name="safe_media_volume_warning" product="default" msgid="3751676824423049994">"ድምጹ ከሚመከረው መጠን በላይ ከፍ ይበል?\n\nበከፍተኛ ድምጽ ለረጅም ጊዜ ማዳመጥ ጆሮዎን ሊጎዳው ይችላል።"</string>
<string name="accessibility_shortcut_warning_dialog_title" msgid="4017995837692622933">"የተደራሽነት አቋራጭ ጥቅም ላይ ይዋል?"</string>
<string name="accessibility_shortcut_toogle_warning" msgid="8306239551412868396">"አቋራጩ ሲበራ ሁለቱንም የድምፅ አዝራሮች ለ3 ሰከንዶች ተጭኖ መቆየት የተደራሽነት ባህሪን ያስጀምረዋል።\n\n አሁን ያለ የተደራሽነት ባህሪ፦\n <xliff:g id="SERVICE_NAME">%1$s</xliff:g>\n\n ባህሪውን በቅንብሮች &gt; ተደራሽነት ውስጥ ሊለውጡት ይችላሉ።"</string>
diff --git a/core/res/res/values-ar/strings.xml b/core/res/res/values-ar/strings.xml
index 0d70a11f2eeb..80c04ebee695 100644
--- a/core/res/res/values-ar/strings.xml
+++ b/core/res/res/values-ar/strings.xml
@@ -222,7 +222,7 @@
<string name="reboot_to_update_prepare" msgid="6978842143587422365">"جارٍ الإعداد للتحديث…"</string>
<string name="reboot_to_update_package" msgid="4644104795527534811">"جارٍ معالجة حزمة التحديث…"</string>
<string name="reboot_to_update_reboot" msgid="4474726009984452312">"جارٍ إعادة التشغيل…"</string>
- <string name="reboot_to_reset_title" msgid="2226229680017882787">"إعادة الضبط بحسب بيانات المصنع"</string>
+ <string name="reboot_to_reset_title" msgid="2226229680017882787">"إعادة الضبط على الإعدادات الأصلية"</string>
<string name="reboot_to_reset_message" msgid="3347690497972074356">"جارٍ إعادة التشغيل…"</string>
<string name="shutdown_progress" msgid="5017145516412657345">"جارٍ إيقاف التشغيل..."</string>
<string name="shutdown_confirm" product="tablet" msgid="2872769463279602432">"سيتم إيقاف تشغيل الجهاز اللوحي."</string>
@@ -320,7 +320,7 @@
<string name="capability_title_canRetrieveWindowContent" msgid="7554282892101587296">"استرداد محتوى النافذة"</string>
<string name="capability_desc_canRetrieveWindowContent" msgid="6195610527625237661">"فحص محتوى نافذة يتم التفاعل معها"</string>
<string name="capability_title_canRequestTouchExploration" msgid="327598364696316213">"تشغيل الاستكشاف باللمس"</string>
- <string name="capability_desc_canRequestTouchExploration" msgid="4394677060796752976">"سيتم نطق العناصر التي تم النقر عليها بصوت عال ويمكن استكشاف الشاشة باستخدام الإيماءات."</string>
+ <string name="capability_desc_canRequestTouchExploration" msgid="4394677060796752976">"سيتم قول العناصر التي تم النقر عليها بصوت عال ويمكن استكشاف الشاشة باستخدام الإيماءات."</string>
<string name="capability_title_canRequestFilterKeyEvents" msgid="2772371671541753254">"ملاحظة النص الذي تكتبه"</string>
<string name="capability_desc_canRequestFilterKeyEvents" msgid="2381315802405773092">"يتضمن بيانات شخصية مثل أرقام بطاقات الائتمان وكلمات المرور."</string>
<string name="capability_title_canControlMagnification" msgid="7701572187333415795">"التحكم في تكبير الشاشة"</string>
@@ -676,8 +676,8 @@
<string name="policylab_forceLock" msgid="7360335502968476434">"قفل الشاشة"</string>
<string name="policydesc_forceLock" msgid="1008844760853899693">"التحكّم في طريقة ووقت قفل الشاشة"</string>
<string name="policylab_wipeData" msgid="1359485247727537311">"محو جميع البيانات"</string>
- <string name="policydesc_wipeData" product="tablet" msgid="7245372676261947507">"يمكنك محو بيانات الجهاز اللوحي بدون تحذير، وذلك عبر إجراء إعادة الضبط بحسب بيانات المصنع."</string>
- <string name="policydesc_wipeData" product="tv" msgid="513862488950801261">"‏يمكنك محو بيانات جهاز Android TV بدون تحذير عن طريق تنفيذ إعادة الضبط بحسب بيانات المصنع."</string>
+ <string name="policydesc_wipeData" product="tablet" msgid="7245372676261947507">"يمكنك محو بيانات الجهاز اللوحي بدون تحذير، وذلك عبر إجراء إعادة الضبط على الإعدادات الأصلية."</string>
+ <string name="policydesc_wipeData" product="tv" msgid="513862488950801261">"‏يمكنك محو بيانات جهاز Android TV بدون تحذير عن طريق تنفيذ إعادة الضبط على الإعدادات الأصلية."</string>
<string name="policydesc_wipeData" product="default" msgid="8036084184768379022">"محو بيانات الهاتف بدون تحذير، وذلك من خلال إعادة ضبط البيانات على الإعدادات الأصلية"</string>
<string name="policylab_wipeData_secondaryUser" msgid="413813645323433166">"محو بيانات المستخدم"</string>
<string name="policydesc_wipeData_secondaryUser" product="tablet" msgid="2336676480090926470">"لمحو بيانات هذا المستخدم على هذا الجهاز اللوحي بدون تحذير."</string>
@@ -1701,6 +1701,8 @@
<string name="kg_failed_attempts_almost_at_login" product="default" msgid="5270861875006378092">"لقد رسمت نقش فتح القفل بشكل غير صحيح <xliff:g id="NUMBER_0">%1$d</xliff:g> مرة. بعد إجراء <xliff:g id="NUMBER_1">%2$d</xliff:g> من المحاولات غير الناجحة الأخرى، ستُطالب بإلغاء تأمين الهاتف باستخدام حساب بريد إلكتروني لإلغاء تأمين الهاتف.\n\n أعد المحاولة خلال <xliff:g id="NUMBER_2">%3$d</xliff:g> ثانية."</string>
<string name="kg_text_message_separator" product="default" msgid="4503708889934976866">" — "</string>
<string name="kg_reordering_delete_drop_target_text" msgid="2034358143731750914">"إزالة"</string>
+ <!-- no translation found for allow_while_in_use_permission_in_fgs (4101339676785053656) -->
+ <skip />
<string name="safe_media_volume_warning" product="default" msgid="3751676824423049994">"هل تريد رفع مستوى الصوت فوق المستوى الموصى به؟\n\nقد يضر سماع صوت عالٍ لفترات طويلة بسمعك."</string>
<string name="accessibility_shortcut_warning_dialog_title" msgid="4017995837692622933">"هل تريد استخدام اختصار \"سهولة الاستخدام\"؟"</string>
<string name="accessibility_shortcut_toogle_warning" msgid="8306239551412868396">"عند تشغيل الاختصار، يؤدي الضغط على زرّي مستوى الصوت لمدة 3 ثوانٍ إلى تفعيل ميزة \"سهولة الاستخدام\".\n\n ميزة \"سهولة الاستخدام\" الحالية:\n <xliff:g id="SERVICE_NAME">%1$s</xliff:g>\n\n يمكنك تغيير الميزة من \"الإعدادات\" &gt; \"سهولة الاستخدام\"."</string>
@@ -1932,7 +1934,7 @@
<string name="zen_mode_default_events_name" msgid="2280682960128512257">"حدث"</string>
<string name="zen_mode_default_every_night_name" msgid="1467765312174275823">"النوم"</string>
<string name="muted_by" msgid="91464083490094950">"يعمل <xliff:g id="THIRD_PARTY">%1$s</xliff:g> على كتم بعض الأصوات."</string>
- <string name="system_error_wipe_data" msgid="5910572292172208493">"حدثت مشكلة داخلية في جهازك، وقد لا يستقر وضعه حتى إجراء إعادة الضبط بحسب بيانات المصنع."</string>
+ <string name="system_error_wipe_data" msgid="5910572292172208493">"حدثت مشكلة داخلية في جهازك، وقد لا يستقر وضعه حتى إجراء إعادة الضبط على الإعدادات الأصلية."</string>
<string name="system_error_manufacturer" msgid="703545241070116315">"حدثت مشكلة داخلية في جهازك. يمكنك الاتصال بالمصنِّع للحصول على تفاصيل."</string>
<string name="stk_cc_ussd_to_dial" msgid="3139884150741157610">"‏تم تغيير طلب USSD إلى مكالمة عادية."</string>
<string name="stk_cc_ussd_to_ss" msgid="4826846653052609738">"‏تم تغيير طلب USSD إلى طلب SS."</string>
diff --git a/core/res/res/values-as/strings.xml b/core/res/res/values-as/strings.xml
index ad11a37524a0..73e32b85c224 100644
--- a/core/res/res/values-as/strings.xml
+++ b/core/res/res/values-as/strings.xml
@@ -1613,6 +1613,8 @@
<string name="kg_failed_attempts_almost_at_login" product="default" msgid="5270861875006378092">"আপুনি আপোনাৰ ল\'ক খোলাৰ আৰ্হিটো <xliff:g id="NUMBER_0">%1$d</xliff:g>বাৰ ভুলকৈ আঁকিছে। <xliff:g id="NUMBER_1">%2$d</xliff:g>তকৈ বেছি বাৰ ভুল আৰ্হি আঁকিলে আপোনাৰ ফ\'নটো কোনো একাউণ্টৰ জৰিয়তে আনলক কৰিবলৈ কোৱা হ\'ব।\n\n <xliff:g id="NUMBER_2">%3$d</xliff:g> ছেকেণ্ডৰ পিছত আকৌ চেষ্টা কৰক।"</string>
<string name="kg_text_message_separator" product="default" msgid="4503708889934976866">" — "</string>
<string name="kg_reordering_delete_drop_target_text" msgid="2034358143731750914">"আঁতৰাওক"</string>
+ <!-- no translation found for allow_while_in_use_permission_in_fgs (4101339676785053656) -->
+ <skip />
<string name="safe_media_volume_warning" product="default" msgid="3751676824423049994">"অনুমোদিত স্তৰতকৈ ওপৰলৈ ভলিউম বঢ়াব নেকি?\n\nদীৰ্ঘ সময়ৰ বাবে উচ্চ ভলিউমত শুনাৰ ফলত শ্ৰৱণ ক্ষমতাৰ ক্ষতি হ\'ব পাৰে।"</string>
<string name="accessibility_shortcut_warning_dialog_title" msgid="4017995837692622933">"দিব্যাংগসকলৰ সুবিধাৰ শ্বৰ্টকাট ব্যৱহাৰ কৰেনে?"</string>
<string name="accessibility_shortcut_toogle_warning" msgid="8306239551412868396">"শ্বৰ্টকাট অন হৈ থকাৰ সময়ত দুয়োটা ভলিউম বুটামত ৩ ছেকেণ্ডৰ বাবে ছাপ দি থাকিলে দিব্যাংগসকলৰ বাবে থকা সুবিধা এটা আৰম্ভ হ\'ব। \n\n চলিত দিব্যাংগসকলৰ সুবিধা:\n <xliff:g id="SERVICE_NAME">%1$s</xliff:g>\n\n আপুনি এই সুবিধাটো ছেটিংসমূহ &gt; দিব্যাংগসকলৰ বাবে সুবিধা-লৈ গৈ সলনি কৰিব পাৰে।"</string>
diff --git a/core/res/res/values-az/strings.xml b/core/res/res/values-az/strings.xml
index a535f1f0e2ba..c894c29956dc 100644
--- a/core/res/res/values-az/strings.xml
+++ b/core/res/res/values-az/strings.xml
@@ -1613,6 +1613,8 @@
<string name="kg_failed_attempts_almost_at_login" product="default" msgid="5270861875006378092">"Siz artıq modeli <xliff:g id="NUMBER_0">%1$d</xliff:g> dəfə yanlış daxil etmisiniz.<xliff:g id="NUMBER_1">%2$d</xliff:g> dəfə də yanlış daxil etsəniz, telefonun kilidinin açılması üçün elektron poçt ünvanınız tələb olunacaq.\n\n <xliff:g id="NUMBER_2">%3$d</xliff:g> saniyə ərzində yenidən cəhd edin."</string>
<string name="kg_text_message_separator" product="default" msgid="4503708889934976866">" - "</string>
<string name="kg_reordering_delete_drop_target_text" msgid="2034358143731750914">"Yığışdır"</string>
+ <!-- no translation found for allow_while_in_use_permission_in_fgs (4101339676785053656) -->
+ <skip />
<string name="safe_media_volume_warning" product="default" msgid="3751676824423049994">"Səsin həcmi tövsiyə olunan səviyyədən artıq olsun?\n\nYüksək səsi uzun zaman dinləmək eşitmə qabiliyyətinizə zərər vura bilər."</string>
<string name="accessibility_shortcut_warning_dialog_title" msgid="4017995837692622933">"Əlçatımlılıq Qısayolu istifadə edilsin?"</string>
<string name="accessibility_shortcut_toogle_warning" msgid="8306239551412868396">"Qısayol aktiv olduqda hər iki səs düyməsinə 3 saniyə basıb saxlamaqla əlçatımlılıq funksiyası işə başlayacaq.\n\n Cari əlçatımlılıq funksiyası:\n <xliff:g id="SERVICE_NAME">%1$s</xliff:g>\n\n Funksiyanı Ayarlar və Əçatımlılıq bölməsində dəyişə bilərsiniz."</string>
diff --git a/core/res/res/values-b+sr+Latn/strings.xml b/core/res/res/values-b+sr+Latn/strings.xml
index 249e5af2d585..909c31558e18 100644
--- a/core/res/res/values-b+sr+Latn/strings.xml
+++ b/core/res/res/values-b+sr+Latn/strings.xml
@@ -1635,6 +1635,8 @@
<string name="kg_failed_attempts_almost_at_login" product="default" msgid="5270861875006378092">"Nacrtali ste šablon za otključavanje netačno <xliff:g id="NUMBER_0">%1$d</xliff:g> puta. Posle još <xliff:g id="NUMBER_1">%2$d</xliff:g> neuspešna(ih) pokušaja, od vas će biti zatraženo da otključate telefon pomoću naloga e-pošte.\n\nProbajte ponovo za <xliff:g id="NUMBER_2">%3$d</xliff:g> sekunde/i."</string>
<string name="kg_text_message_separator" product="default" msgid="4503708889934976866">" – "</string>
<string name="kg_reordering_delete_drop_target_text" msgid="2034358143731750914">"Ukloni"</string>
+ <!-- no translation found for allow_while_in_use_permission_in_fgs (4101339676785053656) -->
+ <skip />
<string name="safe_media_volume_warning" product="default" msgid="3751676824423049994">"Želite da pojačate zvuk iznad preporučenog nivoa?\n\nSlušanje glasne muzike duže vreme može da vam ošteti sluh."</string>
<string name="accessibility_shortcut_warning_dialog_title" msgid="4017995837692622933">"Želite li da koristite prečicu za pristupačnost?"</string>
<string name="accessibility_shortcut_toogle_warning" msgid="8306239551412868396">"Kada je prečica uključena, pritisnite oba dugmeta za jačinu zvuka da biste pokrenuli funkciju pristupačnosti.\n\n Aktuelna funkcija pristupačnosti:\n <xliff:g id="SERVICE_NAME">%1$s</xliff:g>\n\n Možete da promenite funkciju u odeljku Podešavanja &gt; Pristupačnost."</string>
diff --git a/core/res/res/values-be/strings.xml b/core/res/res/values-be/strings.xml
index dc4c56e684ee..19dcf927b74d 100644
--- a/core/res/res/values-be/strings.xml
+++ b/core/res/res/values-be/strings.xml
@@ -1657,6 +1657,8 @@
<string name="kg_failed_attempts_almost_at_login" product="default" msgid="5270861875006378092">"Вы няправільна ўвялі графічны ключ разблакiроўкi пэўную колькасць разоў: <xliff:g id="NUMBER_0">%1$d</xliff:g>. Пасля яшчэ некалькiх няўдалых спроб (<xliff:g id="NUMBER_1">%2$d</xliff:g>) вам будзе прапанавана разблакiраваць тэлефон, увайшоўшы ў Google.\n\n Паўтарыце спробу праз <xliff:g id="NUMBER_2">%3$d</xliff:g> с."</string>
<string name="kg_text_message_separator" product="default" msgid="4503708889934976866">" — "</string>
<string name="kg_reordering_delete_drop_target_text" msgid="2034358143731750914">"Выдалiць"</string>
+ <!-- no translation found for allow_while_in_use_permission_in_fgs (4101339676785053656) -->
+ <skip />
<string name="safe_media_volume_warning" product="default" msgid="3751676824423049994">"Павялiчыць гук вышэй рэкамендаванага ўзроўню?\n\nДоўгае праслухоўванне музыкi на вялiкай гучнасцi можа пашкодзiць ваш слых."</string>
<string name="accessibility_shortcut_warning_dialog_title" msgid="4017995837692622933">"Выкарыстоўваць камбінацыю хуткага доступу для спецыяльных магчымасцей?"</string>
<string name="accessibility_shortcut_toogle_warning" msgid="8306239551412868396">"Калі камбінацыя хуткага доступу ўключана, вы можаце націснуць абедзве кнопкі гучнасці і ўтрымліваць іх 3 секунды, каб уключыць функцыю спецыяльных магчымасцей.\n\n Бягучая функцыя спецыяльных магчымасцей:\n <xliff:g id="SERVICE_NAME">%1$s</xliff:g>\n\n Вы можаце змяніць гэту функцыю ў меню \"Налады &gt; Спецыяльныя магчымасці\"."</string>
diff --git a/core/res/res/values-bg/strings.xml b/core/res/res/values-bg/strings.xml
index 30b7d302a273..8a687666ac9b 100644
--- a/core/res/res/values-bg/strings.xml
+++ b/core/res/res/values-bg/strings.xml
@@ -1613,6 +1613,8 @@
<string name="kg_failed_attempts_almost_at_login" product="default" msgid="5270861875006378092">"Начертахте неправилно фигурата си за отключване <xliff:g id="NUMBER_0">%1$d</xliff:g> пъти. След още <xliff:g id="NUMBER_1">%2$d</xliff:g> неуспешни опита ще бъдете помолени да отключите телефона посредством имейл адрес.\n\n Опитайте отново след <xliff:g id="NUMBER_2">%3$d</xliff:g> секунди."</string>
<string name="kg_text_message_separator" product="default" msgid="4503708889934976866">" – "</string>
<string name="kg_reordering_delete_drop_target_text" msgid="2034358143731750914">"Премахване"</string>
+ <!-- no translation found for allow_while_in_use_permission_in_fgs (4101339676785053656) -->
+ <skip />
<string name="safe_media_volume_warning" product="default" msgid="3751676824423049994">"Да се увеличи ли силата на звука над препоръчителното ниво?\n\nПродължителното слушане при висока сила на звука може да увреди слуха ви."</string>
<string name="accessibility_shortcut_warning_dialog_title" msgid="4017995837692622933">"Искате ли да използвате пряк път към функцията за достъпност?"</string>
<string name="accessibility_shortcut_toogle_warning" msgid="8306239551412868396">"Когато прекият път е включен, можете да стартирате дадена функция за достъпност, като натиснете двата бутона за промяна на силата на звука и ги задържите 3 секунди.\n\n Текущата функция за достъпност е:\n <xliff:g id="SERVICE_NAME">%1$s</xliff:g>\n\n Можете да промените функцията от „Настройки“ &gt; „Достъпност“."</string>
diff --git a/core/res/res/values-bn/strings.xml b/core/res/res/values-bn/strings.xml
index d85b5d6ee716..9ca383d30b04 100644
--- a/core/res/res/values-bn/strings.xml
+++ b/core/res/res/values-bn/strings.xml
@@ -1613,6 +1613,8 @@
<string name="kg_failed_attempts_almost_at_login" product="default" msgid="5270861875006378092">"আপনি আপনার আনলকের প্যাটার্ন আঁকার ক্ষেত্রে <xliff:g id="NUMBER_0">%1$d</xliff:g> বার ভুল করেছেন৷ আর <xliff:g id="NUMBER_1">%2$d</xliff:g> বার অসফল প্রচেষ্টা করা হলে আপনাকে একটি ইমেল অ্যাকাউন্ট মারফত আপনার ফোন আনলক করতে বলা হবে৷\n\n <xliff:g id="NUMBER_2">%3$d</xliff:g> সেকেন্ডের মধ্যে আবার চেষ্টা করুন৷"</string>
<string name="kg_text_message_separator" product="default" msgid="4503708889934976866">" — "</string>
<string name="kg_reordering_delete_drop_target_text" msgid="2034358143731750914">"সরান"</string>
+ <!-- no translation found for allow_while_in_use_permission_in_fgs (4101339676785053656) -->
+ <skip />
<string name="safe_media_volume_warning" product="default" msgid="3751676824423049994">"প্রস্তাবিত স্তরের চেয়ে বেশি উঁচুতে ভলিউম বাড়াবেন?\n\nউঁচু ভলিউমে বেশি সময় ধরে কিছু শুনলে আপনার শ্রবনশক্তির ক্ষতি হতে পারে।"</string>
<string name="accessibility_shortcut_warning_dialog_title" msgid="4017995837692622933">"অ্যাক্সেসযোগ্যতা শর্টকাট ব্যবহার করবেন?"</string>
<string name="accessibility_shortcut_toogle_warning" msgid="8306239551412868396">"শর্টকাটটি চালু থাকলে দুটি ভলিউম বোতাম একসাথে ৩ সেকেন্ড টিপে ধরে রাখলে একটি অ্যাকসেসিবিলিটি বৈশিষ্ট্য চালু হবে।\n\n বর্তমান অ্যাকসেসিবিলিটি বৈশিষ্ট্য:\n <xliff:g id="SERVICE_NAME">%1$s</xliff:g>\n\n আপনি এই বৈশিষ্ট্যটি সেটিংস &gt; অ্যাকসেসিবিলিটিতে গিয়ে পরিবর্তন করতে পারবেন।"</string>
diff --git a/core/res/res/values-bs/strings.xml b/core/res/res/values-bs/strings.xml
index c8c65bb15eb0..5f83aa427eef 100644
--- a/core/res/res/values-bs/strings.xml
+++ b/core/res/res/values-bs/strings.xml
@@ -1637,6 +1637,8 @@
<string name="kg_failed_attempts_almost_at_login" product="default" msgid="5270861875006378092">"Pogrešno ste nacrtali uzorak za otključavanje <xliff:g id="NUMBER_0">%1$d</xliff:g> puta. Ako napravite još <xliff:g id="NUMBER_1">%2$d</xliff:g> pokušaja bez uspjeha, od vas će se tražiti da otključate telefon pomoću e-pošte. \n\n Pokušajte ponovo za <xliff:g id="NUMBER_2">%3$d</xliff:g> s."</string>
<string name="kg_text_message_separator" product="default" msgid="4503708889934976866">" — "</string>
<string name="kg_reordering_delete_drop_target_text" msgid="2034358143731750914">"Ukloni"</string>
+ <!-- no translation found for allow_while_in_use_permission_in_fgs (4101339676785053656) -->
+ <skip />
<string name="safe_media_volume_warning" product="default" msgid="3751676824423049994">"Želite li pojačati zvuk iznad preporučenog nivoa?\n\nDužim slušanjem glasnog zvuka možete oštetiti sluh."</string>
<string name="accessibility_shortcut_warning_dialog_title" msgid="4017995837692622933">"Želite li koristiti Prečicu za pristupačnost?"</string>
<string name="accessibility_shortcut_toogle_warning" msgid="8306239551412868396">"Kada je prečica uključena, pritiskom na oba dugmeta za podešavanje jačine zvuka u trajanju od 3 sekunde pokrenut će se funkcija za pristupačnost.\n\n Trenutna funkcija za pristupačnost je:\n <xliff:g id="SERVICE_NAME">%1$s</xliff:g>\n\n Funkciju možete promijeniti ako odete u Postavke &gt; Pristupačnost."</string>
diff --git a/core/res/res/values-ca/strings.xml b/core/res/res/values-ca/strings.xml
index 42bebe16a004..930065b02883 100644
--- a/core/res/res/values-ca/strings.xml
+++ b/core/res/res/values-ca/strings.xml
@@ -1613,6 +1613,8 @@
<string name="kg_failed_attempts_almost_at_login" product="default" msgid="5270861875006378092">"Has dibuixat el patró de desbloqueig <xliff:g id="NUMBER_0">%1$d</xliff:g> vegades de manera incorrecta. Si falles <xliff:g id="NUMBER_1">%2$d</xliff:g> vegades més, se\'t demanarà que desbloquegis el telèfon amb un compte de correu electrònic.\n\n Torna-ho a provar d\'aquí a <xliff:g id="NUMBER_2">%3$d</xliff:g> segons."</string>
<string name="kg_text_message_separator" product="default" msgid="4503708889934976866">" — "</string>
<string name="kg_reordering_delete_drop_target_text" msgid="2034358143731750914">"Elimina"</string>
+ <!-- no translation found for allow_while_in_use_permission_in_fgs (4101339676785053656) -->
+ <skip />
<string name="safe_media_volume_warning" product="default" msgid="3751676824423049994">"Vols apujar el volum per sobre del nivell recomanat?\n\nSi escoltes música a un volum alt durant períodes llargs, pots danyar-te l\'oïda."</string>
<string name="accessibility_shortcut_warning_dialog_title" msgid="4017995837692622933">"Vols fer servir la drecera d\'accessibilitat?"</string>
<string name="accessibility_shortcut_toogle_warning" msgid="8306239551412868396">"Si la drecera està activada, prem els dos botons de volum durant 3 segons, per iniciar una funció d\'accessibilitat.\n\n Funció d\'accessibilitat actual:\n <xliff:g id="SERVICE_NAME">%1$s</xliff:g>\n\n Pots canviar la funció a Configuració &gt; Accessibilitat."</string>
diff --git a/core/res/res/values-cs/strings.xml b/core/res/res/values-cs/strings.xml
index 2c162914274b..2c017dd52ee1 100644
--- a/core/res/res/values-cs/strings.xml
+++ b/core/res/res/values-cs/strings.xml
@@ -1657,6 +1657,8 @@
<string name="kg_failed_attempts_almost_at_login" product="default" msgid="5270861875006378092">"Již <xliff:g id="NUMBER_0">%1$d</xliff:g>krát jste nesprávně nakreslili své heslo odemknutí. Po <xliff:g id="NUMBER_1">%2$d</xliff:g> dalších neúspěšných pokusech budete požádáni o odemčení telefonu pomocí e-mailového účtu.\n\n Zkuste to znovu za <xliff:g id="NUMBER_2">%3$d</xliff:g> s."</string>
<string name="kg_text_message_separator" product="default" msgid="4503708889934976866">" – "</string>
<string name="kg_reordering_delete_drop_target_text" msgid="2034358143731750914">"Odebrat"</string>
+ <!-- no translation found for allow_while_in_use_permission_in_fgs (4101339676785053656) -->
+ <skip />
<string name="safe_media_volume_warning" product="default" msgid="3751676824423049994">"Zvýšit hlasitost nad doporučenou úroveň?\n\nDlouhodobý poslech hlasitého zvuku může poškodit sluch."</string>
<string name="accessibility_shortcut_warning_dialog_title" msgid="4017995837692622933">"Použít zkratku přístupnosti?"</string>
<string name="accessibility_shortcut_toogle_warning" msgid="8306239551412868396">"Když je tato zkratka zapnutá, můžete funkci přístupnosti spustit tím, že na tři sekundy podržíte obě tlačítka hlasitosti.\n\n Aktuální funkce přístupnosti:\n <xliff:g id="SERVICE_NAME">%1$s</xliff:g>\n\n Funkci můžete změnit v Nastavení &gt; Přístupnost."</string>
diff --git a/core/res/res/values-da/strings.xml b/core/res/res/values-da/strings.xml
index f86db77184e6..4ebea8947666 100644
--- a/core/res/res/values-da/strings.xml
+++ b/core/res/res/values-da/strings.xml
@@ -1613,6 +1613,8 @@
<string name="kg_failed_attempts_almost_at_login" product="default" msgid="5270861875006378092">"Du har tegnet dit oplåsningsmønster forkert <xliff:g id="NUMBER_0">%1$d</xliff:g> gange. Efter <xliff:g id="NUMBER_1">%2$d</xliff:g> yderligere mislykkede forsøg til vil du blive bedt om at låse din telefon op ved hjælp af en mailkonto.\n\n Prøv igen om <xliff:g id="NUMBER_2">%3$d</xliff:g> sekunder."</string>
<string name="kg_text_message_separator" product="default" msgid="4503708889934976866">" – "</string>
<string name="kg_reordering_delete_drop_target_text" msgid="2034358143731750914">"Fjern"</string>
+ <!-- no translation found for allow_while_in_use_permission_in_fgs (4101339676785053656) -->
+ <skip />
<string name="safe_media_volume_warning" product="default" msgid="3751676824423049994">"Vil du skrue højere op end det anbefalede lydstyrkeniveau?\n\nDu kan skade hørelsen ved at lytte til meget høj musik over længere tid."</string>
<string name="accessibility_shortcut_warning_dialog_title" msgid="4017995837692622933">"Vil du bruge genvejen til Hjælpefunktioner?"</string>
<string name="accessibility_shortcut_toogle_warning" msgid="8306239551412868396">"Når genvejen er slået til, kan du starte en hjælpefunktion ved at trykke på begge lydstyrkeknapper i tre sekunder.\n\n Nuværende hjælpefunktion:\n<xliff:g id="SERVICE_NAME">%1$s</xliff:g>\n\n Du kan skifte funktion i Indstillinger &gt; Hjælpefunktioner."</string>
diff --git a/core/res/res/values-de/strings.xml b/core/res/res/values-de/strings.xml
index d6a9a010d9da..6fa0ac1791fa 100644
--- a/core/res/res/values-de/strings.xml
+++ b/core/res/res/values-de/strings.xml
@@ -1613,6 +1613,8 @@
<string name="kg_failed_attempts_almost_at_login" product="default" msgid="5270861875006378092">"Du hast dein Entsperrungsmuster <xliff:g id="NUMBER_0">%1$d</xliff:g>-mal falsch gezeichnet. Nach <xliff:g id="NUMBER_1">%2$d</xliff:g> weiteren erfolglosen Versuchen wirst du aufgefordert, dein Telefon mithilfe eines E-Mail-Kontos zu entsperren.\n\n Versuche es in <xliff:g id="NUMBER_2">%3$d</xliff:g> Sekunden erneut."</string>
<string name="kg_text_message_separator" product="default" msgid="4503708889934976866">" – "</string>
<string name="kg_reordering_delete_drop_target_text" msgid="2034358143731750914">"Entfernen"</string>
+ <!-- no translation found for allow_while_in_use_permission_in_fgs (4101339676785053656) -->
+ <skip />
<string name="safe_media_volume_warning" product="default" msgid="3751676824423049994">"Lautstärke über den Schwellenwert anheben?\n\nWenn du über einen längeren Zeitraum Musik in hoher Lautstärke hörst, kann dies dein Gehör schädigen."</string>
<string name="accessibility_shortcut_warning_dialog_title" msgid="4017995837692622933">"Verknüpfung für Bedienungshilfen verwenden?"</string>
<string name="accessibility_shortcut_toogle_warning" msgid="8306239551412868396">"Wenn die Verknüpfung aktiviert ist, kannst du die beiden Lautstärketasten drei Sekunden lang gedrückt halten, um eine Bedienungshilfe zu starten.\n\n Aktuelle Bedienungshilfe:\n<xliff:g id="SERVICE_NAME">%1$s</xliff:g>\n\n Du kannst die Bedienungshilfe unter \"Einstellungen\" &gt; \"Bedienungshilfen\" ändern."</string>
diff --git a/core/res/res/values-el/strings.xml b/core/res/res/values-el/strings.xml
index 67285e9b0bf3..81a16f28651c 100644
--- a/core/res/res/values-el/strings.xml
+++ b/core/res/res/values-el/strings.xml
@@ -1613,6 +1613,8 @@
<string name="kg_failed_attempts_almost_at_login" product="default" msgid="5270861875006378092">"Σχεδιάσατε το μοτίβο ξεκλειδώματος εσφαλμένα <xliff:g id="NUMBER_0">%1$d</xliff:g> φορές. Μετά από <xliff:g id="NUMBER_1">%2$d</xliff:g> ανεπιτυχείς προσπάθειες ακόμη, θα σας ζητηθεί να ξεκλειδώσετε το τηλέφωνό σας με τη χρήση ενός λογαριασμού ηλεκτρονικού ταχυδρομείου.\n\n Δοκιμάστε ξανά σε <xliff:g id="NUMBER_2">%3$d</xliff:g> δευτερόλεπτα."</string>
<string name="kg_text_message_separator" product="default" msgid="4503708889934976866">" — "</string>
<string name="kg_reordering_delete_drop_target_text" msgid="2034358143731750914">"Κατάργηση"</string>
+ <!-- no translation found for allow_while_in_use_permission_in_fgs (4101339676785053656) -->
+ <skip />
<string name="safe_media_volume_warning" product="default" msgid="3751676824423049994">"Αυξάνετε την ένταση ήχου πάνω από το επίπεδο ασφαλείας;\n\nΑν ακούτε μουσική σε υψηλή ένταση για μεγάλο χρονικό διάστημα ενδέχεται να προκληθεί βλάβη στην ακοή σας."</string>
<string name="accessibility_shortcut_warning_dialog_title" msgid="4017995837692622933">"Να χρησιμοποιείται η συντόμευση προσβασιμότητας;"</string>
<string name="accessibility_shortcut_toogle_warning" msgid="8306239551412868396">"Όταν η συντόμευση είναι ενεργοποιημένη, το πάτημα και των δύο κουμπιών έντασης ήχου για 3 δευτερόλεπτα θα ξεκινήσει μια λειτουργία προσβασιμότητας.\n\n Τρέχουσα λειτουργία προσβασιμότητας:\n <xliff:g id="SERVICE_NAME">%1$s</xliff:g>\n\n Μπορείτε να αλλάξετε τη λειτουργία από τις Ρυθμίσεις &gt; Προσβασιμότητα."</string>
diff --git a/core/res/res/values-en-rAU/strings.xml b/core/res/res/values-en-rAU/strings.xml
index ed31c2274696..87e0c902df8c 100644
--- a/core/res/res/values-en-rAU/strings.xml
+++ b/core/res/res/values-en-rAU/strings.xml
@@ -1613,6 +1613,8 @@
<string name="kg_failed_attempts_almost_at_login" product="default" msgid="5270861875006378092">"You have incorrectly drawn your unlock pattern <xliff:g id="NUMBER_0">%1$d</xliff:g> times. After <xliff:g id="NUMBER_1">%2$d</xliff:g> more unsuccessful attempts, you will be asked to unlock your phone using an email account.\n\n Try again in <xliff:g id="NUMBER_2">%3$d</xliff:g> seconds."</string>
<string name="kg_text_message_separator" product="default" msgid="4503708889934976866">" — "</string>
<string name="kg_reordering_delete_drop_target_text" msgid="2034358143731750914">"Remove"</string>
+ <!-- no translation found for allow_while_in_use_permission_in_fgs (4101339676785053656) -->
+ <skip />
<string name="safe_media_volume_warning" product="default" msgid="3751676824423049994">"Raise volume above recommended level?\n\nListening at high volume for long periods may damage your hearing."</string>
<string name="accessibility_shortcut_warning_dialog_title" msgid="4017995837692622933">"Use Accessibility Shortcut?"</string>
<string name="accessibility_shortcut_toogle_warning" msgid="8306239551412868396">"When the shortcut is on, pressing both volume buttons for 3 seconds will start an accessibility feature.\n\n Current accessibility feature:\n <xliff:g id="SERVICE_NAME">%1$s</xliff:g>\n\n You can change the feature in Settings &gt; Accessibility."</string>
diff --git a/core/res/res/values-en-rCA/strings.xml b/core/res/res/values-en-rCA/strings.xml
index 6ae46b4c646e..008ad8aec7fc 100644
--- a/core/res/res/values-en-rCA/strings.xml
+++ b/core/res/res/values-en-rCA/strings.xml
@@ -1613,6 +1613,8 @@
<string name="kg_failed_attempts_almost_at_login" product="default" msgid="5270861875006378092">"You have incorrectly drawn your unlock pattern <xliff:g id="NUMBER_0">%1$d</xliff:g> times. After <xliff:g id="NUMBER_1">%2$d</xliff:g> more unsuccessful attempts, you will be asked to unlock your phone using an email account.\n\n Try again in <xliff:g id="NUMBER_2">%3$d</xliff:g> seconds."</string>
<string name="kg_text_message_separator" product="default" msgid="4503708889934976866">" — "</string>
<string name="kg_reordering_delete_drop_target_text" msgid="2034358143731750914">"Remove"</string>
+ <!-- no translation found for allow_while_in_use_permission_in_fgs (4101339676785053656) -->
+ <skip />
<string name="safe_media_volume_warning" product="default" msgid="3751676824423049994">"Raise volume above recommended level?\n\nListening at high volume for long periods may damage your hearing."</string>
<string name="accessibility_shortcut_warning_dialog_title" msgid="4017995837692622933">"Use Accessibility Shortcut?"</string>
<string name="accessibility_shortcut_toogle_warning" msgid="8306239551412868396">"When the shortcut is on, pressing both volume buttons for 3 seconds will start an accessibility feature.\n\n Current accessibility feature:\n <xliff:g id="SERVICE_NAME">%1$s</xliff:g>\n\n You can change the feature in Settings &gt; Accessibility."</string>
diff --git a/core/res/res/values-en-rGB/strings.xml b/core/res/res/values-en-rGB/strings.xml
index ed31c2274696..87e0c902df8c 100644
--- a/core/res/res/values-en-rGB/strings.xml
+++ b/core/res/res/values-en-rGB/strings.xml
@@ -1613,6 +1613,8 @@
<string name="kg_failed_attempts_almost_at_login" product="default" msgid="5270861875006378092">"You have incorrectly drawn your unlock pattern <xliff:g id="NUMBER_0">%1$d</xliff:g> times. After <xliff:g id="NUMBER_1">%2$d</xliff:g> more unsuccessful attempts, you will be asked to unlock your phone using an email account.\n\n Try again in <xliff:g id="NUMBER_2">%3$d</xliff:g> seconds."</string>
<string name="kg_text_message_separator" product="default" msgid="4503708889934976866">" — "</string>
<string name="kg_reordering_delete_drop_target_text" msgid="2034358143731750914">"Remove"</string>
+ <!-- no translation found for allow_while_in_use_permission_in_fgs (4101339676785053656) -->
+ <skip />
<string name="safe_media_volume_warning" product="default" msgid="3751676824423049994">"Raise volume above recommended level?\n\nListening at high volume for long periods may damage your hearing."</string>
<string name="accessibility_shortcut_warning_dialog_title" msgid="4017995837692622933">"Use Accessibility Shortcut?"</string>
<string name="accessibility_shortcut_toogle_warning" msgid="8306239551412868396">"When the shortcut is on, pressing both volume buttons for 3 seconds will start an accessibility feature.\n\n Current accessibility feature:\n <xliff:g id="SERVICE_NAME">%1$s</xliff:g>\n\n You can change the feature in Settings &gt; Accessibility."</string>
diff --git a/core/res/res/values-en-rIN/strings.xml b/core/res/res/values-en-rIN/strings.xml
index ed31c2274696..87e0c902df8c 100644
--- a/core/res/res/values-en-rIN/strings.xml
+++ b/core/res/res/values-en-rIN/strings.xml
@@ -1613,6 +1613,8 @@
<string name="kg_failed_attempts_almost_at_login" product="default" msgid="5270861875006378092">"You have incorrectly drawn your unlock pattern <xliff:g id="NUMBER_0">%1$d</xliff:g> times. After <xliff:g id="NUMBER_1">%2$d</xliff:g> more unsuccessful attempts, you will be asked to unlock your phone using an email account.\n\n Try again in <xliff:g id="NUMBER_2">%3$d</xliff:g> seconds."</string>
<string name="kg_text_message_separator" product="default" msgid="4503708889934976866">" — "</string>
<string name="kg_reordering_delete_drop_target_text" msgid="2034358143731750914">"Remove"</string>
+ <!-- no translation found for allow_while_in_use_permission_in_fgs (4101339676785053656) -->
+ <skip />
<string name="safe_media_volume_warning" product="default" msgid="3751676824423049994">"Raise volume above recommended level?\n\nListening at high volume for long periods may damage your hearing."</string>
<string name="accessibility_shortcut_warning_dialog_title" msgid="4017995837692622933">"Use Accessibility Shortcut?"</string>
<string name="accessibility_shortcut_toogle_warning" msgid="8306239551412868396">"When the shortcut is on, pressing both volume buttons for 3 seconds will start an accessibility feature.\n\n Current accessibility feature:\n <xliff:g id="SERVICE_NAME">%1$s</xliff:g>\n\n You can change the feature in Settings &gt; Accessibility."</string>
diff --git a/core/res/res/values-en-rXC/strings.xml b/core/res/res/values-en-rXC/strings.xml
index 07cb6c7f1c7c..777107a60820 100644
--- a/core/res/res/values-en-rXC/strings.xml
+++ b/core/res/res/values-en-rXC/strings.xml
@@ -1613,6 +1613,8 @@
<string name="kg_failed_attempts_almost_at_login" product="default" msgid="5270861875006378092">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‎‎‏‎‎‏‎‎‏‎‎‏‎‏‏‏‎‏‏‏‎‎‏‏‏‏‎‎‏‎‏‏‏‏‎‏‏‏‏‏‏‎‎‏‏‏‏‎‎‏‎‎‎‎‎‏‏‎‏‏‎‎‎You have incorrectly drawn your unlock pattern ‎‏‎‎‏‏‎<xliff:g id="NUMBER_0">%1$d</xliff:g>‎‏‎‎‏‏‏‎ times. After ‎‏‎‎‏‏‎<xliff:g id="NUMBER_1">%2$d</xliff:g>‎‏‎‎‏‏‏‎ more unsuccessful attempts, you will be asked to unlock your phone using an email account.‎‏‎‎‏‏‎\n‎‏‎‎‏‏‏‎‎‏‎‎‏‏‎\n‎‏‎‎‏‏‏‎ Try again in ‎‏‎‎‏‏‎<xliff:g id="NUMBER_2">%3$d</xliff:g>‎‏‎‎‏‏‏‎ seconds.‎‏‎‎‏‎"</string>
<string name="kg_text_message_separator" product="default" msgid="4503708889934976866">" ‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‎‏‏‏‏‏‎‏‎‎‎‎‎‎‎‎‏‏‎‎‎‏‏‎‏‎‏‏‏‏‏‏‎‏‎‏‏‎‎‎‏‎‏‎‏‏‏‏‏‎‏‎‏‏‏‎‏‏‎‎‎‏‎‎ — ‎‏‎‎‏‎ "</string>
<string name="kg_reordering_delete_drop_target_text" msgid="2034358143731750914">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‎‏‏‏‏‎‎‎‎‏‏‏‎‏‏‎‏‏‏‏‏‎‏‏‏‏‏‎‏‎‎‎‎‎‎‎‏‏‏‎‎‎‎‏‏‎‏‏‏‎‏‏‎‎‎‎‎‎‎‎‎‏‎‎Remove‎‏‎‎‏‎"</string>
+ <!-- no translation found for allow_while_in_use_permission_in_fgs (4101339676785053656) -->
+ <skip />
<string name="safe_media_volume_warning" product="default" msgid="3751676824423049994">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‎‏‏‎‏‎‎‎‎‎‏‎‎‎‎‏‎‏‎‎‎‏‎‎‎‏‏‎‎‎‏‏‏‎‎‏‏‏‏‏‏‎‎‏‏‎‏‎‎‏‎‏‎‏‏‎‎‎‎‏‎‏‎‎Raise volume above recommended level?‎‏‎‎‏‏‎\n‎‏‎‎‏‏‏‎‎‏‎‎‏‏‎\n‎‏‎‎‏‏‏‎Listening at high volume for long periods may damage your hearing.‎‏‎‎‏‎"</string>
<string name="accessibility_shortcut_warning_dialog_title" msgid="4017995837692622933">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‎‏‏‎‏‏‏‏‏‎‎‎‎‏‎‏‏‎‎‏‎‎‏‏‏‏‎‏‏‎‏‎‎‎‏‎‏‏‎‏‎‏‎‏‎‏‎‏‎‏‏‏‎‎‎‎‏‎‏‎‏‎‏‎Use Accessibility Shortcut?‎‏‎‎‏‎"</string>
<string name="accessibility_shortcut_toogle_warning" msgid="8306239551412868396">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‏‏‎‎‏‏‎‏‎‎‎‏‎‏‏‎‏‏‎‎‎‎‏‏‎‏‏‏‎‏‎‎‎‏‏‎‎‏‎‏‎‎‎‎‏‏‏‎‎‏‎‏‎‏‎‎‏‎‏‏‎‎‎When the shortcut is on, pressing both volume buttons for 3 seconds will start an accessibility feature.‎‏‎‎‏‏‎\n‎‏‎‎‏‏‏‎‎‏‎‎‏‏‎\n‎‏‎‎‏‏‏‎ Current accessibility feature:‎‏‎‎‏‏‎\n‎‏‎‎‏‏‏‎ ‎‏‎‎‏‏‎<xliff:g id="SERVICE_NAME">%1$s</xliff:g>‎‏‎‎‏‏‏‎‎‏‎‎‏‏‎\n‎‏‎‎‏‏‏‎‎‏‎‎‏‏‎\n‎‏‎‎‏‏‏‎ You can change the feature in Settings &gt; Accessibility.‎‏‎‎‏‎"</string>
diff --git a/core/res/res/values-es-rUS/strings.xml b/core/res/res/values-es-rUS/strings.xml
index 6f83551eca5a..4335c2bf9f27 100644
--- a/core/res/res/values-es-rUS/strings.xml
+++ b/core/res/res/values-es-rUS/strings.xml
@@ -1613,6 +1613,8 @@
<string name="kg_failed_attempts_almost_at_login" product="default" msgid="5270861875006378092">"Dibujaste incorrectamente tu patrón de desbloqueo <xliff:g id="NUMBER_0">%1$d</xliff:g> veces. Luego de <xliff:g id="NUMBER_1">%2$d</xliff:g> intentos incorrectos más, se te solicitará que desbloquees tu dispositivo mediante el uso de una cuenta de correo.\n\n Vuelve a intentarlo en <xliff:g id="NUMBER_2">%3$d</xliff:g> segundos."</string>
<string name="kg_text_message_separator" product="default" msgid="4503708889934976866">" — "</string>
<string name="kg_reordering_delete_drop_target_text" msgid="2034358143731750914">"Eliminar"</string>
+ <!-- no translation found for allow_while_in_use_permission_in_fgs (4101339676785053656) -->
+ <skip />
<string name="safe_media_volume_warning" product="default" msgid="3751676824423049994">"¿Quieres subir el volumen por encima del nivel recomendado?\n\nEscuchar a un alto volumen durante largos períodos puede dañar tu audición."</string>
<string name="accessibility_shortcut_warning_dialog_title" msgid="4017995837692622933">"¿Usar acceso directo de accesibilidad?"</string>
<string name="accessibility_shortcut_toogle_warning" msgid="8306239551412868396">"Cuando el acceso directo está activado, puedes presionar los botones de volumen durante 3 segundos para iniciar una función de accesibilidad.\n\n Función de accesibilidad actual:\n <xliff:g id="SERVICE_NAME">%1$s</xliff:g>\n\n Puedes cambiar la función en Configuración &gt; Accesibilidad."</string>
diff --git a/core/res/res/values-es/strings.xml b/core/res/res/values-es/strings.xml
index 737a83b4dbfe..8d750caa6dba 100644
--- a/core/res/res/values-es/strings.xml
+++ b/core/res/res/values-es/strings.xml
@@ -1613,6 +1613,8 @@
<string name="kg_failed_attempts_almost_at_login" product="default" msgid="5270861875006378092">"Has fallado <xliff:g id="NUMBER_0">%1$d</xliff:g> veces al dibujar el patrón de desbloqueo. Si fallas otras <xliff:g id="NUMBER_1">%2$d</xliff:g> veces, deberás usar una cuenta de correo electrónico para desbloquear el teléfono.\n\n Inténtalo de nuevo en <xliff:g id="NUMBER_2">%3$d</xliff:g> segundos."</string>
<string name="kg_text_message_separator" product="default" msgid="4503708889934976866">" — "</string>
<string name="kg_reordering_delete_drop_target_text" msgid="2034358143731750914">"Quitar"</string>
+ <!-- no translation found for allow_while_in_use_permission_in_fgs (4101339676785053656) -->
+ <skip />
<string name="safe_media_volume_warning" product="default" msgid="3751676824423049994">"¿Quieres subir el volumen por encima del nivel recomendado?\n\nEscuchar sonidos fuertes durante mucho tiempo puede dañar los oídos."</string>
<string name="accessibility_shortcut_warning_dialog_title" msgid="4017995837692622933">"¿Utilizar acceso directo de accesibilidad?"</string>
<string name="accessibility_shortcut_toogle_warning" msgid="8306239551412868396">"Si el acceso directo está activado, pulsa los dos botones de volumen durante tres segundos para iniciar una función de accesibilidad.\n\n Función de accesibilidad actual:\n <xliff:g id="SERVICE_NAME">%1$s</xliff:g>\n\n Puedes cambiar la función en Ajustes &gt; Accesibilidad."</string>
diff --git a/core/res/res/values-et/strings.xml b/core/res/res/values-et/strings.xml
index 021985f50e59..7f423e935b47 100644
--- a/core/res/res/values-et/strings.xml
+++ b/core/res/res/values-et/strings.xml
@@ -1613,6 +1613,8 @@
<string name="kg_failed_attempts_almost_at_login" product="default" msgid="5270861875006378092">"Joonistasite oma avamismustri <xliff:g id="NUMBER_0">%1$d</xliff:g> korda valesti. Pärast veel <xliff:g id="NUMBER_1">%2$d</xliff:g> ebaõnnestunud katset palutakse teil telefon avada meilikontoga.\n\n Proovige uuesti <xliff:g id="NUMBER_2">%3$d</xliff:g> sekundi pärast."</string>
<string name="kg_text_message_separator" product="default" msgid="4503708889934976866">" — "</string>
<string name="kg_reordering_delete_drop_target_text" msgid="2034358143731750914">"Eemalda"</string>
+ <!-- no translation found for allow_while_in_use_permission_in_fgs (4101339676785053656) -->
+ <skip />
<string name="safe_media_volume_warning" product="default" msgid="3751676824423049994">"Kas suurendada helitugevuse taset üle soovitatud taseme?\n\nPikaajaline valju helitugevusega kuulamine võib kuulmist kahjustada."</string>
<string name="accessibility_shortcut_warning_dialog_title" msgid="4017995837692622933">"Kas kasutada juurdepääsetavuse otseteed?"</string>
<string name="accessibility_shortcut_toogle_warning" msgid="8306239551412868396">"Kui otsetee on sisse lülitatud, käivitab mõlema helitugevuse nupu kolm sekundit all hoidmine juurdepääsetavuse funktsiooni.\n\n Praegune juurdepääsetavuse funktsioon:\n <xliff:g id="SERVICE_NAME">%1$s</xliff:g>\n\n Saate seda funktsiooni muuta valikutega Seaded &gt; Juurdepääsetavus."</string>
diff --git a/core/res/res/values-eu/strings.xml b/core/res/res/values-eu/strings.xml
index 8ecdd0765a67..9d014a3b875a 100644
--- a/core/res/res/values-eu/strings.xml
+++ b/core/res/res/values-eu/strings.xml
@@ -1613,6 +1613,8 @@
<string name="kg_failed_attempts_almost_at_login" product="default" msgid="5270861875006378092">"Desblokeatzeko eredua oker marraztu duzu <xliff:g id="NUMBER_0">%1$d</xliff:g> aldiz. Beste <xliff:g id="NUMBER_1">%2$d</xliff:g> aldiz oker marrazten baduzu, telefonoa posta-kontu baten bidez desblokeatzeko eskatuko dizugu.\n\n Saiatu berriro <xliff:g id="NUMBER_2">%3$d</xliff:g> segundo barru."</string>
<string name="kg_text_message_separator" product="default" msgid="4503708889934976866">" — "</string>
<string name="kg_reordering_delete_drop_target_text" msgid="2034358143731750914">"Kendu"</string>
+ <!-- no translation found for allow_while_in_use_permission_in_fgs (4101339676785053656) -->
+ <skip />
<string name="safe_media_volume_warning" product="default" msgid="3751676824423049994">"Bolumena gomendatutako mailatik gora igo nahi duzu?\n\nMusika bolumen handian eta denbora luzez entzuteak entzumena kalte diezazuke."</string>
<string name="accessibility_shortcut_warning_dialog_title" msgid="4017995837692622933">"Erabilerraztasun-lasterbidea erabili nahi duzu?"</string>
<string name="accessibility_shortcut_toogle_warning" msgid="8306239551412868396">"Lasterbidea aktibatuta dagoenean, bi bolumen-botoiak hiru segundoz sakatuta abiaraziko da erabilerraztasun-eginbidea.\n\n Uneko erabilerraztasun-eginbidea:\n <xliff:g id="SERVICE_NAME">%1$s</xliff:g>\n\n Eginbidea aldatzeko, joan Ezarpenak &gt; Erabilerraztasuna atalera."</string>
diff --git a/core/res/res/values-fa/strings.xml b/core/res/res/values-fa/strings.xml
index 9c3403b57eac..58ce7a7e547c 100644
--- a/core/res/res/values-fa/strings.xml
+++ b/core/res/res/values-fa/strings.xml
@@ -1613,6 +1613,8 @@
<string name="kg_failed_attempts_almost_at_login" product="default" msgid="5270861875006378092">"‏شما الگوی بازگشایی قفل خود را <xliff:g id="NUMBER_0">%1$d</xliff:g> بار اشتباه کشیده‌اید. پس از <xliff:g id="NUMBER_1">%2$d</xliff:g> تلاش ناموفق، از شما خواسته می‎شود که با استفاده از یک حساب ایمیل قفل تلفن خود را باز کنید.\n\n لطفاً پس از <xliff:g id="NUMBER_2">%3$d</xliff:g> ثانیه دوباره امتحان کنید."</string>
<string name="kg_text_message_separator" product="default" msgid="4503708889934976866">" — "</string>
<string name="kg_reordering_delete_drop_target_text" msgid="2034358143731750914">"حذف"</string>
+ <!-- no translation found for allow_while_in_use_permission_in_fgs (4101339676785053656) -->
+ <skip />
<string name="safe_media_volume_warning" product="default" msgid="3751676824423049994">"میزان صدا را به بالاتر از حد توصیه شده افزایش می‌دهید؟\n\nگوش دادن به صداهای بلند برای مدت طولانی می‌تواند به شنوایی‌تان آسیب وارد کند."</string>
<string name="accessibility_shortcut_warning_dialog_title" msgid="4017995837692622933">"از میان‌بر دسترس‌پذیری استفاده شود؟"</string>
<string name="accessibility_shortcut_toogle_warning" msgid="8306239551412868396">"وقتی میان‌بر روشن است،‌ اگر هر دو دکمه صدا را ۳ ثانیه فشار دهید یکی از قابلیت‌های دسترس‌پذیری شروع می‌شود.\n\n قابلیت دسترس‌پذیری کنونی:\n <xliff:g id="SERVICE_NAME">%1$s</xliff:g>\n\n می‌توانید در «تنظیمات &gt; دسترس‌پذیری»، قابلیت را تغییر دهید."</string>
diff --git a/core/res/res/values-fi/strings.xml b/core/res/res/values-fi/strings.xml
index a42b87d5d7f2..e0f4ed49b6a3 100644
--- a/core/res/res/values-fi/strings.xml
+++ b/core/res/res/values-fi/strings.xml
@@ -1613,6 +1613,8 @@
<string name="kg_failed_attempts_almost_at_login" product="default" msgid="5270861875006378092">"Piirsit lukituksenpoistokuvion väärin <xliff:g id="NUMBER_0">%1$d</xliff:g> kertaa. Jos piirrät kuvion väärin vielä <xliff:g id="NUMBER_1">%2$d</xliff:g> kertaa, sinua pyydetään poistamaan puhelimesi lukitus sähköpostitilin avulla.\n\n Yritä uudelleen <xliff:g id="NUMBER_2">%3$d</xliff:g> sekunnin kuluttua."</string>
<string name="kg_text_message_separator" product="default" msgid="4503708889934976866">" – "</string>
<string name="kg_reordering_delete_drop_target_text" msgid="2034358143731750914">"Poista"</string>
+ <!-- no translation found for allow_while_in_use_permission_in_fgs (4101339676785053656) -->
+ <skip />
<string name="safe_media_volume_warning" product="default" msgid="3751676824423049994">"Nostetaanko äänenvoimakkuus suositellun tason yläpuolelle?\n\nPitkäkestoinen kova äänenvoimakkuus saattaa heikentää kuuloa."</string>
<string name="accessibility_shortcut_warning_dialog_title" msgid="4017995837692622933">"Käytetäänkö esteettömyyden pikanäppäintä?"</string>
<string name="accessibility_shortcut_toogle_warning" msgid="8306239551412868396">"Kun pikanäppäin on käytössä, voit käynnistää esteettömyystoiminnon pitämällä molempia äänenvoimakkuuspainikkeita painettuna kolmen sekunnin ajan.\n\n Tällä hetkellä valittu esteettömyystoiminto:\n<xliff:g id="SERVICE_NAME">%1$s</xliff:g>\n\n Voit vaihtaa toimintoa valitsemalla Asetukset &gt; Esteettömyys."</string>
diff --git a/core/res/res/values-fr-rCA/strings.xml b/core/res/res/values-fr-rCA/strings.xml
index 47f41841f747..5d99db170087 100644
--- a/core/res/res/values-fr-rCA/strings.xml
+++ b/core/res/res/values-fr-rCA/strings.xml
@@ -1613,6 +1613,8 @@
<string name="kg_failed_attempts_almost_at_login" product="default" msgid="5270861875006378092">"Vous avez dessiné un schéma de déverrouillage incorrect à <xliff:g id="NUMBER_0">%1$d</xliff:g> reprises. Si vous échouez encore <xliff:g id="NUMBER_1">%2$d</xliff:g> fois, vous devrez déverrouiller votre téléphone à l\'aide d\'un compte de messagerie électronique.\n\n Veuillez réessayer dans <xliff:g id="NUMBER_2">%3$d</xliff:g> secondes."</string>
<string name="kg_text_message_separator" product="default" msgid="4503708889934976866">" — "</string>
<string name="kg_reordering_delete_drop_target_text" msgid="2034358143731750914">"Supprimer"</string>
+ <!-- no translation found for allow_while_in_use_permission_in_fgs (4101339676785053656) -->
+ <skip />
<string name="safe_media_volume_warning" product="default" msgid="3751676824423049994">"Augmenter le volume au-dessus du niveau recommandé?\n\nL\'écoute prolongée à un volume élevé peut endommager vos facultés auditives."</string>
<string name="accessibility_shortcut_warning_dialog_title" msgid="4017995837692622933">"Utiliser le raccourci d\'accessibilité?"</string>
<string name="accessibility_shortcut_toogle_warning" msgid="8306239551412868396">"Quand le raccourci est activé, appuyez sur les deux boutons de volume pendant trois secondes pour lancer une fonctionnalité d\'accessibilité.\n\n Fonctionnalité d\'accessibilité utilisée actuellement :\n <xliff:g id="SERVICE_NAME">%1$s</xliff:g>\n\n Vous pouvez changer de fonctionnalité sous Paramètres &gt; Accessibilité."</string>
diff --git a/core/res/res/values-fr/strings.xml b/core/res/res/values-fr/strings.xml
index 7898e797c8e3..3f4f3e6549e4 100644
--- a/core/res/res/values-fr/strings.xml
+++ b/core/res/res/values-fr/strings.xml
@@ -1613,6 +1613,8 @@
<string name="kg_failed_attempts_almost_at_login" product="default" msgid="5270861875006378092">"Vous avez dessiné un schéma de déverrouillage incorrect à <xliff:g id="NUMBER_0">%1$d</xliff:g> reprises. Si vous échouez encore <xliff:g id="NUMBER_1">%2$d</xliff:g> fois, vous devrez déverrouiller votre téléphone à l\'aide d\'un compte de messagerie électronique.\n\n Veuillez réessayer dans <xliff:g id="NUMBER_2">%3$d</xliff:g> secondes."</string>
<string name="kg_text_message_separator" product="default" msgid="4503708889934976866">" — "</string>
<string name="kg_reordering_delete_drop_target_text" msgid="2034358143731750914">"Supprimer"</string>
+ <!-- no translation found for allow_while_in_use_permission_in_fgs (4101339676785053656) -->
+ <skip />
<string name="safe_media_volume_warning" product="default" msgid="3751676824423049994">"Augmenter le volume au dessus du niveau recommandé ?\n\nL\'écoute prolongée à un volume élevé peut endommager vos facultés auditives."</string>
<string name="accessibility_shortcut_warning_dialog_title" msgid="4017995837692622933">"Utiliser le raccourci d\'accessibilité ?"</string>
<string name="accessibility_shortcut_toogle_warning" msgid="8306239551412868396">"Quand le raccourci est activé, appuyez sur les deux boutons de volume pendant trois secondes pour lancer une fonctionnalité d\'accessibilité.\n\n Fonctionnalité d\'accessibilité utilisée actuellement :\n <xliff:g id="SERVICE_NAME">%1$s</xliff:g>\n\n Vous pouvez changer de fonctionnalité dans Paramètres &gt; Accessibilité."</string>
diff --git a/core/res/res/values-gl/strings.xml b/core/res/res/values-gl/strings.xml
index e7498b954194..2e5052c134b1 100644
--- a/core/res/res/values-gl/strings.xml
+++ b/core/res/res/values-gl/strings.xml
@@ -1613,6 +1613,8 @@
<string name="kg_failed_attempts_almost_at_login" product="default" msgid="5270861875006378092">"Debuxaches o padrón de desbloqueo incorrectamente <xliff:g id="NUMBER_0">%1$d</xliff:g> veces. Se realizas <xliff:g id="NUMBER_1">%2$d</xliff:g> intentos incorrectos máis, terás que desbloquear o teléfono a través dunha conta de correo electrónico.\n\n Téntao de novo dentro de <xliff:g id="NUMBER_2">%3$d</xliff:g> segundos."</string>
<string name="kg_text_message_separator" product="default" msgid="4503708889934976866">" — "</string>
<string name="kg_reordering_delete_drop_target_text" msgid="2034358143731750914">"Eliminar"</string>
+ <!-- no translation found for allow_while_in_use_permission_in_fgs (4101339676785053656) -->
+ <skip />
<string name="safe_media_volume_warning" product="default" msgid="3751676824423049994">"Queres subir o volume máis do nivel recomendado?\n\nA reprodución de son a un volume elevado durante moito tempo pode provocar danos nos oídos."</string>
<string name="accessibility_shortcut_warning_dialog_title" msgid="4017995837692622933">"Queres utilizar o atallo de accesibilidade?"</string>
<string name="accessibility_shortcut_toogle_warning" msgid="8306239551412868396">"Cando o atallo está activado, podes premer os dous botóns de volume durante 3 segundos para iniciar unha función de accesibilidade.\n\n Función de accesibilidade actual:\n <xliff:g id="SERVICE_NAME">%1$s</xliff:g>\n\n Podes cambiar a función en Configuración &gt; Accesibilidade."</string>
diff --git a/core/res/res/values-gu/strings.xml b/core/res/res/values-gu/strings.xml
index b52d99ef7b8a..8081ef2709ba 100644
--- a/core/res/res/values-gu/strings.xml
+++ b/core/res/res/values-gu/strings.xml
@@ -1613,6 +1613,8 @@
<string name="kg_failed_attempts_almost_at_login" product="default" msgid="5270861875006378092">"તમે તમારી અનલૉક પૅટર્ન <xliff:g id="NUMBER_0">%1$d</xliff:g> વખત ખોટી રીતે દોરી. હજી <xliff:g id="NUMBER_1">%2$d</xliff:g> અસફળ પ્રયાસ પછી, તમને ઇમેઇલ એકાઉન્ટનો ઉપયોગ કરીને ફોનને અનલૉક કરવાનું કહેવામાં આવશે.\n\n<xliff:g id="NUMBER_2">%3$d</xliff:g> સેકન્ડમાં ફરીથી પ્રયાસ કરો."</string>
<string name="kg_text_message_separator" product="default" msgid="4503708889934976866">" — "</string>
<string name="kg_reordering_delete_drop_target_text" msgid="2034358143731750914">"દૂર કરો"</string>
+ <!-- no translation found for allow_while_in_use_permission_in_fgs (4101339676785053656) -->
+ <skip />
<string name="safe_media_volume_warning" product="default" msgid="3751676824423049994">"ભલામણ કરેલ સ્તરની ઉપર વૉલ્યૂમ વધાર્યો?\n\nલાંબા સમય સુધી ઊંચા અવાજે સાંભળવું તમારી શ્રવણક્ષમતાને નુકસાન પહોંચાડી શકે છે."</string>
<string name="accessibility_shortcut_warning_dialog_title" msgid="4017995837692622933">"ઍક્સેસિબિલિટી શૉર્ટકટનો ઉપયોગ કરીએ?"</string>
<string name="accessibility_shortcut_toogle_warning" msgid="8306239551412868396">"જ્યારે શૉર્ટકટ ચાલુ હોય, ત્યારે બન્ને વૉલ્યૂમ બટનને 3 સેકન્ડ સુધી દબાવી રાખવાથી ઍક્સેસિબિલિટી સુવિધા શરૂ થઈ જશે.\n\n વર્તમાન ઍક્સેસિબિલિટી સુવિધા:\n <xliff:g id="SERVICE_NAME">%1$s</xliff:g>\n\n તમે સેટિંગ્સ &gt; ઍક્સેસિબિલિટીમાં જઈને આ સુવિધા બદલી શકો છો."</string>
@@ -1987,8 +1989,7 @@
<string name="mime_type_spreadsheet_ext" msgid="8720173181137254414">"<xliff:g id="EXTENSION">%1$s</xliff:g> સ્પ્રેડશીટ"</string>
<string name="mime_type_presentation" msgid="1145384236788242075">"પ્રસ્તુતિ"</string>
<string name="mime_type_presentation_ext" msgid="8761049335564371468">"<xliff:g id="EXTENSION">%1$s</xliff:g> પ્રસ્તુતિ"</string>
- <!-- no translation found for bluetooth_airplane_mode_toast (2066399056595768554) -->
- <skip />
+ <string name="bluetooth_airplane_mode_toast" msgid="2066399056595768554">"એરપ્લેન મોડ દરમિયાન બ્લૂટૂથ ચાલુ રહેશે"</string>
<string name="car_loading_profile" msgid="8219978381196748070">"લોડિંગ"</string>
<plurals name="file_count" formatted="false" msgid="7063513834724389247">
<item quantity="one"><xliff:g id="FILE_NAME_2">%s</xliff:g> + <xliff:g id="COUNT_3">%d</xliff:g> ફાઇલ</item>
@@ -2009,8 +2010,7 @@
<!-- no translation found for accessibility_system_action_accessibility_menu_label (8436484650391125184) -->
<skip />
<string name="accessibility_freeform_caption" msgid="8377519323496290122">"<xliff:g id="APP_NAME">%1$s</xliff:g>નું કૅપ્શન બાર."</string>
- <!-- no translation found for as_app_forced_to_restricted_bucket (8233871289353898964) -->
- <skip />
+ <string name="as_app_forced_to_restricted_bucket" msgid="8233871289353898964">"<xliff:g id="PACKAGE_NAME">%1$s</xliff:g>ને પ્રતિબંધિત સમૂહમાં મૂકવામાં આવ્યું છે"</string>
<!-- no translation found for resolver_personal_tab (2051260504014442073) -->
<skip />
<!-- no translation found for resolver_work_tab (2690019516263167035) -->
diff --git a/core/res/res/values-hi/strings.xml b/core/res/res/values-hi/strings.xml
index 3140c76ac43c..05efcd8942cb 100644
--- a/core/res/res/values-hi/strings.xml
+++ b/core/res/res/values-hi/strings.xml
@@ -1613,6 +1613,8 @@
<string name="kg_failed_attempts_almost_at_login" product="default" msgid="5270861875006378092">"आपने अपने अनलॉक आकार को <xliff:g id="NUMBER_0">%1$d</xliff:g> बार गलत तरीके से आरेखित किया है. <xliff:g id="NUMBER_1">%2$d</xliff:g> और असफल प्रयासों के बाद, आपसे अपने फ़ोन को किसी ईमेल खाते का उपयोग करके अनलॉक करने के लिए कहा जाएगा.\n\n <xliff:g id="NUMBER_2">%3$d</xliff:g> सेकंड में फिर से प्रयास करें."</string>
<string name="kg_text_message_separator" product="default" msgid="4503708889934976866">" — "</string>
<string name="kg_reordering_delete_drop_target_text" msgid="2034358143731750914">"निकालें"</string>
+ <!-- no translation found for allow_while_in_use_permission_in_fgs (4101339676785053656) -->
+ <skip />
<string name="safe_media_volume_warning" product="default" msgid="3751676824423049994">"वॉल्यूम को सुझाए गए स्तर से ऊपर बढ़ाएं?\n\nअत्यधिक वॉल्यूम पर ज़्यादा समय तक सुनने से आपकी सुनने की क्षमता को नुकसान हो सकता है."</string>
<string name="accessibility_shortcut_warning_dialog_title" msgid="4017995837692622933">"सुलभता शॉर्टकट का इस्तेमाल करना चाहते हैं?"</string>
<string name="accessibility_shortcut_toogle_warning" msgid="8306239551412868396">"इस शॉर्टकट के चालू होने पर, दोनों वॉल्यूम बटनों को 3 सेकंड तक दबाने से सुलभता सुविधा शुरू हो जाएगी.\n\n मौजूदा सुलभता सुविधा:\n <xliff:g id="SERVICE_NAME">%1$s</xliff:g>\n\n आप इस सुविधा को सेटिंग &gt; सुलभता पर जाकर बदल सकते हैं."</string>
diff --git a/core/res/res/values-hr/strings.xml b/core/res/res/values-hr/strings.xml
index feb29e22b7ab..e4cefe8149d7 100644
--- a/core/res/res/values-hr/strings.xml
+++ b/core/res/res/values-hr/strings.xml
@@ -1635,6 +1635,8 @@
<string name="kg_failed_attempts_almost_at_login" product="default" msgid="5270861875006378092">"Netočno ste iscrtali obrazac za otključavanje <xliff:g id="NUMBER_0">%1$d</xliff:g> puta. Nakon još ovoliko neuspješnih pokušaja: <xliff:g id="NUMBER_1">%2$d</xliff:g> morat ćete otključati telefon pomoću računa e-pošte.\n\n Pokušajte ponovo za <xliff:g id="NUMBER_2">%3$d</xliff:g> s."</string>
<string name="kg_text_message_separator" product="default" msgid="4503708889934976866">" – "</string>
<string name="kg_reordering_delete_drop_target_text" msgid="2034358143731750914">"Ukloni"</string>
+ <!-- no translation found for allow_while_in_use_permission_in_fgs (4101339676785053656) -->
+ <skip />
<string name="safe_media_volume_warning" product="default" msgid="3751676824423049994">"Želite li pojačati zvuk iznad preporučene razine?\n\nDugotrajno slušanje glasne glazbe može vam oštetiti sluh."</string>
<string name="accessibility_shortcut_warning_dialog_title" msgid="4017995837692622933">"Želite li upotrebljavati prečac za pristupačnost?"</string>
<string name="accessibility_shortcut_toogle_warning" msgid="8306239551412868396">"Kada je taj prečac uključen, pritiskom na obje tipke za glasnoću na 3 sekunde pokrenut će se značajka pristupačnosti.\n\n Trenutačna značajka pristupačnosti:\n <xliff:g id="SERVICE_NAME">%1$s</xliff:g>\n\n Značajku možete promijeniti u Postavkama &gt; Pristupačnost."</string>
diff --git a/core/res/res/values-hu/strings.xml b/core/res/res/values-hu/strings.xml
index 20d38ae44291..08cb1c774b33 100644
--- a/core/res/res/values-hu/strings.xml
+++ b/core/res/res/values-hu/strings.xml
@@ -1613,6 +1613,8 @@
<string name="kg_failed_attempts_almost_at_login" product="default" msgid="5270861875006378092">"<xliff:g id="NUMBER_0">%1$d</xliff:g> alkalommal helytelenül rajzolta le a feloldási mintát. További <xliff:g id="NUMBER_1">%2$d</xliff:g> sikertelen kísérlet után egy e-mail fiók használatával kell feloldania a telefonját.\n\n Kérjük, próbálja újra <xliff:g id="NUMBER_2">%3$d</xliff:g> másodperc múlva."</string>
<string name="kg_text_message_separator" product="default" msgid="4503708889934976866">" – "</string>
<string name="kg_reordering_delete_drop_target_text" msgid="2034358143731750914">"Eltávolítás"</string>
+ <!-- no translation found for allow_while_in_use_permission_in_fgs (4101339676785053656) -->
+ <skip />
<string name="safe_media_volume_warning" product="default" msgid="3751676824423049994">"Az ajánlott szint fölé szeretné emelni a hangerőt?\n\nHa hosszú időn át teszi ki magát nagy hangerőnek, azzal károsíthatja a hallását."</string>
<string name="accessibility_shortcut_warning_dialog_title" msgid="4017995837692622933">"Szeretné használni a Kisegítő lehetőségek billentyűparancsot?"</string>
<string name="accessibility_shortcut_toogle_warning" msgid="8306239551412868396">"Ha be van kapcsolva a billentyűparancs, a két hangerőgomb 3 másodpercig tartó lenyomásával elindíthatja a kisegítő lehetőségek egyik funkcióját.\n\n A kisegítő lehetőségek jelenleg beállított funkciója:\n <xliff:g id="SERVICE_NAME">%1$s</xliff:g>\n\n A funkciót a Beállítások &gt; Kisegítő lehetőségek menüpontban módosíthatja."</string>
diff --git a/core/res/res/values-hy/strings.xml b/core/res/res/values-hy/strings.xml
index d13b9ea0770a..9d33f6b8e4fd 100644
--- a/core/res/res/values-hy/strings.xml
+++ b/core/res/res/values-hy/strings.xml
@@ -1613,6 +1613,8 @@
<string name="kg_failed_attempts_almost_at_login" product="default" msgid="5270861875006378092">"Դուք <xliff:g id="NUMBER_0">%1$d</xliff:g> անգամ սխալ եք հավաքել ձեր ապակողպման նմուշը: <xliff:g id="NUMBER_1">%2$d</xliff:g> անգամից ավել անհաջող փորձերից հետո ձեզ կառաջարկվի ապակողպել ձեր հեռախոսը` օգտագործելով էլփոստի հաշիվ:\n\n Փորձեք կրկին <xliff:g id="NUMBER_2">%3$d</xliff:g> վայրկյանից:"</string>
<string name="kg_text_message_separator" product="default" msgid="4503708889934976866">" — "</string>
<string name="kg_reordering_delete_drop_target_text" msgid="2034358143731750914">"Հեռացնել"</string>
+ <!-- no translation found for allow_while_in_use_permission_in_fgs (4101339676785053656) -->
+ <skip />
<string name="safe_media_volume_warning" product="default" msgid="3751676824423049994">"Ձայնը բարձրացնե՞լ խորհուրդ տրվող մակարդակից ավել:\n\nԵրկարատև բարձրաձայն լսելը կարող է վնասել ձեր լսողությունը:"</string>
<string name="accessibility_shortcut_warning_dialog_title" msgid="4017995837692622933">"Օգտագործե՞լ Մատչելիության դյուրանցումը։"</string>
<string name="accessibility_shortcut_toogle_warning" msgid="8306239551412868396">"Հատուկ գործառույթն օգտագործելու համար սեղմեք և 3 վայրկյան սեղմած պահեք ձայնի ուժգնության երկու կոճակները, երբ գործառույթը միացված է։\n\n Մատչելիության ակտիվ գործառույթը՝\n<xliff:g id="SERVICE_NAME">%1$s</xliff:g>\n\n Գործառույթը կարող եք փոփոխել՝ անցնելով Կարգավորումներ &gt; Հատուկ գործառույթներ։"</string>
diff --git a/core/res/res/values-in/strings.xml b/core/res/res/values-in/strings.xml
index c2a081757300..48136108d7c1 100644
--- a/core/res/res/values-in/strings.xml
+++ b/core/res/res/values-in/strings.xml
@@ -1613,6 +1613,8 @@
<string name="kg_failed_attempts_almost_at_login" product="default" msgid="5270861875006378092">"Anda telah <xliff:g id="NUMBER_0">%1$d</xliff:g> kali salah menggambar pola pembuka kunci. Setelah <xliff:g id="NUMBER_1">%2$d</xliff:g> lagi upaya gagal, Anda akan diminta membuka kunci ponsel menggunakan akun email.\n\nCoba lagi dalam <xliff:g id="NUMBER_2">%3$d</xliff:g> detik."</string>
<string name="kg_text_message_separator" product="default" msgid="4503708889934976866">" — "</string>
<string name="kg_reordering_delete_drop_target_text" msgid="2034358143731750914">"Hapus"</string>
+ <!-- no translation found for allow_while_in_use_permission_in_fgs (4101339676785053656) -->
+ <skip />
<string name="safe_media_volume_warning" product="default" msgid="3751676824423049994">"Mengeraskan volume di atas tingkat yang disarankan?\n\nMendengarkan dengan volume keras dalam waktu yang lama dapat merusak pendengaran Anda."</string>
<string name="accessibility_shortcut_warning_dialog_title" msgid="4017995837692622933">"Gunakan Pintasan Aksesibilitas?"</string>
<string name="accessibility_shortcut_toogle_warning" msgid="8306239551412868396">"Saat pintasan aktif, menekan kedua tombol volume selama 3 detik akan memulai fitur aksesibilitas.\n\n Fitur aksesibilitas saat ini:\n <xliff:g id="SERVICE_NAME">%1$s</xliff:g>\n\n Anda dapat mengubah fitur di Setelan &gt; Aksesibilitas."</string>
diff --git a/core/res/res/values-is/strings.xml b/core/res/res/values-is/strings.xml
index c596c12205e0..bf3fe87f7931 100644
--- a/core/res/res/values-is/strings.xml
+++ b/core/res/res/values-is/strings.xml
@@ -1613,6 +1613,8 @@
<string name="kg_failed_attempts_almost_at_login" product="default" msgid="5270861875006378092">"Þú hefur teiknað rangt opnunarmynstur <xliff:g id="NUMBER_0">%1$d</xliff:g> sinnum. Eftir <xliff:g id="NUMBER_1">%2$d</xliff:g> árangurslausar tilraunir í viðbót verður þú beðin(n) um að opna símann með tölvupóstreikningi.\n\n Reyndu aftur eftir <xliff:g id="NUMBER_2">%3$d</xliff:g> sekúndur."</string>
<string name="kg_text_message_separator" product="default" msgid="4503708889934976866">" — "</string>
<string name="kg_reordering_delete_drop_target_text" msgid="2034358143731750914">"Fjarlægja"</string>
+ <!-- no translation found for allow_while_in_use_permission_in_fgs (4101339676785053656) -->
+ <skip />
<string name="safe_media_volume_warning" product="default" msgid="3751676824423049994">"Hækka hljóðstyrk umfram ráðlagðan styrk?\n\nEf hlustað er á háum hljóðstyrk í langan tíma kann það að skaða heyrnina."</string>
<string name="accessibility_shortcut_warning_dialog_title" msgid="4017995837692622933">"Viltu nota aðgengisflýtileið?"</string>
<string name="accessibility_shortcut_toogle_warning" msgid="8306239551412868396">"Þegar flýtileiðin er virk er kveikt á aðgengiseiginleikanum með því að halda báðum hljóðstyrkshnöppunum inni í þrjár sekúndur.\n\n Virkur aðgengiseiginleiki:\n <xliff:g id="SERVICE_NAME">%1$s</xliff:g>\n\n Hægt er að skipta um eiginleika í Stillingar &gt; Aðgengi."</string>
diff --git a/core/res/res/values-it/strings.xml b/core/res/res/values-it/strings.xml
index 1144995c069e..72ca138f9ed3 100644
--- a/core/res/res/values-it/strings.xml
+++ b/core/res/res/values-it/strings.xml
@@ -1613,6 +1613,8 @@
<string name="kg_failed_attempts_almost_at_login" product="default" msgid="5270861875006378092">"<xliff:g id="NUMBER_0">%1$d</xliff:g> tentativi errati di inserimento della sequenza di sblocco. Dopo altri <xliff:g id="NUMBER_1">%2$d</xliff:g> tentativi falliti, ti verrà chiesto di sbloccare il telefono con un account email.\n\n Riprova tra <xliff:g id="NUMBER_2">%3$d</xliff:g> secondi."</string>
<string name="kg_text_message_separator" product="default" msgid="4503708889934976866">" – "</string>
<string name="kg_reordering_delete_drop_target_text" msgid="2034358143731750914">"Rimuovi"</string>
+ <!-- no translation found for allow_while_in_use_permission_in_fgs (4101339676785053656) -->
+ <skip />
<string name="safe_media_volume_warning" product="default" msgid="3751676824423049994">"Vuoi aumentare il volume oltre il livello consigliato?\n\nL\'ascolto ad alto volume per lunghi periodi di tempo potrebbe danneggiare l\'udito."</string>
<string name="accessibility_shortcut_warning_dialog_title" msgid="4017995837692622933">"Usare la scorciatoia Accessibilità?"</string>
<string name="accessibility_shortcut_toogle_warning" msgid="8306239551412868396">"Quando la scorciatoia è attiva, puoi premere entrambi i pulsanti del volume per tre secondi per avviare una funzione di accessibilità.\n\n Funzione di accessibilità corrente:\n <xliff:g id="SERVICE_NAME">%1$s</xliff:g>\n\n Puoi cambiare la funzione in Impostazioni &gt; Accessibilità."</string>
diff --git a/core/res/res/values-iw/strings.xml b/core/res/res/values-iw/strings.xml
index bce62ab631da..9c8d0a3a64ec 100644
--- a/core/res/res/values-iw/strings.xml
+++ b/core/res/res/values-iw/strings.xml
@@ -1657,6 +1657,8 @@
<string name="kg_failed_attempts_almost_at_login" product="default" msgid="5270861875006378092">"שרטטת את קו ביטול הנעילה באופן שגוי <xliff:g id="NUMBER_0">%1$d</xliff:g> פעמים. לאחר <xliff:g id="NUMBER_1">%2$d</xliff:g> ניסיונות כושלים נוספים, תתבקש לבטל את נעילת הטלפון באמצעות חשבון אימייל‏.\n\nנסה שוב בעוד <xliff:g id="NUMBER_2">%3$d</xliff:g> שניות."</string>
<string name="kg_text_message_separator" product="default" msgid="4503708889934976866">" — "</string>
<string name="kg_reordering_delete_drop_target_text" msgid="2034358143731750914">"הסר"</string>
+ <!-- no translation found for allow_while_in_use_permission_in_fgs (4101339676785053656) -->
+ <skip />
<string name="safe_media_volume_warning" product="default" msgid="3751676824423049994">"האם להעלות את עוצמת הקול מעל לרמה המומלצת?\n\nהאזנה בעוצמת קול גבוהה למשכי זמן ממושכים עלולה לפגוע בשמיעה."</string>
<string name="accessibility_shortcut_warning_dialog_title" msgid="4017995837692622933">"להשתמש בקיצור הדרך לתכונת הנגישות?"</string>
<string name="accessibility_shortcut_toogle_warning" msgid="8306239551412868396">"כשקיצור הדרך מופעל, לחיצה על שני לחצני עוצמת השמע למשך שלוש שניות מפעילה את תכונת הנגישות.\n\n תכונת הנגישות המוגדרת כרגע:\n <xliff:g id="SERVICE_NAME">%1$s</xliff:g>\n\n אפשר לשנות את התכונה בקטע \'הגדרות ונגישות\'."</string>
@@ -2053,8 +2055,7 @@
<string name="mime_type_spreadsheet_ext" msgid="8720173181137254414">"גיליון אלקטרוני <xliff:g id="EXTENSION">%1$s</xliff:g>"</string>
<string name="mime_type_presentation" msgid="1145384236788242075">"מצגת"</string>
<string name="mime_type_presentation_ext" msgid="8761049335564371468">"מצגת <xliff:g id="EXTENSION">%1$s</xliff:g>"</string>
- <!-- no translation found for bluetooth_airplane_mode_toast (2066399056595768554) -->
- <skip />
+ <string name="bluetooth_airplane_mode_toast" msgid="2066399056595768554">"‏Bluetooth יישאר מופעל במהלך מצב טיסה"</string>
<string name="car_loading_profile" msgid="8219978381196748070">"בטעינה"</string>
<plurals name="file_count" formatted="false" msgid="7063513834724389247">
<item quantity="two"><xliff:g id="FILE_NAME_2">%s</xliff:g> + <xliff:g id="COUNT_3">%d</xliff:g> קבצים</item>
@@ -2077,8 +2078,7 @@
<!-- no translation found for accessibility_system_action_accessibility_menu_label (8436484650391125184) -->
<skip />
<string name="accessibility_freeform_caption" msgid="8377519323496290122">"סרגל כיתוב של <xliff:g id="APP_NAME">%1$s</xliff:g>."</string>
- <!-- no translation found for as_app_forced_to_restricted_bucket (8233871289353898964) -->
- <skip />
+ <string name="as_app_forced_to_restricted_bucket" msgid="8233871289353898964">"<xliff:g id="PACKAGE_NAME">%1$s</xliff:g> התווספה לקטגוריה \'מוגבל\'"</string>
<!-- no translation found for resolver_personal_tab (2051260504014442073) -->
<skip />
<!-- no translation found for resolver_work_tab (2690019516263167035) -->
diff --git a/core/res/res/values-ja/strings.xml b/core/res/res/values-ja/strings.xml
index 1ef4a1391860..529d0119f0be 100644
--- a/core/res/res/values-ja/strings.xml
+++ b/core/res/res/values-ja/strings.xml
@@ -1613,6 +1613,8 @@
<string name="kg_failed_attempts_almost_at_login" product="default" msgid="5270861875006378092">"ロック解除パターンの入力を<xliff:g id="NUMBER_0">%1$d</xliff:g>回間違えました。あと<xliff:g id="NUMBER_1">%2$d</xliff:g>回間違えると、モバイルデバイスのロック解除にメールアカウントが必要になります。\n\n<xliff:g id="NUMBER_2">%3$d</xliff:g>秒後にもう一度お試しください。"</string>
<string name="kg_text_message_separator" product="default" msgid="4503708889934976866">" - "</string>
<string name="kg_reordering_delete_drop_target_text" msgid="2034358143731750914">"削除"</string>
+ <!-- no translation found for allow_while_in_use_permission_in_fgs (4101339676785053656) -->
+ <skip />
<string name="safe_media_volume_warning" product="default" msgid="3751676824423049994">"推奨レベルを超えるまで音量を上げますか?\n\n大音量で長時間聞き続けると、聴力を損なう恐れがあります。"</string>
<string name="accessibility_shortcut_warning_dialog_title" msgid="4017995837692622933">"ユーザー補助機能のショートカットの使用"</string>
<string name="accessibility_shortcut_toogle_warning" msgid="8306239551412868396">"ショートカットが ON の場合、両方の音量ボタンを 3 秒間押し続けるとユーザー補助機能が起動します。\n\n現在のユーザー補助機能:\n <xliff:g id="SERVICE_NAME">%1$s</xliff:g>\n\nユーザー補助機能は [設定] &gt; [ユーザー補助] で変更できます。"</string>
diff --git a/core/res/res/values-ka/strings.xml b/core/res/res/values-ka/strings.xml
index 41b23887e827..3f97338ade76 100644
--- a/core/res/res/values-ka/strings.xml
+++ b/core/res/res/values-ka/strings.xml
@@ -1613,6 +1613,8 @@
<string name="kg_failed_attempts_almost_at_login" product="default" msgid="5270861875006378092">"თქვენ არასწორად დახატეთ თქვენი განბლოკვის ნიმუში <xliff:g id="NUMBER_0">%1$d</xliff:g>-ჯერ. კიდევ <xliff:g id="NUMBER_1">%2$d</xliff:g> წარუმატებელი ცდის შემდეგ, დაგჭირდებათ თქვენი ტელეფონის განბლოკვა ელფოსტის ანგარიშის გამოყენებით.\n\n ხელახლა სცადეთ <xliff:g id="NUMBER_2">%3$d</xliff:g> წამში."</string>
<string name="kg_text_message_separator" product="default" msgid="4503708889934976866">" — "</string>
<string name="kg_reordering_delete_drop_target_text" msgid="2034358143731750914">"ამოშლა"</string>
+ <!-- no translation found for allow_while_in_use_permission_in_fgs (4101339676785053656) -->
+ <skip />
<string name="safe_media_volume_warning" product="default" msgid="3751676824423049994">"გსურთ ხმის რეკომენდებულ დონეზე მაღლა აწევა?\n\nხანგრძლივად ხმამაღლა მოსმენით შესაძლოა სმენადობა დაიზიანოთ."</string>
<string name="accessibility_shortcut_warning_dialog_title" msgid="4017995837692622933">"გსურთ მარტივი წვდომის მალსახმობის გამოყენება?"</string>
<string name="accessibility_shortcut_toogle_warning" msgid="8306239551412868396">"მალსახმობის ჩართვის შემთხვევაში, ხმის ორივე ღილაკზე 3 წამის განმავლობაში დაჭერით მარტივი წვდომის ფუნქცია ჩაირთვება.\n\n მარტივი წვდომის ამჟამინდელი ფუნქციაა:\n <xliff:g id="SERVICE_NAME">%1$s</xliff:g>\n\n ამ ფუნქციის შეცვლა შეგიძლიათ აქ: პარამეტრები &gt; მარტივი წვდომა."</string>
diff --git a/core/res/res/values-kk/strings.xml b/core/res/res/values-kk/strings.xml
index 3ea4d351edd9..bccd3f4b6a77 100644
--- a/core/res/res/values-kk/strings.xml
+++ b/core/res/res/values-kk/strings.xml
@@ -1613,6 +1613,8 @@
<string name="kg_failed_attempts_almost_at_login" product="default" msgid="5270861875006378092">"Бекітпені ашу кескінін <xliff:g id="NUMBER_0">%1$d</xliff:g> рет қате сыздыңыз. <xliff:g id="NUMBER_1">%2$d</xliff:g> сәтсіз әрекеттен кейін телефоныңызды есептік жазба арқылы ашу өтінішін аласыз. \n\n <xliff:g id="NUMBER_2">%3$d</xliff:g> секундтан кейін қайта әрекеттеніңіз."</string>
<string name="kg_text_message_separator" product="default" msgid="4503708889934976866">" — "</string>
<string name="kg_reordering_delete_drop_target_text" msgid="2034358143731750914">"Алып тастау"</string>
+ <!-- no translation found for allow_while_in_use_permission_in_fgs (4101339676785053656) -->
+ <skip />
<string name="safe_media_volume_warning" product="default" msgid="3751676824423049994">"Дыбыс деңгейін ұсынылған деңгейден көтеру керек пе?\n\nЖоғары дыбыс деңгейінде ұзақ кезеңдер бойы тыңдау есту қабілетіңізге зиян тигізуі мүмкін."</string>
<string name="accessibility_shortcut_warning_dialog_title" msgid="4017995837692622933">"Арнайы мүмкіндік төте жолын пайдалану керек пе?"</string>
<string name="accessibility_shortcut_toogle_warning" msgid="8306239551412868396">"Бұл төте жол қосулы кезде дыбыс деңгейі түймелерінің екеуін де 3 секунд бойы басқанда арнайы мүмкіндік іске қосылады.\n\n Ағымдағы арнайы мүмкіндік:\n <xliff:g id="SERVICE_NAME">%1$s</xliff:g>\n\n Бұл мүмкіндікті \"Параметрлер\" &gt; \"Арнайы мүмкіндіктер\" тармағында өзгертуге болады."</string>
diff --git a/core/res/res/values-km/strings.xml b/core/res/res/values-km/strings.xml
index 4f7c0febb6ce..ee451e936020 100644
--- a/core/res/res/values-km/strings.xml
+++ b/core/res/res/values-km/strings.xml
@@ -1615,6 +1615,8 @@
<string name="kg_failed_attempts_almost_at_login" product="default" msgid="5270861875006378092">"អ្នក​បាន​គូរ​លំនាំ​ដោះ​​សោ​របស់​អ្នក​មិន​ត្រឹមត្រូវ​ចំនួន <xliff:g id="NUMBER_0">%1$d</xliff:g> ដង។ បន្ទាប់​ពី​ការ​ព្យាយាម​មិន​ជោគជ័យ​​ច្រើនជាង <xliff:g id="NUMBER_1">%2$d</xliff:g> ដង អ្នក​នឹង​ត្រូវ​បាន​​ស្នើ​ឲ្យ​ដោះ​សោ​ទូរស័ព្ទ​របស់​អ្នក​ដោយ​ប្រើ​គណនី​អ៊ីមែល។\n\n ព្យាយាម​ម្ដង​ទៀត​ក្នុង​រយៈ​ពេល <xliff:g id="NUMBER_2">%3$d</xliff:g> វិនាទី។"</string>
<string name="kg_text_message_separator" product="default" msgid="4503708889934976866">" — "</string>
<string name="kg_reordering_delete_drop_target_text" msgid="2034358143731750914">"លុប​ចេញ"</string>
+ <!-- no translation found for allow_while_in_use_permission_in_fgs (4101339676785053656) -->
+ <skip />
<string name="safe_media_volume_warning" product="default" msgid="3751676824423049994">"បង្កើន​កម្រិត​សំឡេង​លើស​ពី​កម្រិត​បាន​ផ្ដល់​យោបល់?\n\nការ​ស្ដាប់​នៅ​កម្រិត​សំឡេង​ខ្លាំង​យូរ​អាច​ធ្វើឲ្យ​ខូច​ត្រចៀក។"</string>
<string name="accessibility_shortcut_warning_dialog_title" msgid="4017995837692622933">"ប្រើប្រាស់​ផ្លូវកាត់​ភាព​ងាយស្រួល?"</string>
<string name="accessibility_shortcut_toogle_warning" msgid="8306239551412868396">"នៅពេល​ផ្លូវកាត់​នេះបើក ការ​ចុច​ប៊ូតុង​កម្រិត​សំឡេង​ទាំង​ពីរ​ឲ្យ​ជាប់​រយៈពេល​ 3 វិនាទីនឹង​ចាប់ផ្តើម​មុខងារ​ភាពងាយស្រួល។\n\n មុខងារ​ភាពងាយស្រួល​បច្ចុប្បន្ន៖\n <xliff:g id="SERVICE_NAME">%1$s</xliff:g>\n\n អ្នក​អាច​ផ្លាស់​ប្តូរ​មុខងារ​នេះ​បាន​នៅក្នុង​ការ កំណត់ &gt; ភាព​ងាយស្រួល។"</string>
diff --git a/core/res/res/values-kn/strings.xml b/core/res/res/values-kn/strings.xml
index 9efa85774bab..75282842175c 100644
--- a/core/res/res/values-kn/strings.xml
+++ b/core/res/res/values-kn/strings.xml
@@ -1613,6 +1613,8 @@
<string name="kg_failed_attempts_almost_at_login" product="default" msgid="5270861875006378092">"ನಿಮ್ಮ ಅನ್‍‍ಲಾಕ್ ಪ್ಯಾಟರ್ನ್ ಅನ್ನು ನೀವು <xliff:g id="NUMBER_0">%1$d</xliff:g> ಬಾರಿ ತಪ್ಪಾಗಿ ಡ್ರಾ ಮಾಡಿರುವಿರಿ. <xliff:g id="NUMBER_1">%2$d</xliff:g> ಹೆಚ್ಚಿನ ವಿಫಲ ಪ್ರಯತ್ನಗಳ ಬಳಿಕ, ನಿಮ್ಮ ಇಮೇಲ್ ಖಾತೆಯನ್ನು ಬಳಸಿಕೊಂಡು ನಿಮ್ಮ ಫೋನ್ ಅನ್‌ಲಾಕ್ ಮಾಡುವಂತೆ ನಿಮ್ಮಲ್ಲಿ ಕೇಳಿಕೊಳ್ಳಲಾಗುತ್ತದೆ.\n\n <xliff:g id="NUMBER_2">%3$d</xliff:g> ಸೆಕೆಂಡುಗಳಲ್ಲಿ ಮತ್ತೆ ಪ್ರಯತ್ನಿಸಿ."</string>
<string name="kg_text_message_separator" product="default" msgid="4503708889934976866">" — "</string>
<string name="kg_reordering_delete_drop_target_text" msgid="2034358143731750914">"ತೆಗೆದುಹಾಕು"</string>
+ <!-- no translation found for allow_while_in_use_permission_in_fgs (4101339676785053656) -->
+ <skip />
<string name="safe_media_volume_warning" product="default" msgid="3751676824423049994">"ವಾಲ್ಯೂಮ್‌ ಅನ್ನು ಶಿಫಾರಸು ಮಾಡಲಾದ ಮಟ್ಟಕ್ಕಿಂತಲೂ ಹೆಚ್ಚು ಮಾಡುವುದೇ?\n\nದೀರ್ಘ ಅವಧಿಯವರೆಗೆ ಹೆಚ್ಚಿನ ವಾಲ್ಯೂಮ್‌ನಲ್ಲಿ ಆಲಿಸುವುದರಿಂದ ನಿಮ್ಮ ಆಲಿಸುವಿಕೆ ಸಾಮರ್ಥ್ಯಕ್ಕೆ ಹಾನಿಯುಂಟು ಮಾಡಬಹುದು."</string>
<string name="accessibility_shortcut_warning_dialog_title" msgid="4017995837692622933">"ಪ್ರವೇಶಿಸುವಿಕೆ ಶಾರ್ಟ್‌ಕಟ್ ಬಳಸುವುದೇ?"</string>
<string name="accessibility_shortcut_toogle_warning" msgid="8306239551412868396">"ಶಾರ್ಟ್‌ಕಟ್ ಆನ್ ಆಗಿರುವಾಗ ಪ್ರವೇಶಿಸುವಿಕೆ ವೈಶಿಷ್ಟ್ಯ ಆನ್ ಮಾಡಲು, ಎರಡೂ ವಾಲ್ಯೂಮ್ ಬಟನ್‌ಗಳನ್ನು ನೀವು 3 ಸೆಕೆಂಡುಗಳ ಕಾಲ ಒತ್ತಬೇಕು.\n\nಪ್ರಸ್ತುತ ಪ್ರವೇಶಿಸುವಿಕೆ ವೈಶಿಷ್ಟ್ಯ: \n <xliff:g id="SERVICE_NAME">%1$s</xliff:g>\n\n ಸೆಟ್ಟಿಂಗ್‌ಗಳು ಮತ್ತು ಪ್ರವೇಶಿಸುವಿಕೆಯಲ್ಲಿ ನೀವು ವೈಶಿಷ್ಟ್ಯವನ್ನು ಬದಲಾಯಿಸಬಹುದು."</string>
@@ -1987,8 +1989,7 @@
<string name="mime_type_spreadsheet_ext" msgid="8720173181137254414">"<xliff:g id="EXTENSION">%1$s</xliff:g> ಸ್ಪ್ರೆಡ್‌ಶೀಟ್"</string>
<string name="mime_type_presentation" msgid="1145384236788242075">"ಪ್ರಸ್ತುತಿ"</string>
<string name="mime_type_presentation_ext" msgid="8761049335564371468">"<xliff:g id="EXTENSION">%1$s</xliff:g> ಪ್ರಸ್ತುತಿ"</string>
- <!-- no translation found for bluetooth_airplane_mode_toast (2066399056595768554) -->
- <skip />
+ <string name="bluetooth_airplane_mode_toast" msgid="2066399056595768554">"ಏರ್‌ಪ್ಲೇನ್ ಮೋಡ್‌ನಲ್ಲಿರುವಾಗಲೂ ಬ್ಲೂಟೂತ್ ಆನ್ ಆಗಿರುತ್ತದೆ"</string>
<string name="car_loading_profile" msgid="8219978381196748070">"ಲೋಡ್ ಆಗುತ್ತಿದೆ"</string>
<plurals name="file_count" formatted="false" msgid="7063513834724389247">
<item quantity="one"><xliff:g id="FILE_NAME_2">%s</xliff:g> + <xliff:g id="COUNT_3">%d</xliff:g> ಫೈಲ್‌ಗಳು</item>
@@ -2009,8 +2010,7 @@
<!-- no translation found for accessibility_system_action_accessibility_menu_label (8436484650391125184) -->
<skip />
<string name="accessibility_freeform_caption" msgid="8377519323496290122">"<xliff:g id="APP_NAME">%1$s</xliff:g> ಆ್ಯಪ್‌ನ ಶೀರ್ಷಿಕೆಯ ಪಟ್ಟಿ."</string>
- <!-- no translation found for as_app_forced_to_restricted_bucket (8233871289353898964) -->
- <skip />
+ <string name="as_app_forced_to_restricted_bucket" msgid="8233871289353898964">"<xliff:g id="PACKAGE_NAME">%1$s</xliff:g> ಅನ್ನು ನಿರ್ಬಂಧಿತ ಬಕೆಟ್‌ಗೆ ಹಾಕಲಾಗಿದೆ"</string>
<!-- no translation found for resolver_personal_tab (2051260504014442073) -->
<skip />
<!-- no translation found for resolver_work_tab (2690019516263167035) -->
diff --git a/core/res/res/values-ko/strings.xml b/core/res/res/values-ko/strings.xml
index 90fa0fa65f1d..d5b450757bf9 100644
--- a/core/res/res/values-ko/strings.xml
+++ b/core/res/res/values-ko/strings.xml
@@ -1613,6 +1613,8 @@
<string name="kg_failed_attempts_almost_at_login" product="default" msgid="5270861875006378092">"잠금해제 패턴을 <xliff:g id="NUMBER_0">%1$d</xliff:g>회 잘못 그렸습니다. <xliff:g id="NUMBER_1">%2$d</xliff:g>회 더 실패하면 이메일 계정을 사용하여 휴대전화를 잠금해제해야 합니다.\n\n <xliff:g id="NUMBER_2">%3$d</xliff:g>초 후에 다시 시도해 주세요."</string>
<string name="kg_text_message_separator" product="default" msgid="4503708889934976866">" — "</string>
<string name="kg_reordering_delete_drop_target_text" msgid="2034358143731750914">"삭제"</string>
+ <!-- no translation found for allow_while_in_use_permission_in_fgs (4101339676785053656) -->
+ <skip />
<string name="safe_media_volume_warning" product="default" msgid="3751676824423049994">"권장 수준 이상으로 볼륨을 높이시겠습니까?\n\n높은 볼륨으로 장시간 청취하면 청력에 손상이 올 수 있습니다."</string>
<string name="accessibility_shortcut_warning_dialog_title" msgid="4017995837692622933">"접근성 단축키를 사용하시겠습니까?"</string>
<string name="accessibility_shortcut_toogle_warning" msgid="8306239551412868396">"단축키가 사용 설정된 경우 두 개의 볼륨 버튼을 3초간 누르면 접근성 기능이 시작됩니다.\n\n 현재 접근성 기능:\n <xliff:g id="SERVICE_NAME">%1$s</xliff:g>\n\n \'설정 &gt; 접근성\'에서 기능을 변경할 수 있습니다."</string>
diff --git a/core/res/res/values-ky/strings.xml b/core/res/res/values-ky/strings.xml
index 07a6e3f75222..533e0b816142 100644
--- a/core/res/res/values-ky/strings.xml
+++ b/core/res/res/values-ky/strings.xml
@@ -540,7 +540,7 @@
<string name="fingerprint_error_timeout" msgid="2946635815726054226">"Манжа изин күтүү мөөнөтү бүттү. Кайра аракет кылыңыз."</string>
<string name="fingerprint_error_canceled" msgid="540026881380070750">"Манжа изи иш-аракети жокко чыгарылды."</string>
<string name="fingerprint_error_user_canceled" msgid="7685676229281231614">"Манжа изи операциясын колдонуучу жокко чыгарды."</string>
- <string name="fingerprint_error_lockout" msgid="7853461265604738671">"Аракеттер өтө көп болду. Кийинчерээк кайра аракет кылыңыз."</string>
+ <string name="fingerprint_error_lockout" msgid="7853461265604738671">"Аракеттер өтө көп болду. Бир аздан кийин кайталап көрүңүз."</string>
<string name="fingerprint_error_lockout_permanent" msgid="3895478283943513746">"Өтө көп жолу аракет жасадыңыз. Манжа изинин сенсору өчүрүлдү."</string>
<string name="fingerprint_error_unable_to_process" msgid="1148553603490048742">"Кайра бир аракеттениңиз."</string>
<string name="fingerprint_error_no_fingerprints" msgid="8671811719699072411">"Бир да манжа изи катталган эмес."</string>
@@ -583,7 +583,7 @@
<string name="face_error_no_space" msgid="5649264057026021723">"Жаңы жүздү сактоо мүмкүн эмес. Адегенде эскисин өчүрүңүз."</string>
<string name="face_error_canceled" msgid="2164434737103802131">"Жүздүн аныктыгын текшерүү жокко чыгарылды."</string>
<string name="face_error_user_canceled" msgid="8553045452825849843">"Жүзүнөн таануу функциясын колдонуучу өчүрүп салды."</string>
- <string name="face_error_lockout" msgid="7864408714994529437">"Өтө көп жолу аракет жасадыңыз. Кийинчерээк кайра аракет кылыңыз."</string>
+ <string name="face_error_lockout" msgid="7864408714994529437">"Өтө көп жолу аракет жасадыңыз. Бир аздан кийин кайталап көрүңүз."</string>
<string name="face_error_lockout_permanent" msgid="8277853602168960343">"Өтө көп жолу аракет кылдыңыз. Жүзүнөн таануу функциясы өчүрүлдү."</string>
<string name="face_error_unable_to_process" msgid="5723292697366130070">"Жүз ырасталбай жатат. Кайра аракет кылыңыз."</string>
<string name="face_error_not_enrolled" msgid="7369928733504691611">"Жүзүнөн таануу функциясын жөндөй элексиз."</string>
@@ -1613,6 +1613,8 @@
<string name="kg_failed_attempts_almost_at_login" product="default" msgid="5270861875006378092">"Графикалык ачкычты <xliff:g id="NUMBER_0">%1$d</xliff:g> жолу туура эмес көрсөттүңүз. <xliff:g id="NUMBER_1">%2$d</xliff:g> жолу туура эмес көрсөтүлгөндөн кийин, телефондун кулпусун ачуу үчүн Google аккаунтуңузга кирүүгө туура келет.\n\n<xliff:g id="NUMBER_2">%3$d</xliff:g> секундадан кийин кайталап көрсөңүз болот."</string>
<string name="kg_text_message_separator" product="default" msgid="4503708889934976866">" — "</string>
<string name="kg_reordering_delete_drop_target_text" msgid="2034358143731750914">"Алып салуу"</string>
+ <!-- no translation found for allow_while_in_use_permission_in_fgs (4101339676785053656) -->
+ <skip />
<string name="safe_media_volume_warning" product="default" msgid="3751676824423049994">"Сунушталган деңгээлден да катуулатып уккуңуз келеби?\n\nМузыканы узакка чейин катуу уксаңыз, угууңуз начарлап кетиши мүмкүн."</string>
<string name="accessibility_shortcut_warning_dialog_title" msgid="4017995837692622933">"Ыкчам иштетесизби?"</string>
<string name="accessibility_shortcut_toogle_warning" msgid="8306239551412868396">"Атайын мүмкүнчүлүктөр функциясын пайдалануу үчүн, ал күйгүзүлгөндө, үндү катуулатып/акырындаткан эки баскычты тең үч секунддай кое бербей басып туруңуз.\n\n Учурдагы атайын мүмкүнчүлүктөрдүн жөндөөлөрү:\n <xliff:g id="SERVICE_NAME">%1$s</xliff:g>\n\nЖөндөөлөр &gt; Атайын мүмкүнчүлүктөр бөлүмүнөн өзгөртө аласыз."</string>
diff --git a/core/res/res/values-lo/strings.xml b/core/res/res/values-lo/strings.xml
index 05f9698cd48d..7a3e85e7f5b2 100644
--- a/core/res/res/values-lo/strings.xml
+++ b/core/res/res/values-lo/strings.xml
@@ -1613,6 +1613,8 @@
<string name="kg_failed_attempts_almost_at_login" product="default" msgid="5270861875006378092">"ທ່ານແຕ້ມຮູບແບບປົດລັອກຂອງທ່ານຜິດ <xliff:g id="NUMBER_0">%1$d</xliff:g> ເທື່ອແລ້ວ. ຫຼັງຈາກຄວາມພະຍາຍາມອີກ <xliff:g id="NUMBER_1">%2$d</xliff:g> ເທື່ອ ທ່ານຈະຖືກຖາມໃຫ້ປົດລັອກໂທລະສັບຂອງທ່ານດ້ວຍບັນຊີອີເມວ.\n\n ລອງໃໝ່ອີກຄັ້ງໃນ <xliff:g id="NUMBER_2">%3$d</xliff:g> ວິນາທີ."</string>
<string name="kg_text_message_separator" product="default" msgid="4503708889934976866">" — "</string>
<string name="kg_reordering_delete_drop_target_text" msgid="2034358143731750914">"ລຶບອອກ"</string>
+ <!-- no translation found for allow_while_in_use_permission_in_fgs (4101339676785053656) -->
+ <skip />
<string name="safe_media_volume_warning" product="default" msgid="3751676824423049994">"ເພີ່ມ​ລະ​ດັບ​ສຽງ​ໃຫ້​ເກີນກວ່າ​ລະ​ດັບ​ທີ່​ແນະ​ນຳ​ບໍ?\n\n​ການ​ຮັບ​ຟັງ​ສຽງ​ໃນ​ລະ​ດັບ​ທີ່​ສູງ​ເປັນ​ໄລ​ຍະ​ເວ​ລາ​ດົນ​​ອາດ​ເຮັດ​ໃຫ້​ການ​ຟັງ​ຂອງ​ທ່ານ​ມີ​ບັນ​ຫາ​ໄດ້."</string>
<string name="accessibility_shortcut_warning_dialog_title" msgid="4017995837692622933">"ໃຊ້ປຸ່ມລັດການຊ່ວຍເຂົ້າເຖິງບໍ?"</string>
<string name="accessibility_shortcut_toogle_warning" msgid="8306239551412868396">"ເມື່ອເປີດໃຊ້ປຸ່ມລັດແລ້ວ, ໃຫ້ກົດປຸ່ມສຽງທັງສອງຄ້າງໄວ້ 3 ວິນາທີເພື່ອເລີ່ມຄຸນສົມບັດການຊ່ວຍເຂົ້າເຖິງ.\n\n ຄຸນສົມບັດການຊ່ວຍເຂົ້າເຖິງປັດຈຸບັນ:\n <xliff:g id="SERVICE_NAME">%1$s</xliff:g>\n\n ທ່ານສາມາດປ່ຽນຄຸນສົມບັດໄດ້ໃນການຕັ້ງຄ່າ &gt; ການຊ່ວຍເຂົ້າເຖິງ."</string>
diff --git a/core/res/res/values-lt/strings.xml b/core/res/res/values-lt/strings.xml
index 4f48685f2a3b..265c97e4b228 100644
--- a/core/res/res/values-lt/strings.xml
+++ b/core/res/res/values-lt/strings.xml
@@ -1657,6 +1657,8 @@
<string name="kg_failed_attempts_almost_at_login" product="default" msgid="5270861875006378092">"Netinkamai nupiešėte atrakinimo piešinį <xliff:g id="NUMBER_0">%1$d</xliff:g> k. Po dar <xliff:g id="NUMBER_1">%2$d</xliff:g> nesėkm. band. būsite paprašyti atrakinti telefoną naudodami „Google“ prisijungimo duomenis.\n\n Bandykite dar kartą po <xliff:g id="NUMBER_2">%3$d</xliff:g> sek."</string>
<string name="kg_text_message_separator" product="default" msgid="4503708889934976866">" – "</string>
<string name="kg_reordering_delete_drop_target_text" msgid="2034358143731750914">"Pašalinti"</string>
+ <!-- no translation found for allow_while_in_use_permission_in_fgs (4101339676785053656) -->
+ <skip />
<string name="safe_media_volume_warning" product="default" msgid="3751676824423049994">"Padidinti garsą daugiau nei rekomenduojamas lygis?\n\nIlgai klausydami dideliu garsu galite pažeisti klausą."</string>
<string name="accessibility_shortcut_warning_dialog_title" msgid="4017995837692622933">"Naudoti spartųjį pritaikymo neįgaliesiems klavišą?"</string>
<string name="accessibility_shortcut_toogle_warning" msgid="8306239551412868396">"Kai spartusis klavišas įjungtas, spaudžiant abu garsumo mygtukus 3 sekundes bus paleista pritaikymo neįgaliesiems funkcija.\n\n Dabartinė pritaikymo neįgaliesiems funkcija:\n <xliff:g id="SERVICE_NAME">%1$s</xliff:g>„\n“\n Funkciją galite pakeisti skiltyje „Nustatymai“ &gt; „Pritaikymas neįgaliesiems“."</string>
diff --git a/core/res/res/values-lv/strings.xml b/core/res/res/values-lv/strings.xml
index ae9abc6331a3..a535f6dd9feb 100644
--- a/core/res/res/values-lv/strings.xml
+++ b/core/res/res/values-lv/strings.xml
@@ -1635,6 +1635,8 @@
<string name="kg_failed_attempts_almost_at_login" product="default" msgid="5270861875006378092">"Jūs nepareizi norādījāt atbloķēšanas kombināciju <xliff:g id="NUMBER_0">%1$d</xliff:g> reizes. Pēc vēl <xliff:g id="NUMBER_1">%2$d</xliff:g> neveiksmīgiem mēģinājumiem tālrunis būs jāatbloķē, izmantojot e-pasta kontu.\n\nMēģiniet vēlreiz pēc <xliff:g id="NUMBER_2">%3$d</xliff:g> sekundēm."</string>
<string name="kg_text_message_separator" product="default" msgid="4503708889934976866">"  — "</string>
<string name="kg_reordering_delete_drop_target_text" msgid="2034358143731750914">"Noņemt"</string>
+ <!-- no translation found for allow_while_in_use_permission_in_fgs (4101339676785053656) -->
+ <skip />
<string name="safe_media_volume_warning" product="default" msgid="3751676824423049994">"Vai palielināt skaļumu virs ieteicamā līmeņa?\n\nIlgstoši klausoties skaņu lielā skaļumā, var tikt bojāta dzirde."</string>
<string name="accessibility_shortcut_warning_dialog_title" msgid="4017995837692622933">"Vai izmantot pieejamības saīsni?"</string>
<string name="accessibility_shortcut_toogle_warning" msgid="8306239551412868396">"Ja saīsne ir iespējota, vienlaikus nospiežot abas skaļuma regulēšanas pogas un trīs sekundes turot tās, tiks palaista pieejamības funkcija.\n\n Pašreiz iestatītā pieejamības funkcija:\n <xliff:g id="SERVICE_NAME">%1$s</xliff:g>\n\n Šo funkciju var mainīt sadaļā Iestatījumi &gt; Pieejamība."</string>
diff --git a/core/res/res/values-mk/strings.xml b/core/res/res/values-mk/strings.xml
index f1e54064036f..2f75269cf421 100644
--- a/core/res/res/values-mk/strings.xml
+++ b/core/res/res/values-mk/strings.xml
@@ -1615,6 +1615,8 @@
<string name="kg_failed_attempts_almost_at_login" product="default" msgid="5270861875006378092">"Погрешно сте ја употребиле вашата шема на отклучување <xliff:g id="NUMBER_0">%1$d</xliff:g> пати. По <xliff:g id="NUMBER_1">%2$d</xliff:g> неуспешни обиди, ќе побараат од вас да го отклучите телефонот со користење сметка на е-пошта.\n\n Обидете се повторно за <xliff:g id="NUMBER_2">%3$d</xliff:g> секунди."</string>
<string name="kg_text_message_separator" product="default" msgid="4503708889934976866">" — "</string>
<string name="kg_reordering_delete_drop_target_text" msgid="2034358143731750914">"Отстрани"</string>
+ <!-- no translation found for allow_while_in_use_permission_in_fgs (4101339676785053656) -->
+ <skip />
<string name="safe_media_volume_warning" product="default" msgid="3751676824423049994">"Да го зголемиме звукот над препорачаното ниво?\n\nСлушањето звуци со голема јачина подолги периоди може да ви го оштети сетилото за слух."</string>
<string name="accessibility_shortcut_warning_dialog_title" msgid="4017995837692622933">"Да се користи кратенка за „Пристапност“?"</string>
<string name="accessibility_shortcut_toogle_warning" msgid="8306239551412868396">"Кога е вклучена кратенката, ако ги притиснете двете копчиња за јачина на звук во времетраење од 3 секунди, ќе се стартува функција на пристапност.\n\n Тековна функција на пристапност:\n <xliff:g id="SERVICE_NAME">%1$s</xliff:g>\n\n Функцијата може да ја промените во „Поставки“ &gt; „Пристапност“."</string>
diff --git a/core/res/res/values-ml/strings.xml b/core/res/res/values-ml/strings.xml
index f0cc48cf9bd9..25d72126418f 100644
--- a/core/res/res/values-ml/strings.xml
+++ b/core/res/res/values-ml/strings.xml
@@ -1613,6 +1613,8 @@
<string name="kg_failed_attempts_almost_at_login" product="default" msgid="5270861875006378092">"നിങ്ങളുടെ അൺലോക്ക് പാറ്റേൺ <xliff:g id="NUMBER_0">%1$d</xliff:g> തവണ തെറ്റായി വരച്ചു. <xliff:g id="NUMBER_1">%2$d</xliff:g> ശ്രമങ്ങൾ കൂടി വിജയിച്ചില്ലെങ്കിൽ, ഒരു ഇമെയിൽ അക്കൗണ്ട് ഉപയോഗിച്ച് ഫോൺ അൺലോക്ക് ചെയ്യാൻ നിങ്ങളോട് ആവശ്യപ്പെടും.\n\n <xliff:g id="NUMBER_2">%3$d</xliff:g> സെക്കൻഡിനുള്ള വീണ്ടും ശ്രമിക്കുക."</string>
<string name="kg_text_message_separator" product="default" msgid="4503708889934976866">" — "</string>
<string name="kg_reordering_delete_drop_target_text" msgid="2034358143731750914">"നീക്കംചെയ്യുക"</string>
+ <!-- no translation found for allow_while_in_use_permission_in_fgs (4101339676785053656) -->
+ <skip />
<string name="safe_media_volume_warning" product="default" msgid="3751676824423049994">"മുകളിൽക്കൊടുത്തിരിക്കുന്ന ശുപാർശചെയ്‌ത ലെവലിലേക്ക് വോളിയം വർദ്ധിപ്പിക്കണോ?\n\nഉയർന്ന വോളിയത്തിൽ ദീർഘനേരം കേൾക്കുന്നത് നിങ്ങളുടെ ശ്രവണ ശേഷിയെ ദോഷകരമായി ബാധിക്കാം."</string>
<string name="accessibility_shortcut_warning_dialog_title" msgid="4017995837692622933">"ഉപയോഗസഹായി കുറുക്കുവഴി ഉപയോഗിക്കണോ?"</string>
<string name="accessibility_shortcut_toogle_warning" msgid="8306239551412868396">"കുറുക്കുവഴി ഓണാണെങ്കിൽ, രണ്ട് വോളിയം ബട്ടണുകളും 3 സെക്കൻഡ് നേരത്തേക്ക് അമർത്തുന്നത് ഉപയോഗസഹായി ഫീച്ചർ ആരംഭിക്കും.\n\n നിലവിലെ ഉപയോഗസഹായി ഫീച്ചർ:\n <xliff:g id="SERVICE_NAME">%1$s</xliff:g>\n\n ക്രമീകരണം &gt; ഉപയോഗസഹായി എന്നതിൽ ഏത് സമയത്തും നിങ്ങൾക്ക് ഫീച്ചർ മാറ്റാവുന്നതാണ്."</string>
@@ -1987,8 +1989,7 @@
<string name="mime_type_spreadsheet_ext" msgid="8720173181137254414">"<xliff:g id="EXTENSION">%1$s</xliff:g> സ്പ്രെഡ്ഷീറ്റ്"</string>
<string name="mime_type_presentation" msgid="1145384236788242075">"അവതരണം"</string>
<string name="mime_type_presentation_ext" msgid="8761049335564371468">"<xliff:g id="EXTENSION">%1$s</xliff:g> അവതരണം"</string>
- <!-- no translation found for bluetooth_airplane_mode_toast (2066399056595768554) -->
- <skip />
+ <string name="bluetooth_airplane_mode_toast" msgid="2066399056595768554">"ഫ്ലൈറ്റ് മോഡ് ഓണാക്കിയിരിക്കുമ്പോഴും Bluetooth ലഭ്യമാകും"</string>
<string name="car_loading_profile" msgid="8219978381196748070">"ലോഡ് ചെയ്യുന്നു"</string>
<plurals name="file_count" formatted="false" msgid="7063513834724389247">
<item quantity="other"><xliff:g id="FILE_NAME_2">%s</xliff:g> + <xliff:g id="COUNT_3">%d</xliff:g> ഫയലുകൾ</item>
@@ -2009,8 +2010,7 @@
<!-- no translation found for accessibility_system_action_accessibility_menu_label (8436484650391125184) -->
<skip />
<string name="accessibility_freeform_caption" msgid="8377519323496290122">"<xliff:g id="APP_NAME">%1$s</xliff:g> എന്നതിന്റെ അടിക്കുറിപ്പ് ബാർ."</string>
- <!-- no translation found for as_app_forced_to_restricted_bucket (8233871289353898964) -->
- <skip />
+ <string name="as_app_forced_to_restricted_bucket" msgid="8233871289353898964">"<xliff:g id="PACKAGE_NAME">%1$s</xliff:g> നിയന്ത്രിത ബക്കറ്റിലേക്ക് നീക്കി"</string>
<!-- no translation found for resolver_personal_tab (2051260504014442073) -->
<skip />
<!-- no translation found for resolver_work_tab (2690019516263167035) -->
diff --git a/core/res/res/values-mn/strings.xml b/core/res/res/values-mn/strings.xml
index 3be3a0442401..3acdb7245b7c 100644
--- a/core/res/res/values-mn/strings.xml
+++ b/core/res/res/values-mn/strings.xml
@@ -1613,6 +1613,8 @@
<string name="kg_failed_attempts_almost_at_login" product="default" msgid="5270861875006378092">"Та тайлах хээг <xliff:g id="NUMBER_0">%1$d</xliff:g> удаа буруу зурлаа. <xliff:g id="NUMBER_1">%2$d</xliff:g> удаа дахин буруу оруулбал, та утсаа тайлахын тулд имэйл бүртгэлээ ашиглах шаардлагатай болно.\n\n <xliff:g id="NUMBER_2">%3$d</xliff:g> секундын дараа дахин оролдоно уу."</string>
<string name="kg_text_message_separator" product="default" msgid="4503708889934976866">" — "</string>
<string name="kg_reordering_delete_drop_target_text" msgid="2034358143731750914">"Устгах"</string>
+ <!-- no translation found for allow_while_in_use_permission_in_fgs (4101339676785053656) -->
+ <skip />
<string name="safe_media_volume_warning" product="default" msgid="3751676824423049994">"Дууг санал болгосноос чанга болгож өсгөх үү?\n\nУрт хугацаанд чанга хөгжим сонсох нь таны сонсголыг муутгаж болно."</string>
<string name="accessibility_shortcut_warning_dialog_title" msgid="4017995837692622933">"Хүртээмжийн товчлолыг ашиглах уу?"</string>
<string name="accessibility_shortcut_toogle_warning" msgid="8306239551412868396">"Товчлолыг асаасан үед дуун товчлуурыг 3 секунд дарснаар хүртээмжийн онцлогийг эхлүүлнэ.\n\n Одоогийн хүртээмжийн онцлог:\n <xliff:g id="SERVICE_NAME">%1$s</xliff:g>\n\n Онцлогийг Тохиргоо &gt; Хүртээмж хэсэгт өөрчлөх боломжтой."</string>
@@ -1987,8 +1989,7 @@
<string name="mime_type_spreadsheet_ext" msgid="8720173181137254414">"<xliff:g id="EXTENSION">%1$s</xliff:g>-н хүснэгт"</string>
<string name="mime_type_presentation" msgid="1145384236788242075">"Үзүүлэн"</string>
<string name="mime_type_presentation_ext" msgid="8761049335564371468">"<xliff:g id="EXTENSION">%1$s</xliff:g>-н үзүүлэн"</string>
- <!-- no translation found for bluetooth_airplane_mode_toast (2066399056595768554) -->
- <skip />
+ <string name="bluetooth_airplane_mode_toast" msgid="2066399056595768554">"Нислэгийн горимын үеэр Bluetooth асаалттай байх болно"</string>
<string name="car_loading_profile" msgid="8219978381196748070">"Ачаалж байна"</string>
<plurals name="file_count" formatted="false" msgid="7063513834724389247">
<item quantity="other"><xliff:g id="FILE_NAME_2">%s</xliff:g> + <xliff:g id="COUNT_3">%d</xliff:g> файл</item>
@@ -2009,8 +2010,7 @@
<!-- no translation found for accessibility_system_action_accessibility_menu_label (8436484650391125184) -->
<skip />
<string name="accessibility_freeform_caption" msgid="8377519323496290122">"<xliff:g id="APP_NAME">%1$s</xliff:g>-н гарчгийн талбар."</string>
- <!-- no translation found for as_app_forced_to_restricted_bucket (8233871289353898964) -->
- <skip />
+ <string name="as_app_forced_to_restricted_bucket" msgid="8233871289353898964">"<xliff:g id="PACKAGE_NAME">%1$s</xliff:g>-г ХЯЗГААРЛАСАН сагс руу орууллаа"</string>
<!-- no translation found for resolver_personal_tab (2051260504014442073) -->
<skip />
<!-- no translation found for resolver_work_tab (2690019516263167035) -->
diff --git a/core/res/res/values-mr/strings.xml b/core/res/res/values-mr/strings.xml
index a2bf4796eaec..ff9f3e741c85 100644
--- a/core/res/res/values-mr/strings.xml
+++ b/core/res/res/values-mr/strings.xml
@@ -1613,6 +1613,8 @@
<string name="kg_failed_attempts_almost_at_login" product="default" msgid="5270861875006378092">"तुम्ही तुमचा अनलॉक पॅटर्न <xliff:g id="NUMBER_0">%1$d</xliff:g> वेळा अयोग्यपणे काढला आहे. आणखी <xliff:g id="NUMBER_1">%2$d</xliff:g> अयशस्वी प्रयत्नांनंतर, तुम्हाला ईमेल खाते वापरून तुमचा फोन अनलॉक करण्यास सांगितले जाईल.\n\n <xliff:g id="NUMBER_2">%3$d</xliff:g> सेकंदांमध्ये पुन्हा प्रयत्न करा."</string>
<string name="kg_text_message_separator" product="default" msgid="4503708889934976866">" — "</string>
<string name="kg_reordering_delete_drop_target_text" msgid="2034358143731750914">"काढा"</string>
+ <!-- no translation found for allow_while_in_use_permission_in_fgs (4101339676785053656) -->
+ <skip />
<string name="safe_media_volume_warning" product="default" msgid="3751676824423049994">"शिफारस केलेल्‍या पातळीच्या वर आवाज वाढवायचा?\n\nउच्च आवाजात दीर्घ काळ ऐकण्‍याने आपल्‍या श्रवणशक्तीची हानी होऊ शकते."</string>
<string name="accessibility_shortcut_warning_dialog_title" msgid="4017995837692622933">"प्रवेशयोग्यता शॉर्टकट वापरायचा?"</string>
<string name="accessibility_shortcut_toogle_warning" msgid="8306239551412868396">"शॉर्टकट चालू असताना, दोन्ही आवाज बटणे 3 सेकंद दाबल्याने प्रवेशयोग्यता वैशिष्ट्य सुरू होईल.\n\n वर्तमान प्रवेशयोग्यता वैशिष्ट्य:\n <xliff:g id="SERVICE_NAME">%1$s</xliff:g>\n\n तुम्ही सेटिंग्ज &gt; प्रवेशयोग्यता मध्ये वैशिष्ट्य बदलू शकता."</string>
diff --git a/core/res/res/values-ms/strings.xml b/core/res/res/values-ms/strings.xml
index f7626dbe1935..e4364083751d 100644
--- a/core/res/res/values-ms/strings.xml
+++ b/core/res/res/values-ms/strings.xml
@@ -1613,6 +1613,8 @@
<string name="kg_failed_attempts_almost_at_login" product="default" msgid="5270861875006378092">"Anda telah tersilap lukis corak buka kunci sebanyak <xliff:g id="NUMBER_0">%1$d</xliff:g> kali. Selepas <xliff:g id="NUMBER_1">%2$d</xliff:g> lagi percubaan yang tidak berjaya, anda akan diminta membuka kunci telefon anda menggunakan log masuk Google anda.\n\n Cuba lagi dalam <xliff:g id="NUMBER_2">%3$d</xliff:g> saat."</string>
<string name="kg_text_message_separator" product="default" msgid="4503708889934976866">" — "</string>
<string name="kg_reordering_delete_drop_target_text" msgid="2034358143731750914">"Alih keluar"</string>
+ <!-- no translation found for allow_while_in_use_permission_in_fgs (4101339676785053656) -->
+ <skip />
<string name="safe_media_volume_warning" product="default" msgid="3751676824423049994">"Naikkan kelantangan melebihi paras yang disyokorkan?\n\nMendengar pada kelantangan yang tinggi untuk tempoh yang lama boleh merosakkan pendengaran anda."</string>
<string name="accessibility_shortcut_warning_dialog_title" msgid="4017995837692622933">"Gunakan Pintasan Kebolehaksesan?"</string>
<string name="accessibility_shortcut_toogle_warning" msgid="8306239551412868396">"Apabila pintasan dihidupkan, tindakan menekan kedua-dua butang kelantangan selama 3 saat akan memulakan ciri kebolehaksesan.\n\n Ciri kebolehaksesan semasa:\n <xliff:g id="SERVICE_NAME">%1$s</xliff:g>\n\n Anda boleh menukar ciri itu dalam Tetapan &gt; Kebolehaksesan."</string>
diff --git a/core/res/res/values-my/strings.xml b/core/res/res/values-my/strings.xml
index 556837efa8a3..56f49c28b26d 100644
--- a/core/res/res/values-my/strings.xml
+++ b/core/res/res/values-my/strings.xml
@@ -1613,6 +1613,8 @@
<string name="kg_failed_attempts_almost_at_login" product="default" msgid="5270861875006378092">"သင် ပုံဖော်၍သော့ဖွင့်ခြင်းကို <xliff:g id="NUMBER_0">%1$d</xliff:g> အကြိမ် မှန်ကန်စွာ မပြုလုပ်နိုင်ပါ။ နောက်ထပ် <xliff:g id="NUMBER_1">%2$d</xliff:g> အကြိမ် မမှန်ကန်ပါက သင့်ဖုန်းအား အီးမေးလ်အသုံးပြု၍ သော့ဖွင့်ရန် တောင်းဆိုပါလိမ့်မည်။ \n\n <xliff:g id="NUMBER_2">%3$d</xliff:g> စက္ကန့်အကြာတွင် ပြန်လည် ကြိုးစားပါ"</string>
<string name="kg_text_message_separator" product="default" msgid="4503708889934976866">" — "</string>
<string name="kg_reordering_delete_drop_target_text" msgid="2034358143731750914">"ဖယ်ရှားရန်"</string>
+ <!-- no translation found for allow_while_in_use_permission_in_fgs (4101339676785053656) -->
+ <skip />
<string name="safe_media_volume_warning" product="default" msgid="3751676824423049994">"အသံကို အကြံပြုထားသည့် ပမာဏထက် မြှင့်ပေးရမလား?\n\nအသံကို မြင့်သည့် အဆင့်မှာ ကြာရှည်စွာ နားထောင်ခြင်းက သင်၏ နားကို ထိခိုက်စေနိုင်သည်။"</string>
<string name="accessibility_shortcut_warning_dialog_title" msgid="4017995837692622933">"အများသုံးစွဲနိုင်မှု ဖြတ်လမ်းလင့်ခ်ကို အသုံးပြုလိုပါသလား။"</string>
<string name="accessibility_shortcut_toogle_warning" msgid="8306239551412868396">"ဖြတ်လမ်းလင့်ခ်ကို ဖွင့်ထားစဉ် အသံအတိုးအလျှော့ခလုတ် နှစ်ခုစလုံးကို ၃ စက္ကန့်ခန့် ဖိထားခြင်းဖြင့် အများသုံးစွဲနိုင်မှုဆိုင်ရာ ဝန်ဆောင်မှုကို ဖွင့်နိုင်သည်။\n\n လက်ရှိ အများသုံးစွဲနိုင်မှုဆိုင်ရာ ဝန်ဆောင်မှု−\n <xliff:g id="SERVICE_NAME">%1$s</xliff:g>\n\n ဝန်ဆောင်မှုကို ဆက်တင်များ &gt; အများသုံးစွဲနိုင်မှုတွင် ပြောင်းလဲနိုင်ပါသည်။"</string>
diff --git a/core/res/res/values-nb/strings.xml b/core/res/res/values-nb/strings.xml
index 6f3b6c213cad..bb3d52f7b854 100644
--- a/core/res/res/values-nb/strings.xml
+++ b/core/res/res/values-nb/strings.xml
@@ -1613,6 +1613,8 @@
<string name="kg_failed_attempts_almost_at_login" product="default" msgid="5270861875006378092">"Du har tegnet opplåsningsmønsteret feil <xliff:g id="NUMBER_0">%1$d</xliff:g> ganger. Etter ytterligere <xliff:g id="NUMBER_1">%2$d</xliff:g> gale forsøk, blir du bedt om å låse opp telefonen via en e-postkonto.\n\n Prøv på nytt om <xliff:g id="NUMBER_2">%3$d</xliff:g> sekunder."</string>
<string name="kg_text_message_separator" product="default" msgid="4503708889934976866">" — "</string>
<string name="kg_reordering_delete_drop_target_text" msgid="2034358143731750914">"Fjern"</string>
+ <!-- no translation found for allow_while_in_use_permission_in_fgs (4101339676785053656) -->
+ <skip />
<string name="safe_media_volume_warning" product="default" msgid="3751676824423049994">"Vil du øke volumet til over anbefalt nivå?\n\nHvis du hører på et høyt volum over lengre perioder, kan det skade hørselen din."</string>
<string name="accessibility_shortcut_warning_dialog_title" msgid="4017995837692622933">"Vil du bruke tilgjengelighetssnarveien?"</string>
<string name="accessibility_shortcut_toogle_warning" msgid="8306239551412868396">"Når snarveien er på, starter en tilgjengelighetsfunksjon når du trykker inn begge volumknappene i tre sekunder.\n\n Nåværende tilgjengelighetsfunksjon:\n <xliff:g id="SERVICE_NAME">%1$s</xliff:g>\n\n Du kan endre funksjonen i Innstillinger &gt; Tilgjengelighet."</string>
diff --git a/core/res/res/values-ne/strings.xml b/core/res/res/values-ne/strings.xml
index ebbf50bf41e4..b3d6cdecf8ed 100644
--- a/core/res/res/values-ne/strings.xml
+++ b/core/res/res/values-ne/strings.xml
@@ -1619,6 +1619,8 @@
<string name="kg_failed_attempts_almost_at_login" product="default" msgid="5270861875006378092">"तपाईँले आफ्नो अनलक ढाँचा गलत रूपमा <xliff:g id="NUMBER_0">%1$d</xliff:g> पटक तान्नु भएको छ। <xliff:g id="NUMBER_1">%2$d</xliff:g> धेरै असफल प्रयासहरूपछि, तपाईँलाई एउटा इमेल खाताको प्रयोग गरेर तपाईँको फोन अनलक गर्न सोधिने छ।\n\n फेरि <xliff:g id="NUMBER_2">%3$d</xliff:g> सेकेन्डमा प्रयास गर्नुहोस्।"</string>
<string name="kg_text_message_separator" product="default" msgid="4503708889934976866">" — "</string>
<string name="kg_reordering_delete_drop_target_text" msgid="2034358143731750914">"हटाउनुहोस्"</string>
+ <!-- no translation found for allow_while_in_use_permission_in_fgs (4101339676785053656) -->
+ <skip />
<string name="safe_media_volume_warning" product="default" msgid="3751676824423049994">"सिफारिस तहभन्दा आवाज ठुलो गर्नुहुन्छ?\n\nलामो समय सम्म उच्च आवाजमा सुन्दा तपाईँको सुन्ने शक्तिलाई हानी गर्न सक्छ।"</string>
<string name="accessibility_shortcut_warning_dialog_title" msgid="4017995837692622933">"पहुँच सम्बन्धी सर्टकट प्रयोग गर्ने हो?"</string>
<string name="accessibility_shortcut_toogle_warning" msgid="8306239551412868396">"सर्टकट सक्रिय हुँदा, भोल्युमका दुवै बटनहरूलाई ३ सेकेन्डसम्म थिची राख्नाले पहुँच सम्बन्धी कुनै सुविधा सुरु हुनेछ।\n\n हाल व्यवहारमा रहेको पहुँच सम्बन्धी सुविधा:\n <xliff:g id="SERVICE_NAME">%1$s</xliff:g>\n\n तपाईं सेटिङहरू अन्तर्गतको पहुँच सम्बन्धी विकल्पमा गई उक्त सुविधालाई बदल्न सक्नुहुन्छ।"</string>
@@ -1993,8 +1995,7 @@
<string name="mime_type_spreadsheet_ext" msgid="8720173181137254414">"<xliff:g id="EXTENSION">%1$s</xliff:g> स्प्रेडसिट"</string>
<string name="mime_type_presentation" msgid="1145384236788242075">"प्रस्तुति"</string>
<string name="mime_type_presentation_ext" msgid="8761049335564371468">"<xliff:g id="EXTENSION">%1$s</xliff:g> प्रस्तुति"</string>
- <!-- no translation found for bluetooth_airplane_mode_toast (2066399056595768554) -->
- <skip />
+ <string name="bluetooth_airplane_mode_toast" msgid="2066399056595768554">"हवाइजहाज मोडमा ब्लुटुथ सक्रिय रहने छ"</string>
<string name="car_loading_profile" msgid="8219978381196748070">"लोड गर्दै"</string>
<plurals name="file_count" formatted="false" msgid="7063513834724389247">
<item quantity="other"><xliff:g id="FILE_NAME_2">%s</xliff:g> + <xliff:g id="COUNT_3">%d</xliff:g> फाइलहरू</item>
@@ -2015,8 +2016,7 @@
<!-- no translation found for accessibility_system_action_accessibility_menu_label (8436484650391125184) -->
<skip />
<string name="accessibility_freeform_caption" msgid="8377519323496290122">"<xliff:g id="APP_NAME">%1$s</xliff:g> को क्याप्सन बार।"</string>
- <!-- no translation found for as_app_forced_to_restricted_bucket (8233871289353898964) -->
- <skip />
+ <string name="as_app_forced_to_restricted_bucket" msgid="8233871289353898964">"<xliff:g id="PACKAGE_NAME">%1$s</xliff:g> लाई प्रतिबन्धित बाल्टीमा राखियो"</string>
<!-- no translation found for resolver_personal_tab (2051260504014442073) -->
<skip />
<!-- no translation found for resolver_work_tab (2690019516263167035) -->
diff --git a/core/res/res/values-nl/strings.xml b/core/res/res/values-nl/strings.xml
index 0635c214a4c1..47de950c5c7d 100644
--- a/core/res/res/values-nl/strings.xml
+++ b/core/res/res/values-nl/strings.xml
@@ -1613,6 +1613,8 @@
<string name="kg_failed_attempts_almost_at_login" product="default" msgid="5270861875006378092">"Je hebt je ontgrendelingspatroon <xliff:g id="NUMBER_0">%1$d</xliff:g> keer onjuist getekend. Na nog eens <xliff:g id="NUMBER_1">%2$d</xliff:g> mislukte pogingen wordt u gevraagd je telefoon te ontgrendelen via een e-mailaccount.\n\n Probeer het over <xliff:g id="NUMBER_2">%3$d</xliff:g> seconden opnieuw."</string>
<string name="kg_text_message_separator" product="default" msgid="4503708889934976866">" — "</string>
<string name="kg_reordering_delete_drop_target_text" msgid="2034358143731750914">"Verwijderen"</string>
+ <!-- no translation found for allow_while_in_use_permission_in_fgs (4101339676785053656) -->
+ <skip />
<string name="safe_media_volume_warning" product="default" msgid="3751676824423049994">"Volume verhogen tot boven het aanbevolen niveau?\n\nAls je langere tijd op hoog volume naar muziek luistert, raakt je gehoor mogelijk beschadigd."</string>
<string name="accessibility_shortcut_warning_dialog_title" msgid="4017995837692622933">"Snelkoppeling toegankelijkheid gebruiken?"</string>
<string name="accessibility_shortcut_toogle_warning" msgid="8306239551412868396">"Wanneer de snelkoppeling is ingeschakeld, kun je drie seconden op beide volumeknoppen drukken om een toegankelijkheidsfunctie te starten.\n\n Huidige toegankelijkheidsfunctie:\n <xliff:g id="SERVICE_NAME">%1$s</xliff:g>\n\n Je kunt de functie wijzigen in Instellingen &gt; Toegankelijkheid."</string>
diff --git a/core/res/res/values-or/strings.xml b/core/res/res/values-or/strings.xml
index 03a7ea3be704..1242188f791a 100644
--- a/core/res/res/values-or/strings.xml
+++ b/core/res/res/values-or/strings.xml
@@ -1613,6 +1613,8 @@
<string name="kg_failed_attempts_almost_at_login" product="default" msgid="5270861875006378092">"ଆପଣଙ୍କ ଅନଲକ୍‍ ପାଟର୍ନକୁ ଆପଣ <xliff:g id="NUMBER_0">%1$d</xliff:g> ଥର ଭୁଲ ଭାବେ ଅଙ୍କନ କରିଛନ୍ତି। ଆଉ <xliff:g id="NUMBER_1">%2$d</xliff:g>ଟି ଭୁଲ ପ୍ରୟାସ ପରେ ଏକ ଇମେଲ୍‍ ଆକାଉଣ୍ଟ ବ୍ୟବହାର କରି ନିଜ ଫୋନ୍‌କୁ ଅନଲକ୍‌ କରିବା ପାଇଁ କୁହାଯିବ।\n\n<xliff:g id="NUMBER_2">%3$d</xliff:g> ସେକେଣ୍ଡ ପରେ ପୁଣି ଚେଷ୍ଟା କରନ୍ତୁ।"</string>
<string name="kg_text_message_separator" product="default" msgid="4503708889934976866">" — "</string>
<string name="kg_reordering_delete_drop_target_text" msgid="2034358143731750914">"ବାହାର କରନ୍ତୁ"</string>
+ <!-- no translation found for allow_while_in_use_permission_in_fgs (4101339676785053656) -->
+ <skip />
<string name="safe_media_volume_warning" product="default" msgid="3751676824423049994">"ମାତ୍ରା ବଢ଼ାଇ ସୁପାରିଶ ସ୍ତର ବଢ଼ାଉଛନ୍ତି? \n\n ଲମ୍ବା ସମୟ ପର୍ଯ୍ୟନ୍ତ ଉଚ୍ଚ ଶବ୍ଦରେ ଶୁଣିଲେ ଆପଣଙ୍କ ଶ୍ରବଣ ଶକ୍ତି ଖରାପ ହୋଇପାରେ।"</string>
<string name="accessibility_shortcut_warning_dialog_title" msgid="4017995837692622933">"ଆକ୍ସେସବିଲିଟି ଶର୍ଟକଟ୍‍ ବ୍ୟବହାର କରିବେ?"</string>
<string name="accessibility_shortcut_toogle_warning" msgid="8306239551412868396">"ସର୍ଟକଟ୍‌ ଅନ୍‌ ଥିବା ବେଳେ, ଉଭୟ ଭଲ୍ୟୁମ୍‍ ବଟନ୍‍ 3 ସେକେଣ୍ଡ ପାଇଁ ଦବାଇବା ଦ୍ୱାରା ଆକ୍ସେସବିଲିଟି ବୈଶିଷ୍ଟ ଆରମ୍ଭ ହେବ।\n\n ସମ୍ପ୍ରତି ଆକ୍ସେସବିଲିଟି ବୈଶିଷ୍ଟ୍ୟ:\n <xliff:g id="SERVICE_NAME">%1$s</xliff:g>\n\n ସେଟିଙ୍ଗ ଓ ଆକ୍ସେସବିଲିଟିରେ ଆପଣ ବୈଶିଷ୍ଟ୍ୟ ବଦଳାଇ ପାରିବେ।"</string>
diff --git a/core/res/res/values-pa/strings.xml b/core/res/res/values-pa/strings.xml
index b2cca4db6615..b6513c399559 100644
--- a/core/res/res/values-pa/strings.xml
+++ b/core/res/res/values-pa/strings.xml
@@ -1613,6 +1613,8 @@
<string name="kg_failed_attempts_almost_at_login" product="default" msgid="5270861875006378092">"ਤੁਸੀਂ <xliff:g id="NUMBER_0">%1$d</xliff:g> ਵਾਰ ਆਪਣਾ ਅਣਲਾਕ ਪੈਟਰਨ ਗਲਤ ਢੰਗ ਨਾਲ ਡ੍ਰਾ ਕੀਤਾ ਹੈ। <xliff:g id="NUMBER_1">%2$d</xliff:g> ਹੋਰ ਅਸਫਲ ਕੋਸ਼ਿਸ਼ਾਂ ਤੋਂ ਬਾਅਦ, ਤੁਹਾਨੂੰ ਇੱਕ ਈਮੇਲ ਖਾਤਾ ਵਰਤਦੇ ਹੋਏ ਆਪਣਾ ਫ਼ੋਨ ਅਣਲਾਕ ਕਰਨ ਲਈ ਕਿਹਾ ਜਾਏਗਾ।\n\n <xliff:g id="NUMBER_2">%3$d</xliff:g> ਸਕਿੰਟਾਂ ਵਿੱਚ ਦੁਬਾਰਾ ਕੋਸ਼ਿਸ਼ ਕਰੋ।"</string>
<string name="kg_text_message_separator" product="default" msgid="4503708889934976866">" — "</string>
<string name="kg_reordering_delete_drop_target_text" msgid="2034358143731750914">"ਹਟਾਓ"</string>
+ <!-- no translation found for allow_while_in_use_permission_in_fgs (4101339676785053656) -->
+ <skip />
<string name="safe_media_volume_warning" product="default" msgid="3751676824423049994">"ਕੀ ਵੌਲਿਊਮ ਸਿਫ਼ਾਰਸ਼ ਕੀਤੇ ਪੱਧਰ ਤੋਂ ਵਧਾਉਣੀ ਹੈ?\n\nਲੰਮੇ ਸਮੇਂ ਤੱਕ ਉੱਚ ਵੌਲਿਊਮ ਤੇ ਸੁਣਨ ਨਾਲ ਤੁਹਾਡੀ ਸੁਣਨ ਸ਼ਕਤੀ ਨੂੰ ਨੁਕਸਾਨ ਪਹੁੰਚ ਸਕਦਾ ਹੈ।"</string>
<string name="accessibility_shortcut_warning_dialog_title" msgid="4017995837692622933">"ਕੀ ਪਹੁੰਚਯੋਗਤਾ ਸ਼ਾਰਟਕੱਟ ਵਰਤਣਾ ਹੈ?"</string>
<string name="accessibility_shortcut_toogle_warning" msgid="8306239551412868396">"ਸ਼ਾਰਟਕੱਟ ਚਾਲੂ ਹੋਣ \'ਤੇ, ਕਿਸੇ ਪਹੁੰਚਯੋਗਤਾ ਵਿਸ਼ੇਸ਼ਤਾ ਨੂੰ ਸ਼ੁਰੂ ਕਰਨ ਲਈ ਅਵਾਜ਼ ਬਟਨਾਂ ਨੂੰ 3 ਸਕਿੰਟ ਲਈ ਦਬਾ ਕੇ ਰੱਖੋ।\n\n ਵਰਤਮਾਨ ਪਹੁੰਚਯੋਗਤਾ ਵਿਸ਼ੇਸ਼ਤਾ:\n <xliff:g id="SERVICE_NAME">%1$s</xliff:g>\n\n ਤੁਸੀਂ ਸੈਟਿੰਗਾਂ &gt; ਪਹੁੰਚਯੋਗਤਾ ਵਿੱਚ ਵਿਸ਼ੇਸ਼ਤਾ ਨੂੰ ਬਦਲ ਸਕਦੇ ਹੋ।"</string>
diff --git a/core/res/res/values-pl/strings.xml b/core/res/res/values-pl/strings.xml
index 94fd851f7c66..964c973e491f 100644
--- a/core/res/res/values-pl/strings.xml
+++ b/core/res/res/values-pl/strings.xml
@@ -1657,6 +1657,8 @@
<string name="kg_failed_attempts_almost_at_login" product="default" msgid="5270861875006378092">"Po raz <xliff:g id="NUMBER_0">%1$d</xliff:g> nieprawidłowo narysowałeś wzór odblokowania. Po kolejnych <xliff:g id="NUMBER_1">%2$d</xliff:g> nieudanych próbach konieczne będzie odblokowanie telefonu przy użyciu danych logowania na konto Google.\n\n Spróbuj ponownie za <xliff:g id="NUMBER_2">%3$d</xliff:g> s."</string>
<string name="kg_text_message_separator" product="default" msgid="4503708889934976866">" – "</string>
<string name="kg_reordering_delete_drop_target_text" msgid="2034358143731750914">"Usuń"</string>
+ <!-- no translation found for allow_while_in_use_permission_in_fgs (4101339676785053656) -->
+ <skip />
<string name="safe_media_volume_warning" product="default" msgid="3751676824423049994">"Zwiększyć głośność ponad zalecany poziom?\n\nSłuchanie głośno przez długi czas może uszkodzić Twój słuch."</string>
<string name="accessibility_shortcut_warning_dialog_title" msgid="4017995837692622933">"Użyć skrótu do ułatwień dostępu?"</string>
<string name="accessibility_shortcut_toogle_warning" msgid="8306239551412868396">"Gdy skrót jest włączony, jednoczesne naciśnięcie przez trzy sekundy obu klawiszy sterowania głośnością uruchomi funkcję ułatwień dostępu.\n\nBieżąca funkcja ułatwień dostępu:\n<xliff:g id="SERVICE_NAME">%1$s</xliff:g>\n\nFunkcję możesz zmienić, wybierając Ustawienia &gt; Ułatwienia dostępu."</string>
diff --git a/core/res/res/values-pt-rBR/strings.xml b/core/res/res/values-pt-rBR/strings.xml
index 72a3ce904a8c..a6fd0fe230ec 100644
--- a/core/res/res/values-pt-rBR/strings.xml
+++ b/core/res/res/values-pt-rBR/strings.xml
@@ -1613,6 +1613,8 @@
<string name="kg_failed_attempts_almost_at_login" product="default" msgid="5270861875006378092">"Você desenhou sua sequência de desbloqueio incorretamente <xliff:g id="NUMBER_0">%1$d</xliff:g> vezes. Se fizer mais <xliff:g id="NUMBER_1">%2$d</xliff:g> tentativas incorretas, será solicitado que você use o login do Google para desbloquear.\n\n Tente novamente em <xliff:g id="NUMBER_2">%3$d</xliff:g> segundos."</string>
<string name="kg_text_message_separator" product="default" msgid="4503708889934976866">" — "</string>
<string name="kg_reordering_delete_drop_target_text" msgid="2034358143731750914">"Remover"</string>
+ <!-- no translation found for allow_while_in_use_permission_in_fgs (4101339676785053656) -->
+ <skip />
<string name="safe_media_volume_warning" product="default" msgid="3751676824423049994">"Aumentar o volume acima do nível recomendado?\n\nOuvir em volume alto por longos períodos pode danificar sua audição."</string>
<string name="accessibility_shortcut_warning_dialog_title" msgid="4017995837692622933">"Usar atalho de Acessibilidade?"</string>
<string name="accessibility_shortcut_toogle_warning" msgid="8306239551412868396">"Quando o atalho está ativado, pressione os dois botões de volume por três segundos para iniciar um recurso de acessibilidade.\n\n Recurso de acessibilidade atual:\n <xliff:g id="SERVICE_NAME">%1$s</xliff:g>\n\n É possível alterar o recurso em Configurações &gt; Acessibilidade."</string>
diff --git a/core/res/res/values-pt-rPT/strings.xml b/core/res/res/values-pt-rPT/strings.xml
index 4f45517c0bb9..81f5ced0a044 100644
--- a/core/res/res/values-pt-rPT/strings.xml
+++ b/core/res/res/values-pt-rPT/strings.xml
@@ -1613,6 +1613,8 @@
<string name="kg_failed_attempts_almost_at_login" product="default" msgid="5270861875006378092">"Desenhou o padrão de desbloqueio incorretamente <xliff:g id="NUMBER_0">%1$d</xliff:g> vezes. Depois de mais <xliff:g id="NUMBER_1">%2$d</xliff:g> tentativas sem sucesso, ser-lhe-á pedido para desbloquear o telemóvel através de uma conta de email.\n\n Tente novamente dentro de <xliff:g id="NUMBER_2">%3$d</xliff:g> segundos."</string>
<string name="kg_text_message_separator" product="default" msgid="4503708889934976866">" - "</string>
<string name="kg_reordering_delete_drop_target_text" msgid="2034358143731750914">"Remover"</string>
+ <!-- no translation found for allow_while_in_use_permission_in_fgs (4101339676785053656) -->
+ <skip />
<string name="safe_media_volume_warning" product="default" msgid="3751676824423049994">"Aumentar o volume acima do nível recomendado?\n\nOuvir com um volume elevado durante longos períodos poderá ser prejudicial para a sua audição."</string>
<string name="accessibility_shortcut_warning_dialog_title" msgid="4017995837692622933">"Pretende utilizar o atalho de acessibilidade?"</string>
<string name="accessibility_shortcut_toogle_warning" msgid="8306239551412868396">"Quando o atalho está ativado, premir ambos os botões de volume durante 3 segundos inicia uma funcionalidade de acessibilidade.\n\n Funcionalidade de acessibilidade atual:\n <xliff:g id="SERVICE_NAME">%1$s</xliff:g>\n\n Pode alterar a funcionalidade em Definições &gt; Acessibilidade."</string>
diff --git a/core/res/res/values-pt/strings.xml b/core/res/res/values-pt/strings.xml
index 72a3ce904a8c..a6fd0fe230ec 100644
--- a/core/res/res/values-pt/strings.xml
+++ b/core/res/res/values-pt/strings.xml
@@ -1613,6 +1613,8 @@
<string name="kg_failed_attempts_almost_at_login" product="default" msgid="5270861875006378092">"Você desenhou sua sequência de desbloqueio incorretamente <xliff:g id="NUMBER_0">%1$d</xliff:g> vezes. Se fizer mais <xliff:g id="NUMBER_1">%2$d</xliff:g> tentativas incorretas, será solicitado que você use o login do Google para desbloquear.\n\n Tente novamente em <xliff:g id="NUMBER_2">%3$d</xliff:g> segundos."</string>
<string name="kg_text_message_separator" product="default" msgid="4503708889934976866">" — "</string>
<string name="kg_reordering_delete_drop_target_text" msgid="2034358143731750914">"Remover"</string>
+ <!-- no translation found for allow_while_in_use_permission_in_fgs (4101339676785053656) -->
+ <skip />
<string name="safe_media_volume_warning" product="default" msgid="3751676824423049994">"Aumentar o volume acima do nível recomendado?\n\nOuvir em volume alto por longos períodos pode danificar sua audição."</string>
<string name="accessibility_shortcut_warning_dialog_title" msgid="4017995837692622933">"Usar atalho de Acessibilidade?"</string>
<string name="accessibility_shortcut_toogle_warning" msgid="8306239551412868396">"Quando o atalho está ativado, pressione os dois botões de volume por três segundos para iniciar um recurso de acessibilidade.\n\n Recurso de acessibilidade atual:\n <xliff:g id="SERVICE_NAME">%1$s</xliff:g>\n\n É possível alterar o recurso em Configurações &gt; Acessibilidade."</string>
diff --git a/core/res/res/values-ro/strings.xml b/core/res/res/values-ro/strings.xml
index d7cdade6c674..cb8d3fee32ca 100644
--- a/core/res/res/values-ro/strings.xml
+++ b/core/res/res/values-ro/strings.xml
@@ -1635,6 +1635,8 @@
<string name="kg_failed_attempts_almost_at_login" product="default" msgid="5270861875006378092">"Ați desenat incorect modelul pentru deblocare de <xliff:g id="NUMBER_0">%1$d</xliff:g> ori. După încă <xliff:g id="NUMBER_1">%2$d</xliff:g> încercări nereușite, vi se va solicita să deblocați telefonul cu ajutorul unui cont de e-mail.\n\n Încercați din nou peste <xliff:g id="NUMBER_2">%3$d</xliff:g> secunde."</string>
<string name="kg_text_message_separator" product="default" msgid="4503708889934976866">" — "</string>
<string name="kg_reordering_delete_drop_target_text" msgid="2034358143731750914">"Eliminați"</string>
+ <!-- no translation found for allow_while_in_use_permission_in_fgs (4101339676785053656) -->
+ <skip />
<string name="safe_media_volume_warning" product="default" msgid="3751676824423049994">"Ridicați volumul mai sus de nivelul recomandat?\n\nAscultarea la volum ridicat pe perioade lungi de timp vă poate afecta auzul."</string>
<string name="accessibility_shortcut_warning_dialog_title" msgid="4017995837692622933">"Utilizați comanda rapidă pentru accesibilitate?"</string>
<string name="accessibility_shortcut_toogle_warning" msgid="8306239551412868396">"Când comanda rapidă este activată, dacă apăsați ambele butoane de volum timp de 3 secunde, veți lansa o funcție de accesibilitate.\n\n Funcția actuală de accesibilitate:\n <xliff:g id="SERVICE_NAME">%1$s</xliff:g>\n\n Puteți schimba funcția în Setări &gt; Accesibilitate."</string>
diff --git a/core/res/res/values-ru/strings.xml b/core/res/res/values-ru/strings.xml
index 91b7ca6ab62c..4821c7f9d175 100644
--- a/core/res/res/values-ru/strings.xml
+++ b/core/res/res/values-ru/strings.xml
@@ -1657,6 +1657,8 @@
<string name="kg_failed_attempts_almost_at_login" product="default" msgid="5270861875006378092">"Вы <xliff:g id="NUMBER_0">%1$d</xliff:g> раз неверно указали графический ключ. После <xliff:g id="NUMBER_1">%2$d</xliff:g> неверных попыток для разблокировки телефона потребуется войти в аккаунт Google.\n\nПовтор через <xliff:g id="NUMBER_2">%3$d</xliff:g> сек."</string>
<string name="kg_text_message_separator" product="default" msgid="4503708889934976866">" – "</string>
<string name="kg_reordering_delete_drop_target_text" msgid="2034358143731750914">"Удалить"</string>
+ <!-- no translation found for allow_while_in_use_permission_in_fgs (4101339676785053656) -->
+ <skip />
<string name="safe_media_volume_warning" product="default" msgid="3751676824423049994">"Установить громкость выше рекомендуемого уровня?\n\nВоздействие громкого звука в течение долгого времени может привести к повреждению слуха."</string>
<string name="accessibility_shortcut_warning_dialog_title" msgid="4017995837692622933">"Использовать быстрое включение?"</string>
<string name="accessibility_shortcut_toogle_warning" msgid="8306239551412868396">"Чтобы использовать функцию специальных возможностей, когда она включена, нажмите и удерживайте три секунды обе кнопки регулировки громкости.\n\nТекущая функция специальных возможностей:\n<xliff:g id="SERVICE_NAME">%1$s</xliff:g>\n\nВы можете изменить ее в разделе \"Настройки &gt; Специальные возможности\"."</string>
diff --git a/core/res/res/values-si/strings.xml b/core/res/res/values-si/strings.xml
index 601f9b9ee6c6..df14036c1a07 100644
--- a/core/res/res/values-si/strings.xml
+++ b/core/res/res/values-si/strings.xml
@@ -1615,6 +1615,8 @@
<string name="kg_failed_attempts_almost_at_login" product="default" msgid="5270861875006378092">"ඔබ වැරදියට <xliff:g id="NUMBER_0">%1$d</xliff:g> වතාවක් ඔබගේ අගුළු හැරීමේ රටාව ඇඳ ඇත. අසාර්ථක උත්සහ කිරීම් <xliff:g id="NUMBER_1">%2$d</xliff:g> න් පසුව, ඔබගේ ඊ-තැපැල් ලිපිනය භාවිතයෙන් ඔබගේ දුරකථනය අගුළු හැරීමට ඔබගෙන් අසයි.\n\n තත්පර <xliff:g id="NUMBER_2">%3$d</xliff:g> න් පසුව නැවත උත්සහ කරන්න."</string>
<string name="kg_text_message_separator" product="default" msgid="4503708889934976866">" — "</string>
<string name="kg_reordering_delete_drop_target_text" msgid="2034358143731750914">"ඉවත් කරන්න"</string>
+ <!-- no translation found for allow_while_in_use_permission_in_fgs (4101339676785053656) -->
+ <skip />
<string name="safe_media_volume_warning" product="default" msgid="3751676824423049994">"නිර්දේශිතයි මට්ටමට වඩා ශබ්දය වැඩිද?\n\nදිගු කාලයක් සඳහා ඉහළ ශබ්දයක් ඇසීමෙන් ඇතැම් විට ඔබගේ ඇසීමට හානි විය හැක."</string>
<string name="accessibility_shortcut_warning_dialog_title" msgid="4017995837692622933">"ප්‍රවේශ්‍යතා කෙටිමඟ භාවිතා කරන්නද?"</string>
<string name="accessibility_shortcut_toogle_warning" msgid="8306239551412868396">"කෙටිමඟ සක්‍රිය විට, හඬ බොත්තම් දෙකම තත්පර 3ක් අල්ලාගෙන සිටීමෙන් ප්‍රවේශ්‍යත අංගයක් ඇරඹේ.\n\n වත්මන් ප්‍රවේශ්‍යතා අංගය:\n <xliff:g id="SERVICE_NAME">%1$s</xliff:g>\n\n සැකසීම් &gt; ප්‍රවේශ්‍යතාව තුළ ඔබට අංගය වෙනස් කළ හැක."</string>
diff --git a/core/res/res/values-sk/strings.xml b/core/res/res/values-sk/strings.xml
index fb81855b4590..b0dc5f3cd342 100644
--- a/core/res/res/values-sk/strings.xml
+++ b/core/res/res/values-sk/strings.xml
@@ -1657,6 +1657,8 @@
<string name="kg_failed_attempts_almost_at_login" product="default" msgid="5270861875006378092">"<xliff:g id="NUMBER_0">%1$d</xliff:g>-krát ste nesprávne nakreslili svoj bezpečnostný vzor. Po <xliff:g id="NUMBER_1">%2$d</xliff:g> ďalších neúspešných pokusoch sa zobrazí výzva na odomknutie telefónu pomocou e-mailového účtu.\n\n Skúste to znova o <xliff:g id="NUMBER_2">%3$d</xliff:g> s."</string>
<string name="kg_text_message_separator" product="default" msgid="4503708889934976866">" — "</string>
<string name="kg_reordering_delete_drop_target_text" msgid="2034358143731750914">"Odstrániť"</string>
+ <!-- no translation found for allow_while_in_use_permission_in_fgs (4101339676785053656) -->
+ <skip />
<string name="safe_media_volume_warning" product="default" msgid="3751676824423049994">"Zvýšiť hlasitosť nad odporúčanú úroveň?\n\nDlhodobé počúvanie pri vysokej hlasitosti môže poškodiť váš sluch."</string>
<string name="accessibility_shortcut_warning_dialog_title" msgid="4017995837692622933">"Použiť skratku dostupnosti?"</string>
<string name="accessibility_shortcut_toogle_warning" msgid="8306239551412868396">"Keď je skratka zapnutá, stlačením obidvoch tlačidiel hlasitosti na tri sekundy spustíte funkciu dostupnosti.\n\n Aktuálna funkcia dostupnosti:\n <xliff:g id="SERVICE_NAME">%1$s</xliff:g>\n\n Funkciu môžete zmeniť v časti Nastavenia &gt; Dostupnosť."</string>
diff --git a/core/res/res/values-sl/strings.xml b/core/res/res/values-sl/strings.xml
index 9405fb2a4e13..bdd5925d8a3e 100644
--- a/core/res/res/values-sl/strings.xml
+++ b/core/res/res/values-sl/strings.xml
@@ -1657,6 +1657,8 @@
<string name="kg_failed_attempts_almost_at_login" product="default" msgid="5270861875006378092">"Vzorec za odklepanje ste <xliff:g id="NUMBER_0">%1$d</xliff:g>-krat napačno vnesli. Po nadaljnjih <xliff:g id="NUMBER_1">%2$d</xliff:g> neuspešnih poskusih boste pozvani, da odklenete telefon z Googlovimi podatki za prijavo.\n\nPoskusite znova čez <xliff:g id="NUMBER_2">%3$d</xliff:g> s."</string>
<string name="kg_text_message_separator" product="default" msgid="4503708889934976866">" – "</string>
<string name="kg_reordering_delete_drop_target_text" msgid="2034358143731750914">"Odstrani"</string>
+ <!-- no translation found for allow_while_in_use_permission_in_fgs (4101339676785053656) -->
+ <skip />
<string name="safe_media_volume_warning" product="default" msgid="3751676824423049994">"Ali želite povečati glasnost nad priporočeno raven?\n\nDolgotrajno poslušanje pri veliki glasnosti lahko poškoduje sluh."</string>
<string name="accessibility_shortcut_warning_dialog_title" msgid="4017995837692622933">"Želite uporabljati bližnjico funkcij za ljudi s posebnimi potrebami?"</string>
<string name="accessibility_shortcut_toogle_warning" msgid="8306239551412868396">"Ko je bližnjica vklopljena, pritisnite gumba za glasnost in ju pridržite tri sekunde, če želite zagnati funkcijo za ljudi s posebnimi potrebami.\n\n Trenutna funkcija za ljudi s posebnimi potrebami:\n <xliff:g id="SERVICE_NAME">%1$s</xliff:g>\n\n Funkcijo lahko spremenite v »Nastavitve &gt; Funkcije za ljudi s posebnimi potrebami«."</string>
diff --git a/core/res/res/values-sq/strings.xml b/core/res/res/values-sq/strings.xml
index fe167da393fa..fd8dda060448 100644
--- a/core/res/res/values-sq/strings.xml
+++ b/core/res/res/values-sq/strings.xml
@@ -1613,6 +1613,8 @@
<string name="kg_failed_attempts_almost_at_login" product="default" msgid="5270861875006378092">"Ke vizatuar <xliff:g id="NUMBER_0">%1$d</xliff:g> herë pa sukses motivin tënd. Pas <xliff:g id="NUMBER_1">%2$d</xliff:g> tentativave të tjera të pasuksesshme, do të të duhet ta shkyçësh telefonin duke përdorur një llogari mail-i.\n\n Provo sërish për <xliff:g id="NUMBER_2">%3$d</xliff:g> sekonda."</string>
<string name="kg_text_message_separator" product="default" msgid="4503708889934976866">" - "</string>
<string name="kg_reordering_delete_drop_target_text" msgid="2034358143731750914">"Hiq"</string>
+ <!-- no translation found for allow_while_in_use_permission_in_fgs (4101339676785053656) -->
+ <skip />
<string name="safe_media_volume_warning" product="default" msgid="3751676824423049994">"Të ngrihet volumi mbi nivelin e rekomanduar?\n\nDëgjimi me volum të lartë për periudha të gjata mund të dëmtojë dëgjimin."</string>
<string name="accessibility_shortcut_warning_dialog_title" msgid="4017995837692622933">"Të përdoret shkurtorja e qasshmërisë?"</string>
<string name="accessibility_shortcut_toogle_warning" msgid="8306239551412868396">"Kur shkurtorja është e aktivizuar, shtypja e të dy butonave për 3 sekonda do të nisë një funksion qasshmërie.\n\n Funksioni aktual i qasshmërisë:\n <xliff:g id="SERVICE_NAME">%1$s</xliff:g>\n\n Mund ta ndryshosh funksionin te Cilësimet &gt; Qasshmëria."</string>
diff --git a/core/res/res/values-sr/strings.xml b/core/res/res/values-sr/strings.xml
index 564612c454ce..d82faa66ed12 100644
--- a/core/res/res/values-sr/strings.xml
+++ b/core/res/res/values-sr/strings.xml
@@ -1635,6 +1635,8 @@
<string name="kg_failed_attempts_almost_at_login" product="default" msgid="5270861875006378092">"Нацртали сте шаблон за откључавање нетачно <xliff:g id="NUMBER_0">%1$d</xliff:g> пута. После још <xliff:g id="NUMBER_1">%2$d</xliff:g> неуспешна(их) покушаја, од вас ће бити затражено да откључате телефон помоћу налога е-поште.\n\nПробајте поново за <xliff:g id="NUMBER_2">%3$d</xliff:g> секунде/и."</string>
<string name="kg_text_message_separator" product="default" msgid="4503708889934976866">" – "</string>
<string name="kg_reordering_delete_drop_target_text" msgid="2034358143731750914">"Уклони"</string>
+ <!-- no translation found for allow_while_in_use_permission_in_fgs (4101339676785053656) -->
+ <skip />
<string name="safe_media_volume_warning" product="default" msgid="3751676824423049994">"Желите да појачате звук изнад препорученог нивоа?\n\nСлушање гласне музике дуже време може да вам оштети слух."</string>
<string name="accessibility_shortcut_warning_dialog_title" msgid="4017995837692622933">"Желите ли да користите пречицу за приступачност?"</string>
<string name="accessibility_shortcut_toogle_warning" msgid="8306239551412868396">"Када је пречица укључена, притисните оба дугмета за јачину звука да бисте покренули функцију приступачности.\n\n Актуелна функција приступачности:\n <xliff:g id="SERVICE_NAME">%1$s</xliff:g>\n\n Можете да промените функцију у одељку Подешавања &gt; Приступачност."</string>
diff --git a/core/res/res/values-sv/strings.xml b/core/res/res/values-sv/strings.xml
index 4332352909f6..9c7e1e5ecc4b 100644
--- a/core/res/res/values-sv/strings.xml
+++ b/core/res/res/values-sv/strings.xml
@@ -1613,6 +1613,8 @@
<string name="kg_failed_attempts_almost_at_login" product="default" msgid="5270861875006378092">"Du har ritat ditt grafiska lösenord fel <xliff:g id="NUMBER_0">%1$d</xliff:g> gånger. Efter ytterligare <xliff:g id="NUMBER_1">%2$d</xliff:g> försök ombeds du låsa upp mobilen med hjälp av ett e-postkonto.\n\n Försök igen om <xliff:g id="NUMBER_2">%3$d</xliff:g> sekunder."</string>
<string name="kg_text_message_separator" product="default" msgid="4503708889934976866">" – "</string>
<string name="kg_reordering_delete_drop_target_text" msgid="2034358143731750914">"Ta bort"</string>
+ <!-- no translation found for allow_while_in_use_permission_in_fgs (4101339676785053656) -->
+ <skip />
<string name="safe_media_volume_warning" product="default" msgid="3751676824423049994">"Vill du höja volymen över den rekommenderade nivån?\n\nAtt lyssna med stark volym långa stunder åt gången kan skada hörseln."</string>
<string name="accessibility_shortcut_warning_dialog_title" msgid="4017995837692622933">"Vill du använda Aktivera tillgänglighet snabbt?"</string>
<string name="accessibility_shortcut_toogle_warning" msgid="8306239551412868396">"När kortkommandot har aktiverats startar du en tillgänglighetsfunktion genom att trycka ned båda volymknapparna i tre sekunder.\n\n Aktuell tillgänglighetsfunktion:\n <xliff:g id="SERVICE_NAME">%1$s</xliff:g>\n\n Du kan ändra funktionen i Inställningar &gt; Tillgänglighet."</string>
diff --git a/core/res/res/values-sw/strings.xml b/core/res/res/values-sw/strings.xml
index d1bff3f55906..f53efe068aec 100644
--- a/core/res/res/values-sw/strings.xml
+++ b/core/res/res/values-sw/strings.xml
@@ -1613,6 +1613,8 @@
<string name="kg_failed_attempts_almost_at_login" product="default" msgid="5270861875006378092">"Umekosea kuchora mchoro wako wa kufungua mara <xliff:g id="NUMBER_0">%1$d</xliff:g>. Baada ya majaribio <xliff:g id="NUMBER_1">%2$d</xliff:g> yasiyofaulu, utaombwa kufungua simu yako kwa kutumia akaunti ya barua pepe.\n\n Jaribu tena baada ya sekunde <xliff:g id="NUMBER_2">%3$d</xliff:g>."</string>
<string name="kg_text_message_separator" product="default" msgid="4503708889934976866">" — "</string>
<string name="kg_reordering_delete_drop_target_text" msgid="2034358143731750914">"Ondoa"</string>
+ <!-- no translation found for allow_while_in_use_permission_in_fgs (4101339676785053656) -->
+ <skip />
<string name="safe_media_volume_warning" product="default" msgid="3751676824423049994">"Ungependa kupandisha sauti zaidi ya kiwango kinachopendekezwa?\n\nKusikiliza kwa sauti ya juu kwa muda mrefu kunaweza kuharibu uwezo wako wa kusikia."</string>
<string name="accessibility_shortcut_warning_dialog_title" msgid="4017995837692622933">"Ungependa kutumia njia ya mkato ya ufikivu?"</string>
<string name="accessibility_shortcut_toogle_warning" msgid="8306239551412868396">"Unapowasha kipengele cha njia ya mkato, hatua ya kubonyeza vitufe vyote viwili vya sauti kwa dakika 3 itafungua kipengele cha ufikivu.\n\n Kipengele cha ufikivu kilichopo kwa sasa:\n <xliff:g id="SERVICE_NAME">%1$s</xliff:g>\n\n Unaweza kubadilisha kipengele hiki katika Mipangilio &gt; Zana za ufikivu."</string>
diff --git a/core/res/res/values-ta/strings.xml b/core/res/res/values-ta/strings.xml
index 5444f36081f3..c7c4ea3c6a5b 100644
--- a/core/res/res/values-ta/strings.xml
+++ b/core/res/res/values-ta/strings.xml
@@ -1613,6 +1613,8 @@
<string name="kg_failed_attempts_almost_at_login" product="default" msgid="5270861875006378092">"திறப்பதற்கான வடிவத்தை <xliff:g id="NUMBER_0">%1$d</xliff:g> முறை தவறாக வரைந்துள்ளீர்கள். மேலும் <xliff:g id="NUMBER_1">%2$d</xliff:g> தோல்வி முயற்சிகளுக்குப் பிறகு, மின்னஞ்சல் கணக்கைப் பயன்படுத்தி உங்கள் மொபைலைத் திறக்கக் கேட்கப்படுவீர்கள்.\n\n <xliff:g id="NUMBER_2">%3$d</xliff:g> வினாடிகள் கழித்து முயற்சிக்கவும்."</string>
<string name="kg_text_message_separator" product="default" msgid="4503708889934976866">" — "</string>
<string name="kg_reordering_delete_drop_target_text" msgid="2034358143731750914">"அகற்று"</string>
+ <!-- no translation found for allow_while_in_use_permission_in_fgs (4101339676785053656) -->
+ <skip />
<string name="safe_media_volume_warning" product="default" msgid="3751676824423049994">"பரிந்துரைத்த அளவை விட ஒலியை அதிகரிக்கவா?\n\nநீண்ட நேரத்திற்கு அதிகளவில் ஒலி கேட்பது கேட்கும் திறனைப் பாதிக்கலாம்."</string>
<string name="accessibility_shortcut_warning_dialog_title" msgid="4017995837692622933">"அணுகல்தன்மை ஷார்ட்கட்டைப் பயன்படுத்தவா?"</string>
<string name="accessibility_shortcut_toogle_warning" msgid="8306239551412868396">"ஷார்ட்கட் இயக்கத்தில் இருந்தால், இரண்டு ஒலியளவு பொத்தான்களையும் 3 வினாடிகள் அழுத்தி, அணுகல்தன்மை அம்சத்தை இயக்கலாம்.\n\n தற்போதைய அணுகல்தன்மை அம்சம்:\n <xliff:g id="SERVICE_NAME">%1$s</xliff:g>\n\n அமைப்புகள் &gt; அணுகல்தன்மை என்பதற்குச் சென்று, அம்சத்தை மாற்றலாம்."</string>
diff --git a/core/res/res/values-te/strings.xml b/core/res/res/values-te/strings.xml
index f9f6479eece2..9c77ccd01b92 100644
--- a/core/res/res/values-te/strings.xml
+++ b/core/res/res/values-te/strings.xml
@@ -1613,6 +1613,8 @@
<string name="kg_failed_attempts_almost_at_login" product="default" msgid="5270861875006378092">"మీరు మీ అన్‌లాక్ నమూనాను <xliff:g id="NUMBER_0">%1$d</xliff:g> సార్లు తప్పుగా గీసారు. మరో <xliff:g id="NUMBER_1">%2$d</xliff:g> విఫల ప్రయత్నాల తర్వాత, ఇమెయిల్ ఖాతాను ఉపయోగించి మీ ఫోన్‌ను అన్‌లాక్ చేయాల్సిందిగా మిమ్మల్ని అడుగుతారు.\n\n <xliff:g id="NUMBER_2">%3$d</xliff:g> సెకన్లలో మళ్లీ ప్రయత్నించండి."</string>
<string name="kg_text_message_separator" product="default" msgid="4503708889934976866">" — "</string>
<string name="kg_reordering_delete_drop_target_text" msgid="2034358143731750914">"తీసివేయి"</string>
+ <!-- no translation found for allow_while_in_use_permission_in_fgs (4101339676785053656) -->
+ <skip />
<string name="safe_media_volume_warning" product="default" msgid="3751676824423049994">"వాల్యూమ్‌ను సిఫార్సు చేయబడిన స్థాయి కంటే ఎక్కువగా పెంచాలా?\n\nసుదీర్ఘ వ్యవధుల పాటు అధిక వాల్యూమ్‌లో వినడం వలన మీ వినికిడి శక్తి దెబ్బ తినవచ్చు."</string>
<string name="accessibility_shortcut_warning_dialog_title" msgid="4017995837692622933">"యాక్సెస్ సామర్థ్యం షార్ట్‌కట్‌ను ఉపయోగించాలా?"</string>
<string name="accessibility_shortcut_toogle_warning" msgid="8306239551412868396">"షార్ట్‌కట్ ఆన్‌లో ఉన్నప్పుడు, రెండు వాల్యూమ్ బటన్‌లను 3 సెకన్ల పాటు నొక్కితే యాక్సెస్ సామర్థ్య ఫీచర్ ప్రారంభం అవుతుంది.\n\n ప్రస్తుత యాక్సెస్ సామర్థ్య ఫీచర్:\n <xliff:g id="SERVICE_NAME">%1$s</xliff:g>\n\n సెట్టింగ్‌లు &gt; యాక్సెస్ సామర్థ్యంలో మీరు ఫీచర్‌ను మార్చవచ్చు."</string>
@@ -1987,8 +1989,7 @@
<string name="mime_type_spreadsheet_ext" msgid="8720173181137254414">"<xliff:g id="EXTENSION">%1$s</xliff:g> స్ప్రెడ్‌షీట్"</string>
<string name="mime_type_presentation" msgid="1145384236788242075">"ప్రదర్శన"</string>
<string name="mime_type_presentation_ext" msgid="8761049335564371468">"<xliff:g id="EXTENSION">%1$s</xliff:g> ప్రదర్శన"</string>
- <!-- no translation found for bluetooth_airplane_mode_toast (2066399056595768554) -->
- <skip />
+ <string name="bluetooth_airplane_mode_toast" msgid="2066399056595768554">"విమానం మోడ్‌లో బ్లూటూత్ ఆన్‌లో ఉంటుంది"</string>
<string name="car_loading_profile" msgid="8219978381196748070">"లోడవుతోంది"</string>
<plurals name="file_count" formatted="false" msgid="7063513834724389247">
<item quantity="other"><xliff:g id="FILE_NAME_2">%s</xliff:g> + <xliff:g id="COUNT_3">%d</xliff:g> ఫైల్‌లు</item>
@@ -2009,8 +2010,7 @@
<!-- no translation found for accessibility_system_action_accessibility_menu_label (8436484650391125184) -->
<skip />
<string name="accessibility_freeform_caption" msgid="8377519323496290122">"<xliff:g id="APP_NAME">%1$s</xliff:g> క్యాప్షన్ బార్."</string>
- <!-- no translation found for as_app_forced_to_restricted_bucket (8233871289353898964) -->
- <skip />
+ <string name="as_app_forced_to_restricted_bucket" msgid="8233871289353898964">"<xliff:g id="PACKAGE_NAME">%1$s</xliff:g> పరిమితం చేయబడిన బకెట్‌లో ఉంచబడింది"</string>
<!-- no translation found for resolver_personal_tab (2051260504014442073) -->
<skip />
<!-- no translation found for resolver_work_tab (2690019516263167035) -->
diff --git a/core/res/res/values-th/strings.xml b/core/res/res/values-th/strings.xml
index 7796b03b1ae0..efff25bf74c8 100644
--- a/core/res/res/values-th/strings.xml
+++ b/core/res/res/values-th/strings.xml
@@ -1613,6 +1613,8 @@
<string name="kg_failed_attempts_almost_at_login" product="default" msgid="5270861875006378092">"คุณวาดรูปแบบการปลดล็อกไม่ถูกต้อง <xliff:g id="NUMBER_0">%1$d</xliff:g> ครั้งแล้ว หากทำไม่สำเร็จอีก <xliff:g id="NUMBER_1">%2$d</xliff:g> ครั้ง ระบบจะขอให้คุณปลดล็อกโทรศัพท์โดยใช้ับัญชีอีเมล\n\n โปรดลองอีกครั้งในอีก <xliff:g id="NUMBER_2">%3$d</xliff:g> วินาที"</string>
<string name="kg_text_message_separator" product="default" msgid="4503708889934976866">" — "</string>
<string name="kg_reordering_delete_drop_target_text" msgid="2034358143731750914">"ลบ"</string>
+ <!-- no translation found for allow_while_in_use_permission_in_fgs (4101339676785053656) -->
+ <skip />
<string name="safe_media_volume_warning" product="default" msgid="3751676824423049994">"นี่เป็นการเพิ่มระดับเสียงเกินระดับที่แนะนำ\n\nการฟังเสียงดังเป็นเวลานานอาจทำให้การได้ยินของคุณบกพร่องได้"</string>
<string name="accessibility_shortcut_warning_dialog_title" msgid="4017995837692622933">"ใช้ทางลัดการช่วยเหลือพิเศษไหม"</string>
<string name="accessibility_shortcut_toogle_warning" msgid="8306239551412868396">"เมื่อทางลัดเปิดอยู่ การกดปุ่มปรับระดับเสียงทั้ง 2 ปุ่มเป็นเวลา 3 วินาทีจะเริ่มฟีเจอร์การช่วยเหลือพิเศษ\n\n ฟีเจอร์การช่วยเหลือพิเศษปัจจุบัน:\n <xliff:g id="SERVICE_NAME">%1$s</xliff:g>\n\n คุณสามารถเปลี่ยนฟีเจอร์ในการตั้งค่า &gt; การช่วยเหลือพิเศษ"</string>
diff --git a/core/res/res/values-tl/strings.xml b/core/res/res/values-tl/strings.xml
index d15bcd7f48d7..7cfa7b02ebaa 100644
--- a/core/res/res/values-tl/strings.xml
+++ b/core/res/res/values-tl/strings.xml
@@ -1613,6 +1613,8 @@
<string name="kg_failed_attempts_almost_at_login" product="default" msgid="5270861875006378092">"Naguhit mo nang hindi tama ang iyong pattern sa pag-unlock nang <xliff:g id="NUMBER_0">%1$d</xliff:g> (na) beses. Pagkatapos ng <xliff:g id="NUMBER_1">%2$d</xliff:g> pang hindi matagumpay na pagtatangka, hihilingin sa iyong i-unlock ang telepono mo gamit ang isang email account.\n\n Subukang muli sa loob ng <xliff:g id="NUMBER_2">%3$d</xliff:g> (na) segundo."</string>
<string name="kg_text_message_separator" product="default" msgid="4503708889934976866">" — "</string>
<string name="kg_reordering_delete_drop_target_text" msgid="2034358143731750914">"Alisin"</string>
+ <!-- no translation found for allow_while_in_use_permission_in_fgs (4101339676785053656) -->
+ <skip />
<string name="safe_media_volume_warning" product="default" msgid="3751676824423049994">"Lakasan ang volume nang lagpas sa inirerekomendang antas?\n\nMaaaring mapinsala ng pakikinig sa malakas na volume sa loob ng mahahabang panahon ang iyong pandinig."</string>
<string name="accessibility_shortcut_warning_dialog_title" msgid="4017995837692622933">"Gagamitin ang Shortcut sa Pagiging Accessible?"</string>
<string name="accessibility_shortcut_toogle_warning" msgid="8306239551412868396">"Kapag naka-on ang shortcut, magsisimula ang isang feature ng pagiging naa-access kapag pinindot ang parehong button ng volume sa loob ng 3 segundo.\n\n Kasalukuyang feature ng pagiging naa-access:\n <xliff:g id="SERVICE_NAME">%1$s</xliff:g>\n\n Maaari mong baguhin ang feature sa Mga Setting &gt; Pagiging Accessible."</string>
diff --git a/core/res/res/values-tr/strings.xml b/core/res/res/values-tr/strings.xml
index e5559c507c30..b47eb4bb025c 100644
--- a/core/res/res/values-tr/strings.xml
+++ b/core/res/res/values-tr/strings.xml
@@ -1613,6 +1613,8 @@
<string name="kg_failed_attempts_almost_at_login" product="default" msgid="5270861875006378092">"Kilit açma deseninizi <xliff:g id="NUMBER_0">%1$d</xliff:g> kez yanlış çizdiniz. <xliff:g id="NUMBER_1">%2$d</xliff:g> başarısız denemeden sonra telefonunuzu bir e-posta hesabı kullanarak açmanız istenir.\n\n <xliff:g id="NUMBER_2">%3$d</xliff:g> saniye içinde tekrar deneyin."</string>
<string name="kg_text_message_separator" product="default" msgid="4503708889934976866">" — "</string>
<string name="kg_reordering_delete_drop_target_text" msgid="2034358143731750914">"Kaldır"</string>
+ <!-- no translation found for allow_while_in_use_permission_in_fgs (4101339676785053656) -->
+ <skip />
<string name="safe_media_volume_warning" product="default" msgid="3751676824423049994">"Ses seviyesi önerilen düzeyin üzerine yükseltilsin mi?\n\nUzun süre yüksek ses seviyesinde dinlemek işitme duyunuza zarar verebilir."</string>
<string name="accessibility_shortcut_warning_dialog_title" msgid="4017995837692622933">"Erişilebilirlik Kısayolu Kullanılsın mı?"</string>
<string name="accessibility_shortcut_toogle_warning" msgid="8306239551412868396">"Kısayol açık olduğunda, ses düğmelerinin ikisini birden 3 saniyeliğine basılı tutmanız bir erişilebilirlik özelliğini başlatır.\n\n Geçerli erişilebilirlik özelliği:\n <xliff:g id="SERVICE_NAME">%1$s</xliff:g>\n\n Özelliği, Ayarlar &gt; Erişilebilirlik seçeneğinden değiştirebilirsiniz."</string>
diff --git a/core/res/res/values-uk/strings.xml b/core/res/res/values-uk/strings.xml
index 7c06892e070c..22aa199d9964 100644
--- a/core/res/res/values-uk/strings.xml
+++ b/core/res/res/values-uk/strings.xml
@@ -1657,6 +1657,8 @@
<string name="kg_failed_attempts_almost_at_login" product="default" msgid="5270861875006378092">"Ключ розблокування неправильно намальовано стільки разів: <xliff:g id="NUMBER_0">%1$d</xliff:g>. У вас є ще стільки спроб: <xliff:g id="NUMBER_1">%2$d</xliff:g>. У разі невдачі з’явиться запит розблокувати телефон за допомогою облікового запису електронної пошти.\n\n Повторіть спробу через <xliff:g id="NUMBER_2">%3$d</xliff:g> сек."</string>
<string name="kg_text_message_separator" product="default" msgid="4503708889934976866">" – "</string>
<string name="kg_reordering_delete_drop_target_text" msgid="2034358143731750914">"Вилучити"</string>
+ <!-- no translation found for allow_while_in_use_permission_in_fgs (4101339676785053656) -->
+ <skip />
<string name="safe_media_volume_warning" product="default" msgid="3751676824423049994">"Збільшити гучність понад рекомендований рівень?\n\nЯкщо слухати надто гучну музику тривалий час, можна пошкодити слух."</string>
<string name="accessibility_shortcut_warning_dialog_title" msgid="4017995837692622933">"Використовувати швидке ввімкнення?"</string>
<string name="accessibility_shortcut_toogle_warning" msgid="8306239551412868396">"Коли ярлик увімкнено, після натискання обох клавіш гучності й утримування їх протягом 3 секунд увімкнеться функція спеціальних можливостей.\n\n Поточна функція спеціальних можливостей:\n <xliff:g id="SERVICE_NAME">%1$s</xliff:g>\n\n Цю функцію можна змінити в меню \"Налаштування\" &gt; \"Спеціальні можливості\"."</string>
diff --git a/core/res/res/values-ur/strings.xml b/core/res/res/values-ur/strings.xml
index 1b007200def6..a88d6c504964 100644
--- a/core/res/res/values-ur/strings.xml
+++ b/core/res/res/values-ur/strings.xml
@@ -1613,6 +1613,8 @@
<string name="kg_failed_attempts_almost_at_login" product="default" msgid="5270861875006378092">"آپ نے اپنا غیر مقفل کرنے کا پیٹرن <xliff:g id="NUMBER_0">%1$d</xliff:g> بار غلط طریقے سے ڈرا کیا ہے۔ <xliff:g id="NUMBER_1">%2$d</xliff:g> مزید ناکام کوششوں کے بعد، آپ سے ایک ای میل اکاؤنٹ استعمال کرکے اپنا فون غیر مقفل کرنے کو کہا جائے گا۔\n\n <xliff:g id="NUMBER_2">%3$d</xliff:g> سیکنڈ میں دوبارہ کوشش کریں۔"</string>
<string name="kg_text_message_separator" product="default" msgid="4503708889934976866">" — "</string>
<string name="kg_reordering_delete_drop_target_text" msgid="2034358143731750914">"ہٹائیں"</string>
+ <!-- no translation found for allow_while_in_use_permission_in_fgs (4101339676785053656) -->
+ <skip />
<string name="safe_media_volume_warning" product="default" msgid="3751676824423049994">"والیوم کو تجویز کردہ سطح سے زیادہ کریں؟\n\nزیادہ وقت تک اونچی آواز میں سننے سے آپ کی سماعت کو نقصان پہنچ سکتا ہے۔"</string>
<string name="accessibility_shortcut_warning_dialog_title" msgid="4017995837692622933">"ایکسیسبیلٹی شارٹ کٹ استعمال کریں؟"</string>
<string name="accessibility_shortcut_toogle_warning" msgid="8306239551412868396">"شارٹ کٹ آن ہونے پر، 3 سیکنڈ تک دونوں والیوم بٹنز کو دبانے سے ایک ایکسیسبیلٹی خصوصیت شروع ہو جائے گی۔\n\n موجودہ ایکسیسبیلٹی خصوصیت:\n <xliff:g id="SERVICE_NAME">%1$s</xliff:g>\n\n آپ خصوصیت کو ترتیبات &gt; ایکسیسبیلٹی میں جا کر تبدیل کر سکتے ہیں۔"</string>
@@ -1987,8 +1989,7 @@
<string name="mime_type_spreadsheet_ext" msgid="8720173181137254414">"<xliff:g id="EXTENSION">%1$s</xliff:g> اسپریڈشیٹ"</string>
<string name="mime_type_presentation" msgid="1145384236788242075">"پیشکش"</string>
<string name="mime_type_presentation_ext" msgid="8761049335564371468">"<xliff:g id="EXTENSION">%1$s</xliff:g> پیشکش"</string>
- <!-- no translation found for bluetooth_airplane_mode_toast (2066399056595768554) -->
- <skip />
+ <string name="bluetooth_airplane_mode_toast" msgid="2066399056595768554">"ہوائی جہاز وضع کے دوران بلوٹوتھ آن رہے گا"</string>
<string name="car_loading_profile" msgid="8219978381196748070">"لوڈ ہو رہا ہے"</string>
<plurals name="file_count" formatted="false" msgid="7063513834724389247">
<item quantity="other"><xliff:g id="FILE_NAME_2">%s</xliff:g> + <xliff:g id="COUNT_3">%d</xliff:g> فائلز</item>
@@ -2009,8 +2010,7 @@
<!-- no translation found for accessibility_system_action_accessibility_menu_label (8436484650391125184) -->
<skip />
<string name="accessibility_freeform_caption" msgid="8377519323496290122">"<xliff:g id="APP_NAME">%1$s</xliff:g> کی کیپشن بار۔"</string>
- <!-- no translation found for as_app_forced_to_restricted_bucket (8233871289353898964) -->
- <skip />
+ <string name="as_app_forced_to_restricted_bucket" msgid="8233871289353898964">"<xliff:g id="PACKAGE_NAME">%1$s</xliff:g> کو پابند کردہ بکٹ میں رکھ دیا گیا ہے"</string>
<!-- no translation found for resolver_personal_tab (2051260504014442073) -->
<skip />
<!-- no translation found for resolver_work_tab (2690019516263167035) -->
diff --git a/core/res/res/values-uz/strings.xml b/core/res/res/values-uz/strings.xml
index 1193d278774d..a08b1788c5d6 100644
--- a/core/res/res/values-uz/strings.xml
+++ b/core/res/res/values-uz/strings.xml
@@ -1613,6 +1613,8 @@
<string name="kg_failed_attempts_almost_at_login" product="default" msgid="5270861875006378092">"Siz grafik kalitni <xliff:g id="NUMBER_0">%1$d</xliff:g> marta noto‘g‘ri chizdingiz. <xliff:g id="NUMBER_1">%2$d</xliff:g> marta muvaffaqiyatsiz urinishdan so‘ng, sizdan e-pochtangizdan foydalanib, telefon qulfini ochishingiz so‘raladi.\n\n <xliff:g id="NUMBER_2">%3$d</xliff:g> soniyadan so‘ng yana urinib ko‘ring."</string>
<string name="kg_text_message_separator" product="default" msgid="4503708889934976866">" — "</string>
<string name="kg_reordering_delete_drop_target_text" msgid="2034358143731750914">"Olib tashlash"</string>
+ <!-- no translation found for allow_while_in_use_permission_in_fgs (4101339676785053656) -->
+ <skip />
<string name="safe_media_volume_warning" product="default" msgid="3751676824423049994">"Tovush balandligi tavsiya etilgan darajadan ham yuqori qilinsinmi?\n\nUzoq vaqt davomida baland ovozda tinglash eshitish qobiliyatingizga salbiy ta’sir ko‘rsatishi mumkin."</string>
<string name="accessibility_shortcut_warning_dialog_title" msgid="4017995837692622933">"Tezkor ishga tushirishdan foydalanilsinmi?"</string>
<string name="accessibility_shortcut_toogle_warning" msgid="8306239551412868396">"Maxsus imkoniyatlar funksiyasidan foydalanish uchun u yoniqligida ikkala ovoz balandligini boshqarish tugmasini 3 soniya bosib turing.\n\n Joriy maxsus imkoniyatlar funksiyasi:\n <xliff:g id="SERVICE_NAME">%1$s</xliff:g>\n\n Bu funksiyani Sozlamalar &gt; Maxsus imkoniyatlar orqali o‘zgartirish mumkin."</string>
diff --git a/core/res/res/values-vi/strings.xml b/core/res/res/values-vi/strings.xml
index 7cfd87051a2b..db55613dce7e 100644
--- a/core/res/res/values-vi/strings.xml
+++ b/core/res/res/values-vi/strings.xml
@@ -1613,6 +1613,8 @@
<string name="kg_failed_attempts_almost_at_login" product="default" msgid="5270861875006378092">"Bạn đã <xliff:g id="NUMBER_0">%1$d</xliff:g> lần vẽ không chính xác hình mở khóa của mình. Sau <xliff:g id="NUMBER_1">%2$d</xliff:g> lần thử không thành công nữa, bạn sẽ được yêu cầu mở khóa điện thoại bằng tài khoản email.\n\n Vui lòng thử lại sau <xliff:g id="NUMBER_2">%3$d</xliff:g> giây."</string>
<string name="kg_text_message_separator" product="default" msgid="4503708889934976866">" — "</string>
<string name="kg_reordering_delete_drop_target_text" msgid="2034358143731750914">"Xóa"</string>
+ <!-- no translation found for allow_while_in_use_permission_in_fgs (4101339676785053656) -->
+ <skip />
<string name="safe_media_volume_warning" product="default" msgid="3751676824423049994">"Bạn tăng âm lượng lên quá mức khuyên dùng?\n\nViệc nghe ở mức âm lượng cao trong thời gian dài có thể gây tổn thương thính giác của bạn."</string>
<string name="accessibility_shortcut_warning_dialog_title" msgid="4017995837692622933">"Sử dụng phím tắt Hỗ trợ tiếp cận?"</string>
<string name="accessibility_shortcut_toogle_warning" msgid="8306239551412868396">"Khi phím tắt được bật, nhấn cả hai nút âm lượng trong 3 giây sẽ bắt đầu một tính năng trợ năng.\n\n Tính năng trợ năng hiện tại:\n <xliff:g id="SERVICE_NAME">%1$s</xliff:g>\n\n Bạn có thể thay đổi tính năng trong Cài đặt &gt; Trợ năng."</string>
diff --git a/core/res/res/values-zh-rCN/strings.xml b/core/res/res/values-zh-rCN/strings.xml
index 5d7ad5ad0361..08d232683c57 100644
--- a/core/res/res/values-zh-rCN/strings.xml
+++ b/core/res/res/values-zh-rCN/strings.xml
@@ -1613,6 +1613,8 @@
<string name="kg_failed_attempts_almost_at_login" product="default" msgid="5270861875006378092">"您已连续 <xliff:g id="NUMBER_0">%1$d</xliff:g> 次画错解锁图案。如果再尝试 <xliff:g id="NUMBER_1">%2$d</xliff:g> 次后仍不成功,系统就会要求您使用自己的电子邮件帐号解锁手机。\n\n请在 <xliff:g id="NUMBER_2">%3$d</xliff:g> 秒后重试。"</string>
<string name="kg_text_message_separator" product="default" msgid="4503708889934976866">" — "</string>
<string name="kg_reordering_delete_drop_target_text" msgid="2034358143731750914">"删除"</string>
+ <!-- no translation found for allow_while_in_use_permission_in_fgs (4101339676785053656) -->
+ <skip />
<string name="safe_media_volume_warning" product="default" msgid="3751676824423049994">"要将音量调高到建议的音量以上吗?\n\n长时间保持高音量可能会损伤听力。"</string>
<string name="accessibility_shortcut_warning_dialog_title" msgid="4017995837692622933">"要使用无障碍快捷方式吗?"</string>
<string name="accessibility_shortcut_toogle_warning" msgid="8306239551412868396">"开启快捷方式后,同时按下两个音量按钮 3 秒钟即可启动所设定的无障碍功能。\n\n当前设定的无障碍功能:\n<xliff:g id="SERVICE_NAME">%1$s</xliff:g>\n\n如需更改设定的功能,请依次转到“设置”&gt;“无障碍”。"</string>
@@ -1987,8 +1989,7 @@
<string name="mime_type_spreadsheet_ext" msgid="8720173181137254414">"<xliff:g id="EXTENSION">%1$s</xliff:g> 电子表格"</string>
<string name="mime_type_presentation" msgid="1145384236788242075">"演示文稿"</string>
<string name="mime_type_presentation_ext" msgid="8761049335564371468">"<xliff:g id="EXTENSION">%1$s</xliff:g> 演示文稿"</string>
- <!-- no translation found for bluetooth_airplane_mode_toast (2066399056595768554) -->
- <skip />
+ <string name="bluetooth_airplane_mode_toast" msgid="2066399056595768554">"在飞行模式下,蓝牙将保持开启状态"</string>
<string name="car_loading_profile" msgid="8219978381196748070">"正在加载"</string>
<plurals name="file_count" formatted="false" msgid="7063513834724389247">
<item quantity="other"><xliff:g id="FILE_NAME_2">%s</xliff:g> + <xliff:g id="COUNT_3">%d</xliff:g> 个文件</item>
@@ -2009,8 +2010,7 @@
<!-- no translation found for accessibility_system_action_accessibility_menu_label (8436484650391125184) -->
<skip />
<string name="accessibility_freeform_caption" msgid="8377519323496290122">"<xliff:g id="APP_NAME">%1$s</xliff:g>的标题栏。"</string>
- <!-- no translation found for as_app_forced_to_restricted_bucket (8233871289353898964) -->
- <skip />
+ <string name="as_app_forced_to_restricted_bucket" msgid="8233871289353898964">"<xliff:g id="PACKAGE_NAME">%1$s</xliff:g> 已被放入受限存储分区"</string>
<!-- no translation found for resolver_personal_tab (2051260504014442073) -->
<skip />
<!-- no translation found for resolver_work_tab (2690019516263167035) -->
diff --git a/core/res/res/values-zh-rHK/strings.xml b/core/res/res/values-zh-rHK/strings.xml
index 361eb4c66029..ea106c6c2a78 100644
--- a/core/res/res/values-zh-rHK/strings.xml
+++ b/core/res/res/values-zh-rHK/strings.xml
@@ -1613,6 +1613,8 @@
<string name="kg_failed_attempts_almost_at_login" product="default" msgid="5270861875006378092">"您已畫錯解鎖圖案 <xliff:g id="NUMBER_0">%1$d</xliff:g> 次,如果再嘗試 <xliff:g id="NUMBER_1">%2$d</xliff:g> 次仍未成功,系統會要求您透過電郵帳戶解開上鎖的手機。\n\n請在 <xliff:g id="NUMBER_2">%3$d</xliff:g> 秒後再試一次。"</string>
<string name="kg_text_message_separator" product="default" msgid="4503708889934976866">" — "</string>
<string name="kg_reordering_delete_drop_target_text" msgid="2034358143731750914">"移除"</string>
+ <!-- no translation found for allow_while_in_use_permission_in_fgs (4101339676785053656) -->
+ <skip />
<string name="safe_media_volume_warning" product="default" msgid="3751676824423049994">"要調高音量 (比建議的音量更大聲) 嗎?\n\n長時間聆聽高分貝音量可能會導致您的聽力受損。"</string>
<string name="accessibility_shortcut_warning_dialog_title" msgid="4017995837692622933">"要使用無障礙功能快速鍵嗎?"</string>
<string name="accessibility_shortcut_toogle_warning" msgid="8306239551412868396">"快速鍵開啟後,同時按住音量按鈕 3 秒,無障礙功能便會啟用。\n\n目前的無障礙功能:\n <xliff:g id="SERVICE_NAME">%1$s</xliff:g>\n\n如要變更功能,請前往「設定」&gt;「無障礙功能」。"</string>
diff --git a/core/res/res/values-zh-rTW/strings.xml b/core/res/res/values-zh-rTW/strings.xml
index 2bcae59eb4cf..70651816bfbd 100644
--- a/core/res/res/values-zh-rTW/strings.xml
+++ b/core/res/res/values-zh-rTW/strings.xml
@@ -1613,6 +1613,8 @@
<string name="kg_failed_attempts_almost_at_login" product="default" msgid="5270861875006378092">"你的解鎖圖案已畫錯 <xliff:g id="NUMBER_0">%1$d</xliff:g> 次,如果再嘗試 <xliff:g id="NUMBER_1">%2$d</xliff:g> 次仍未成功,系統就會要求你透過電子郵件帳戶解除手機的鎖定狀態。\n\n請在 <xliff:g id="NUMBER_2">%3$d</xliff:g> 秒後再試一次。"</string>
<string name="kg_text_message_separator" product="default" msgid="4503708889934976866">" — "</string>
<string name="kg_reordering_delete_drop_target_text" msgid="2034358143731750914">"移除"</string>
+ <!-- no translation found for allow_while_in_use_permission_in_fgs (4101339676785053656) -->
+ <skip />
<string name="safe_media_volume_warning" product="default" msgid="3751676824423049994">"要調高音量,比建議的音量更大聲嗎?\n\n長時間聆聽高分貝音量可能會使你的聽力受損。"</string>
<string name="accessibility_shortcut_warning_dialog_title" msgid="4017995837692622933">"要使用無障礙捷徑嗎?"</string>
<string name="accessibility_shortcut_toogle_warning" msgid="8306239551412868396">"啟用捷徑功能後,只要同時按下兩個音量鍵 3 秒,就能啟動無障礙功能。\n\n 目前設定的無障礙功能為:\n<xliff:g id="SERVICE_NAME">%1$s</xliff:g>\n\n 如要變更設定的功能,請依序輕觸 [設定] &gt; [無障礙設定]。"</string>
diff --git a/core/res/res/values-zu/strings.xml b/core/res/res/values-zu/strings.xml
index e96696ea0f4a..1cb67e162da7 100644
--- a/core/res/res/values-zu/strings.xml
+++ b/core/res/res/values-zu/strings.xml
@@ -1613,6 +1613,8 @@
<string name="kg_failed_attempts_almost_at_login" product="default" msgid="5270861875006378092">"Ukulayisha ungenisa iphathini yakho yokuvula ngendlela engalungile izikhathi ezi-<xliff:g id="NUMBER_0">%1$d</xliff:g> Emva kweminye imizamo engu-<xliff:g id="NUMBER_1">%2$d</xliff:g>, uzocelwa ukuvula ifoni yakho usebenzisa ukungena ngemvume ku-Google\n\n Zame futhi emumva kwengu- <xliff:g id="NUMBER_2">%3$d</xliff:g> amasekhondi."</string>
<string name="kg_text_message_separator" product="default" msgid="4503708889934976866">" — "</string>
<string name="kg_reordering_delete_drop_target_text" msgid="2034358143731750914">"Susa"</string>
+ <!-- no translation found for allow_while_in_use_permission_in_fgs (4101339676785053656) -->
+ <skip />
<string name="safe_media_volume_warning" product="default" msgid="3751676824423049994">"Khuphukisa ivolumu ngaphezu kweleveli enconyiwe?\n\nUkulalela ngevolumu ephezulu izikhathi ezide kungahle kulimaze ukuzwa kwakho."</string>
<string name="accessibility_shortcut_warning_dialog_title" msgid="4017995837692622933">"Sebenzisa isinqamuleli sokufinyelela?"</string>
<string name="accessibility_shortcut_toogle_warning" msgid="8306239551412868396">"Uma kuvulwe isinqamuleli, ukucindezela zombili izinkinobho zevolumu amasekhondi angu-3 kuzoqala isici sokufinyelela.\n\n Isici samanje sokufinyelela:\n <xliff:g id="SERVICE_NAME">%1$s</xliff:g>\n\n Ungashintsha isici kuzilungiselelo &gt; Ukufinyelela."</string>
diff --git a/core/tests/coretests/Android.bp b/core/tests/coretests/Android.bp
index 3836d6f4115d..38454ecb6111 100644
--- a/core/tests/coretests/Android.bp
+++ b/core/tests/coretests/Android.bp
@@ -41,6 +41,7 @@ android_test {
"truth-prebuilt",
"print-test-util-lib",
"testng",
+ "servicestests-utils"
],
libs: [
diff --git a/core/tests/coretests/src/android/app/PullAtomMetadataTest.java b/core/tests/coretests/src/android/app/PullAtomMetadataTest.java
index 2e3f8928d9c2..0ae613400b18 100644
--- a/core/tests/coretests/src/android/app/PullAtomMetadataTest.java
+++ b/core/tests/coretests/src/android/app/PullAtomMetadataTest.java
@@ -32,7 +32,7 @@ public final class PullAtomMetadataTest {
@Test
public void testEmpty() {
- PullAtomMetadata metadata = PullAtomMetadata.newBuilder().build();
+ PullAtomMetadata metadata = new PullAtomMetadata.Builder().build();
assertThat(metadata.getTimeoutNs()).isEqualTo(StatsManager.DEFAULT_TIMEOUT_NS);
assertThat(metadata.getCoolDownNs()).isEqualTo(StatsManager.DEFAULT_COOL_DOWN_NS);
assertThat(metadata.getAdditiveFields()).isNull();
@@ -42,7 +42,7 @@ public final class PullAtomMetadataTest {
public void testSetTimeoutNs() {
long timeoutNs = 500_000_000L;
PullAtomMetadata metadata =
- PullAtomMetadata.newBuilder().setTimeoutNs(timeoutNs).build();
+ new PullAtomMetadata.Builder().setTimeoutNs(timeoutNs).build();
assertThat(metadata.getTimeoutNs()).isEqualTo(timeoutNs);
assertThat(metadata.getCoolDownNs()).isEqualTo(StatsManager.DEFAULT_COOL_DOWN_NS);
assertThat(metadata.getAdditiveFields()).isNull();
@@ -52,7 +52,7 @@ public final class PullAtomMetadataTest {
public void testSetCoolDownNs() {
long coolDownNs = 10_000_000_000L;
PullAtomMetadata metadata =
- PullAtomMetadata.newBuilder().setCoolDownNs(coolDownNs).build();
+ new PullAtomMetadata.Builder().setCoolDownNs(coolDownNs).build();
assertThat(metadata.getTimeoutNs()).isEqualTo(StatsManager.DEFAULT_TIMEOUT_NS);
assertThat(metadata.getCoolDownNs()).isEqualTo(coolDownNs);
assertThat(metadata.getAdditiveFields()).isNull();
@@ -62,7 +62,7 @@ public final class PullAtomMetadataTest {
public void testSetAdditiveFields() {
int[] fields = {2, 4, 6};
PullAtomMetadata metadata =
- PullAtomMetadata.newBuilder().setAdditiveFields(fields).build();
+ new PullAtomMetadata.Builder().setAdditiveFields(fields).build();
assertThat(metadata.getTimeoutNs()).isEqualTo(StatsManager.DEFAULT_TIMEOUT_NS);
assertThat(metadata.getCoolDownNs()).isEqualTo(StatsManager.DEFAULT_COOL_DOWN_NS);
assertThat(metadata.getAdditiveFields()).isEqualTo(fields);
@@ -73,7 +73,7 @@ public final class PullAtomMetadataTest {
long timeoutNs = 300L;
long coolDownNs = 9572L;
int[] fields = {3, 2};
- PullAtomMetadata metadata = PullAtomMetadata.newBuilder()
+ PullAtomMetadata metadata = new PullAtomMetadata.Builder()
.setTimeoutNs(timeoutNs)
.setCoolDownNs(coolDownNs)
.setAdditiveFields(fields)
diff --git a/core/tests/coretests/src/android/content/integrity/AtomicFormulaTest.java b/core/tests/coretests/src/android/content/integrity/AtomicFormulaTest.java
index c0d9be5dde59..7328f35790da 100644
--- a/core/tests/coretests/src/android/content/integrity/AtomicFormulaTest.java
+++ b/core/tests/coretests/src/android/content/integrity/AtomicFormulaTest.java
@@ -16,10 +16,10 @@
package android.content.integrity;
-import static android.content.integrity.TestUtils.assertExpectException;
-
import static com.google.common.truth.Truth.assertThat;
+import static org.testng.Assert.expectThrows;
+
import android.content.integrity.AtomicFormula.BooleanAtomicFormula;
import android.content.integrity.AtomicFormula.StringAtomicFormula;
import android.os.Parcel;
@@ -133,35 +133,38 @@ public class AtomicFormulaTest {
@Test
public void testInvalidAtomicFormula_stringValue() {
- assertExpectException(
- IllegalArgumentException.class,
- /* expectedExceptionMessageRegex */
- String.format("Key VERSION_CODE cannot be used with StringAtomicFormula"),
- () ->
- new StringAtomicFormula(
- AtomicFormula.VERSION_CODE,
- "test-value",
- /* isHashedValue= */ false));
+ Exception e =
+ expectThrows(
+ IllegalArgumentException.class,
+ () ->
+ new StringAtomicFormula(
+ AtomicFormula.VERSION_CODE,
+ "test-value",
+ /* isHashedValue= */ false));
+ assertThat(e.getMessage()).matches(
+ "Key VERSION_CODE cannot be used with StringAtomicFormula");
}
@Test
public void testInvalidAtomicFormula_longValue() {
- assertExpectException(
- IllegalArgumentException.class,
- /* expectedExceptionMessageRegex */
- String.format("Key PACKAGE_NAME cannot be used with LongAtomicFormula"),
- () ->
- new AtomicFormula.LongAtomicFormula(
- AtomicFormula.PACKAGE_NAME, AtomicFormula.EQ, 1));
+ Exception e =
+ expectThrows(
+ IllegalArgumentException.class,
+ () ->
+ new AtomicFormula.LongAtomicFormula(
+ AtomicFormula.PACKAGE_NAME, AtomicFormula.EQ, 1));
+ assertThat(e.getMessage()).matches(
+ "Key PACKAGE_NAME cannot be used with LongAtomicFormula");
}
@Test
public void testInvalidAtomicFormula_boolValue() {
- assertExpectException(
- IllegalArgumentException.class,
- /* expectedExceptionMessageRegex */
- String.format("Key PACKAGE_NAME cannot be used with BooleanAtomicFormula"),
- () -> new BooleanAtomicFormula(AtomicFormula.PACKAGE_NAME, true));
+ Exception e =
+ expectThrows(
+ IllegalArgumentException.class,
+ () -> new BooleanAtomicFormula(AtomicFormula.PACKAGE_NAME, true));
+ assertThat(e.getMessage()).matches(
+ "Key PACKAGE_NAME cannot be used with BooleanAtomicFormula");
}
@Test
@@ -205,20 +208,24 @@ public class AtomicFormulaTest {
@Test
public void testInvalidAtomicFormula_invalidKey() {
- assertExpectException(
- IllegalArgumentException.class,
- /* expectedExceptionMessageRegex */ "Unknown key: -1",
- () -> new AtomicFormula.LongAtomicFormula(/* key= */ -1, AtomicFormula.EQ, 0));
+ Exception e =
+ expectThrows(
+ IllegalArgumentException.class,
+ () -> new AtomicFormula.LongAtomicFormula(/* key= */ -1,
+ AtomicFormula.EQ, /* value= */0));
+ assertThat(e.getMessage()).matches("Unknown key: -1");
}
@Test
public void testInvalidAtomicFormula_invalidOperator() {
- assertExpectException(
- IllegalArgumentException.class,
- /* expectedExceptionMessageRegex */ "Unknown operator: -1",
- () ->
- new AtomicFormula.LongAtomicFormula(
- AtomicFormula.VERSION_CODE, /* operator= */ -1, 0));
+ Exception e =
+ expectThrows(
+ IllegalArgumentException.class,
+ () ->
+ new AtomicFormula.LongAtomicFormula(
+ AtomicFormula.VERSION_CODE, /* operator= */ -1, /* value= */
+ 0));
+ assertThat(e.getMessage()).matches("Unknown operator: -1");
}
@Test
diff --git a/core/tests/coretests/src/android/content/integrity/CompoundFormulaTest.java b/core/tests/coretests/src/android/content/integrity/CompoundFormulaTest.java
index fa3d671c09e5..4054c4916089 100644
--- a/core/tests/coretests/src/android/content/integrity/CompoundFormulaTest.java
+++ b/core/tests/coretests/src/android/content/integrity/CompoundFormulaTest.java
@@ -16,11 +16,9 @@
package android.content.integrity;
-import static android.content.integrity.TestUtils.assertExpectException;
-
import static com.google.common.truth.Truth.assertThat;
-import static org.junit.Assert.assertEquals;
+import static org.testng.Assert.expectThrows;
import android.os.Parcel;
@@ -46,32 +44,32 @@ public class CompoundFormulaTest {
new CompoundFormula(
CompoundFormula.AND, Arrays.asList(ATOMIC_FORMULA_1, ATOMIC_FORMULA_2));
- assertEquals(CompoundFormula.AND, compoundFormula.getConnector());
- assertEquals(
- Arrays.asList(ATOMIC_FORMULA_1, ATOMIC_FORMULA_2), compoundFormula.getFormulas());
+ assertThat(compoundFormula.getConnector()).isEqualTo(CompoundFormula.AND);
+ assertThat(compoundFormula.getFormulas()).containsAllOf(ATOMIC_FORMULA_1, ATOMIC_FORMULA_2);
}
@Test
public void testValidateAuxiliaryFormula_binaryConnectors() {
- assertExpectException(
- IllegalArgumentException.class,
- /* expectedExceptionMessageRegex */
- "Connector AND must have at least 2 formulas",
- () ->
- new CompoundFormula(
- CompoundFormula.AND, Collections.singletonList(ATOMIC_FORMULA_1)));
+ Exception e =
+ expectThrows(
+ IllegalArgumentException.class,
+ () ->
+ new CompoundFormula(
+ CompoundFormula.AND,
+ Collections.singletonList(ATOMIC_FORMULA_1)));
+ assertThat(e.getMessage()).matches("Connector AND must have at least 2 formulas");
}
@Test
public void testValidateAuxiliaryFormula_unaryConnectors() {
- assertExpectException(
- IllegalArgumentException.class,
- /* expectedExceptionMessageRegex */
- "Connector NOT must have 1 formula only",
- () ->
- new CompoundFormula(
- CompoundFormula.NOT,
- Arrays.asList(ATOMIC_FORMULA_1, ATOMIC_FORMULA_2)));
+ Exception e =
+ expectThrows(
+ IllegalArgumentException.class,
+ () ->
+ new CompoundFormula(
+ CompoundFormula.NOT,
+ Arrays.asList(ATOMIC_FORMULA_1, ATOMIC_FORMULA_2)));
+ assertThat(e.getMessage()).matches("Connector NOT must have 1 formula only");
}
@Test
@@ -82,20 +80,20 @@ public class CompoundFormulaTest {
Parcel p = Parcel.obtain();
formula.writeToParcel(p, 0);
p.setDataPosition(0);
- CompoundFormula newFormula = CompoundFormula.CREATOR.createFromParcel(p);
- assertEquals(formula, newFormula);
+ assertThat(CompoundFormula.CREATOR.createFromParcel(p)).isEqualTo(formula);
}
@Test
public void testInvalidCompoundFormula_invalidConnector() {
- assertExpectException(
- IllegalArgumentException.class,
- /* expectedExceptionMessageRegex */ "Unknown connector: -1",
- () ->
- new CompoundFormula(
- /* connector= */ -1,
- Arrays.asList(ATOMIC_FORMULA_1, ATOMIC_FORMULA_2)));
+ Exception e =
+ expectThrows(
+ IllegalArgumentException.class,
+ () ->
+ new CompoundFormula(
+ /* connector= */ -1,
+ Arrays.asList(ATOMIC_FORMULA_1, ATOMIC_FORMULA_2)));
+ assertThat(e.getMessage()).matches("Unknown connector: -1");
}
@Test
diff --git a/core/tests/coretests/src/android/content/integrity/IntegrityUtilsTest.java b/core/tests/coretests/src/android/content/integrity/IntegrityUtilsTest.java
index 639adf6a05d7..e6f6686dcb6f 100644
--- a/core/tests/coretests/src/android/content/integrity/IntegrityUtilsTest.java
+++ b/core/tests/coretests/src/android/content/integrity/IntegrityUtilsTest.java
@@ -18,6 +18,7 @@ package android.content.integrity;
import static com.google.common.truth.Truth.assertThat;
+import static org.testng.Assert.expectThrows;
import static org.testng.internal.junit.ArrayAsserts.assertArrayEquals;
import org.junit.Test;
@@ -43,15 +44,20 @@ public class IntegrityUtilsTest {
}
@Test
- public void testInvalidHexDigest() {
- TestUtils.assertExpectException(
- IllegalArgumentException.class,
- "must have even length",
- () -> IntegrityUtils.getBytesFromHexDigest("ABC"));
-
- TestUtils.assertExpectException(
- IllegalArgumentException.class,
- "Invalid hex char",
- () -> IntegrityUtils.getBytesFromHexDigest("GH"));
+ public void testInvalidHexDigest_mustHaveEvenLength() {
+ Exception e =
+ expectThrows(
+ IllegalArgumentException.class,
+ () -> IntegrityUtils.getBytesFromHexDigest("ABC"));
+ assertThat(e.getMessage()).containsMatch("must have even length");
+ }
+
+ @Test
+ public void testInvalidHexDigest_invalidHexChar() {
+ Exception e =
+ expectThrows(
+ IllegalArgumentException.class,
+ () -> IntegrityUtils.getBytesFromHexDigest("GH"));
+ assertThat(e.getMessage()).containsMatch("Invalid hex char");
}
}
diff --git a/core/tests/coretests/src/android/content/integrity/RuleTest.java b/core/tests/coretests/src/android/content/integrity/RuleTest.java
index 8c4cfca39e90..8faf8ba2f52a 100644
--- a/core/tests/coretests/src/android/content/integrity/RuleTest.java
+++ b/core/tests/coretests/src/android/content/integrity/RuleTest.java
@@ -16,10 +16,9 @@
package android.content.integrity;
-import static android.content.integrity.TestUtils.assertExpectException;
+import static com.google.common.truth.Truth.assertThat;
-import static org.junit.Assert.assertEquals;
-import static org.junit.Assert.assertNotEquals;
+import static org.testng.Assert.expectThrows;
import android.os.Parcel;
@@ -50,16 +49,16 @@ public class RuleTest {
public void testValidRule() {
Rule validRule = new Rule(PACKAGE_NAME_ATOMIC_FORMULA, DENY_EFFECT);
- assertEquals(PACKAGE_NAME_ATOMIC_FORMULA, validRule.getFormula());
- assertEquals(DENY_EFFECT, validRule.getEffect());
+ assertThat(validRule.getFormula()).isEqualTo(PACKAGE_NAME_ATOMIC_FORMULA);
+ assertThat(validRule.getEffect()).isEqualTo(DENY_EFFECT);
}
@Test
public void testInvalidRule_invalidFormula() {
- assertExpectException(
- NullPointerException.class,
- /* expectedExceptionMessageRegex */ null,
- () -> new Rule(null, DENY_EFFECT));
+ Exception e =
+ expectThrows(
+ NullPointerException.class,
+ () -> new Rule(null, DENY_EFFECT));
}
@Test
@@ -70,27 +69,23 @@ public class RuleTest {
Arrays.asList(PACKAGE_NAME_ATOMIC_FORMULA, APP_CERTIFICATE_ATOMIC_FORMULA));
Rule rule = new Rule(compoundFormula, Rule.DENY);
- assertEquals(
- String.format(
- "Rule: (PACKAGE_NAME EQ %s) AND (APP_CERTIFICATE EQ %s), DENY",
- PACKAGE_NAME, APP_CERTIFICATE),
- rule.toString());
+ assertThat(rule.toString())
+ .isEqualTo(
+ String.format(
+ "Rule: (PACKAGE_NAME EQ %s) AND (APP_CERTIFICATE EQ %s), DENY",
+ PACKAGE_NAME, APP_CERTIFICATE));
}
@Test
public void testEquals_trueCase() {
- Rule rule1 = new Rule(PACKAGE_NAME_ATOMIC_FORMULA, DENY_EFFECT);
- Rule rule2 = new Rule(PACKAGE_NAME_ATOMIC_FORMULA, DENY_EFFECT);
-
- assertEquals(rule1, rule2);
+ assertThat(new Rule(PACKAGE_NAME_ATOMIC_FORMULA, DENY_EFFECT))
+ .isEqualTo(new Rule(PACKAGE_NAME_ATOMIC_FORMULA, DENY_EFFECT));
}
@Test
public void testEquals_falseCase() {
- Rule rule1 = new Rule(PACKAGE_NAME_ATOMIC_FORMULA, DENY_EFFECT);
- Rule rule2 = new Rule(APP_CERTIFICATE_ATOMIC_FORMULA, DENY_EFFECT);
-
- assertNotEquals(rule1, rule2);
+ assertThat(new Rule(PACKAGE_NAME_ATOMIC_FORMULA, DENY_EFFECT))
+ .isNotEqualTo(new Rule(APP_CERTIFICATE_ATOMIC_FORMULA, DENY_EFFECT));
}
@Test
@@ -108,16 +103,16 @@ public class RuleTest {
Parcel p = Parcel.obtain();
rule.writeToParcel(p, 0);
p.setDataPosition(0);
- Rule newRule = Rule.CREATOR.createFromParcel(p);
- assertEquals(newRule, rule);
+ assertThat(Rule.CREATOR.createFromParcel(p)).isEqualTo(rule);
}
@Test
public void testInvalidRule_invalidEffect() {
- assertExpectException(
- IllegalArgumentException.class,
- /* expectedExceptionMessageRegex */ "Unknown effect: -1",
- () -> new Rule(PACKAGE_NAME_ATOMIC_FORMULA, /* effect= */ -1));
+ Exception e =
+ expectThrows(
+ IllegalArgumentException.class,
+ () -> new Rule(PACKAGE_NAME_ATOMIC_FORMULA, /* effect= */ -1));
+ assertThat(e.getMessage()).isEqualTo("Unknown effect: -1");
}
}
diff --git a/core/tests/coretests/src/android/content/integrity/TestUtils.java b/core/tests/coretests/src/android/content/integrity/TestUtils.java
deleted file mode 100644
index af984cf318f5..000000000000
--- a/core/tests/coretests/src/android/content/integrity/TestUtils.java
+++ /dev/null
@@ -1,51 +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.content.integrity;
-
-import android.test.MoreAsserts;
-
-import junit.framework.Assert;
-
-/** Helper methods used in tests. */
-class TestUtils {
- private TestUtils() {}
-
- public interface ExceptionRunnable {
- void run() throws Exception;
- }
-
- public static void assertExpectException(
- Class<? extends Throwable> expectedExceptionType,
- String expectedExceptionMessageRegex,
- ExceptionRunnable r) {
- try {
- r.run();
- } catch (Throwable e) {
- Assert.assertTrue(
- "Expected exception type was "
- + expectedExceptionType.getName()
- + " but caught "
- + e.getClass().getName(),
- expectedExceptionType.isAssignableFrom(e.getClass()));
- if (expectedExceptionMessageRegex != null) {
- MoreAsserts.assertContainsRegex(expectedExceptionMessageRegex, e.getMessage());
- }
- return; // Pass.
- }
- Assert.fail(
- "Expected exception type " + expectedExceptionType.getName() + " was not thrown");
- }
-}
diff --git a/core/tests/coretests/src/android/hardware/display/BrightnessConfigurationTest.java b/core/tests/coretests/src/android/hardware/display/BrightnessConfigurationTest.java
index 895b22c7037c..4370462279b2 100644
--- a/core/tests/coretests/src/android/hardware/display/BrightnessConfigurationTest.java
+++ b/core/tests/coretests/src/android/hardware/display/BrightnessConfigurationTest.java
@@ -132,7 +132,7 @@ public class BrightnessConfigurationTest {
BrightnessConfiguration.Builder builder =
new BrightnessConfiguration.Builder(LUX_LEVELS, NITS_LEVELS);
builder.setShouldCollectColorSamples(true);
- builder.setShortTermModelTimeout(1234L);
+ builder.setShortTermModelTimeoutMillis(1234L);
builder.setShortTermModelLowerLuxMultiplier(0.9f);
builder.setShortTermModelUpperLuxMultiplier(0.2f);
builder.addCorrectionByCategory(3,
@@ -153,7 +153,7 @@ public class BrightnessConfigurationTest {
BrightnessConfiguration.Builder builder =
new BrightnessConfiguration.Builder(LUX_LEVELS, NITS_LEVELS);
builder.setShouldCollectColorSamples(true);
- builder.setShortTermModelTimeout(123L);
+ builder.setShortTermModelTimeoutMillis(123L);
builder.setShortTermModelLowerLuxMultiplier(0.4f);
builder.setShortTermModelUpperLuxMultiplier(0.8f);
builder.addCorrectionByCategory(3,
@@ -236,7 +236,7 @@ public class BrightnessConfigurationTest {
assertNotEquals(baseConfig, colorCollectionDiffers);
builder = new BrightnessConfiguration.Builder(LUX_LEVELS, NITS_LEVELS);
- builder.setShortTermModelTimeout(300L);
+ builder.setShortTermModelTimeoutMillis(300L);
BrightnessConfiguration timeoutDiffers = builder.build();
assertNotEquals(baseConfig, timeoutDiffers);
diff --git a/core/tests/coretests/src/android/provider/NameValueCacheTest.java b/core/tests/coretests/src/android/provider/NameValueCacheTest.java
new file mode 100644
index 000000000000..4212ef21e0af
--- /dev/null
+++ b/core/tests/coretests/src/android/provider/NameValueCacheTest.java
@@ -0,0 +1,232 @@
+/*
+ * 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.provider;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.eq;
+import static org.mockito.Mockito.times;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.verifyNoMoreInteractions;
+import static org.mockito.Mockito.when;
+
+import android.content.ContentProvider;
+import android.content.IContentProvider;
+import android.os.Bundle;
+import android.platform.test.annotations.Presubmit;
+import android.test.mock.MockContentResolver;
+import android.util.MemoryIntArray;
+
+import androidx.test.filters.SmallTest;
+import androidx.test.runner.AndroidJUnit4;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.Map;
+
+/**
+ * These tests are verifying that Settings#NameValueCache is working as expected with DeviceConfig.
+ * Due to how the classes are structured, we have to test it in a somewhat roundabout way. We're
+ * mocking out the contentProvider and are handcrafting very specific Bundles to answer the queries.
+ */
+@Presubmit
+@RunWith(AndroidJUnit4.class)
+@SmallTest
+public class NameValueCacheTest {
+
+ private static final String NAMESPACE = "namespace";
+ private static final String NAMESPACE2 = "namespace2";
+
+ @Mock
+ private IContentProvider mMockIContentProvider;
+ @Mock
+ private ContentProvider mMockContentProvider;
+ private MockContentResolver mMockContentResolver;
+ private MemoryIntArray mCacheGenerationStore;
+ private int mCurrentGeneration = 123;
+
+ private HashMap<String, HashMap<String, String>> mStorage;
+
+ @Before
+ public void setUp() throws Exception {
+ Settings.Config.clearProviderForTest();
+ MockitoAnnotations.initMocks(this);
+ when(mMockContentProvider.getIContentProvider()).thenReturn(mMockIContentProvider);
+ mMockContentResolver = new MockContentResolver();
+ mMockContentResolver.addProvider(DeviceConfig.CONTENT_URI.getAuthority(),
+ mMockContentProvider);
+ mCacheGenerationStore = new MemoryIntArray(1);
+ mStorage = new HashMap<>();
+
+ // Stores keyValues for a given prefix and increments the generation. (Note that this
+ // increments the generation no matter what, it doesn't pay attention to if anything
+ // actually changed).
+ when(mMockIContentProvider.call(any(), any(), eq(DeviceConfig.CONTENT_URI.getAuthority()),
+ eq(Settings.CALL_METHOD_SET_ALL_CONFIG),
+ any(), any(Bundle.class))).thenAnswer(invocationOnMock -> {
+ Bundle incomingBundle = invocationOnMock.getArgument(5);
+ HashMap<String, String> keyValues =
+ (HashMap<String, String>) incomingBundle.getSerializable(
+ Settings.CALL_METHOD_FLAGS_KEY);
+ String prefix = incomingBundle.getString(Settings.CALL_METHOD_PREFIX_KEY);
+ mStorage.put(prefix, keyValues);
+ mCacheGenerationStore.set(0, ++mCurrentGeneration);
+
+ Bundle result = new Bundle();
+ result.putBoolean(Settings.KEY_CONFIG_SET_RETURN, true);
+ return result;
+ });
+
+ // Returns the keyValues corresponding to a namespace, or an empty map if the namespace
+ // doesn't have anything stored for it. Returns the generation key if the caller asked
+ // for one.
+ when(mMockIContentProvider.call(any(), any(), eq(DeviceConfig.CONTENT_URI.getAuthority()),
+ eq(Settings.CALL_METHOD_LIST_CONFIG),
+ any(), any(Bundle.class))).thenAnswer(invocationOnMock -> {
+ Bundle incomingBundle = invocationOnMock.getArgument(5);
+
+ String prefix = incomingBundle.getString(Settings.CALL_METHOD_PREFIX_KEY);
+
+ if (!mStorage.containsKey(prefix)) {
+ mStorage.put(prefix, new HashMap<>());
+ }
+ HashMap<String, String> keyValues = mStorage.get(prefix);
+
+ Bundle bundle = new Bundle();
+ bundle.putSerializable(Settings.NameValueTable.VALUE, keyValues);
+
+ if (incomingBundle.containsKey(Settings.CALL_METHOD_TRACK_GENERATION_KEY)) {
+ bundle.putParcelable(Settings.CALL_METHOD_TRACK_GENERATION_KEY,
+ mCacheGenerationStore);
+ bundle.putInt(Settings.CALL_METHOD_GENERATION_INDEX_KEY, 0);
+ bundle.putInt(Settings.CALL_METHOD_GENERATION_KEY,
+ mCacheGenerationStore.get(0));
+ }
+ return bundle;
+ });
+ }
+
+ @Test
+ public void testCaching_singleNamespace() throws Exception {
+ HashMap<String, String> keyValues = new HashMap<>();
+ keyValues.put("a", "b");
+ Settings.Config.setStrings(mMockContentResolver, NAMESPACE, keyValues);
+ verify(mMockIContentProvider).call(any(), any(), any(),
+ eq(Settings.CALL_METHOD_SET_ALL_CONFIG),
+ any(), any(Bundle.class));
+
+ Map<String, String> returnedValues = Settings.Config.getStrings(mMockContentResolver,
+ NAMESPACE,
+ Collections.emptyList());
+ verify(mMockIContentProvider).call(any(), any(), any(),
+ eq(Settings.CALL_METHOD_LIST_CONFIG),
+ any(), any(Bundle.class));
+ assertThat(returnedValues).containsExactlyEntriesIn(keyValues);
+
+ Map<String, String> cachedKeyValues = Settings.Config.getStrings(mMockContentResolver,
+ NAMESPACE, Collections.emptyList());
+ verifyNoMoreInteractions(mMockIContentProvider);
+ assertThat(cachedKeyValues).containsExactlyEntriesIn(keyValues);
+
+ // Modify the value to invalidate the cache.
+ keyValues.put("a", "c");
+ Settings.Config.setStrings(mMockContentResolver, NAMESPACE, keyValues);
+ verify(mMockIContentProvider, times(2)).call(any(), any(), any(),
+ eq(Settings.CALL_METHOD_SET_ALL_CONFIG),
+ any(), any(Bundle.class));
+
+ Map<String, String> returnedValues2 = Settings.Config.getStrings(mMockContentResolver,
+ NAMESPACE, Collections.emptyList());
+ verify(mMockIContentProvider, times(2)).call(any(), any(), any(),
+ eq(Settings.CALL_METHOD_LIST_CONFIG),
+ any(), any(Bundle.class));
+ assertThat(returnedValues2).containsExactlyEntriesIn(keyValues);
+
+ Map<String, String> cachedKeyValues2 = Settings.Config.getStrings(mMockContentResolver,
+ NAMESPACE, Collections.emptyList());
+ verifyNoMoreInteractions(mMockIContentProvider);
+ assertThat(cachedKeyValues2).containsExactlyEntriesIn(keyValues);
+ }
+
+ @Test
+ public void testCaching_multipleNamespaces() throws Exception {
+ HashMap<String, String> keyValues = new HashMap<>();
+ keyValues.put("a", "b");
+ Settings.Config.setStrings(mMockContentResolver, NAMESPACE, keyValues);
+ verify(mMockIContentProvider).call(any(), any(), any(),
+ eq(Settings.CALL_METHOD_SET_ALL_CONFIG),
+ any(), any(Bundle.class));
+
+ HashMap<String, String> keyValues2 = new HashMap<>();
+ keyValues2.put("c", "d");
+ keyValues2.put("e", "f");
+ Settings.Config.setStrings(mMockContentResolver, NAMESPACE2, keyValues2);
+ verify(mMockIContentProvider, times(2)).call(any(), any(), any(),
+ eq(Settings.CALL_METHOD_SET_ALL_CONFIG),
+ any(), any(Bundle.class));
+
+ Map<String, String> returnedValues = Settings.Config.getStrings(mMockContentResolver,
+ NAMESPACE,
+ Collections.emptyList());
+ verify(mMockIContentProvider).call(any(), any(), any(),
+ eq(Settings.CALL_METHOD_LIST_CONFIG),
+ any(), any(Bundle.class));
+ assertThat(returnedValues).containsExactlyEntriesIn(keyValues);
+
+ Map<String, String> returnedValues2 = Settings.Config.getStrings(mMockContentResolver,
+ NAMESPACE2,
+ Collections.emptyList());
+ verify(mMockIContentProvider, times(2)).call(any(), any(), any(),
+ eq(Settings.CALL_METHOD_LIST_CONFIG),
+ any(), any(Bundle.class));
+ assertThat(returnedValues2).containsExactlyEntriesIn(keyValues2);
+
+ Map<String, String> cachedKeyValues = Settings.Config.getStrings(mMockContentResolver,
+ NAMESPACE, Collections.emptyList());
+ verifyNoMoreInteractions(mMockIContentProvider);
+ assertThat(cachedKeyValues).containsExactlyEntriesIn(keyValues);
+
+ Map<String, String> cachedKeyValues2 = Settings.Config.getStrings(mMockContentResolver,
+ NAMESPACE2, Collections.emptyList());
+ verifyNoMoreInteractions(mMockIContentProvider);
+ assertThat(cachedKeyValues2).containsExactlyEntriesIn(keyValues2);
+ }
+
+ @Test
+ public void testCaching_emptyNamespace() throws Exception {
+ Map<String, String> returnedValues = Settings.Config.getStrings(mMockContentResolver,
+ NAMESPACE,
+ Collections.emptyList());
+ verify(mMockIContentProvider).call(any(), any(), any(),
+ eq(Settings.CALL_METHOD_LIST_CONFIG),
+ any(), any(Bundle.class));
+ assertThat(returnedValues).isEmpty();
+
+ Map<String, String> cachedKeyValues = Settings.Config.getStrings(mMockContentResolver,
+ NAMESPACE, Collections.emptyList());
+ verifyNoMoreInteractions(mMockIContentProvider);
+ assertThat(cachedKeyValues).isEmpty();
+ }
+
+}
diff --git a/core/tests/coretests/src/android/view/InsetsAnimationControlImplTest.java b/core/tests/coretests/src/android/view/InsetsAnimationControlImplTest.java
index bcf0b8ce4439..169716f99dea 100644
--- a/core/tests/coretests/src/android/view/InsetsAnimationControlImplTest.java
+++ b/core/tests/coretests/src/android/view/InsetsAnimationControlImplTest.java
@@ -176,8 +176,6 @@ public class InsetsAnimationControlImplTest {
when(mMockController.getState()).thenReturn(mInsetsState);
mController.finish(true /* shown */);
mController.applyChangeInsets(mInsetsState);
- assertTrue(mInsetsState.getSource(ITYPE_STATUS_BAR).isVisible());
- assertTrue(mInsetsState.getSource(ITYPE_NAVIGATION_BAR).isVisible());
assertEquals(Insets.of(0, 100, 100, 0), mController.getCurrentInsets());
verify(mMockController).notifyFinished(eq(mController), eq(true /* shown */));
}
diff --git a/core/tests/coretests/src/android/view/InsetsControllerTest.java b/core/tests/coretests/src/android/view/InsetsControllerTest.java
index f720c987a525..e68c4b8d2ab3 100644
--- a/core/tests/coretests/src/android/view/InsetsControllerTest.java
+++ b/core/tests/coretests/src/android/view/InsetsControllerTest.java
@@ -19,10 +19,13 @@ package android.view;
import static android.view.InsetsController.ANIMATION_TYPE_HIDE;
import static android.view.InsetsController.ANIMATION_TYPE_NONE;
import static android.view.InsetsController.ANIMATION_TYPE_SHOW;
+import static android.view.InsetsSourceConsumer.ShowResult.IME_SHOW_DELAYED;
+import static android.view.InsetsSourceConsumer.ShowResult.SHOW_IMMEDIATELY;
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.statusBars;
import static android.view.WindowManager.LayoutParams.SOFT_INPUT_ADJUST_RESIZE;
@@ -32,6 +35,8 @@ import static org.junit.Assert.assertNull;
import static org.junit.Assert.assertTrue;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.anyInt;
+import static org.mockito.ArgumentMatchers.eq;
+import static org.mockito.ArgumentMatchers.notNull;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.never;
import static org.mockito.Mockito.verify;
@@ -41,6 +46,7 @@ import android.graphics.Insets;
import android.graphics.Point;
import android.graphics.Rect;
import android.platform.test.annotations.Presubmit;
+import android.view.SurfaceControl.Transaction;
import android.view.WindowInsets.Type;
import android.view.WindowManager.BadTokenException;
import android.view.WindowManager.LayoutParams;
@@ -48,6 +54,9 @@ import android.view.animation.LinearInterpolator;
import android.view.test.InsetsModeSession;
import android.widget.TextView;
+import com.android.server.testutils.OffsettableClock;
+import com.android.server.testutils.TestHandler;
+
import androidx.test.InstrumentationRegistry;
import androidx.test.runner.AndroidJUnit4;
@@ -59,6 +68,7 @@ import org.junit.runner.RunWith;
import org.mockito.ArgumentCaptor;
import java.util.concurrent.CountDownLatch;
+import java.util.function.Supplier;
/**
* Tests for {@link InsetsController}.
@@ -77,6 +87,8 @@ public class InsetsControllerTest {
private SurfaceSession mSession = new SurfaceSession();
private SurfaceControl mLeash;
private ViewRootImpl mViewRoot;
+ private TestHandler mTestHandler;
+ private OffsettableClock mTestClock;
private static InsetsModeSession sInsetsModeSession;
@BeforeClass
@@ -103,7 +115,26 @@ public class InsetsControllerTest {
} catch (BadTokenException e) {
// activity isn't running, we will ignore BadTokenException.
}
- mController = new InsetsController(mViewRoot);
+ mTestClock = new OffsettableClock();
+ mTestHandler = new TestHandler(null, mTestClock);
+ mController = new InsetsController(mViewRoot, (controller, type) -> {
+ if (type == ITYPE_IME) {
+ return new InsetsSourceConsumer(type, controller.getState(),
+ Transaction::new, controller) {
+ @Override
+ public int requestShow(boolean fromController) {
+ if (fromController) {
+ return SHOW_IMMEDIATELY;
+ } else {
+ return IME_SHOW_DELAYED;
+ }
+ }
+ };
+ } else {
+ return new InsetsSourceConsumer(type, controller.getState(), Transaction::new,
+ controller);
+ }
+ }, mTestHandler);
final Rect rect = new Rect(5, 5, 5, 5);
mController.getState().getSource(ITYPE_STATUS_BAR).setFrame(new Rect(0, 0, 100, 10));
mController.getState().getSource(ITYPE_NAVIGATION_BAR).setFrame(
@@ -395,6 +426,71 @@ public class InsetsControllerTest {
InstrumentationRegistry.getInstrumentation().waitForIdleSync();
}
+ @Test
+ public void testControlImeNotReady() {
+ prepareControls();
+ InstrumentationRegistry.getInstrumentation().runOnMainSync(() -> {
+ WindowInsetsAnimationControlListener listener =
+ mock(WindowInsetsAnimationControlListener.class);
+ mController.controlInputMethodAnimation(0, new LinearInterpolator(), listener);
+
+ // Ready gets deferred until next predraw
+ mViewRoot.getView().getViewTreeObserver().dispatchOnPreDraw();
+
+ verify(listener, never()).onReady(any(), anyInt());
+
+ // Pretend that IME is calling.
+ mController.show(ime(), true);
+
+ // Ready gets deferred until next predraw
+ mViewRoot.getView().getViewTreeObserver().dispatchOnPreDraw();
+
+ verify(listener).onReady(notNull(), eq(ime()));
+
+ });
+ }
+
+ @Test
+ public void testControlImeNotReady_controlRevoked() {
+ prepareControls();
+ InstrumentationRegistry.getInstrumentation().runOnMainSync(() -> {
+ WindowInsetsAnimationControlListener listener =
+ mock(WindowInsetsAnimationControlListener.class);
+ mController.controlInputMethodAnimation(0, new LinearInterpolator(), listener);
+
+ // Ready gets deferred until next predraw
+ mViewRoot.getView().getViewTreeObserver().dispatchOnPreDraw();
+
+ verify(listener, never()).onReady(any(), anyInt());
+
+ // Pretend that we are losing control
+ mController.onControlsChanged(new InsetsSourceControl[0]);
+
+ verify(listener).onCancelled();
+ });
+ }
+
+ @Test
+ public void testControlImeNotReady_timeout() {
+ prepareControls();
+ InstrumentationRegistry.getInstrumentation().runOnMainSync(() -> {
+ WindowInsetsAnimationControlListener listener =
+ mock(WindowInsetsAnimationControlListener.class);
+ mController.controlInputMethodAnimation(0, new LinearInterpolator(), listener);
+
+ // Ready gets deferred until next predraw
+ mViewRoot.getView().getViewTreeObserver().dispatchOnPreDraw();
+
+ verify(listener, never()).onReady(any(), anyInt());
+
+ // Pretend that timeout is happening
+ mTestClock.fastForward(2500);
+ mTestHandler.timeAdvance();
+
+ verify(listener).onCancelled();
+ });
+ }
+
private void waitUntilNextFrame() throws Exception {
final CountDownLatch latch = new CountDownLatch(1);
Choreographer.getMainThreadInstance().postCallback(Choreographer.CALLBACK_COMMIT,
diff --git a/core/tests/coretests/src/android/view/KeyEventTest.java b/core/tests/coretests/src/android/view/KeyEventTest.java
index 88d3bdbe3c92..14999c7f5642 100644
--- a/core/tests/coretests/src/android/view/KeyEventTest.java
+++ b/core/tests/coretests/src/android/view/KeyEventTest.java
@@ -20,8 +20,10 @@ import static android.view.Display.INVALID_DISPLAY;
import static org.junit.Assert.assertEquals;
+import android.os.Parcel;
+
+import androidx.test.ext.junit.runners.AndroidJUnit4;
import androidx.test.filters.SmallTest;
-import androidx.test.runner.AndroidJUnit4;
import org.junit.Test;
import org.junit.runner.RunWith;
@@ -40,6 +42,7 @@ public class KeyEventTest {
private static final int SCAN_CODE = 0;
private static final int FLAGS = 0;
private static final int SOURCE = InputDevice.SOURCE_KEYBOARD;
+ private static final byte[] HMAC = null;
private static final String CHARACTERS = null;
@Test
@@ -65,25 +68,15 @@ public class KeyEventTest {
KeyEvent keyEvent = KeyEvent.obtain(DOWN_TIME, EVENT_TIME, ACTION, KEYCODE, REPEAT,
METASTATE, DEVICE_ID, SCAN_CODE, FLAGS, SOURCE, CHARACTERS);
KeyEvent keyEvent2 = KeyEvent.obtain(keyEvent);
- assertEquals(keyEvent.getDownTime(), keyEvent2.getDownTime());
- assertEquals(keyEvent.getEventTime(), keyEvent2.getEventTime());
- assertEquals(keyEvent.getAction(), keyEvent2.getAction());
- assertEquals(keyEvent.getKeyCode(), keyEvent2.getKeyCode());
- assertEquals(keyEvent.getRepeatCount(), keyEvent2.getRepeatCount());
- assertEquals(keyEvent.getMetaState(), keyEvent2.getMetaState());
- assertEquals(keyEvent.getDeviceId(), keyEvent2.getDeviceId());
- assertEquals(keyEvent.getScanCode(), keyEvent2.getScanCode());
- assertEquals(keyEvent.getFlags(), keyEvent2.getFlags());
- assertEquals(keyEvent.getSource(), keyEvent2.getSource());
- assertEquals(keyEvent.getDisplayId(), keyEvent2.getDisplayId());
- assertEquals(keyEvent.getCharacters(), keyEvent2.getCharacters());
+ compareKeys(keyEvent, keyEvent2);
}
@Test
public void testObtainWithDisplayId() {
final int displayId = 5;
KeyEvent keyEvent = KeyEvent.obtain(DOWN_TIME, EVENT_TIME, ACTION, KEYCODE, REPEAT,
- METASTATE, DEVICE_ID, SCAN_CODE, FLAGS, SOURCE, displayId, CHARACTERS);
+ METASTATE, DEVICE_ID, SCAN_CODE, FLAGS, SOURCE, displayId, null /* hmac*/,
+ CHARACTERS);
assertEquals(DOWN_TIME, keyEvent.getDownTime());
assertEquals(EVENT_TIME, keyEvent.getEventTime());
assertEquals(ACTION, keyEvent.getAction());
@@ -97,4 +90,44 @@ public class KeyEventTest {
assertEquals(displayId, keyEvent.getDisplayId());
assertEquals(CHARACTERS, keyEvent.getCharacters());
}
+
+ @Test
+ public void testParcelUnparcel() {
+ KeyEvent key1 = createKey();
+ Parcel parcel = Parcel.obtain();
+ key1.writeToParcel(parcel, 0 /*flags*/);
+ parcel.setDataPosition(0);
+
+ KeyEvent key2 = KeyEvent.CREATOR.createFromParcel(parcel);
+ parcel.recycle();
+
+ compareKeys(key1, key2);
+ }
+
+ @Test
+ public void testConstructor() {
+ KeyEvent key1 = createKey();
+ KeyEvent key2 = new KeyEvent(key1);
+ compareKeys(key1, key2);
+ }
+
+ private static KeyEvent createKey() {
+ return KeyEvent.obtain(DOWN_TIME, EVENT_TIME, ACTION, KEYCODE, REPEAT,
+ METASTATE, DEVICE_ID, SCAN_CODE, FLAGS, SOURCE, INVALID_DISPLAY, HMAC, CHARACTERS);
+ }
+
+ private static void compareKeys(KeyEvent key1, KeyEvent key2) {
+ assertEquals(key1.getDownTime(), key2.getDownTime());
+ assertEquals(key1.getEventTime(), key2.getEventTime());
+ assertEquals(key1.getAction(), key2.getAction());
+ assertEquals(key1.getKeyCode(), key2.getKeyCode());
+ assertEquals(key1.getRepeatCount(), key2.getRepeatCount());
+ assertEquals(key1.getMetaState(), key2.getMetaState());
+ assertEquals(key1.getDeviceId(), key2.getDeviceId());
+ assertEquals(key1.getScanCode(), key2.getScanCode());
+ assertEquals(key1.getFlags(), key2.getFlags());
+ assertEquals(key1.getSource(), key2.getSource());
+ assertEquals(key1.getDisplayId(), key2.getDisplayId());
+ assertEquals(key1.getCharacters(), key2.getCharacters());
+ }
}
diff --git a/core/tests/hosttests/test-apps/DownloadManagerTestApp/src/com/android/frameworks/downloadmanagertests/DownloadManagerTestRunner.java b/core/tests/hosttests/test-apps/DownloadManagerTestApp/src/com/android/frameworks/downloadmanagertests/DownloadManagerTestRunner.java
index 70c266a765e4..ac6385333b35 100644
--- a/core/tests/hosttests/test-apps/DownloadManagerTestApp/src/com/android/frameworks/downloadmanagertests/DownloadManagerTestRunner.java
+++ b/core/tests/hosttests/test-apps/DownloadManagerTestApp/src/com/android/frameworks/downloadmanagertests/DownloadManagerTestRunner.java
@@ -58,7 +58,7 @@ public class DownloadManagerTestRunner extends InstrumentationTestRunner {
}
// enable verbose wifi logging
((WifiManager)getContext().getSystemService(Context.WIFI_SERVICE))
- .enableVerboseLogging(1);
+ .setVerboseLoggingEnabled(true);
super.onCreate(icicle);
}
diff --git a/core/tests/utiltests/src/com/android/internal/util/ConnectivityUtilTest.java b/core/tests/utiltests/src/com/android/internal/util/LocationPermissionCheckerTest.java
index 0809f693ddd5..6010f3962d68 100644
--- a/core/tests/utiltests/src/com/android/internal/util/ConnectivityUtilTest.java
+++ b/core/tests/utiltests/src/com/android/internal/util/LocationPermissionCheckerTest.java
@@ -28,6 +28,7 @@ import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
import android.Manifest;
+import android.app.ActivityManager;
import android.app.AppOpsManager;
import android.content.Context;
import android.content.pm.ApplicationInfo;
@@ -48,8 +49,8 @@ import org.mockito.stubbing.Answer;
import java.util.HashMap;
-/** Unit tests for {@link ConnectivityUtil}. */
-public class ConnectivityUtilTest {
+/** Unit tests for {@link LocationPermissionChecker}. */
+public class LocationPermissionCheckerTest {
public static final String TAG = "ConnectivityUtilTest";
@@ -85,18 +86,7 @@ public class ConnectivityUtilTest {
private boolean mThrowSecurityException;
private Answer<Integer> mReturnPermission;
private HashMap<String, Integer> mPermissionsList = new HashMap<String, Integer>();
-
- private class TestConnectivityUtil extends ConnectivityUtil {
-
- TestConnectivityUtil(Context context) {
- super(context);
- }
-
- @Override
- protected int getCurrentUser() {
- return mCurrentUser;
- }
- }
+ private LocationPermissionChecker mChecker;
@Before
public void setUp() {
@@ -141,11 +131,12 @@ public class ConnectivityUtilTest {
mThrowSecurityException = true;
mMockApplInfo.targetSdkVersion = Build.VERSION_CODES.M;
mIsLocationEnabled = false;
- mCurrentUser = UserHandle.USER_SYSTEM;
+ mCurrentUser = ActivityManager.getCurrentUser();
mCoarseLocationPermission = PackageManager.PERMISSION_DENIED;
mFineLocationPermission = PackageManager.PERMISSION_DENIED;
mAllowCoarseLocationApps = AppOpsManager.MODE_ERRORED;
mAllowFineLocationApps = AppOpsManager.MODE_ERRORED;
+ mChecker = new LocationPermissionChecker(mMockContext);
}
private void setupMockInterface() {
@@ -189,8 +180,7 @@ public class ConnectivityUtilTest {
mWifiScanAllowApps = AppOpsManager.MODE_ALLOWED;
mUid = mCurrentUser;
setupTestCase();
- new TestConnectivityUtil(mMockContext)
- .enforceLocationPermission(TEST_PKG_NAME, TEST_FEATURE_ID, mUid, null);
+ mChecker.enforceLocationPermission(TEST_PKG_NAME, TEST_FEATURE_ID, mUid, null);
}
@Test
@@ -203,8 +193,7 @@ public class ConnectivityUtilTest {
mAllowFineLocationApps = AppOpsManager.MODE_ALLOWED;
mWifiScanAllowApps = AppOpsManager.MODE_ALLOWED;
setupTestCase();
- new TestConnectivityUtil(mMockContext)
- .enforceLocationPermission(TEST_PKG_NAME, TEST_FEATURE_ID, mUid, null);
+ mChecker.enforceLocationPermission(TEST_PKG_NAME, TEST_FEATURE_ID, mUid, null);
}
@Test
@@ -217,22 +206,8 @@ public class ConnectivityUtilTest {
setupTestCase();
assertThrows(SecurityException.class,
- () -> new TestConnectivityUtil(mMockContext)
- .enforceLocationPermission(TEST_PKG_NAME, TEST_FEATURE_ID, mUid, null));
- }
-
- @Test
- public void testenforceCanAccessScanResults_UserOrProfileNotCurrent() throws Exception {
- mIsLocationEnabled = true;
- mThrowSecurityException = false;
- mCoarseLocationPermission = PackageManager.PERMISSION_GRANTED;
- mAllowCoarseLocationApps = AppOpsManager.MODE_ALLOWED;
- mWifiScanAllowApps = AppOpsManager.MODE_ALLOWED;
- setupTestCase();
-
- assertThrows(SecurityException.class,
- () -> new TestConnectivityUtil(mMockContext)
- .enforceLocationPermission(TEST_PKG_NAME, TEST_FEATURE_ID, mUid, null));
+ () -> mChecker.enforceLocationPermission(
+ TEST_PKG_NAME, TEST_FEATURE_ID, mUid, null));
}
@Test
@@ -241,8 +216,8 @@ public class ConnectivityUtilTest {
mIsLocationEnabled = true;
setupTestCase();
assertThrows(SecurityException.class,
- () -> new TestConnectivityUtil(mMockContext)
- .enforceLocationPermission(TEST_PKG_NAME, TEST_FEATURE_ID, mUid, null));
+ () -> mChecker.enforceLocationPermission(
+ TEST_PKG_NAME, TEST_FEATURE_ID, mUid, null));
}
@Test
@@ -256,8 +231,8 @@ public class ConnectivityUtilTest {
setupTestCase();
assertThrows(SecurityException.class,
- () -> new TestConnectivityUtil(mMockContext)
- .enforceLocationPermission(TEST_PKG_NAME, TEST_FEATURE_ID, mUid, null));
+ () -> mChecker.enforceLocationPermission(
+ TEST_PKG_NAME, TEST_FEATURE_ID, mUid, null));
verify(mMockAppOps, never()).noteOp(anyInt(), anyInt(), anyString());
}
@@ -272,8 +247,8 @@ public class ConnectivityUtilTest {
setupTestCase();
assertThrows(SecurityException.class,
- () -> new TestConnectivityUtil(mMockContext)
- .enforceLocationPermission(TEST_PKG_NAME, TEST_FEATURE_ID, mUid, null));
+ () -> mChecker.enforceLocationPermission(
+ TEST_PKG_NAME, TEST_FEATURE_ID, mUid, null));
}
private static void assertThrows(Class<? extends Exception> exceptionClass, Runnable r) {
diff --git a/data/etc/platform.xml b/data/etc/platform.xml
index 0541db121ae6..da505505628e 100644
--- a/data/etc/platform.xml
+++ b/data/etc/platform.xml
@@ -60,6 +60,10 @@
<group gid="log" />
</permission>
+ <permission name="android.permission.MANAGE_EXTERNAL_STORAGE" >
+ <group gid="external_storage" />
+ </permission>
+
<permission name="android.permission.ACCESS_MTP" >
<group gid="mtp" />
</permission>
diff --git a/graphics/java/android/graphics/Bitmap.java b/graphics/java/android/graphics/Bitmap.java
index ac094ba5d5d2..9c2e95fab455 100644
--- a/graphics/java/android/graphics/Bitmap.java
+++ b/graphics/java/android/graphics/Bitmap.java
@@ -2132,7 +2132,7 @@ public final class Bitmap implements Parcelable {
public void writeToParcel(Parcel p, int flags) {
checkRecycled("Can't parcel a recycled bitmap");
noteHardwareBitmapSlowCall();
- if (!nativeWriteToParcel(mNativePtr, isMutable(), mDensity, p)) {
+ if (!nativeWriteToParcel(mNativePtr, mDensity, p)) {
throw new RuntimeException("native writeToParcel failed");
}
}
@@ -2285,7 +2285,6 @@ public final class Bitmap implements Parcelable {
private static native Bitmap nativeCreateFromParcel(Parcel p);
// returns true on success
private static native boolean nativeWriteToParcel(long nativeBitmap,
- boolean isMutable,
int density,
Parcel p);
// returns a new bitmap built from the native bitmap's alpha, and the paint
diff --git a/graphics/java/android/graphics/Canvas.java b/graphics/java/android/graphics/Canvas.java
index 9a0ca3e4ad9b..d949444d44d6 100644
--- a/graphics/java/android/graphics/Canvas.java
+++ b/graphics/java/android/graphics/Canvas.java
@@ -1162,6 +1162,7 @@ public class Canvas extends BaseCanvas {
* @see #quickReject(float, float, float, float, EdgeType)
* @see #quickReject(Path, EdgeType)
* @see #quickReject(RectF, EdgeType)
+ * @deprecated quickReject no longer uses this.
*/
public enum EdgeType {
@@ -1197,13 +1198,30 @@ public class Canvas extends BaseCanvas {
* non-antialiased ({@link Canvas.EdgeType#BW}).
* @return true if the rect (transformed by the canvas' matrix)
* does not intersect with the canvas' clip
+ * @deprecated The EdgeType is ignored. Use {@link #quickReject(RectF)} instead.
*/
+ @Deprecated
public boolean quickReject(@NonNull RectF rect, @NonNull EdgeType type) {
return nQuickReject(mNativeCanvasWrapper,
rect.left, rect.top, rect.right, rect.bottom);
}
/**
+ * Return true if the specified rectangle, after being transformed by the
+ * current matrix, would lie completely outside of the current clip. Call
+ * this to check if an area you intend to draw into is clipped out (and
+ * therefore you can skip making the draw calls).
+ *
+ * @param rect the rect to compare with the current clip
+ * @return true if the rect (transformed by the canvas' matrix)
+ * does not intersect with the canvas' clip
+ */
+ public boolean quickReject(@NonNull RectF rect) {
+ return nQuickReject(mNativeCanvasWrapper,
+ rect.left, rect.top, rect.right, rect.bottom);
+ }
+
+ /**
* Return true if the specified path, after being transformed by the
* current matrix, would lie completely outside of the current clip. Call
* this to check if an area you intend to draw into is clipped out (and
@@ -1217,12 +1235,30 @@ public class Canvas extends BaseCanvas {
* non-antialiased ({@link Canvas.EdgeType#BW}).
* @return true if the path (transformed by the canvas' matrix)
* does not intersect with the canvas' clip
+ * @deprecated The EdgeType is ignored. Use {@link #quickReject(Path)} instead.
*/
+ @Deprecated
public boolean quickReject(@NonNull Path path, @NonNull EdgeType type) {
return nQuickReject(mNativeCanvasWrapper, path.readOnlyNI());
}
/**
+ * Return true if the specified path, after being transformed by the
+ * current matrix, would lie completely outside of the current clip. Call
+ * this to check if an area you intend to draw into is clipped out (and
+ * therefore you can skip making the draw calls). Note: for speed it may
+ * return false even if the path itself might not intersect the clip
+ * (i.e. the bounds of the path intersects, but the path does not).
+ *
+ * @param path The path to compare with the current clip
+ * @return true if the path (transformed by the canvas' matrix)
+ * does not intersect with the canvas' clip
+ */
+ public boolean quickReject(@NonNull Path path) {
+ return nQuickReject(mNativeCanvasWrapper, path.readOnlyNI());
+ }
+
+ /**
* Return true if the specified rectangle, after being transformed by the
* current matrix, would lie completely outside of the current clip. Call
* this to check if an area you intend to draw into is clipped out (and
@@ -1241,13 +1277,37 @@ public class Canvas extends BaseCanvas {
* non-antialiased ({@link Canvas.EdgeType#BW}).
* @return true if the rect (transformed by the canvas' matrix)
* does not intersect with the canvas' clip
+ * @deprecated The EdgeType is ignored. Use {@link #quickReject(float, float, float, float)}
+ * instead.
*/
+ @Deprecated
public boolean quickReject(float left, float top, float right, float bottom,
@NonNull EdgeType type) {
return nQuickReject(mNativeCanvasWrapper, left, top, right, bottom);
}
/**
+ * Return true if the specified rectangle, after being transformed by the
+ * current matrix, would lie completely outside of the current clip. Call
+ * this to check if an area you intend to draw into is clipped out (and
+ * therefore you can skip making the draw calls).
+ *
+ * @param left The left side of the rectangle to compare with the
+ * current clip
+ * @param top The top of the rectangle to compare with the current
+ * clip
+ * @param right The right side of the rectangle to compare with the
+ * current clip
+ * @param bottom The bottom of the rectangle to compare with the
+ * current clip
+ * @return true if the rect (transformed by the canvas' matrix)
+ * does not intersect with the canvas' clip
+ */
+ public boolean quickReject(float left, float top, float right, float bottom) {
+ return nQuickReject(mNativeCanvasWrapper, left, top, right, bottom);
+ }
+
+ /**
* Return the bounds of the current clip (in local coordinates) in the
* bounds parameter, and return true if it is non-empty. This can be useful
* in a way similar to quickReject, in that it tells you that drawing
diff --git a/media/java/android/media/MediaFormat.java b/media/java/android/media/MediaFormat.java
index f61d55ef2a30..57405d762b76 100644
--- a/media/java/android/media/MediaFormat.java
+++ b/media/java/android/media/MediaFormat.java
@@ -52,6 +52,7 @@ import java.util.stream.Collectors;
* <tr><td>{@link #KEY_PIXEL_ASPECT_RATIO_WIDTH}</td><td>Integer</td><td>optional, the pixel aspect ratio width</td></tr>
* <tr><td>{@link #KEY_PIXEL_ASPECT_RATIO_HEIGHT}</td><td>Integer</td><td>optional, the pixel aspect ratio height</td></tr>
* <tr><td>{@link #KEY_BIT_RATE}</td><td>Integer</td><td><b>encoder-only</b>, desired bitrate in bits/second</td></tr>
+ * <tr><td>{@link #KEY_DURATION}</td><td>long</td><td>the duration of the content (in microseconds)</td></tr>
* </table>
*
* Video formats have the following keys:
diff --git a/media/java/android/media/audiopolicy/AudioProductStrategy.java b/media/java/android/media/audiopolicy/AudioProductStrategy.java
index 612f83a14e12..60b3fc642ee4 100644
--- a/media/java/android/media/audiopolicy/AudioProductStrategy.java
+++ b/media/java/android/media/audiopolicy/AudioProductStrategy.java
@@ -134,7 +134,9 @@ public final class AudioProductStrategy implements Parcelable {
+ "DO NOT USE STREAM TO CONTROL THE VOLUME");
return AudioSystem.STREAM_MUSIC;
}
- return streamType;
+ if (streamType < AudioSystem.getNumStreamTypes()) {
+ return streamType;
+ }
}
}
return AudioSystem.STREAM_MUSIC;
diff --git a/media/java/android/media/tv/ITvInputManager.aidl b/media/java/android/media/tv/ITvInputManager.aidl
index c199c6f8b761..508a46f492dd 100644
--- a/media/java/android/media/tv/ITvInputManager.aidl
+++ b/media/java/android/media/tv/ITvInputManager.aidl
@@ -89,7 +89,7 @@ interface ITvInputManager {
void timeShiftEnablePositionTracking(in IBinder sessionToken, boolean enable, int userId);
// For the recording session
- void startRecording(in IBinder sessionToken, in Uri programUri, int userId);
+ void startRecording(in IBinder sessionToken, in Uri programUri, in Bundle params, int userId);
void stopRecording(in IBinder sessionToken, int userId);
// For TV input hardware binding
diff --git a/media/java/android/media/tv/ITvInputSession.aidl b/media/java/android/media/tv/ITvInputSession.aidl
index 356ec3cd1f11..24b87d50b33e 100644
--- a/media/java/android/media/tv/ITvInputSession.aidl
+++ b/media/java/android/media/tv/ITvInputSession.aidl
@@ -56,6 +56,6 @@ oneway interface ITvInputSession {
void timeShiftEnablePositionTracking(boolean enable);
// For the recording session
- void startRecording(in Uri programUri);
+ void startRecording(in Uri programUri, in Bundle params);
void stopRecording();
}
diff --git a/media/java/android/media/tv/ITvInputSessionWrapper.java b/media/java/android/media/tv/ITvInputSessionWrapper.java
index 07cfbda7ac2b..e89d33d70d5c 100644
--- a/media/java/android/media/tv/ITvInputSessionWrapper.java
+++ b/media/java/android/media/tv/ITvInputSessionWrapper.java
@@ -216,7 +216,8 @@ public class ITvInputSessionWrapper extends ITvInputSession.Stub implements Hand
break;
}
case DO_START_RECORDING: {
- mTvInputRecordingSessionImpl.startRecording((Uri) msg.obj);
+ SomeArgs args = (SomeArgs) msg.obj;
+ mTvInputRecordingSessionImpl.startRecording((Uri) args.arg1, (Bundle) args.arg2);
break;
}
case DO_STOP_RECORDING: {
@@ -352,8 +353,9 @@ public class ITvInputSessionWrapper extends ITvInputSession.Stub implements Hand
}
@Override
- public void startRecording(@Nullable Uri programUri) {
- mCaller.executeOrSendMessage(mCaller.obtainMessageO(DO_START_RECORDING, programUri));
+ public void startRecording(@Nullable Uri programUri, @Nullable Bundle params) {
+ mCaller.executeOrSendMessage(mCaller.obtainMessageOO(DO_START_RECORDING, programUri,
+ params));
}
@Override
diff --git a/media/java/android/media/tv/TvInputManager.java b/media/java/android/media/tv/TvInputManager.java
index bb867630ca01..2589521ca370 100644
--- a/media/java/android/media/tv/TvInputManager.java
+++ b/media/java/android/media/tv/TvInputManager.java
@@ -2381,12 +2381,23 @@ public final class TvInputManager {
* {@link TvContract#buildProgramUri(long)}. Can be {@code null}.
*/
void startRecording(@Nullable Uri programUri) {
+ startRecording(programUri, null);
+ }
+
+ /**
+ * Starts TV program recording in the current recording session.
+ *
+ * @param programUri The URI for the TV program to record as a hint, built by
+ * {@link TvContract#buildProgramUri(long)}. Can be {@code null}.
+ * @param params A set of extra parameters which might be handled with this event.
+ */
+ void startRecording(@Nullable Uri programUri, @Nullable Bundle params) {
if (mToken == null) {
Log.w(TAG, "The session has been already released");
return;
}
try {
- mService.startRecording(mToken, programUri, mUserId);
+ mService.startRecording(mToken, programUri, params, mUserId);
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
diff --git a/media/java/android/media/tv/TvInputService.java b/media/java/android/media/tv/TvInputService.java
index 7e1f44cb0899..62c7e51f3edf 100755
--- a/media/java/android/media/tv/TvInputService.java
+++ b/media/java/android/media/tv/TvInputService.java
@@ -1818,6 +1818,30 @@ public abstract class TvInputService extends Service {
public abstract void onStartRecording(@Nullable Uri programUri);
/**
+ * Called when the application requests to start TV program recording. Recording must start
+ * immediately when this method is called.
+ *
+ * <p>The application may supply the URI for a TV program for filling in program specific
+ * data fields in the {@link android.media.tv.TvContract.RecordedPrograms} table.
+ * A non-null {@code programUri} implies the started recording should be of that specific
+ * program, whereas null {@code programUri} does not impose such a requirement and the
+ * recording can span across multiple TV programs. In either case, the application must call
+ * {@link TvRecordingClient#stopRecording()} to stop the recording.
+ *
+ * <p>The session must call {@link #notifyError(int)} if the start request cannot be
+ * fulfilled.
+ *
+ * @param programUri The URI for the TV program to record, built by
+ * {@link TvContract#buildProgramUri(long)}. Can be {@code null}.
+ * @param params Domain-specific data for this tune request. Keys <em>must</em> be a scoped
+ * name, i.e. prefixed with a package name you own, so that different developers
+ * will not create conflicting keys.
+ */
+ public void onStartRecording(@Nullable Uri programUri, @NonNull Bundle params) {
+ onStartRecording(programUri);
+ }
+
+ /**
* Called when the application requests to stop TV program recording. Recording must stop
* immediately when this method is called.
*
@@ -1867,11 +1891,11 @@ public abstract class TvInputService extends Service {
}
/**
- * Calls {@link #onStartRecording(Uri)}.
+ * Calls {@link #onStartRecording(Uri, Bundle)}.
*
*/
- void startRecording(@Nullable Uri programUri) {
- onStartRecording(programUri);
+ void startRecording(@Nullable Uri programUri, @NonNull Bundle params) {
+ onStartRecording(programUri, params);
}
/**
diff --git a/media/java/android/media/tv/TvRecordingClient.java b/media/java/android/media/tv/TvRecordingClient.java
index 5aadeb6ecfa9..8ae98ae5e937 100644
--- a/media/java/android/media/tv/TvRecordingClient.java
+++ b/media/java/android/media/tv/TvRecordingClient.java
@@ -170,11 +170,37 @@ public class TvRecordingClient {
* @throws IllegalStateException If {@link #tune} request hasn't been handled yet.
*/
public void startRecording(@Nullable Uri programUri) {
+ startRecording(programUri, Bundle.EMPTY);
+ }
+
+ /**
+ * Starts TV program recording in the current recording session. Recording is expected to start
+ * immediately when this method is called. If the current recording session has not yet tuned to
+ * any channel, this method throws an exception.
+ *
+ * <p>The application may supply the URI for a TV program for filling in program specific data
+ * fields in the {@link android.media.tv.TvContract.RecordedPrograms} table.
+ * A non-null {@code programUri} implies the started recording should be of that specific
+ * program, whereas null {@code programUri} does not impose such a requirement and the
+ * recording can span across multiple TV programs. In either case, the application must call
+ * {@link TvRecordingClient#stopRecording()} to stop the recording.
+ *
+ * <p>The recording session will respond by calling {@link RecordingCallback#onError(int)} if
+ * the start request cannot be fulfilled.
+ *
+ * @param programUri The URI for the TV program to record, built by
+ * {@link TvContract#buildProgramUri(long)}. Can be {@code null}.
+ * @param params Domain-specific data for this request. Keys <em>must</em> be a scoped
+ * name, i.e. prefixed with a package name you own, so that different developers will
+ * not create conflicting keys.
+ * @throws IllegalStateException If {@link #tune} request hasn't been handled yet.
+ */
+ public void startRecording(@Nullable Uri programUri, @NonNull Bundle params) {
if (!mIsTuned) {
throw new IllegalStateException("startRecording failed - not yet tuned");
}
if (mSession != null) {
- mSession.startRecording(programUri);
+ mSession.startRecording(programUri, params);
mIsRecordingStarted = true;
}
}
diff --git a/media/jni/android_media_tv_Tuner.cpp b/media/jni/android_media_tv_Tuner.cpp
index 4f1125f5e482..007d367a533a 100644
--- a/media/jni/android_media_tv_Tuner.cpp
+++ b/media/jni/android_media_tv_Tuner.cpp
@@ -314,7 +314,7 @@ jobject JTuner::openLnbById(int id) {
JNIEnv *env = AndroidRuntime::getJNIEnv();
return env->NewObject(
- env->FindClass("android/media/tv/tuner/Tuner$Lnb"),
+ env->FindClass("android/media/tv/tuner/Lnb"),
gFields.lnbInitID,
mObject,
id);
@@ -373,7 +373,7 @@ jobject JTuner::openDescrambler() {
JNIEnv *env = AndroidRuntime::getJNIEnv();
jobject descramblerObj =
env->NewObject(
- env->FindClass("android/media/tv/tuner/Tuner$Descrambler"),
+ env->FindClass("android/media/tv/tuner/Descrambler"),
gFields.descramblerInitID,
mObject);
@@ -408,7 +408,7 @@ jobject JTuner::openFilter(DemuxFilterType type, int bufferSize) {
JNIEnv *env = AndroidRuntime::getJNIEnv();
jobject filterObj =
env->NewObject(
- env->FindClass("android/media/tv/tuner/Tuner$Filter"),
+ env->FindClass("android/media/tv/tuner/filter/Filter"),
gFields.filterInitID,
mObject,
(jint) fId);
@@ -443,7 +443,7 @@ jobject JTuner::openDvr(DvrType type, int bufferSize) {
JNIEnv *env = AndroidRuntime::getJNIEnv();
jobject dvrObj =
env->NewObject(
- env->FindClass("android/media/tv/tuner/Tuner$Dvr"),
+ env->FindClass("android/media/tv/tuner/dvr/Dvr"),
gFields.dvrInitID,
mObject);
sp<Dvr> dvrSp = new Dvr(iDvrSp, dvrObj);
@@ -495,14 +495,14 @@ static DemuxPid getDemuxPid(int pidType, int pid) {
static FrontendSettings getFrontendSettings(JNIEnv *env, int type, jobject settings) {
FrontendSettings frontendSettings;
- jclass clazz = env->FindClass("android/media/tv/tuner/FrontendSettings");
+ jclass clazz = env->FindClass("android/media/tv/tuner/frontend/FrontendSettings");
jfieldID freqField = env->GetFieldID(clazz, "mFrequency", "I");
uint32_t freq = static_cast<uint32_t>(env->GetIntField(clazz, freqField));
// TODO: handle the other 8 types of settings
if (type == 1) {
// analog
- clazz = env->FindClass("android/media/tv/tuner/FrontendSettings$FrontendAnalogSettings");
+ clazz = env->FindClass("android/media/tv/tuner/frontend/AnalogFrontendSettings");
FrontendAnalogType analogType =
static_cast<FrontendAnalogType>(
env->GetIntField(settings, env->GetFieldID(clazz, "mAnalogType", "I")));
@@ -525,7 +525,7 @@ static sp<Filter> getFilter(JNIEnv *env, jobject filter) {
static DvrSettings getDvrSettings(JNIEnv *env, jobject settings) {
DvrSettings dvrSettings;
- jclass clazz = env->FindClass("android/media/tv/tuner/DvrSettings");
+ jclass clazz = env->FindClass("android/media/tv/tuner/dvr/DvrSettings");
uint32_t statusMask =
static_cast<uint32_t>(env->GetIntField(
settings, env->GetFieldID(clazz, "mStatusMask", "I")));
@@ -585,23 +585,23 @@ static void android_media_tv_Tuner_native_init(JNIEnv *env) {
gFields.frontendInitID =
env->GetMethodID(frontendClazz, "<init>", "(Landroid/media/tv/tuner/Tuner;I)V");
- jclass lnbClazz = env->FindClass("android/media/tv/tuner/Tuner$Lnb");
+ jclass lnbClazz = env->FindClass("android/media/tv/tuner/Lnb");
gFields.lnbInitID =
env->GetMethodID(lnbClazz, "<init>", "(Landroid/media/tv/tuner/Tuner;I)V");
- jclass filterClazz = env->FindClass("android/media/tv/tuner/Tuner$Filter");
+ jclass filterClazz = env->FindClass("android/media/tv/tuner/filter/Filter");
gFields.filterContext = env->GetFieldID(filterClazz, "mNativeContext", "J");
gFields.filterInitID =
env->GetMethodID(filterClazz, "<init>", "(Landroid/media/tv/tuner/Tuner;I)V");
gFields.onFilterStatusID =
env->GetMethodID(filterClazz, "onFilterStatus", "(I)V");
- jclass descramblerClazz = env->FindClass("android/media/tv/tuner/Tuner$Descrambler");
+ jclass descramblerClazz = env->FindClass("android/media/tv/tuner/Descrambler");
gFields.descramblerContext = env->GetFieldID(descramblerClazz, "mNativeContext", "J");
gFields.descramblerInitID =
env->GetMethodID(descramblerClazz, "<init>", "(Landroid/media/tv/tuner/Tuner;)V");
- jclass dvrClazz = env->FindClass("android/media/tv/tuner/Tuner$Dvr");
+ jclass dvrClazz = env->FindClass("android/media/tv/tuner/dvr/Dvr");
gFields.dvrContext = env->GetFieldID(dvrClazz, "mNativeContext", "J");
gFields.dvrInitID = env->GetMethodID(dvrClazz, "<init>", "(Landroid/media/tv/tuner/Tuner;)V");
}
@@ -649,7 +649,7 @@ static int android_media_tv_Tuner_set_lna(JNIEnv*, jobject, jint, jboolean) {
return 0;
}
-static jobjectArray android_media_tv_Tuner_get_frontend_status(JNIEnv, jobject, jintArray) {
+static jobject android_media_tv_Tuner_get_frontend_status(JNIEnv, jobject, jintArray) {
return NULL;
}
@@ -684,7 +684,7 @@ static jobject android_media_tv_Tuner_open_lnb_by_id(JNIEnv *env, jobject thiz,
}
static jobject android_media_tv_Tuner_open_filter(
- JNIEnv *env, jobject thiz, jint type, jint subType, jint bufferSize) {
+ JNIEnv *env, jobject thiz, jint type, jint subType, jlong bufferSize) {
sp<JTuner> tuner = getTuner(env, thiz);
DemuxFilterType filterType {
.mainType = static_cast<DemuxFilterMainType>(type),
@@ -708,17 +708,17 @@ static DemuxFilterSettings getFilterSettings(
env->GetObjectField(
filterSettingsObj,
env->GetFieldID(
- env->FindClass("android/media/tv/tuner/FilterSettings"),
+ env->FindClass("android/media/tv/tuner/filter/FilterConfiguration"),
"mSettings",
- "Landroid/media/tv/tuner/FilterSettings$Settings;"));
+ "Landroid/media/tv/tuner/filter/Settings;"));
if (type == (int)DemuxFilterMainType::TS) {
// DemuxTsFilterSettings
- jclass clazz = env->FindClass("android/media/tv/tuner/FilterSettings$TsFilterSettings");
+ jclass clazz = env->FindClass("android/media/tv/tuner/filter/TsFilterConfiguration");
int tpid = env->GetIntField(filterSettingsObj, env->GetFieldID(clazz, "mTpid", "I"));
if (subtype == (int)DemuxTsFilterType::PES) {
// DemuxFilterPesDataSettings
jclass settingClazz =
- env->FindClass("android/media/tv/tuner/FilterSettings$PesSettings");
+ env->FindClass("android/media/tv/tuner/filter/PesSettings");
int streamId = env->GetIntField(
settingsObj, env->GetFieldID(settingClazz, "mStreamId", "I"));
bool isRaw = (bool)env->GetBooleanField(
@@ -831,7 +831,7 @@ static int android_media_tv_Tuner_flush_filter(JNIEnv *env, jobject filter) {
}
static int android_media_tv_Tuner_read_filter_fmq(
- JNIEnv *env, jobject filter, jbyteArray buffer, jint offset, jint size) {
+ JNIEnv *env, jobject filter, jbyteArray buffer, jlong offset, jlong size) {
sp<Filter> filterSp = getFilter(env, filter);
if (filterSp == NULL) {
ALOGD("Failed to read filter FMQ: filter not found");
@@ -901,9 +901,14 @@ static int android_media_tv_Tuner_close_descrambler(JNIEnv, jobject) {
return 0;
}
-static jobject android_media_tv_Tuner_open_dvr(JNIEnv *env, jobject thiz, jint type, jint bufferSize) {
- sp<JTuner> tuner = getTuner(env, thiz);
- return tuner->openDvr(static_cast<DvrType>(type), bufferSize);
+static jobject android_media_tv_Tuner_open_dvr_recorder(
+ JNIEnv* /* env */, jobject /* thiz */, jlong /* bufferSize */) {
+ return NULL;
+}
+
+static jobject android_media_tv_Tuner_open_dvr_playback(
+ JNIEnv* /* env */, jobject /* thiz */, jlong /* bufferSize */) {
+ return NULL;
}
static jobject android_media_tv_Tuner_get_demux_caps(JNIEnv*, jobject) {
@@ -1019,35 +1024,35 @@ static void android_media_tv_Tuner_dvr_set_fd(JNIEnv *env, jobject dvr, jobject
ALOGD("set fd = %d", dvrSp->mFd);
}
-static int android_media_tv_Tuner_read_dvr(JNIEnv *env, jobject dvr, jint size) {
+static jlong android_media_tv_Tuner_read_dvr(JNIEnv *env, jobject dvr, jlong size) {
sp<Dvr> dvrSp = getDvr(env, dvr);
if (dvrSp == NULL) {
ALOGD("Failed to read dvr: dvr not found");
}
- int available = dvrSp->mDvrMQ->availableToWrite();
- int write = std::min(size, available);
+ long available = dvrSp->mDvrMQ->availableToWrite();
+ long write = std::min((long) size, available);
DvrMQ::MemTransaction tx;
- int ret = 0;
+ long ret = 0;
if (dvrSp->mDvrMQ->beginWrite(write, &tx)) {
auto first = tx.getFirstRegion();
auto data = first.getAddress();
- int length = first.getLength();
- int firstToWrite = std::min(length, write);
+ long length = first.getLength();
+ long firstToWrite = std::min(length, write);
ret = read(dvrSp->mFd, data, firstToWrite);
if (ret < firstToWrite) {
- ALOGW("[DVR] file to MQ, first region: %d bytes to write, but %d bytes written",
+ ALOGW("[DVR] file to MQ, first region: %ld bytes to write, but %ld bytes written",
firstToWrite, ret);
} else if (firstToWrite < write) {
- ALOGD("[DVR] write second region: %d bytes written, %d bytes in total", ret, write);
+ ALOGD("[DVR] write second region: %ld bytes written, %ld bytes in total", ret, write);
auto second = tx.getSecondRegion();
data = second.getAddress();
length = second.getLength();
int secondToWrite = std::min(length, write - firstToWrite);
ret += read(dvrSp->mFd, data, secondToWrite);
}
- ALOGD("[DVR] file to MQ: %d bytes need to be written, %d bytes written", write, ret);
+ ALOGD("[DVR] file to MQ: %ld bytes need to be written, %ld bytes written", write, ret);
if (!dvrSp->mDvrMQ->commitWrite(ret)) {
ALOGE("[DVR] Error: failed to commit write!");
}
@@ -1055,17 +1060,17 @@ static int android_media_tv_Tuner_read_dvr(JNIEnv *env, jobject dvr, jint size)
} else {
ALOGE("dvrMq.beginWrite failed");
}
- return ret;
+ return (jlong) ret;
}
-static int android_media_tv_Tuner_read_dvr_from_array(
- JNIEnv /* *env */, jobject /* dvr */, jbyteArray /* bytes */, jint /* offset */,
- jint /* size */) {
+static jlong android_media_tv_Tuner_read_dvr_from_array(
+ JNIEnv /* *env */, jobject /* dvr */, jbyteArray /* bytes */, jlong /* offset */,
+ jlong /* size */) {
//TODO: impl
return 0;
}
-static int android_media_tv_Tuner_write_dvr(JNIEnv *env, jobject dvr, jint size) {
+static jlong android_media_tv_Tuner_write_dvr(JNIEnv *env, jobject dvr, jlong size) {
sp<Dvr> dvrSp = getDvr(env, dvr);
if (dvrSp == NULL) {
ALOGW("Failed to write dvr: dvr not found");
@@ -1079,28 +1084,28 @@ static int android_media_tv_Tuner_write_dvr(JNIEnv *env, jobject dvr, jint size)
DvrMQ& dvrMq = dvrSp->getDvrMQ();
- int available = dvrMq.availableToRead();
- int toRead = std::min(size, available);
+ long available = dvrMq.availableToRead();
+ long toRead = std::min((long) size, available);
- int ret = 0;
+ long ret = 0;
DvrMQ::MemTransaction tx;
if (dvrMq.beginRead(toRead, &tx)) {
auto first = tx.getFirstRegion();
auto data = first.getAddress();
- int length = first.getLength();
- int firstToRead = std::min(length, toRead);
+ long length = first.getLength();
+ long firstToRead = std::min(length, toRead);
ret = write(dvrSp->mFd, data, firstToRead);
if (ret < firstToRead) {
- ALOGW("[DVR] MQ to file: %d bytes read, but %d bytes written", firstToRead, ret);
+ ALOGW("[DVR] MQ to file: %ld bytes read, but %ld bytes written", firstToRead, ret);
} else if (firstToRead < toRead) {
- ALOGD("[DVR] read second region: %d bytes read, %d bytes in total", ret, toRead);
+ ALOGD("[DVR] read second region: %ld bytes read, %ld bytes in total", ret, toRead);
auto second = tx.getSecondRegion();
data = second.getAddress();
length = second.getLength();
int secondToRead = toRead - firstToRead;
ret += write(dvrSp->mFd, data, secondToRead);
}
- ALOGD("[DVR] MQ to file: %d bytes to be read, %d bytes written", toRead, ret);
+ ALOGD("[DVR] MQ to file: %ld bytes to be read, %ld bytes written", toRead, ret);
if (!dvrMq.commitRead(ret)) {
ALOGE("[DVR] Error: failed to commit read!");
}
@@ -1109,12 +1114,12 @@ static int android_media_tv_Tuner_write_dvr(JNIEnv *env, jobject dvr, jint size)
ALOGE("dvrMq.beginRead failed");
}
- return ret;
+ return (jlong) ret;
}
-static int android_media_tv_Tuner_write_dvr_to_array(
- JNIEnv /* *env */, jobject /* dvr */, jbyteArray /* bytes */, jint /* offset */,
- jint /* size */) {
+static jlong android_media_tv_Tuner_write_dvr_to_array(
+ JNIEnv /* *env */, jobject /* dvr */, jbyteArray /* bytes */, jlong /* offset */,
+ jlong /* size */) {
//TODO: impl
return 0;
}
@@ -1126,49 +1131,51 @@ static const JNINativeMethod gTunerMethods[] = {
(void *)android_media_tv_Tuner_get_frontend_ids },
{ "nativeOpenFrontendById", "(I)Landroid/media/tv/tuner/Tuner$Frontend;",
(void *)android_media_tv_Tuner_open_frontend_by_id },
- { "nativeTune", "(ILandroid/media/tv/tuner/FrontendSettings;)I",
+ { "nativeTune", "(ILandroid/media/tv/tuner/frontend/FrontendSettings;)I",
(void *)android_media_tv_Tuner_tune },
{ "nativeStopTune", "()I", (void *)android_media_tv_Tuner_stop_tune },
- { "nativeScan", "(ILandroid/media/tv/tuner/FrontendSettings;I)I",
+ { "nativeScan", "(ILandroid/media/tv/tuner/frontend/FrontendSettings;I)I",
(void *)android_media_tv_Tuner_scan },
{ "nativeStopScan", "()I", (void *)android_media_tv_Tuner_stop_scan },
{ "nativeSetLnb", "(I)I", (void *)android_media_tv_Tuner_set_lnb },
{ "nativeSetLna", "(Z)I", (void *)android_media_tv_Tuner_set_lna },
- { "nativeGetFrontendStatus", "([I)[Landroid/media/tv/tuner/FrontendStatus;",
+ { "nativeGetFrontendStatus", "([I)Landroid/media/tv/tuner/frontend/FrontendStatus;",
(void *)android_media_tv_Tuner_get_frontend_status },
- { "nativeGetAvSyncHwId", "(Landroid/media/tv/tuner/Tuner$Filter;)I",
+ { "nativeGetAvSyncHwId", "(Landroid/media/tv/tuner/Tuner/filter/Filter;)I",
(void *)android_media_tv_Tuner_gat_av_sync_hw_id },
{ "nativeGetAvSyncTime", "(I)J", (void *)android_media_tv_Tuner_gat_av_sync_time },
{ "nativeConnectCiCam", "(I)I", (void *)android_media_tv_Tuner_connect_cicam },
{ "nativeDisconnectCiCam", "()I", (void *)android_media_tv_Tuner_disconnect_cicam },
- { "nativeGetFrontendInfo", "(I)[Landroid/media/tv/tuner/FrontendInfo;",
+ { "nativeGetFrontendInfo", "(I)Landroid/media/tv/tuner/FrontendInfo;",
(void *)android_media_tv_Tuner_get_frontend_info },
- { "nativeOpenFilter", "(III)Landroid/media/tv/tuner/Tuner$Filter;",
+ { "nativeOpenFilter", "(IIJ)Landroid/media/tv/tuner/Tuner/filter/Filter;",
(void *)android_media_tv_Tuner_open_filter },
- { "nativeOpenTimeFilter", "()Landroid/media/tv/tuner/Tuner$TimeFilter;",
+ { "nativeOpenTimeFilter", "()Landroid/media/tv/tuner/Tuner/filter/TimeFilter;",
(void *)android_media_tv_Tuner_open_time_filter },
{ "nativeGetLnbIds", "()Ljava/util/List;",
(void *)android_media_tv_Tuner_get_lnb_ids },
- { "nativeOpenLnbById", "(I)Landroid/media/tv/tuner/Tuner$Lnb;",
+ { "nativeOpenLnbById", "(I)Landroid/media/tv/tuner/Lnb;",
(void *)android_media_tv_Tuner_open_lnb_by_id },
- { "nativeOpenDescrambler", "()Landroid/media/tv/tuner/Tuner$Descrambler;",
+ { "nativeOpenDescrambler", "()Landroid/media/tv/tuner/Descrambler;",
(void *)android_media_tv_Tuner_open_descrambler },
- { "nativeOpenDvr", "(II)Landroid/media/tv/tuner/Tuner$Dvr;",
- (void *)android_media_tv_Tuner_open_dvr },
+ { "nativeOpenDvrRecorder", "(J)Landroid/media/tv/tuner/dvr/DvrRecorder;",
+ (void *)android_media_tv_Tuner_open_dvr_recorder },
+ { "nativeOpenDvrPlayback", "(J)Landroid/media/tv/tuner/dvr/DvrPlayback;",
+ (void *)android_media_tv_Tuner_open_dvr_playback },
{ "nativeGetDemuxCapabilities", "()Landroid/media/tv/tuner/DemuxCapabilities;",
(void *)android_media_tv_Tuner_get_demux_caps },
};
static const JNINativeMethod gFilterMethods[] = {
- { "nativeConfigureFilter", "(IILandroid/media/tv/tuner/FilterSettings;)I",
+ { "nativeConfigureFilter", "(IILandroid/media/tv/tuner/filter/FilterConfiguration;)I",
(void *)android_media_tv_Tuner_configure_filter },
{ "nativeGetId", "()I", (void *)android_media_tv_Tuner_get_filter_id },
- { "nativeSetDataSource", "(Landroid/media/tv/tuner/Tuner$Filter;)I",
+ { "nativeSetDataSource", "(Landroid/media/tv/tuner/filter/Filter;)I",
(void *)android_media_tv_Tuner_set_filter_data_source },
{ "nativeStartFilter", "()I", (void *)android_media_tv_Tuner_start_filter },
{ "nativeStopFilter", "()I", (void *)android_media_tv_Tuner_stop_filter },
{ "nativeFlushFilter", "()I", (void *)android_media_tv_Tuner_flush_filter },
- { "nativeRead", "([BII)I", (void *)android_media_tv_Tuner_read_filter_fmq },
+ { "nativeRead", "([BJJ)I", (void *)android_media_tv_Tuner_read_filter_fmq },
{ "nativeClose", "()I", (void *)android_media_tv_Tuner_close_filter },
};
@@ -1183,31 +1190,36 @@ static const JNINativeMethod gTimeFilterMethods[] = {
};
static const JNINativeMethod gDescramblerMethods[] = {
- { "nativeAddPid", "(IILandroid/media/tv/tuner/Tuner$Filter;)I",
+ { "nativeAddPid", "(IILandroid/media/tv/tuner/filter/Filter;)I",
(void *)android_media_tv_Tuner_add_pid },
- { "nativeRemovePid", "(IILandroid/media/tv/tuner/Tuner$Filter;)I",
+ { "nativeRemovePid", "(IILandroid/media/tv/tuner/filter/Filter;)I",
(void *)android_media_tv_Tuner_remove_pid },
{ "nativeSetKeyToken", "([B)I", (void *)android_media_tv_Tuner_set_key_token },
{ "nativeClose", "()I", (void *)android_media_tv_Tuner_close_descrambler },
};
static const JNINativeMethod gDvrMethods[] = {
- { "nativeAttachFilter", "(Landroid/media/tv/tuner/Tuner$Filter;)I",
+ { "nativeAttachFilter", "(Landroid/media/tv/tuner/filter/Filter;)I",
(void *)android_media_tv_Tuner_attach_filter },
- { "nativeDetachFilter", "(Landroid/media/tv/tuner/Tuner$Filter;)I",
+ { "nativeDetachFilter", "(Landroid/media/tv/tuner/filter/Filter;)I",
(void *)android_media_tv_Tuner_detach_filter },
- { "nativeConfigureDvr", "(Landroid/media/tv/tuner/DvrSettings;)I",
+ { "nativeConfigureDvr", "(Landroid/media/tv/tuner/dvr/DvrSettings;)I",
(void *)android_media_tv_Tuner_configure_dvr },
{ "nativeStartDvr", "()I", (void *)android_media_tv_Tuner_start_dvr },
{ "nativeStopDvr", "()I", (void *)android_media_tv_Tuner_stop_dvr },
{ "nativeFlushDvr", "()I", (void *)android_media_tv_Tuner_flush_dvr },
{ "nativeClose", "()I", (void *)android_media_tv_Tuner_close_dvr },
- { "nativeSetFileDescriptor", "(Ljava/io/FileDescriptor;)V",
- (void *)android_media_tv_Tuner_dvr_set_fd },
- { "nativeRead", "(I)I", (void *)android_media_tv_Tuner_read_dvr },
- { "nativeRead", "([BII)I", (void *)android_media_tv_Tuner_read_dvr_from_array },
- { "nativeWrite", "(I)I", (void *)android_media_tv_Tuner_write_dvr },
- { "nativeWrite", "([BII)I", (void *)android_media_tv_Tuner_write_dvr_to_array },
+ { "nativeSetFileDescriptor", "(I)V", (void *)android_media_tv_Tuner_dvr_set_fd },
+};
+
+static const JNINativeMethod gDvrRecorderMethods[] = {
+ { "nativeWrite", "(J)J", (void *)android_media_tv_Tuner_write_dvr },
+ { "nativeWrite", "([BJJ)J", (void *)android_media_tv_Tuner_write_dvr_to_array },
+};
+
+static const JNINativeMethod gDvrPlaybackMethods[] = {
+ { "nativeRead", "(J)J", (void *)android_media_tv_Tuner_read_dvr },
+ { "nativeRead", "([BJJ)J", (void *)android_media_tv_Tuner_read_dvr_from_array },
};
static const JNINativeMethod gLnbMethods[] = {
@@ -1225,35 +1237,49 @@ static bool register_android_media_tv_Tuner(JNIEnv *env) {
return false;
}
if (AndroidRuntime::registerNativeMethods(
- env, "android/media/tv/tuner/Tuner$Filter",
+ env, "android/media/tv/tuner/filter/Filter",
gFilterMethods,
NELEM(gFilterMethods)) != JNI_OK) {
ALOGE("Failed to register filter native methods");
return false;
}
if (AndroidRuntime::registerNativeMethods(
- env, "android/media/tv/tuner/Tuner$TimeFilter",
+ env, "android/media/tv/tuner/filter/TimeFilter",
gTimeFilterMethods,
NELEM(gTimeFilterMethods)) != JNI_OK) {
ALOGE("Failed to register time filter native methods");
return false;
}
if (AndroidRuntime::registerNativeMethods(
- env, "android/media/tv/tuner/Tuner$Descrambler",
+ env, "android/media/tv/tuner/Descrambler",
gDescramblerMethods,
NELEM(gDescramblerMethods)) != JNI_OK) {
ALOGE("Failed to register descrambler native methods");
return false;
}
if (AndroidRuntime::registerNativeMethods(
- env, "android/media/tv/tuner/Tuner$Dvr",
+ env, "android/media/tv/tuner/dvr/Dvr",
gDvrMethods,
NELEM(gDvrMethods)) != JNI_OK) {
ALOGE("Failed to register dvr native methods");
return false;
}
if (AndroidRuntime::registerNativeMethods(
- env, "android/media/tv/tuner/Tuner$Lnb",
+ env, "android/media/tv/tuner/dvr/DvrRecorder",
+ gDvrRecorderMethods,
+ NELEM(gDvrRecorderMethods)) != JNI_OK) {
+ ALOGE("Failed to register dvr recorder native methods");
+ return false;
+ }
+ if (AndroidRuntime::registerNativeMethods(
+ env, "android/media/tv/tuner/dvr/DvrPlayback",
+ gDvrPlaybackMethods,
+ NELEM(gDvrPlaybackMethods)) != JNI_OK) {
+ ALOGE("Failed to register dvr playback native methods");
+ return false;
+ }
+ if (AndroidRuntime::registerNativeMethods(
+ env, "android/media/tv/tuner/Lnb",
gLnbMethods,
NELEM(gLnbMethods)) != JNI_OK) {
ALOGE("Failed to register lnb native methods");
diff --git a/native/graphics/jni/imagedecoder.cpp b/native/graphics/jni/imagedecoder.cpp
index 79bcc15e1f0f..c1143ce9c3dc 100644
--- a/native/graphics/jni/imagedecoder.cpp
+++ b/native/graphics/jni/imagedecoder.cpp
@@ -325,11 +325,9 @@ int AImageDecoder_decodeImage(AImageDecoder* decoder,
ImageDecoder* imageDecoder = toDecoder(decoder);
- const int height = imageDecoder->getOutputInfo().height();
- const size_t minStride = AImageDecoder_getMinimumStride(decoder);
- // If this calculation were to overflow, it would have been caught in
- // setTargetSize.
- if (stride < minStride || size < stride * (height - 1) + minStride) {
+ SkImageInfo info = imageDecoder->getOutputInfo();
+ size_t minSize = info.computeByteSize(stride);
+ if (SkImageInfo::ByteSizeOverflowed(minSize) || size < minSize || !info.validRowBytes(stride)) {
return ANDROID_IMAGE_DECODER_BAD_PARAMETER;
}
diff --git a/packages/CarSystemUI/src/com/android/systemui/TEST_MAPPING b/packages/CarSystemUI/TEST_MAPPING
index 6056ddfc0bc7..6056ddfc0bc7 100644
--- a/packages/CarSystemUI/src/com/android/systemui/TEST_MAPPING
+++ b/packages/CarSystemUI/TEST_MAPPING
diff --git a/packages/CarSystemUI/res/values/config.xml b/packages/CarSystemUI/res/values/config.xml
index 981c129a0965..e2297e44fdfe 100644
--- a/packages/CarSystemUI/res/values/config.xml
+++ b/packages/CarSystemUI/res/values/config.xml
@@ -80,5 +80,6 @@
<item>com.android.systemui.statusbar.notification.InstantAppNotifier</item>
<item>com.android.systemui.theme.ThemeOverlayController</item>
<item>com.android.systemui.navigationbar.car.CarNavigationBar</item>
+ <item>com.android.systemui.toast.ToastUI</item>
</string-array>
</resources>
diff --git a/packages/CarSystemUI/src/com/android/systemui/CarSystemUIBinder.java b/packages/CarSystemUI/src/com/android/systemui/CarSystemUIBinder.java
index 0bd2e06d3035..07b7b22d2320 100644
--- a/packages/CarSystemUI/src/com/android/systemui/CarSystemUIBinder.java
+++ b/packages/CarSystemUI/src/com/android/systemui/CarSystemUIBinder.java
@@ -32,6 +32,7 @@ import com.android.systemui.statusbar.notification.InstantAppNotifier;
import com.android.systemui.statusbar.phone.StatusBar;
import com.android.systemui.statusbar.tv.TvStatusBar;
import com.android.systemui.theme.ThemeOverlayController;
+import com.android.systemui.toast.ToastUI;
import com.android.systemui.util.leak.GarbageMonitor;
import com.android.systemui.volume.VolumeUI;
@@ -163,4 +164,10 @@ public abstract class CarSystemUIBinder {
@IntoMap
@ClassKey(VolumeUI.class)
public abstract SystemUI bindVolumeUI(VolumeUI sysui);
+
+ /** Inject into ToastUI. */
+ @Binds
+ @IntoMap
+ @ClassKey(ToastUI.class)
+ public abstract SystemUI bindToastUI(ToastUI service);
}
diff --git a/packages/Incremental/NativeAdbDataLoader/jni/com_android_incremental_nativeadb_DataLoaderService.cpp b/packages/Incremental/NativeAdbDataLoader/jni/com_android_incremental_nativeadb_DataLoaderService.cpp
index 4e49302debcd..01150b773557 100644
--- a/packages/Incremental/NativeAdbDataLoader/jni/com_android_incremental_nativeadb_DataLoaderService.cpp
+++ b/packages/Incremental/NativeAdbDataLoader/jni/com_android_incremental_nativeadb_DataLoaderService.cpp
@@ -32,6 +32,7 @@
#include <utils/Log.h>
#include <charconv>
+#include <span>
#include <string>
#include <thread>
#include <type_traits>
@@ -92,9 +93,7 @@ struct RequestCommand {
static_assert(COMMAND_SIZE == sizeof(RequestCommand));
-static bool sendRequest(int fd,
- RequestType requestType,
- FileId fileId = -1,
+static bool sendRequest(int fd, RequestType requestType, FileId fileId = -1,
BlockIdx blockIdx = -1) {
const RequestCommand command{
.requestType = static_cast<int16_t>(be16toh(requestType)),
@@ -267,25 +266,24 @@ private:
std::lock_guard lock{mMapsMutex};
CHECK(mIfs);
for (auto&& pendingRead : pendingReads) {
- const android::dataloader::Inode ino = pendingRead.file_ino;
- const auto blockIdx =
- static_cast<BlockIdx>(pendingRead.block_index);
+ const android::dataloader::FileId id = pendingRead.id;
+ const auto blockIdx = static_cast<BlockIdx>(pendingRead.block);
/*
ALOGI("[AdbDataLoader] Missing: %d", (int) blockIdx);
*/
- auto fileIdOr = getFileId(ino);
+ auto fileIdOr = getFileId(id);
if (!fileIdOr) {
- ALOGE("[AdbDataLoader] Failed to handle event for inode=%d. "
+ ALOGE("[AdbDataLoader] Failed to handle event for fileid=%s. "
"Ignore.",
- static_cast<int>(ino));
+ android::incfs::toString(id).c_str());
continue;
}
const FileId fileId = *fileIdOr;
if (mRequestedFiles.insert(fileId).second) {
if (!sendRequest(mOutFd, PREFETCH, fileId, blockIdx)) {
ALOGE("[AdbDataLoader] Failed to request prefetch for "
- "inode=%d. Ignore.",
- static_cast<int>(ino));
+ "fileid=%s. Ignore.",
+ android::incfs::toString(id).c_str());
mRequestedFiles.erase(fileId);
mStatusListener->reportStatus(DATA_LOADER_NO_CONNECTION);
}
@@ -296,7 +294,7 @@ private:
struct TracedRead {
uint64_t timestampUs;
- uint64_t fileIno;
+ android::dataloader::FileId fileId;
uint32_t firstBlockIdx;
uint32_t count;
};
@@ -307,26 +305,26 @@ private:
return;
}
- TracedRead last = {0, 0, 0, 0};
+ TracedRead last = {};
std::lock_guard lock{mMapsMutex};
for (auto&& read : pageReads) {
- if (read.file_ino != last.fileIno ||
- read.block_index != last.firstBlockIdx + last.count) {
+ if (read.id != last.fileId || read.block != last.firstBlockIdx + last.count) {
traceOrLogRead(last, trace, log);
- last = {read.timestamp_us, read.file_ino, read.block_index, 1};
+ last = {read.bootClockTsUs, read.id, (uint32_t)read.block, 1};
} else {
++last.count;
}
}
traceOrLogRead(last, trace, log);
}
- void onFileCreated(android::dataloader::Inode inode, const android::dataloader::RawMetadata& metadata) {
- }
+ void onFileCreated(android::dataloader::FileId fileid,
+ const android::dataloader::RawMetadata& metadata) {}
private:
void receiver() {
std::vector<uint8_t> data;
- std::vector<incfs_new_data_block> instructions;
+ std::vector<IncFsDataBlock> instructions;
+ std::unordered_map<android::dataloader::FileId, unique_fd> writeFds;
while (!mStopReceiving) {
const int res = waitForDataOrSignal(mInFd, mEventFd);
if (res == 0) {
@@ -366,21 +364,32 @@ private:
mStopReceiving = true;
break;
}
- const android::dataloader::Inode ino = mIdToNodeMap[header.fileId];
- if (!ino) {
+ const android::dataloader::FileId id = mIdToNodeMap[header.fileId];
+ if (!android::incfs::isValidFileId(id)) {
ALOGE("Unknown data destination for file ID %d. "
"Ignore.",
header.fileId);
continue;
}
- auto inst = incfs_new_data_block{
- .file_ino = static_cast<__aligned_u64>(ino),
- .block_index = static_cast<uint32_t>(header.blockIdx),
- .data_len = static_cast<uint16_t>(header.blockSize),
- .data = reinterpret_cast<uint64_t>(
- remainingData.data()),
- .compression =
- static_cast<uint8_t>(header.compressionType)};
+
+ auto& writeFd = writeFds[id];
+ if (writeFd < 0) {
+ writeFd.reset(this->mIfs->openWrite(id));
+ if (writeFd < 0) {
+ ALOGE("Failed to open file %d for writing (%d). Aboring.", header.fileId,
+ -writeFd);
+ break;
+ }
+ }
+
+ const auto inst = IncFsDataBlock{
+ .fileFd = writeFd,
+ .pageIndex = static_cast<IncFsBlockIndex>(header.blockIdx),
+ .compression = static_cast<IncFsCompressionKind>(header.compressionType),
+ .kind = INCFS_BLOCK_KIND_DATA,
+ .dataSize = static_cast<uint16_t>(header.blockSize),
+ .data = (const char*)remainingData.data(),
+ };
instructions.push_back(inst);
remainingData = remainingData.subspan(header.blockSize);
}
@@ -390,9 +399,8 @@ private:
flushReadLog();
}
- void writeInstructions(std::vector<incfs_new_data_block>& instructions) {
- auto res = this->mIfs->writeBlocks(instructions.data(),
- instructions.size());
+ void writeInstructions(std::vector<IncFsDataBlock>& instructions) {
+ auto res = this->mIfs->writeBlocks(instructions);
if (res != instructions.size()) {
ALOGE("[AdbDataLoader] failed to write data to Incfs (res=%d when "
"expecting %d)",
@@ -406,30 +414,30 @@ private:
FileId fileId;
};
- MetaPair* updateMapsForFile(android::dataloader::Inode ino) {
- android::dataloader::RawMetadata meta = mIfs->getRawMetadata(ino);
+ MetaPair* updateMapsForFile(android::dataloader::FileId id) {
+ android::dataloader::RawMetadata meta = mIfs->getRawMetadata(id);
FileId fileId;
auto res =
std::from_chars(meta.data(), meta.data() + meta.size(), fileId);
if (res.ec != std::errc{} || fileId < 0) {
- ALOGE("[AdbDataLoader] Invalid metadata for inode=%d (%s)",
- static_cast<int>(ino), meta.data());
+ ALOGE("[AdbDataLoader] Invalid metadata for fileid=%s (%s)",
+ android::incfs::toString(id).c_str(), meta.data());
return nullptr;
}
- mIdToNodeMap[fileId] = ino;
- auto& metaPair = mNodeToMetaMap[ino];
+ mIdToNodeMap[fileId] = id;
+ auto& metaPair = mNodeToMetaMap[id];
metaPair.meta = std::move(meta);
metaPair.fileId = fileId;
return &metaPair;
}
- android::dataloader::RawMetadata* getMeta(android::dataloader::Inode ino) {
- auto it = mNodeToMetaMap.find(ino);
+ android::dataloader::RawMetadata* getMeta(android::dataloader::FileId id) {
+ auto it = mNodeToMetaMap.find(id);
if (it != mNodeToMetaMap.end()) {
return &it->second.meta;
}
- auto metaPair = updateMapsForFile(ino);
+ auto metaPair = updateMapsForFile(id);
if (!metaPair) {
return nullptr;
}
@@ -437,13 +445,13 @@ private:
return &metaPair->meta;
}
- FileId* getFileId(android::dataloader::Inode ino) {
- auto it = mNodeToMetaMap.find(ino);
+ FileId* getFileId(android::dataloader::FileId id) {
+ auto it = mNodeToMetaMap.find(id);
if (it != mNodeToMetaMap.end()) {
return &it->second.fileId;
}
- auto* metaPair = updateMapsForFile(ino);
+ auto* metaPair = updateMapsForFile(id);
if (!metaPair) {
return nullptr;
}
@@ -456,7 +464,7 @@ private:
return;
}
if (trace) {
- auto* meta = getMeta(read.fileIno);
+ auto* meta = getMeta(read.fileId);
auto str = android::base::StringPrintf(
"page_read: index=%lld count=%lld meta=%.*s",
static_cast<long long>(read.firstBlockIdx),
@@ -468,7 +476,7 @@ private:
if (log) {
mReadLog.reserve(ReadLogBufferSize);
- auto fileId = getFileId(read.fileIno);
+ auto fileId = getFileId(read.fileId);
android::base::StringAppendF(
&mReadLog, "%lld:%lld:%lld:%lld\n",
static_cast<long long>(read.timestampUs),
@@ -501,8 +509,8 @@ private:
std::string mReadLog;
std::thread mReceiverThread;
std::mutex mMapsMutex;
- std::unordered_map<android::dataloader::Inode, MetaPair> mNodeToMetaMap GUARDED_BY(mMapsMutex);
- std::unordered_map<FileId, android::dataloader::Inode> mIdToNodeMap GUARDED_BY(mMapsMutex);
+ std::unordered_map<android::dataloader::FileId, MetaPair> mNodeToMetaMap GUARDED_BY(mMapsMutex);
+ std::unordered_map<FileId, android::dataloader::FileId> mIdToNodeMap GUARDED_BY(mMapsMutex);
/** Tracks which files have been requested */
std::unordered_set<FileId> mRequestedFiles;
std::atomic<bool> mStopReceiving = false;
diff --git a/packages/SettingsLib/src/com/android/settingslib/wifi/AccessPoint.java b/packages/SettingsLib/src/com/android/settingslib/wifi/AccessPoint.java
index 9d4c24e8faa4..84dde05afb2e 100644
--- a/packages/SettingsLib/src/com/android/settingslib/wifi/AccessPoint.java
+++ b/packages/SettingsLib/src/com/android/settingslib/wifi/AccessPoint.java
@@ -1141,7 +1141,7 @@ public class AccessPoint implements Comparable<AccessPoint> {
} else if (isActive()) {
summary.append(getSummary(mContext, /* ssid */ null, getDetailedState(),
mInfo != null && mInfo.isEphemeral(),
- mInfo != null ? mInfo.getAppPackageName() : null));
+ mInfo != null ? mInfo.getRequestingPackageName() : null));
} else { // not active
if (mConfig != null && mConfig.hasNoInternetAccess()) {
int messageID = mConfig.getNetworkSelectionStatus().isNetworkPermanentlyDisabled()
diff --git a/packages/SettingsLib/src/com/android/settingslib/wifi/WifiTracker.java b/packages/SettingsLib/src/com/android/settingslib/wifi/WifiTracker.java
index ed4ff085aeac..26abf715369c 100644
--- a/packages/SettingsLib/src/com/android/settingslib/wifi/WifiTracker.java
+++ b/packages/SettingsLib/src/com/android/settingslib/wifi/WifiTracker.java
@@ -226,7 +226,7 @@ public class WifiTracker implements LifecycleObserver, OnStart, OnStop, OnDestro
mConnectivityManager = connectivityManager;
// check if verbose logging developer option has been turned on or off
- sVerboseLogging = mWifiManager != null && (mWifiManager.getVerboseLoggingLevel() > 0);
+ sVerboseLogging = mWifiManager != null && mWifiManager.isVerboseLoggingEnabled();
mFilter = filter;
diff --git a/packages/SettingsLib/src/com/android/settingslib/wifi/WifiUtils.java b/packages/SettingsLib/src/com/android/settingslib/wifi/WifiUtils.java
index b93b0001f5de..78ccba02fb04 100644
--- a/packages/SettingsLib/src/com/android/settingslib/wifi/WifiUtils.java
+++ b/packages/SettingsLib/src/com/android/settingslib/wifi/WifiUtils.java
@@ -102,10 +102,10 @@ public class WifiUtils {
if (accessPoint.getSpeed() != AccessPoint.Speed.NONE) {
visibility.append(" speed=").append(accessPoint.getSpeedLabel());
}
- visibility.append(String.format(" tx=%.1f,", info.getTxSuccessRate()));
- visibility.append(String.format("%.1f,", info.getTxRetriesRate()));
- visibility.append(String.format("%.1f ", info.getTxBadRate()));
- visibility.append(String.format("rx=%.1f", info.getRxSuccessRate()));
+ visibility.append(String.format(" tx=%.1f,", info.getSuccessfulTxPacketsPerSecond()));
+ visibility.append(String.format("%.1f,", info.getRetriedTxPacketsPerSecond()));
+ visibility.append(String.format("%.1f ", info.getLostTxPacketsPerSecond()));
+ visibility.append(String.format("rx=%.1f", info.getSuccessfulRxPacketsPerSecond()));
}
int maxRssi5 = INVALID_RSSI;
diff --git a/packages/SettingsLib/tests/integ/src/com/android/settingslib/wifi/AccessPointTest.java b/packages/SettingsLib/tests/integ/src/com/android/settingslib/wifi/AccessPointTest.java
index 03201ae6d5ba..42f3cbb04cf8 100644
--- a/packages/SettingsLib/tests/integ/src/com/android/settingslib/wifi/AccessPointTest.java
+++ b/packages/SettingsLib/tests/integ/src/com/android/settingslib/wifi/AccessPointTest.java
@@ -507,7 +507,7 @@ public class AccessPointTest {
WifiInfo wifiInfo = new WifiInfo();
wifiInfo.setSSID(WifiSsid.createFromAsciiEncoded(TEST_SSID));
wifiInfo.setEphemeral(true);
- wifiInfo.setAppPackageName(appPackageName);
+ wifiInfo.setRequestingPackageName(appPackageName);
wifiInfo.setRssi(rssi);
Context context = mock(Context.class);
diff --git a/packages/Shell/AndroidManifest.xml b/packages/Shell/AndroidManifest.xml
index d40e7d4addab..0f3585303ed8 100644
--- a/packages/Shell/AndroidManifest.xml
+++ b/packages/Shell/AndroidManifest.xml
@@ -110,6 +110,7 @@
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
<uses-permission android:name="android.permission.WRITE_MEDIA_STORAGE" />
+ <uses-permission android:name="android.permission.MANAGE_EXTERNAL_STORAGE" />
<uses-permission android:name="android.permission.INTERACT_ACROSS_USERS" />
<uses-permission android:name="android.permission.INTERACT_ACROSS_USERS_FULL" />
<uses-permission android:name="android.permission.CREATE_USERS" />
diff --git a/packages/SystemUI/TEST_MAPPING b/packages/SystemUI/TEST_MAPPING
index 9f13a7b861a0..cff958faa7e1 100644
--- a/packages/SystemUI/TEST_MAPPING
+++ b/packages/SystemUI/TEST_MAPPING
@@ -16,5 +16,24 @@
}
]
}
+ ],
+ "platinum-postsubmit": [
+ {
+ "name": "PlatformScenarioTests",
+ "options": [
+ {
+ "include-filter": "android.platform.test.scenario.sysui"
+ },
+ {
+ "include-annotation": "android.platform.test.scenario.annotation.Scenario"
+ },
+ {
+ "exclude-annotation": "androidx.test.filters.FlakyTest"
+ },
+ {
+ "exclude-annotation": "android.platform.helpers.Staging"
+ }
+ ]
+ }
]
}
diff --git a/packages/SystemUI/plugin/src/com/android/systemui/plugins/qs/QSTile.java b/packages/SystemUI/plugin/src/com/android/systemui/plugins/qs/QSTile.java
index 6518924ca0c2..17f2f476c9f2 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
@@ -133,6 +133,7 @@ public interface QSTile {
public CharSequence label;
public CharSequence secondaryLabel;
public CharSequence contentDescription;
+ public CharSequence stateDescription;
public CharSequence dualLabelContentDescription;
public boolean disabledByPolicy;
public boolean dualTarget = false;
@@ -151,6 +152,7 @@ public interface QSTile {
|| !Objects.equals(other.label, label)
|| !Objects.equals(other.secondaryLabel, secondaryLabel)
|| !Objects.equals(other.contentDescription, contentDescription)
+ || !Objects.equals(other.stateDescription, stateDescription)
|| !Objects.equals(other.dualLabelContentDescription,
dualLabelContentDescription)
|| !Objects.equals(other.expandedAccessibilityClassName,
@@ -168,6 +170,7 @@ public interface QSTile {
other.label = label;
other.secondaryLabel = secondaryLabel;
other.contentDescription = contentDescription;
+ other.stateDescription = stateDescription;
other.dualLabelContentDescription = dualLabelContentDescription;
other.expandedAccessibilityClassName = expandedAccessibilityClassName;
other.disabledByPolicy = disabledByPolicy;
@@ -195,6 +198,7 @@ public interface QSTile {
sb.append(",label=").append(label);
sb.append(",secondaryLabel=").append(secondaryLabel);
sb.append(",contentDescription=").append(contentDescription);
+ sb.append(",stateDescription=").append(stateDescription);
sb.append(",dualLabelContentDescription=").append(dualLabelContentDescription);
sb.append(",expandedAccessibilityClassName=").append(expandedAccessibilityClassName);
sb.append(",disabledByPolicy=").append(disabledByPolicy);
diff --git a/packages/SystemUI/res-product/values-ky/strings.xml b/packages/SystemUI/res-product/values-ky/strings.xml
index 8d96cb26bf30..043faeeaf89a 100644
--- a/packages/SystemUI/res-product/values-ky/strings.xml
+++ b/packages/SystemUI/res-product/values-ky/strings.xml
@@ -26,18 +26,18 @@
<string name="keyguard_missing_sim_message" product="tablet" msgid="5018086454277963787">"Планшетте SIM-карта жок."</string>
<string name="keyguard_missing_sim_message" product="default" msgid="7053347843877341391">"Телефондо SIM-карта жок."</string>
<string name="kg_invalid_confirm_pin_hint" product="default" msgid="6278551068943958651">"PIN-коддор дал келген жок"</string>
- <string name="kg_failed_attempts_almost_at_wipe" product="tablet" msgid="302165994845009232">"Планшеттин кулпусун <xliff:g id="NUMBER_0">%1$d</xliff:g> жолу туура эмес ачууга аракет жасадыңыз. Дагы <xliff:g id="NUMBER_1">%2$d</xliff:g> жолу ийгиликсиз аракет кылсаңыз, бул планшет баштапкы абалга келтирилип, андагы бардык маалымат жок кылынат."</string>
- <string name="kg_failed_attempts_almost_at_wipe" product="default" msgid="2594813176164266847">"Телефондун кулпусун <xliff:g id="NUMBER_0">%1$d</xliff:g> жолу туура эмес ачууга аракет жасадыңыз. Дагы <xliff:g id="NUMBER_1">%2$d</xliff:g> жолу ийгиликсиз аракет кылсаңыз, бул телефон баштапкы абалга келтирилип, андагы бардык маалымат жок кылынат."</string>
- <string name="kg_failed_attempts_now_wiping" product="tablet" msgid="8710104080409538587">"Планшеттин кулпусун <xliff:g id="NUMBER">%d</xliff:g> жолу туура эмес ачууга аракет жасадыңыз. Бул планшет баштапкы абалга келтирилип, андагы бардык маалымат жок кылынат."</string>
- <string name="kg_failed_attempts_now_wiping" product="default" msgid="6381835450014881813">"Телефондун кулпусун <xliff:g id="NUMBER">%d</xliff:g> жолу туура эмес аракет жасадыңыз. Бул телефон баштапкы абалга келтирилип, андагы бардык маалымат жок кылынат."</string>
- <string name="kg_failed_attempts_almost_at_erase_user" product="tablet" msgid="7325071812832605911">"Планшеттин кулпусун <xliff:g id="NUMBER_0">%1$d</xliff:g> жолу туура эмес ачууга аракет жасадыңыз. Дагы <xliff:g id="NUMBER_1">%2$d</xliff:g> жолу ийгиликсиз аракет кылсаңыз, бул колдонуучу өчүрүлүп, колдонуучунун бардык маалыматы жок кылынат."</string>
- <string name="kg_failed_attempts_almost_at_erase_user" product="default" msgid="8110939900089863103">"Телефондун кулпусун <xliff:g id="NUMBER_0">%1$d</xliff:g> жолу туура эмес ачууга аракет жасадыңыз. Дагы <xliff:g id="NUMBER_1">%2$d</xliff:g> жолу ийгиликсиз аракет кылсаңыз, бул колдонуучу өчүрүлүп, колдонуучунун бардык маалыматы жок кылынат."</string>
- <string name="kg_failed_attempts_now_erasing_user" product="tablet" msgid="8509811676952707883">"Планшеттин кулпусун <xliff:g id="NUMBER">%d</xliff:g> жолу туура эмес ачууга аракет жасадыңыз. Бул колдонуучу өчүрүлүп, колдонуучунун бардык маалыматы жок кылынат."</string>
- <string name="kg_failed_attempts_now_erasing_user" product="default" msgid="3051962486994265014">"Телефондун кулпусун <xliff:g id="NUMBER">%d</xliff:g> жолу туура эмес ачууга аракет жасадыңыз. Бул колдонуучу өчүрүлүп, колдонуучунун бардык маалыматы жок кылынат."</string>
- <string name="kg_failed_attempts_almost_at_erase_profile" product="tablet" msgid="1049523640263353830">"Планшетиңиздин кулпусун <xliff:g id="NUMBER_0">%1$d</xliff:g> жолу туура эмес ачууга аракет жасадыңыз. Дагы <xliff:g id="NUMBER_1">%2$d</xliff:g> жолу ийгиликсиз аракет кылсаңыз, жумуш профилиңиз өчүрүлүп, профилдеги бардык маалымат жок кылынат."</string>
- <string name="kg_failed_attempts_almost_at_erase_profile" product="default" msgid="3280816298678433681">"Телефондун кулпусун <xliff:g id="NUMBER_0">%1$d</xliff:g> жолу туура эмес ачууга аракет жасадыңыз. Дагы <xliff:g id="NUMBER_1">%2$d</xliff:g> жолу ийгиликсиз аракет кылсаңыз, жумуш профилиңиз өчүрүлүп, профилдеги бардык маалымат жок кылынат."</string>
- <string name="kg_failed_attempts_now_erasing_profile" product="tablet" msgid="4417100487251371559">"Планшеттин кулпусун <xliff:g id="NUMBER">%d</xliff:g> жолу туура эмес ачууга аракет жасадыңыз. Жумуш профили өчүрүлүп, андагы бардык маалымат жок кылынат."</string>
- <string name="kg_failed_attempts_now_erasing_profile" product="default" msgid="4682221342671290678">"Телефондун кулпусун <xliff:g id="NUMBER">%d</xliff:g> жолу туура эмес ачууга аракет жасадыңыз. Жумуш профили өчүрүлүп, андагы бардык маалымат жок кылынат."</string>
+ <string name="kg_failed_attempts_almost_at_wipe" product="tablet" msgid="302165994845009232">"Планшеттин кулпусун <xliff:g id="NUMBER_0">%1$d</xliff:g> жолу туура эмес ачууга аракет жасадыңыз. Дагы <xliff:g id="NUMBER_1">%2$d</xliff:g> жолу ийгиликсиз аракет кылсаңыз, бул планшет баштапкы абалга келтирилип, андагы бардык маалымат өчүрүлөт."</string>
+ <string name="kg_failed_attempts_almost_at_wipe" product="default" msgid="2594813176164266847">"Телефондун кулпусун <xliff:g id="NUMBER_0">%1$d</xliff:g> жолу туура эмес ачууга аракет жасадыңыз. Дагы <xliff:g id="NUMBER_1">%2$d</xliff:g> жолу ийгиликсиз аракет кылсаңыз, бул телефон баштапкы абалга келтирилип, андагы бардык маалымат өчүрүлөт."</string>
+ <string name="kg_failed_attempts_now_wiping" product="tablet" msgid="8710104080409538587">"Планшеттин кулпусун <xliff:g id="NUMBER">%d</xliff:g> жолу туура эмес ачууга аракет жасадыңыз. Бул планшет баштапкы абалга келтирилип, андагы бардык маалымат өчүрүлөт."</string>
+ <string name="kg_failed_attempts_now_wiping" product="default" msgid="6381835450014881813">"Телефондун кулпусун <xliff:g id="NUMBER">%d</xliff:g> жолу туура эмес аракет жасадыңыз. Бул телефон баштапкы абалга келтирилип, андагы бардык маалымат өчүрүлөт."</string>
+ <string name="kg_failed_attempts_almost_at_erase_user" product="tablet" msgid="7325071812832605911">"Планшеттин кулпусун <xliff:g id="NUMBER_0">%1$d</xliff:g> жолу туура эмес ачууга аракет жасадыңыз. Дагы <xliff:g id="NUMBER_1">%2$d</xliff:g> жолу ийгиликсиз аракет кылсаңыз, бул колдонуучу өчүрүлүп, колдонуучунун бардык маалыматы өчүрүлөт."</string>
+ <string name="kg_failed_attempts_almost_at_erase_user" product="default" msgid="8110939900089863103">"Телефондун кулпусун <xliff:g id="NUMBER_0">%1$d</xliff:g> жолу туура эмес ачууга аракет жасадыңыз. Дагы <xliff:g id="NUMBER_1">%2$d</xliff:g> жолу ийгиликсиз аракет кылсаңыз, бул колдонуучу өчүрүлүп, колдонуучунун бардык маалыматы өчүрүлөт."</string>
+ <string name="kg_failed_attempts_now_erasing_user" product="tablet" msgid="8509811676952707883">"Планшеттин кулпусун <xliff:g id="NUMBER">%d</xliff:g> жолу туура эмес ачууга аракет жасадыңыз. Бул колдонуучу өчүрүлүп, колдонуучунун бардык маалыматы өчүрүлөт."</string>
+ <string name="kg_failed_attempts_now_erasing_user" product="default" msgid="3051962486994265014">"Телефондун кулпусун <xliff:g id="NUMBER">%d</xliff:g> жолу туура эмес ачууга аракет жасадыңыз. Бул колдонуучу өчүрүлүп, колдонуучунун бардык маалыматы өчүрүлөт."</string>
+ <string name="kg_failed_attempts_almost_at_erase_profile" product="tablet" msgid="1049523640263353830">"Планшетиңиздин кулпусун <xliff:g id="NUMBER_0">%1$d</xliff:g> жолу туура эмес ачууга аракет жасадыңыз. Дагы <xliff:g id="NUMBER_1">%2$d</xliff:g> жолу ийгиликсиз аракет кылсаңыз, жумуш профилиңиз өчүрүлүп, профилдеги бардык маалымат өчүрүлөт."</string>
+ <string name="kg_failed_attempts_almost_at_erase_profile" product="default" msgid="3280816298678433681">"Телефондун кулпусун <xliff:g id="NUMBER_0">%1$d</xliff:g> жолу туура эмес ачууга аракет жасадыңыз. Дагы <xliff:g id="NUMBER_1">%2$d</xliff:g> жолу ийгиликсиз аракет кылсаңыз, жумуш профилиңиз өчүрүлүп, профилдеги бардык маалымат өчүрүлөт."</string>
+ <string name="kg_failed_attempts_now_erasing_profile" product="tablet" msgid="4417100487251371559">"Планшеттин кулпусун <xliff:g id="NUMBER">%d</xliff:g> жолу туура эмес ачууга аракет жасадыңыз. Жумуш профили өчүрүлүп, андагы бардык маалымат өчүрүлөт."</string>
+ <string name="kg_failed_attempts_now_erasing_profile" product="default" msgid="4682221342671290678">"Телефондун кулпусун <xliff:g id="NUMBER">%d</xliff:g> жолу туура эмес ачууга аракет жасадыңыз. Жумуш профили өчүрүлүп, андагы бардык маалымат өчүрүлөт."</string>
<string name="kg_failed_attempts_almost_at_login" product="tablet" msgid="1860049973474855672">"Графикалык ачкычты <xliff:g id="NUMBER_0">%1$d</xliff:g> жолу туура эмес тарттыңыз. Дагы <xliff:g id="NUMBER_1">%2$d</xliff:g> ийгиликсиз аракеттен кийин планшетиңизди бөгөттөн электрондук почтаңыз аркылуу чыгаруу талап кылынат.\n\n <xliff:g id="NUMBER_2">%3$d</xliff:g> секунддан кийин кайра аракеттениңиз."</string>
<string name="kg_failed_attempts_almost_at_login" product="default" msgid="44112553371516141">"Графикалык ачкычты <xliff:g id="NUMBER_0">%1$d</xliff:g> жолу туура эмес тарттыңыз. Дагы <xliff:g id="NUMBER_1">%2$d</xliff:g> ийгиликсиз аракеттен кийин телефонуңузду бөгөттөн электрондук почтаңыз аркылуу чыгаруу талап кылынат.\n\n <xliff:g id="NUMBER_2">%3$d</xliff:g> секунддан кийин кайра аракеттениңиз."</string>
</resources>
diff --git a/packages/SystemUI/res/color/light_background.xml b/packages/SystemUI/res/color/light_background.xml
new file mode 100644
index 000000000000..2effd991f666
--- /dev/null
+++ b/packages/SystemUI/res/color/light_background.xml
@@ -0,0 +1,6 @@
+<?xml version="1.0" encoding="utf-8"?>
+<selector xmlns:android="http://schemas.android.com/apk/res/android">
+ <item android:state_enabled="false"
+ android:color="@color/control_default_background" />
+ <item android:color="@color/GM2_yellow_50" />
+</selector>
diff --git a/packages/SystemUI/res/color/light_foreground.xml b/packages/SystemUI/res/color/light_foreground.xml
new file mode 100644
index 000000000000..8143028795df
--- /dev/null
+++ b/packages/SystemUI/res/color/light_foreground.xml
@@ -0,0 +1,6 @@
+<?xml version="1.0" encoding="utf-8"?>
+<selector xmlns:android="http://schemas.android.com/apk/res/android">
+ <item android:state_enabled="false"
+ android:color="@color/control_default_foreground" />
+ <item android:color="@color/GM2_orange_900" />
+</selector>
diff --git a/packages/SystemUI/res/color/lock_background.xml b/packages/SystemUI/res/color/lock_background.xml
new file mode 100644
index 000000000000..646fe5dfe712
--- /dev/null
+++ b/packages/SystemUI/res/color/lock_background.xml
@@ -0,0 +1,6 @@
+<?xml version="1.0" encoding="utf-8"?>
+<selector xmlns:android="http://schemas.android.com/apk/res/android">
+ <item android:state_enabled="false"
+ android:color="@color/control_default_background" />
+ <item android:color="@color/GM2_blue_50" />
+</selector>
diff --git a/packages/SystemUI/res/color/lock_foreground.xml b/packages/SystemUI/res/color/lock_foreground.xml
new file mode 100644
index 000000000000..3e05653bce92
--- /dev/null
+++ b/packages/SystemUI/res/color/lock_foreground.xml
@@ -0,0 +1,6 @@
+<?xml version="1.0" encoding="utf-8"?>
+<selector xmlns:android="http://schemas.android.com/apk/res/android">
+ <item android:state_enabled="false"
+ android:color="@color/control_default_foreground" />
+ <item android:color="@color/GM2_blue_700" />
+</selector>
diff --git a/packages/SystemUI/res/color/unknown_foreground.xml b/packages/SystemUI/res/color/unknown_foreground.xml
new file mode 100644
index 000000000000..bf028f18a7de
--- /dev/null
+++ b/packages/SystemUI/res/color/unknown_foreground.xml
@@ -0,0 +1,6 @@
+<?xml version="1.0" encoding="utf-8"?>
+<selector xmlns:android="http://schemas.android.com/apk/res/android">
+ <item android:state_enabled="false"
+ android:color="@color/control_default_foreground" />
+ <item android:color="@color/GM2_blue_700" />
+ </selector>
diff --git a/packages/SystemUI/res/drawable/control_background.xml b/packages/SystemUI/res/drawable/control_background.xml
new file mode 100644
index 000000000000..b246ea0a3935
--- /dev/null
+++ b/packages/SystemUI/res/drawable/control_background.xml
@@ -0,0 +1,32 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+/*
+* Copyright 2020, The Android Open Source Project
+*
+* Licensed under the Apache License, Version 2.0 (the "License");
+* you may not use this file except in compliance with the License.
+* You may obtain a copy of the License at
+*
+* http://www.apache.org/licenses/LICENSE-2.0
+*
+* Unless required by applicable law or agreed to in writing, software
+* distributed under the License is distributed on an "AS IS" BASIS,
+* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+* See the License for the specific language governing permissions and
+* limitations under the License.
+*/
+-->
+<layer-list xmlns:android="http://schemas.android.com/apk/res/android">
+ <item>
+ <shape>
+ <solid android:color="?android:attr/colorBackgroundFloating"/>
+ <corners android:radius="@dimen/control_corner_radius" />
+ </shape>
+ </item>
+ <item
+ android:id="@+id/clip_layer">
+ <clip
+ android:clipOrientation="horizontal"
+ android:drawable="@drawable/control_layer"/>
+ </item>
+</layer-list>
diff --git a/packages/SystemUI/res/drawable/control_layer.xml b/packages/SystemUI/res/drawable/control_layer.xml
new file mode 100644
index 000000000000..fe8c4a4af1bb
--- /dev/null
+++ b/packages/SystemUI/res/drawable/control_layer.xml
@@ -0,0 +1,22 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+/*
+* Copyright 2020, The Android Open Source Project
+*
+* Licensed under the Apache License, Version 2.0 (the "License");
+* you may not use this file except in compliance with the License.
+* You may obtain a copy of the License at
+*
+* http://www.apache.org/licenses/LICENSE-2.0
+*
+* Unless required by applicable law or agreed to in writing, software
+* distributed under the License is distributed on an "AS IS" BASIS,
+* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+* See the License for the specific language governing permissions and
+* limitations under the License.
+*/
+-->
+<shape xmlns:android="http://schemas.android.com/apk/res/android">
+ <solid android:color="@android:color/transparent"/>
+ <corners android:radius="@dimen/control_corner_radius" />
+</shape>
diff --git a/packages/SystemUI/res/drawable/control_no_favorites_background.xml b/packages/SystemUI/res/drawable/control_no_favorites_background.xml
new file mode 100644
index 000000000000..1e282ad0eec7
--- /dev/null
+++ b/packages/SystemUI/res/drawable/control_no_favorites_background.xml
@@ -0,0 +1,22 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+/*
+* Copyright 2020, The Android Open Source Project
+*
+* Licensed under the Apache License, Version 2.0 (the "License");
+* you may not use this file except in compliance with the License.
+* You may obtain a copy of the License at
+*
+* http://www.apache.org/licenses/LICENSE-2.0
+*
+* Unless required by applicable law or agreed to in writing, software
+* distributed under the License is distributed on an "AS IS" BASIS,
+* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+* See the License for the specific language governing permissions and
+* limitations under the License.
+*/
+-->
+<shape xmlns:android="http://schemas.android.com/apk/res/android">
+ <stroke android:width="1dp" android:color="?android:attr/colorBackgroundFloating"/>
+ <corners android:radius="@dimen/control_corner_radius" />
+</shape>
diff --git a/packages/SystemUI/res/drawable/ic_device_thermostat_gm2_24px.xml b/packages/SystemUI/res/drawable/ic_device_thermostat_gm2_24px.xml
new file mode 100644
index 000000000000..45a658fe07da
--- /dev/null
+++ b/packages/SystemUI/res/drawable/ic_device_thermostat_gm2_24px.xml
@@ -0,0 +1,9 @@
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="24dp"
+ android:height="24dp"
+ android:viewportWidth="24"
+ android:viewportHeight="24">
+ <path
+ android:fillColor="#FF000000"
+ android:pathData="M18,9h-5v2h5m3,-6h-8v2h8m-9,11.97c0.62,-0.83 1,-1.85 1,-2.97 0,-1.63 -0.79,-3.09 -2,-4V6c0,-1.66 -1.34,-3 -3,-3S5,4.34 5,6v6c-1.21,0.91 -2,2.37 -2,4 0,1.12 0.38,2.14 1,2.97V19h0.02c0.91,1.21 2.35,2 3.98,2s3.06,-0.79 3.98,-2H12v-0.03zM6.2,13.6L7,13V6c0,-0.55 0.45,-1 1,-1s1,0.45 1,1v7l0.8,0.6c0.75,0.57 1.2,1.46 1.2,2.4H5c0,-0.94 0.45,-1.84 1.2,-2.4z"/>
+</vector>
diff --git a/packages/SystemUI/res/drawable/ic_light_off_gm2_24px.xml b/packages/SystemUI/res/drawable/ic_light_off_gm2_24px.xml
new file mode 100644
index 000000000000..78c3cc54f022
--- /dev/null
+++ b/packages/SystemUI/res/drawable/ic_light_off_gm2_24px.xml
@@ -0,0 +1,19 @@
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="24dp"
+ android:height="24dp"
+ android:viewportWidth="24"
+ android:viewportHeight="24">
+ <path
+ android:fillColor="#FF000000"
+ android:pathData="M9,21v-1h6v1c0,0.55 -0.45,1 -1,1h-4c-0.55,0 -1,-0.45 -1,-1z"/>
+ <group>
+ <clip-path android:pathData="M0,0h24v24H0z M 0,0"/>
+ </group>
+ <path
+ android:fillColor="#FF000000"
+ android:pathData="M12,2c-1.89,0 -3.6,0.75 -4.86,1.97l1.41,1.41C9.45,4.53 10.67,4 12,4c2.76,0 5,2.24 5,5 0,1.28 -0.5,2.5 -1.36,3.42l-0.02,0.02 1.41,1.41C18.25,12.6 19,10.89 19,9c0,-3.86 -3.14,-7 -7,-7z"
+ android:fillType="evenOdd"/>
+ <path
+ android:fillColor="#FF000000"
+ android:pathData="M2.92,2.29L1.65,3.57l3.59,3.59C5.09,7.75 5,8.36 5,9c0,2.38 1.19,4.47 3,5.74V17c0,0.55 0.45,1 1,1h6c0.3,0 0.57,-0.13 0.75,-0.34L20.09,22l1.27,-1.27L2.92,2.29zM10,16v-2.3l-0.85,-0.6C7.8,12.16 7,10.63 7,9v-0.08L14.09,16H10z"/>
+</vector>
diff --git a/packages/SystemUI/res/drawable/ic_lock_gm2_24px.xml b/packages/SystemUI/res/drawable/ic_lock_gm2_24px.xml
new file mode 100644
index 000000000000..f4299e6cda11
--- /dev/null
+++ b/packages/SystemUI/res/drawable/ic_lock_gm2_24px.xml
@@ -0,0 +1,9 @@
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="24dp"
+ android:height="24dp"
+ android:viewportWidth="24"
+ android:viewportHeight="24">
+ <path
+ android:fillColor="#FF000000"
+ android:pathData="M18,8h-1L17,6c0,-2.76 -2.24,-5 -5,-5S7,3.24 7,6v2L6,8c-1.1,0 -2,0.9 -2,2v10c0,1.1 0.9,2 2,2h12c1.1,0 2,-0.9 2,-2L20,10c0,-1.1 -0.9,-2 -2,-2zM9,6c0,-1.66 1.34,-3 3,-3s3,1.34 3,3v2L9,8L9,6zM18,20L6,20L6,10h12v10zM12,17c1.1,0 2,-0.9 2,-2s-0.9,-2 -2,-2 -2,0.9 -2,2 0.9,2 2,2z"/>
+</vector>
diff --git a/packages/SystemUI/res/drawable/ic_lock_open_gm2_24px.xml b/packages/SystemUI/res/drawable/ic_lock_open_gm2_24px.xml
new file mode 100644
index 000000000000..59fe0a914656
--- /dev/null
+++ b/packages/SystemUI/res/drawable/ic_lock_open_gm2_24px.xml
@@ -0,0 +1,12 @@
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="24dp"
+ android:height="24dp"
+ android:viewportWidth="24"
+ android:viewportHeight="24">
+ <path
+ android:fillColor="#FF000000"
+ android:pathData="M18,8h-1L17,6c0,-2.76 -2.24,-5 -5,-5S7,3.24 7,6h2c0,-1.66 1.34,-3 3,-3s3,1.34 3,3v2L6,8c-1.1,0 -2,0.9 -2,2v10c0,1.1 0.9,2 2,2h12c1.1,0 2,-0.9 2,-2L20,10c0,-1.1 -0.9,-2 -2,-2zM18,20L6,20L6,10h12v10z"/>
+ <path
+ android:fillColor="#FF000000"
+ android:pathData="M12,15m-2,0a2,2 0,1 1,4 0a2,2 0,1 1,-4 0"/>
+</vector>
diff --git a/packages/SystemUI/res/drawable/ic_more_vert.xml b/packages/SystemUI/res/drawable/ic_more_vert.xml
new file mode 100644
index 000000000000..1309fa875b55
--- /dev/null
+++ b/packages/SystemUI/res/drawable/ic_more_vert.xml
@@ -0,0 +1,24 @@
+<!--
+ Copyright (C) 2020 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="24dp"
+ android:height="24dp"
+ android:viewportWidth="24"
+ android:viewportHeight="24">
+ <path
+ android:fillColor="#FF000000"
+ android:pathData="M12,8c1.1,0 2,-0.9 2,-2s-0.9,-2 -2,-2 -2,0.9 -2,2 0.9,2 2,2zM12,10c-1.1,0 -2,0.9 -2,2s0.9,2 2,2 2,-0.9 2,-2 -0.9,-2 -2,-2zM12,16c-1.1,0 -2,0.9 -2,2s0.9,2 2,2 2,-0.9 2,-2 -0.9,-2 -2,-2z"/>
+</vector>
diff --git a/packages/SystemUI/res/drawable/ic_power_gm2_24px.xml b/packages/SystemUI/res/drawable/ic_power_gm2_24px.xml
new file mode 100644
index 000000000000..cd957196e4dc
--- /dev/null
+++ b/packages/SystemUI/res/drawable/ic_power_gm2_24px.xml
@@ -0,0 +1,9 @@
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="24dp"
+ android:height="24dp"
+ android:viewportWidth="24"
+ android:viewportHeight="24">
+ <path
+ android:fillColor="#FF000000"
+ android:pathData="M16,9v4.66l-3.5,3.51V19h-1v-1.83L8,13.65V9h8m0,-6h-2v4h-4V3H8v4h-0.01C6.9,6.99 6,7.89 6,8.98v5.52L9.5,18v3h5v-3l3.5,-3.51V9c0,-1.1 -0.9,-2 -2,-2V3z"/>
+</vector>
diff --git a/packages/SystemUI/res/drawable/ic_power_off_gm2_24px.xml b/packages/SystemUI/res/drawable/ic_power_off_gm2_24px.xml
new file mode 100644
index 000000000000..3eb7dd637abe
--- /dev/null
+++ b/packages/SystemUI/res/drawable/ic_power_off_gm2_24px.xml
@@ -0,0 +1,9 @@
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="24dp"
+ android:height="24dp"
+ android:viewportWidth="24"
+ android:viewportHeight="24">
+ <path
+ android:fillColor="#FF000000"
+ android:pathData="M21.19,21.19L2.81,2.81 1.39,4.22l4.63,4.63L6,14.5 9.5,18v3h5v-3l0.34,-0.34 4.94,4.94 1.41,-1.41zM12.5,17.17L12.5,19h-1v-1.83L8,13.65v-2.83l5.42,5.42 -0.92,0.93zM11.83,9L8,5.17L8,3h2v4h4L14,3h2v4c1.1,0 2,0.9 2,2v5.49l-0.34,0.34L16,13.17L16,9h-4.17z"/>
+</vector>
diff --git a/packages/SystemUI/res/drawable/ic_power_settings_new_gm2_24px.xml b/packages/SystemUI/res/drawable/ic_power_settings_new_gm2_24px.xml
new file mode 100644
index 000000000000..f4edd875ddff
--- /dev/null
+++ b/packages/SystemUI/res/drawable/ic_power_settings_new_gm2_24px.xml
@@ -0,0 +1,9 @@
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="24dp"
+ android:height="24dp"
+ android:viewportWidth="24"
+ android:viewportHeight="24">
+ <path
+ android:fillColor="#FF000000"
+ android:pathData="M11,2h2v10h-2zM18.37,5.64l-1.41,1.41c2.73,2.73 2.72,7.16 -0.01,9.89 -2.73,2.73 -7.17,2.73 -9.89,0.01 -2.73,-2.73 -2.74,-7.18 -0.01,-9.91l-1.41,-1.4c-3.51,3.51 -3.51,9.21 0.01,12.73 3.51,3.51 9.21,3.51 12.72,-0.01 3.51,-3.51 3.51,-9.2 0,-12.72z"/>
+</vector>
diff --git a/packages/SystemUI/res/drawable/ic_switches_gm2_24px.xml b/packages/SystemUI/res/drawable/ic_switches_gm2_24px.xml
new file mode 100644
index 000000000000..bb535ceaed8c
--- /dev/null
+++ b/packages/SystemUI/res/drawable/ic_switches_gm2_24px.xml
@@ -0,0 +1,9 @@
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="24dp"
+ android:height="24dp"
+ android:viewportWidth="24"
+ android:viewportHeight="24">
+ <path
+ android:fillColor="#FF000000"
+ android:pathData="M19,9h-8.02C10.06,7.79 8.63,7 7,7c-2.76,0 -5,2.24 -5,5s2.24,5 5,5c1.63,0 3.06,-0.79 3.98,-2H19c1.66,0 3,-1.34 3,-3S20.66,9 19,9zM19,13h-7.1c0.07,-0.32 0.1,-0.66 0.1,-1s-0.04,-0.68 -0.1,-1H19c0.55,0 1,0.45 1,1S19.55,13 19,13z"/>
+</vector>
diff --git a/packages/SystemUI/res/drawable/ic_vacuum_gm2_24px.xml b/packages/SystemUI/res/drawable/ic_vacuum_gm2_24px.xml
new file mode 100644
index 000000000000..86b9591238f1
--- /dev/null
+++ b/packages/SystemUI/res/drawable/ic_vacuum_gm2_24px.xml
@@ -0,0 +1,9 @@
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="24dp"
+ android:height="24dp"
+ android:viewportWidth="24"
+ android:viewportHeight="24">
+ <path
+ android:fillColor="#FF000000"
+ android:pathData="M4,16c-1.66,0 -3,1.34 -3,3s1.34,3 3,3 3,-1.34 3,-3 -1.34,-3 -3,-3zM4,20c-0.55,0 -1,-0.45 -1,-1s0.45,-1 1,-1 1,0.45 1,1 -0.45,1 -1,1zM23,20v2h-7v-2h2.49L12.01,4.59C11.6,3.63 10.66,3 9.61,3 8.17,3 7,4.17 7,5.61L7,9h2c2.21,0 4,1.79 4,4v9L7.99,22c0.44,-0.58 0.76,-1.26 0.91,-2L11,20v-7c0,-1.1 -0.9,-2 -2,-2L4,11v3c-0.71,0 -1.39,0.15 -2,0.42L2,9h3L5,5.61C5,3.07 7.07,1 9.61,1c1.86,0 3.53,1.11 4.25,2.82L20.66,20L23,20z"/>
+</vector>
diff --git a/packages/SystemUI/res/drawable/ic_videocam_gm2_24px.xml b/packages/SystemUI/res/drawable/ic_videocam_gm2_24px.xml
new file mode 100644
index 000000000000..687c9c417fa6
--- /dev/null
+++ b/packages/SystemUI/res/drawable/ic_videocam_gm2_24px.xml
@@ -0,0 +1,9 @@
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="24dp"
+ android:height="24dp"
+ android:viewportWidth="24"
+ android:viewportHeight="24">
+ <path
+ android:fillColor="#FF000000"
+ android:pathData="M18,10.48L18,6c0,-1.1 -0.9,-2 -2,-2L4,4c-1.1,0 -2,0.9 -2,2v12c0,1.1 0.9,2 2,2h12c1.1,0 2,-0.9 2,-2v-4.48l4,3.98v-11l-4,3.98zM16,9.69L16,18L4,18L4,6h12v3.69z"/>
+</vector>
diff --git a/packages/SystemUI/res/layout/controls_base_item.xml b/packages/SystemUI/res/layout/controls_base_item.xml
new file mode 100644
index 000000000000..3c4c61e30bc1
--- /dev/null
+++ b/packages/SystemUI/res/layout/controls_base_item.xml
@@ -0,0 +1,79 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ Copyright (C) 2020 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<androidx.constraintlayout.widget.ConstraintLayout
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:app="http://schemas.android.com/apk/res-auto"
+ android:layout_width="0dp"
+ android:layout_weight="1"
+ android:layout_height="@dimen/control_height"
+ android:padding="@dimen/control_padding"
+ android:clickable="true"
+ android:focusable="true"
+ android:layout_marginLeft="3dp"
+ android:layout_marginRight="3dp"
+ android:background="@drawable/control_background">
+
+ <ImageView
+ android:id="@+id/icon"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ app:layout_constraintStart_toStartOf="parent"
+ app:layout_constraintTop_toTopOf="parent" />
+
+ <TextView
+ android:id="@+id/status"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:textSize="@dimen/control_status_normal"
+ android:textColor="?android:attr/textColorPrimary"
+ android:fontFamily="@*android:string/config_bodyFontFamily"
+ android:paddingLeft="3dp"
+ app:layout_constraintBottom_toBottomOf="@+id/icon"
+ app:layout_constraintStart_toEndOf="@+id/icon" />
+
+ <TextView
+ android:id="@+id/status_extra"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:textSize="@dimen/control_status_normal"
+ android:textColor="?android:attr/textColorPrimary"
+ android:fontFamily="@*android:string/config_bodyFontFamily"
+ android:paddingLeft="3dp"
+ app:layout_constraintBottom_toBottomOf="@+id/icon"
+ app:layout_constraintStart_toEndOf="@+id/status" />
+
+ <TextView
+ android:id="@+id/title"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:textSize="18sp"
+ android:textColor="?android:attr/textColorPrimary"
+ android:fontFamily="@*android:string/config_headlineFontFamily"
+ app:layout_constraintBottom_toTopOf="@+id/subtitle"
+ app:layout_constraintStart_toStartOf="parent"
+ app:layout_constraintTop_toBottomOf="@+id/icon" />
+
+ <TextView
+ android:id="@+id/subtitle"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:textSize="16sp"
+ android:textColor="?android:attr/textColorSecondary"
+ android:fontFamily="@*android:string/config_headlineFontFamily"
+ app:layout_constraintBottom_toBottomOf="parent"
+ app:layout_constraintStart_toStartOf="parent" />
+</androidx.constraintlayout.widget.ConstraintLayout>
diff --git a/packages/SystemUI/res/layout/controls_no_favorites.xml b/packages/SystemUI/res/layout/controls_no_favorites.xml
new file mode 100644
index 000000000000..79672caed61b
--- /dev/null
+++ b/packages/SystemUI/res/layout/controls_no_favorites.xml
@@ -0,0 +1,18 @@
+<merge
+ xmlns:android="http://schemas.android.com/apk/res/android">
+ <TextView
+ android:id="@+id/controls_title"
+ android:text="@string/quick_controls_title"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:singleLine="true"
+ android:gravity="center"
+ android:textSize="25dp"
+ android:paddingTop="40dp"
+ android:paddingBottom="40dp"
+ android:layout_marginLeft="10dp"
+ android:layout_marginRight="10dp"
+ android:textColor="?android:attr/textColorPrimary"
+ android:fontFamily="@*android:string/config_headlineFontFamily"
+ android:background="@drawable/control_no_favorites_background"/>
+</merge>
diff --git a/packages/SystemUI/res/layout/controls_row.xml b/packages/SystemUI/res/layout/controls_row.xml
new file mode 100644
index 000000000000..13a6b36accd3
--- /dev/null
+++ b/packages/SystemUI/res/layout/controls_row.xml
@@ -0,0 +1,22 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ Copyright (C) 2020 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<LinearLayout
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ orientation="horizontal"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:layout_marginBottom="@dimen/control_spacing" />
diff --git a/packages/SystemUI/res/layout/controls_with_favorites.xml b/packages/SystemUI/res/layout/controls_with_favorites.xml
new file mode 100644
index 000000000000..7804fe6b6272
--- /dev/null
+++ b/packages/SystemUI/res/layout/controls_with_favorites.xml
@@ -0,0 +1,35 @@
+<merge
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:app="http://schemas.android.com/apk/res-auto">
+
+ <androidx.constraintlayout.widget.ConstraintLayout
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content">
+
+ <TextView
+ android:text="@string/quick_controls_title"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:singleLine="true"
+ android:gravity="center"
+ android:textSize="25dp"
+ android:textColor="?android:attr/textColorPrimary"
+ android:fontFamily="@*android:string/config_headlineFontFamily"
+ app:layout_constraintLeft_toLeftOf="parent"
+ app:layout_constraintRight_toRightOf="parent"/>
+ <ImageView
+ android:id="@+id/controls_more"
+ android:src="@drawable/ic_more_vert"
+ android:layout_width="34dp"
+ android:layout_height="24dp"
+ android:layout_marginEnd="10dp"
+ app:layout_constraintEnd_toEndOf="parent"/>
+
+ </androidx.constraintlayout.widget.ConstraintLayout>
+
+ <LinearLayout
+ android:id="@+id/global_actions_controls_list"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:orientation="vertical" />
+</merge>
diff --git a/packages/SystemUI/res/layout/global_actions_grid_v2.xml b/packages/SystemUI/res/layout/global_actions_grid_v2.xml
index 4cfb47e3c642..674148495478 100644
--- a/packages/SystemUI/res/layout/global_actions_grid_v2.xml
+++ b/packages/SystemUI/res/layout/global_actions_grid_v2.xml
@@ -111,20 +111,7 @@
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintRight_toRightOf="parent"
app:layout_constraintTop_toBottomOf="@id/global_actions_panel">
- <TextView
- android:text="Home"
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:singleLine="true"
- android:gravity="center"
- android:textSize="25dp"
- android:textColor="?android:attr/textColorPrimary"
- android:fontFamily="@*android:string/config_headlineFontFamily" />
- <LinearLayout
- android:id="@+id/global_actions_controls_list"
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:orientation="vertical" />
+
</LinearLayout>
</androidx.constraintlayout.widget.ConstraintLayout>
</ScrollView>
diff --git a/packages/SystemUI/res/values-af/strings.xml b/packages/SystemUI/res/values-af/strings.xml
index 4de854314b1a..823685e15807 100644
--- a/packages/SystemUI/res/values-af/strings.xml
+++ b/packages/SystemUI/res/values-af/strings.xml
@@ -71,6 +71,7 @@
<string name="compat_mode_on" msgid="4963711187149440884">"Zoem om skerm te vul"</string>
<string name="compat_mode_off" msgid="7682459748279487945">"Strek om skerm te vul"</string>
<string name="global_action_screenshot" msgid="2760267567509131654">"Skermkiekie"</string>
+ <string name="remote_input_image_insertion_text" msgid="4613177882724332877">"Prent is ingevoeg"</string>
<string name="screenshot_saving_ticker" msgid="6519186952674544916">"Stoor tans skermkiekie..."</string>
<string name="screenshot_saving_title" msgid="2298349784913287333">"Stoor tans skermkiekie..."</string>
<string name="screenshot_saved_title" msgid="8893267638659083153">"Skermkiekie is gestoor"</string>
@@ -84,6 +85,7 @@
<string name="screenrecord_start_label" msgid="1539048263178882562">"Begin opname"</string>
<string name="screenrecord_mic_label" msgid="6134198080740031632">"Neem stemopname op"</string>
<string name="screenrecord_taps_label" msgid="2518244240225925076">"Wys tikke"</string>
+ <string name="screenrecord_stop_text" msgid="6549288689506057686">"Tik om te stop"</string>
<string name="screenrecord_stop_label" msgid="72699670052087989">"Stop"</string>
<string name="screenrecord_pause_label" msgid="6004054907104549857">"Laat wag"</string>
<string name="screenrecord_resume_label" msgid="4972223043729555575">"Hervat"</string>
@@ -388,15 +390,14 @@
<string name="quick_settings_dark_mode_secondary_label_battery_saver" msgid="4990712734503013251">"Batterybespaarder"</string>
<string name="quick_settings_dark_mode_secondary_label_on_at_sunset" msgid="6017379738102015710">"Aan met sonsondergang"</string>
<string name="quick_settings_dark_mode_secondary_label_until_sunrise" msgid="4404885070316716472">"Tot sonsopkoms"</string>
+ <string name="quick_settings_dark_mode_secondary_label_on_at" msgid="5128758823486361279">"Aan om <xliff:g id="TIME">%s</xliff:g>"</string>
+ <string name="quick_settings_dark_mode_secondary_label_until" msgid="2289774641256492437">"Tot <xliff:g id="TIME">%s</xliff:g>"</string>
<string name="quick_settings_nfc_label" msgid="1054317416221168085">"NFC"</string>
<string name="quick_settings_nfc_off" msgid="3465000058515424663">"NFC is gedeaktiveer"</string>
<string name="quick_settings_nfc_on" msgid="1004976611203202230">"NFC is geaktiveer"</string>
- <!-- no translation found for quick_settings_screen_record_label (1594046461509776676) -->
- <skip />
- <!-- no translation found for quick_settings_screen_record_start (1574725369331638985) -->
- <skip />
- <!-- no translation found for quick_settings_screen_record_stop (8087348522976412119) -->
- <skip />
+ <string name="quick_settings_screen_record_label" msgid="1594046461509776676">"Skermopname"</string>
+ <string name="quick_settings_screen_record_start" msgid="1574725369331638985">"Begin"</string>
+ <string name="quick_settings_screen_record_stop" msgid="8087348522976412119">"Stop"</string>
<string name="recents_swipe_up_onboarding" msgid="2820265886420993995">"Swiep op om programme te wissel"</string>
<string name="recents_quick_scrub_onboarding" msgid="765934300283514912">"Sleep regs om programme vinnig te wissel"</string>
<string name="quick_step_accessibility_toggle_overview" msgid="7908949976727578403">"Wissel oorsig"</string>
@@ -696,22 +697,14 @@
<string name="notification_app_settings" msgid="8963648463858039377">"Pasmaak"</string>
<string name="notification_done" msgid="6215117625922713976">"Klaar"</string>
<string name="inline_undo" msgid="9026953267645116526">"Ontdoen"</string>
- <!-- no translation found for demote (6225813324237153980) -->
- <skip />
- <!-- no translation found for notification_conversation_favorite (8252976467488182853) -->
- <skip />
- <!-- no translation found for notification_conversation_unfavorite (633301300443356176) -->
- <skip />
- <!-- no translation found for notification_conversation_mute (477431709687199671) -->
- <skip />
- <!-- no translation found for notification_conversation_unmute (410885000669775294) -->
- <skip />
- <!-- no translation found for notification_conversation_bubble (4598142032706190028) -->
- <skip />
- <!-- no translation found for notification_conversation_unbubble (2303087159802926401) -->
- <skip />
- <!-- no translation found for notification_conversation_home_screen (8347136037958438935) -->
- <skip />
+ <string name="demote" msgid="6225813324237153980">"Merk hierdie kennisgewing as \"nie \'n gesprek nie\""</string>
+ <string name="notification_conversation_favorite" msgid="8252976467488182853">"Gunsteling"</string>
+ <string name="notification_conversation_unfavorite" msgid="633301300443356176">"Ontmerk as gunsteling"</string>
+ <string name="notification_conversation_mute" msgid="477431709687199671">"Demp"</string>
+ <string name="notification_conversation_unmute" msgid="410885000669775294">"Ontdemp"</string>
+ <string name="notification_conversation_bubble" msgid="4598142032706190028">"Wys as borrel"</string>
+ <string name="notification_conversation_unbubble" msgid="2303087159802926401">"Skakel borrels af"</string>
+ <string name="notification_conversation_home_screen" msgid="8347136037958438935">"Voeg by tuisskerm"</string>
<string name="notification_menu_accessibility" msgid="8984166825879886773">"<xliff:g id="APP_NAME">%1$s</xliff:g> <xliff:g id="MENU_DESCRIPTION">%2$s</xliff:g>"</string>
<string name="notification_menu_gear_description" msgid="6429668976593634862">"kennisgewingkontroles"</string>
<string name="notification_menu_snooze_description" msgid="4740133348901973244">"kennisgewing-sluimeropsies"</string>
diff --git a/packages/SystemUI/res/values-am/strings.xml b/packages/SystemUI/res/values-am/strings.xml
index 87603ac2051a..c3248249ebb6 100644
--- a/packages/SystemUI/res/values-am/strings.xml
+++ b/packages/SystemUI/res/values-am/strings.xml
@@ -71,6 +71,7 @@
<string name="compat_mode_on" msgid="4963711187149440884">"ማያ እንዲሞላ አጉላ"</string>
<string name="compat_mode_off" msgid="7682459748279487945">"ማያ ለመሙለት ሳብ"</string>
<string name="global_action_screenshot" msgid="2760267567509131654">"ቅጽበታዊ ገጽ እይታ"</string>
+ <string name="remote_input_image_insertion_text" msgid="4613177882724332877">"ምስል ገብቷል"</string>
<string name="screenshot_saving_ticker" msgid="6519186952674544916">"ቅጽበታዊ ገጽ እይታ በማስቀመጥ ላይ..."</string>
<string name="screenshot_saving_title" msgid="2298349784913287333">"ቅጽበታዊ ገጽ እይታ በማስቀመጥ ላይ..."</string>
<string name="screenshot_saved_title" msgid="8893267638659083153">"ቅጽበታዊ ገጽ እይታ ተቀምጧል"</string>
@@ -84,6 +85,7 @@
<string name="screenrecord_start_label" msgid="1539048263178882562">"መቅረጽ ጀምር"</string>
<string name="screenrecord_mic_label" msgid="6134198080740031632">"ድምጽን ቅረጽ"</string>
<string name="screenrecord_taps_label" msgid="2518244240225925076">"መታ ማድረጎችን አሳይ"</string>
+ <string name="screenrecord_stop_text" msgid="6549288689506057686">"ለማቆም መታ ያድርጉ"</string>
<string name="screenrecord_stop_label" msgid="72699670052087989">"አቁም"</string>
<string name="screenrecord_pause_label" msgid="6004054907104549857">"ባለበት አቁም"</string>
<string name="screenrecord_resume_label" msgid="4972223043729555575">"ከቆመበት ቀጥል"</string>
@@ -388,6 +390,8 @@
<string name="quick_settings_dark_mode_secondary_label_battery_saver" msgid="4990712734503013251">"ባትሪ ቆጣቢ"</string>
<string name="quick_settings_dark_mode_secondary_label_on_at_sunset" msgid="6017379738102015710">"ጸሐይ ስትጠልቅ ይበራል"</string>
<string name="quick_settings_dark_mode_secondary_label_until_sunrise" msgid="4404885070316716472">"ጸሐይ እስክትወጣ ድረስ"</string>
+ <string name="quick_settings_dark_mode_secondary_label_on_at" msgid="5128758823486361279">"<xliff:g id="TIME">%s</xliff:g> ላይ ይበራል"</string>
+ <string name="quick_settings_dark_mode_secondary_label_until" msgid="2289774641256492437">"እስከ <xliff:g id="TIME">%s</xliff:g> ድረስ"</string>
<string name="quick_settings_nfc_label" msgid="1054317416221168085">"ኤንኤፍሲ"</string>
<string name="quick_settings_nfc_off" msgid="3465000058515424663">"ኤንኤፍሲ ተሰናክሏል"</string>
<string name="quick_settings_nfc_on" msgid="1004976611203202230">"ኤንኤፍሲ ነቅቷል"</string>
diff --git a/packages/SystemUI/res/values-ar/strings.xml b/packages/SystemUI/res/values-ar/strings.xml
index c764004fddfe..113d705d7f31 100644
--- a/packages/SystemUI/res/values-ar/strings.xml
+++ b/packages/SystemUI/res/values-ar/strings.xml
@@ -71,6 +71,7 @@
<string name="compat_mode_on" msgid="4963711187149440884">"تكبير/تصغير لملء الشاشة"</string>
<string name="compat_mode_off" msgid="7682459748279487945">"توسيع بملء الشاشة"</string>
<string name="global_action_screenshot" msgid="2760267567509131654">"لقطة شاشة"</string>
+ <string name="remote_input_image_insertion_text" msgid="4613177882724332877">"تم إدراج الصورة"</string>
<string name="screenshot_saving_ticker" msgid="6519186952674544916">"جارٍ حفظ لقطة الشاشة..."</string>
<string name="screenshot_saving_title" msgid="2298349784913287333">"جارٍ حفظ لقطة الشاشة..."</string>
<string name="screenshot_saved_title" msgid="8893267638659083153">"تم حفظ لقطة الشاشة."</string>
@@ -84,6 +85,7 @@
<string name="screenrecord_start_label" msgid="1539048263178882562">"بدء التسجيل"</string>
<string name="screenrecord_mic_label" msgid="6134198080740031632">"تسجيل التعليق الصوتي"</string>
<string name="screenrecord_taps_label" msgid="2518244240225925076">"عرض النقرات"</string>
+ <string name="screenrecord_stop_text" msgid="6549288689506057686">"انقر لإيقاف التسجيل"</string>
<string name="screenrecord_stop_label" msgid="72699670052087989">"إيقاف"</string>
<string name="screenrecord_pause_label" msgid="6004054907104549857">"إيقاف مؤقت"</string>
<string name="screenrecord_resume_label" msgid="4972223043729555575">"استئناف"</string>
@@ -396,15 +398,14 @@
<string name="quick_settings_dark_mode_secondary_label_battery_saver" msgid="4990712734503013251">"توفير شحن البطارية"</string>
<string name="quick_settings_dark_mode_secondary_label_on_at_sunset" msgid="6017379738102015710">"تفعيل عند غروب الشمس"</string>
<string name="quick_settings_dark_mode_secondary_label_until_sunrise" msgid="4404885070316716472">"حتى شروق الشمس"</string>
+ <string name="quick_settings_dark_mode_secondary_label_on_at" msgid="5128758823486361279">"تفعيل الوضع في <xliff:g id="TIME">%s</xliff:g>"</string>
+ <string name="quick_settings_dark_mode_secondary_label_until" msgid="2289774641256492437">"حتى <xliff:g id="TIME">%s</xliff:g>"</string>
<string name="quick_settings_nfc_label" msgid="1054317416221168085">"‏الاتصالات قصيرة المدى (NFC)"</string>
<string name="quick_settings_nfc_off" msgid="3465000058515424663">"تم إيقاف الاتصال القريب المدى"</string>
<string name="quick_settings_nfc_on" msgid="1004976611203202230">"تم تفعيل الاتصال القريب المدى"</string>
- <!-- no translation found for quick_settings_screen_record_label (1594046461509776676) -->
- <skip />
- <!-- no translation found for quick_settings_screen_record_start (1574725369331638985) -->
- <skip />
- <!-- no translation found for quick_settings_screen_record_stop (8087348522976412119) -->
- <skip />
+ <string name="quick_settings_screen_record_label" msgid="1594046461509776676">"تسجيل الشاشة"</string>
+ <string name="quick_settings_screen_record_start" msgid="1574725369331638985">"بدء"</string>
+ <string name="quick_settings_screen_record_stop" msgid="8087348522976412119">"إيقاف"</string>
<string name="recents_swipe_up_onboarding" msgid="2820265886420993995">"مرّر سريعًا لأعلى لتبديل التطبيقات"</string>
<string name="recents_quick_scrub_onboarding" msgid="765934300283514912">"اسحب لليسار للتبديل السريع بين التطبيقات"</string>
<string name="quick_step_accessibility_toggle_overview" msgid="7908949976727578403">"تبديل \"النظرة العامة\""</string>
@@ -708,22 +709,14 @@
<string name="notification_app_settings" msgid="8963648463858039377">"تخصيص"</string>
<string name="notification_done" msgid="6215117625922713976">"تم"</string>
<string name="inline_undo" msgid="9026953267645116526">"تراجع"</string>
- <!-- no translation found for demote (6225813324237153980) -->
- <skip />
- <!-- no translation found for notification_conversation_favorite (8252976467488182853) -->
- <skip />
- <!-- no translation found for notification_conversation_unfavorite (633301300443356176) -->
- <skip />
- <!-- no translation found for notification_conversation_mute (477431709687199671) -->
- <skip />
- <!-- no translation found for notification_conversation_unmute (410885000669775294) -->
- <skip />
- <!-- no translation found for notification_conversation_bubble (4598142032706190028) -->
- <skip />
- <!-- no translation found for notification_conversation_unbubble (2303087159802926401) -->
- <skip />
- <!-- no translation found for notification_conversation_home_screen (8347136037958438935) -->
- <skip />
+ <string name="demote" msgid="6225813324237153980">"تحويل الإشعار من محادثة إلى إشعار عادي"</string>
+ <string name="notification_conversation_favorite" msgid="8252976467488182853">"المفضّلة"</string>
+ <string name="notification_conversation_unfavorite" msgid="633301300443356176">"إزالة المحادثة من المفضّلة"</string>
+ <string name="notification_conversation_mute" msgid="477431709687199671">"كتم الصوت"</string>
+ <string name="notification_conversation_unmute" msgid="410885000669775294">"إعادة الصوت"</string>
+ <string name="notification_conversation_bubble" msgid="4598142032706190028">"إظهار الإشعار كفقاعة تفسيرية"</string>
+ <string name="notification_conversation_unbubble" msgid="2303087159802926401">"إيقاف الفقاعات التفسيرية"</string>
+ <string name="notification_conversation_home_screen" msgid="8347136037958438935">"إضافة إلى الشاشة الرئيسية"</string>
<string name="notification_menu_accessibility" msgid="8984166825879886773">"<xliff:g id="APP_NAME">%1$s</xliff:g> <xliff:g id="MENU_DESCRIPTION">%2$s</xliff:g>"</string>
<string name="notification_menu_gear_description" msgid="6429668976593634862">"عناصر التحكم في الإشعارات"</string>
<string name="notification_menu_snooze_description" msgid="4740133348901973244">"خيارات تأجيل الإشعارات"</string>
diff --git a/packages/SystemUI/res/values-as/strings.xml b/packages/SystemUI/res/values-as/strings.xml
index 2fc393c6ec68..975d639c2ea4 100644
--- a/packages/SystemUI/res/values-as/strings.xml
+++ b/packages/SystemUI/res/values-as/strings.xml
@@ -71,6 +71,7 @@
<string name="compat_mode_on" msgid="4963711187149440884">"স্ক্ৰীণ পূর্ণ কৰিবলৈ জুম কৰক"</string>
<string name="compat_mode_off" msgid="7682459748279487945">"স্ক্ৰীণ পূর্ণ কৰিবলৈ প্ৰসাৰিত কৰক"</string>
<string name="global_action_screenshot" msgid="2760267567509131654">"স্ক্ৰীনশ্বট"</string>
+ <string name="remote_input_image_insertion_text" msgid="4613177882724332877">"প্ৰতিচ্ছবি ভৰোৱা হ’ল"</string>
<string name="screenshot_saving_ticker" msgid="6519186952674544916">"স্ক্ৰীণশ্বট ছেভ কৰি থকা হৈছে…"</string>
<string name="screenshot_saving_title" msgid="2298349784913287333">"স্ক্ৰীণশ্বট ছেভ কৰি থকা হৈছে…"</string>
<string name="screenshot_saved_title" msgid="8893267638659083153">"স্ক্ৰীণশ্বট ছেভ কৰা হ’ল"</string>
@@ -84,6 +85,7 @@
<string name="screenrecord_start_label" msgid="1539048263178882562">"ৰেকৰ্ডিং কৰা আৰম্ভ কৰক"</string>
<string name="screenrecord_mic_label" msgid="6134198080740031632">"পাৰ্শ্ব-ধ্বনি ৰেকৰ্ড কৰক"</string>
<string name="screenrecord_taps_label" msgid="2518244240225925076">"টিপা ঠাইসমূহ দেখুৱাওক"</string>
+ <string name="screenrecord_stop_text" msgid="6549288689506057686">"বন্ধ কৰিবলৈ টিপক"</string>
<string name="screenrecord_stop_label" msgid="72699670052087989">"বন্ধ কৰক"</string>
<string name="screenrecord_pause_label" msgid="6004054907104549857">"প\'জ কৰক"</string>
<string name="screenrecord_resume_label" msgid="4972223043729555575">"ৰখোৱাৰ পৰা পুনৰ আৰম্ভ কৰক"</string>
@@ -388,15 +390,14 @@
<string name="quick_settings_dark_mode_secondary_label_battery_saver" msgid="4990712734503013251">"বেটাৰী সঞ্চয়কাৰী"</string>
<string name="quick_settings_dark_mode_secondary_label_on_at_sunset" msgid="6017379738102015710">"সূৰ্যাস্তত অন হয়"</string>
<string name="quick_settings_dark_mode_secondary_label_until_sunrise" msgid="4404885070316716472">"সূৰ্যোদয়লৈকে"</string>
+ <string name="quick_settings_dark_mode_secondary_label_on_at" msgid="5128758823486361279">"<xliff:g id="TIME">%s</xliff:g>ত অন কৰক"</string>
+ <string name="quick_settings_dark_mode_secondary_label_until" msgid="2289774641256492437">"<xliff:g id="TIME">%s</xliff:g> পৰ্যন্ত"</string>
<string name="quick_settings_nfc_label" msgid="1054317416221168085">"NFC"</string>
<string name="quick_settings_nfc_off" msgid="3465000058515424663">"NFC নিষ্ক্ৰিয় হৈ আছে"</string>
<string name="quick_settings_nfc_on" msgid="1004976611203202230">"NFC সক্ষম হৈ আছে"</string>
- <!-- no translation found for quick_settings_screen_record_label (1594046461509776676) -->
- <skip />
- <!-- no translation found for quick_settings_screen_record_start (1574725369331638985) -->
- <skip />
- <!-- no translation found for quick_settings_screen_record_stop (8087348522976412119) -->
- <skip />
+ <string name="quick_settings_screen_record_label" msgid="1594046461509776676">"স্ক্ৰীন ৰেকর্ড"</string>
+ <string name="quick_settings_screen_record_start" msgid="1574725369331638985">"আৰম্ভ কৰক"</string>
+ <string name="quick_settings_screen_record_stop" msgid="8087348522976412119">"বন্ধ কৰক"</string>
<string name="recents_swipe_up_onboarding" msgid="2820265886420993995">"আনটো এপ্ ব্য়ৱহাৰ কৰিবলৈ ওপৰলৈ ছোৱাইপ কৰক"</string>
<string name="recents_quick_scrub_onboarding" msgid="765934300283514912">"খৰতকীয়াকৈ আনটো এপ্ ব্য়ৱহাৰ কৰিবলৈ সোঁফালে টানক"</string>
<string name="quick_step_accessibility_toggle_overview" msgid="7908949976727578403">"অৱলোকন ট’গল কৰক"</string>
@@ -696,22 +697,14 @@
<string name="notification_app_settings" msgid="8963648463858039377">"নিজৰ উপযোগিতা অনুসৰি"</string>
<string name="notification_done" msgid="6215117625922713976">"সম্পন্ন হ’ল"</string>
<string name="inline_undo" msgid="9026953267645116526">"আনডু কৰক"</string>
- <!-- no translation found for demote (6225813324237153980) -->
- <skip />
- <!-- no translation found for notification_conversation_favorite (8252976467488182853) -->
- <skip />
- <!-- no translation found for notification_conversation_unfavorite (633301300443356176) -->
- <skip />
- <!-- no translation found for notification_conversation_mute (477431709687199671) -->
- <skip />
- <!-- no translation found for notification_conversation_unmute (410885000669775294) -->
- <skip />
- <!-- no translation found for notification_conversation_bubble (4598142032706190028) -->
- <skip />
- <!-- no translation found for notification_conversation_unbubble (2303087159802926401) -->
- <skip />
- <!-- no translation found for notification_conversation_home_screen (8347136037958438935) -->
- <skip />
+ <string name="demote" msgid="6225813324237153980">"এই জাননীখন এটা বার্তালাপ নহয় বুলি চিহ্নিত কৰক"</string>
+ <string name="notification_conversation_favorite" msgid="8252976467488182853">"অপ্ৰিয় হিচাপে চিহ্নিত কৰক"</string>
+ <string name="notification_conversation_unfavorite" msgid="633301300443356176">"অপ্ৰিয় হিচাপে চিহ্নিত কৰক"</string>
+ <string name="notification_conversation_mute" msgid="477431709687199671">"মিউট কৰক"</string>
+ <string name="notification_conversation_unmute" msgid="410885000669775294">"আনমিউট কৰক"</string>
+ <string name="notification_conversation_bubble" msgid="4598142032706190028">"বাবল হিচাপে দেখুৱাওক"</string>
+ <string name="notification_conversation_unbubble" msgid="2303087159802926401">"বাবলসমূহ অফ কৰক"</string>
+ <string name="notification_conversation_home_screen" msgid="8347136037958438935">"গৃহ স্ক্ৰীনত যোগ কৰক"</string>
<string name="notification_menu_accessibility" msgid="8984166825879886773">"<xliff:g id="APP_NAME">%1$s</xliff:g> <xliff:g id="MENU_DESCRIPTION">%2$s</xliff:g>"</string>
<string name="notification_menu_gear_description" msgid="6429668976593634862">"জাননীৰ নিয়ন্ত্ৰণসমূহ"</string>
<string name="notification_menu_snooze_description" msgid="4740133348901973244">"জাননীক স্নুজ কৰাৰ বিকল্পসমূহ"</string>
diff --git a/packages/SystemUI/res/values-az/strings.xml b/packages/SystemUI/res/values-az/strings.xml
index de9c60520d84..d98d939da47a 100644
--- a/packages/SystemUI/res/values-az/strings.xml
+++ b/packages/SystemUI/res/values-az/strings.xml
@@ -71,6 +71,7 @@
<string name="compat_mode_on" msgid="4963711187149440884">"Ekranı doldurmaq üçün yaxınlaşdır"</string>
<string name="compat_mode_off" msgid="7682459748279487945">"Ekranı doldurmaq üçün uzat"</string>
<string name="global_action_screenshot" msgid="2760267567509131654">"Ekran şəkli"</string>
+ <string name="remote_input_image_insertion_text" msgid="4613177882724332877">"Şəkil daxil edildi"</string>
<string name="screenshot_saving_ticker" msgid="6519186952674544916">"Skrinşot yadda saxlanılır..."</string>
<string name="screenshot_saving_title" msgid="2298349784913287333">"Skrinşot yadda saxlanır..."</string>
<string name="screenshot_saved_title" msgid="8893267638659083153">"Skrinşot yadda saxlandı"</string>
@@ -84,6 +85,7 @@
<string name="screenrecord_start_label" msgid="1539048263178882562">"Ekranın Video Çəkimini Başladın"</string>
<string name="screenrecord_mic_label" msgid="6134198080740031632">"Ekranın səsli video çəkimi"</string>
<string name="screenrecord_taps_label" msgid="2518244240225925076">"Klikləmələri göstərin"</string>
+ <string name="screenrecord_stop_text" msgid="6549288689506057686">"Dayandırmaq üçün toxunun"</string>
<string name="screenrecord_stop_label" msgid="72699670052087989">"Dayandırın"</string>
<string name="screenrecord_pause_label" msgid="6004054907104549857">"Dayandırın"</string>
<string name="screenrecord_resume_label" msgid="4972223043729555575">"Davam edin"</string>
@@ -388,15 +390,14 @@
<string name="quick_settings_dark_mode_secondary_label_battery_saver" msgid="4990712734503013251">"Enerjiyə qənaət"</string>
<string name="quick_settings_dark_mode_secondary_label_on_at_sunset" msgid="6017379738102015710">"Qürubda aktiv olacaq"</string>
<string name="quick_settings_dark_mode_secondary_label_until_sunrise" msgid="4404885070316716472">"Şəfəq vaxtına qədər"</string>
+ <string name="quick_settings_dark_mode_secondary_label_on_at" msgid="5128758823486361279">"Bu vaxt aktiv olur: <xliff:g id="TIME">%s</xliff:g>"</string>
+ <string name="quick_settings_dark_mode_secondary_label_until" msgid="2289774641256492437">"Bu vaxtadək: <xliff:g id="TIME">%s</xliff:g>"</string>
<string name="quick_settings_nfc_label" msgid="1054317416221168085">"NFC"</string>
<string name="quick_settings_nfc_off" msgid="3465000058515424663">"NFC deaktiv edilib"</string>
<string name="quick_settings_nfc_on" msgid="1004976611203202230">"NFC aktiv edilib"</string>
- <!-- no translation found for quick_settings_screen_record_label (1594046461509776676) -->
- <skip />
- <!-- no translation found for quick_settings_screen_record_start (1574725369331638985) -->
- <skip />
- <!-- no translation found for quick_settings_screen_record_stop (8087348522976412119) -->
- <skip />
+ <string name="quick_settings_screen_record_label" msgid="1594046461509776676">"Ekran yazması"</string>
+ <string name="quick_settings_screen_record_start" msgid="1574725369331638985">"Başlayın"</string>
+ <string name="quick_settings_screen_record_stop" msgid="8087348522976412119">"Dayandırın"</string>
<string name="recents_swipe_up_onboarding" msgid="2820265886420993995">"Tətbiqi dəyişmək üçün yuxarı sürüşdürün"</string>
<string name="recents_quick_scrub_onboarding" msgid="765934300283514912">"Tətbiqləri cəld dəyişmək üçün sağa çəkin"</string>
<string name="quick_step_accessibility_toggle_overview" msgid="7908949976727578403">"İcmala Keçin"</string>
@@ -696,22 +697,14 @@
<string name="notification_app_settings" msgid="8963648463858039377">"Fərdiləşdirin"</string>
<string name="notification_done" msgid="6215117625922713976">"Hazırdır"</string>
<string name="inline_undo" msgid="9026953267645116526">"Ləğv edin"</string>
- <!-- no translation found for demote (6225813324237153980) -->
- <skip />
- <!-- no translation found for notification_conversation_favorite (8252976467488182853) -->
- <skip />
- <!-- no translation found for notification_conversation_unfavorite (633301300443356176) -->
- <skip />
- <!-- no translation found for notification_conversation_mute (477431709687199671) -->
- <skip />
- <!-- no translation found for notification_conversation_unmute (410885000669775294) -->
- <skip />
- <!-- no translation found for notification_conversation_bubble (4598142032706190028) -->
- <skip />
- <!-- no translation found for notification_conversation_unbubble (2303087159802926401) -->
- <skip />
- <!-- no translation found for notification_conversation_home_screen (8347136037958438935) -->
- <skip />
+ <string name="demote" msgid="6225813324237153980">"Bu bildirişi \"söhbət deyil\" kimi qeyd edin."</string>
+ <string name="notification_conversation_favorite" msgid="8252976467488182853">"Sevimli"</string>
+ <string name="notification_conversation_unfavorite" msgid="633301300443356176">"Sevimlilərdən silin"</string>
+ <string name="notification_conversation_mute" msgid="477431709687199671">"Səssiz"</string>
+ <string name="notification_conversation_unmute" msgid="410885000669775294">"Susdurmayın"</string>
+ <string name="notification_conversation_bubble" msgid="4598142032706190028">"Qabarcıq kimi göstərin"</string>
+ <string name="notification_conversation_unbubble" msgid="2303087159802926401">"Qabarcıqları deaktiv edin"</string>
+ <string name="notification_conversation_home_screen" msgid="8347136037958438935">"Əsas ekrana əlavə edin"</string>
<string name="notification_menu_accessibility" msgid="8984166825879886773">"<xliff:g id="APP_NAME">%1$s</xliff:g> <xliff:g id="MENU_DESCRIPTION">%2$s</xliff:g>"</string>
<string name="notification_menu_gear_description" msgid="6429668976593634862">"bildiriş nəzarəti"</string>
<string name="notification_menu_snooze_description" msgid="4740133348901973244">"bildiriş təxirə salma seçimləri"</string>
diff --git a/packages/SystemUI/res/values-b+sr+Latn/strings.xml b/packages/SystemUI/res/values-b+sr+Latn/strings.xml
index a895f35cca72..ff03bb9726ae 100644
--- a/packages/SystemUI/res/values-b+sr+Latn/strings.xml
+++ b/packages/SystemUI/res/values-b+sr+Latn/strings.xml
@@ -71,6 +71,7 @@
<string name="compat_mode_on" msgid="4963711187149440884">"Zumiraj na celom ekranu"</string>
<string name="compat_mode_off" msgid="7682459748279487945">"Razvuci na ceo ekran"</string>
<string name="global_action_screenshot" msgid="2760267567509131654">"Snimak ekrana"</string>
+ <string name="remote_input_image_insertion_text" msgid="4613177882724332877">"Slika je umetnuta"</string>
<string name="screenshot_saving_ticker" msgid="6519186952674544916">"Čuvanje snimka ekrana..."</string>
<string name="screenshot_saving_title" msgid="2298349784913287333">"Čuvanje snimka ekrana..."</string>
<string name="screenshot_saved_title" msgid="8893267638659083153">"Snimak ekrana je sačuvan"</string>
@@ -84,6 +85,7 @@
<string name="screenrecord_start_label" msgid="1539048263178882562">"Započni snimanje"</string>
<string name="screenrecord_mic_label" msgid="6134198080740031632">"Snimi prenos glasa"</string>
<string name="screenrecord_taps_label" msgid="2518244240225925076">"Prikazuj dodire"</string>
+ <string name="screenrecord_stop_text" msgid="6549288689506057686">"Dodirnite da biste zaustavili"</string>
<string name="screenrecord_stop_label" msgid="72699670052087989">"Zaustavi"</string>
<string name="screenrecord_pause_label" msgid="6004054907104549857">"Pauziraj"</string>
<string name="screenrecord_resume_label" msgid="4972223043729555575">"Nastavi"</string>
@@ -390,6 +392,8 @@
<string name="quick_settings_dark_mode_secondary_label_battery_saver" msgid="4990712734503013251">"Ušteda baterije"</string>
<string name="quick_settings_dark_mode_secondary_label_on_at_sunset" msgid="6017379738102015710">"Uključuje se po zalasku sunca"</string>
<string name="quick_settings_dark_mode_secondary_label_until_sunrise" msgid="4404885070316716472">"Do izlaska sunca"</string>
+ <string name="quick_settings_dark_mode_secondary_label_on_at" msgid="5128758823486361279">"Uključuje se u <xliff:g id="TIME">%s</xliff:g>"</string>
+ <string name="quick_settings_dark_mode_secondary_label_until" msgid="2289774641256492437">"Do <xliff:g id="TIME">%s</xliff:g>"</string>
<string name="quick_settings_nfc_label" msgid="1054317416221168085">"NFC"</string>
<string name="quick_settings_nfc_off" msgid="3465000058515424663">"NFC je onemogućen"</string>
<string name="quick_settings_nfc_on" msgid="1004976611203202230">"NFC je omogućen"</string>
diff --git a/packages/SystemUI/res/values-be/strings.xml b/packages/SystemUI/res/values-be/strings.xml
index 67aa8996c4ff..1ebbb5ea100c 100644
--- a/packages/SystemUI/res/values-be/strings.xml
+++ b/packages/SystemUI/res/values-be/strings.xml
@@ -71,6 +71,7 @@
<string name="compat_mode_on" msgid="4963711187149440884">"Павял. на ўвесь экран"</string>
<string name="compat_mode_off" msgid="7682459748279487945">"Расцягн. на ўвесь экран"</string>
<string name="global_action_screenshot" msgid="2760267567509131654">"Здымак экрана"</string>
+ <string name="remote_input_image_insertion_text" msgid="4613177882724332877">"Відарыс устаўлены"</string>
<string name="screenshot_saving_ticker" msgid="6519186952674544916">"Захаванне скрыншота..."</string>
<string name="screenshot_saving_title" msgid="2298349784913287333">"Захаванне скрыншота..."</string>
<string name="screenshot_saved_title" msgid="8893267638659083153">"Здымак экрана захаваны"</string>
@@ -84,6 +85,7 @@
<string name="screenrecord_start_label" msgid="1539048263178882562">"Пачаць запіс"</string>
<string name="screenrecord_mic_label" msgid="6134198080740031632">"Закадравае агучванне запісу"</string>
<string name="screenrecord_taps_label" msgid="2518244240225925076">"Паказваць дотыкі"</string>
+ <string name="screenrecord_stop_text" msgid="6549288689506057686">"Націсніце, каб спыніць"</string>
<string name="screenrecord_stop_label" msgid="72699670052087989">"Спыніць"</string>
<string name="screenrecord_pause_label" msgid="6004054907104549857">"Прыпыніць"</string>
<string name="screenrecord_resume_label" msgid="4972223043729555575">"Узнавіць"</string>
@@ -394,15 +396,14 @@
<string name="quick_settings_dark_mode_secondary_label_battery_saver" msgid="4990712734503013251">"Эканомія зараду"</string>
<string name="quick_settings_dark_mode_secondary_label_on_at_sunset" msgid="6017379738102015710">"Уключана ўвечары"</string>
<string name="quick_settings_dark_mode_secondary_label_until_sunrise" msgid="4404885070316716472">"Да ўсходу сонца"</string>
+ <string name="quick_settings_dark_mode_secondary_label_on_at" msgid="5128758823486361279">"Уключана ў <xliff:g id="TIME">%s</xliff:g>"</string>
+ <string name="quick_settings_dark_mode_secondary_label_until" msgid="2289774641256492437">"Да <xliff:g id="TIME">%s</xliff:g>"</string>
<string name="quick_settings_nfc_label" msgid="1054317416221168085">"NFC"</string>
<string name="quick_settings_nfc_off" msgid="3465000058515424663">"NFC адключаны"</string>
<string name="quick_settings_nfc_on" msgid="1004976611203202230">"NFC уключаны"</string>
- <!-- no translation found for quick_settings_screen_record_label (1594046461509776676) -->
- <skip />
- <!-- no translation found for quick_settings_screen_record_start (1574725369331638985) -->
- <skip />
- <!-- no translation found for quick_settings_screen_record_stop (8087348522976412119) -->
- <skip />
+ <string name="quick_settings_screen_record_label" msgid="1594046461509776676">"Запіс экрана"</string>
+ <string name="quick_settings_screen_record_start" msgid="1574725369331638985">"Пачаць"</string>
+ <string name="quick_settings_screen_record_stop" msgid="8087348522976412119">"Спыніць"</string>
<string name="recents_swipe_up_onboarding" msgid="2820265886420993995">"Правядзіце ўверх, каб пераключыць праграмы"</string>
<string name="recents_quick_scrub_onboarding" msgid="765934300283514912">"Каб хутка пераключыцца паміж праграмамі, перацягніце ўправа"</string>
<string name="quick_step_accessibility_toggle_overview" msgid="7908949976727578403">"Уключыць/выключыць агляд"</string>
@@ -704,22 +705,14 @@
<string name="notification_app_settings" msgid="8963648463858039377">"Наладзіць"</string>
<string name="notification_done" msgid="6215117625922713976">"Гатова"</string>
<string name="inline_undo" msgid="9026953267645116526">"Адрабіць"</string>
- <!-- no translation found for demote (6225813324237153980) -->
- <skip />
- <!-- no translation found for notification_conversation_favorite (8252976467488182853) -->
- <skip />
- <!-- no translation found for notification_conversation_unfavorite (633301300443356176) -->
- <skip />
- <!-- no translation found for notification_conversation_mute (477431709687199671) -->
- <skip />
- <!-- no translation found for notification_conversation_unmute (410885000669775294) -->
- <skip />
- <!-- no translation found for notification_conversation_bubble (4598142032706190028) -->
- <skip />
- <!-- no translation found for notification_conversation_unbubble (2303087159802926401) -->
- <skip />
- <!-- no translation found for notification_conversation_home_screen (8347136037958438935) -->
- <skip />
+ <string name="demote" msgid="6225813324237153980">"Не пазначаць гэта апавяшчэнне як размову"</string>
+ <string name="notification_conversation_favorite" msgid="8252976467488182853">"У абранае"</string>
+ <string name="notification_conversation_unfavorite" msgid="633301300443356176">"Выдаліць з абранага"</string>
+ <string name="notification_conversation_mute" msgid="477431709687199671">"Ігнараваць"</string>
+ <string name="notification_conversation_unmute" msgid="410885000669775294">"Уключыць паказ"</string>
+ <string name="notification_conversation_bubble" msgid="4598142032706190028">"Паказваць як усплывальнае апавяшчэнне"</string>
+ <string name="notification_conversation_unbubble" msgid="2303087159802926401">"Выключыць усплывальныя апавяшчэнні"</string>
+ <string name="notification_conversation_home_screen" msgid="8347136037958438935">"Дадаць на галоўны экран"</string>
<string name="notification_menu_accessibility" msgid="8984166825879886773">"<xliff:g id="APP_NAME">%1$s</xliff:g> <xliff:g id="MENU_DESCRIPTION">%2$s</xliff:g>"</string>
<string name="notification_menu_gear_description" msgid="6429668976593634862">"кіраванне апавяшчэннямі"</string>
<string name="notification_menu_snooze_description" msgid="4740133348901973244">"параметры адкладвання апавяшчэнняў"</string>
diff --git a/packages/SystemUI/res/values-bg/strings.xml b/packages/SystemUI/res/values-bg/strings.xml
index f83bf96e2b0d..5cfea60f901f 100644
--- a/packages/SystemUI/res/values-bg/strings.xml
+++ b/packages/SystemUI/res/values-bg/strings.xml
@@ -71,6 +71,7 @@
<string name="compat_mode_on" msgid="4963711187149440884">"Мащаб – запълва екрана"</string>
<string name="compat_mode_off" msgid="7682459748279487945">"Разпъване – запълва екрана"</string>
<string name="global_action_screenshot" msgid="2760267567509131654">"Екранна снимка"</string>
+ <string name="remote_input_image_insertion_text" msgid="4613177882724332877">"Изображението бе вмъкнато"</string>
<string name="screenshot_saving_ticker" msgid="6519186952674544916">"Екранната снимка се запазва..."</string>
<string name="screenshot_saving_title" msgid="2298349784913287333">"Екранната снимка се запазва..."</string>
<string name="screenshot_saved_title" msgid="8893267638659083153">"Екранната снимка е запазена"</string>
@@ -84,6 +85,7 @@
<string name="screenrecord_start_label" msgid="1539048263178882562">"Стартиране на записа"</string>
<string name="screenrecord_mic_label" msgid="6134198080740031632">"Записване на озвучаване"</string>
<string name="screenrecord_taps_label" msgid="2518244240225925076">"Показване на докосванията"</string>
+ <string name="screenrecord_stop_text" msgid="6549288689506057686">"Докоснете за спиране"</string>
<string name="screenrecord_stop_label" msgid="72699670052087989">"Спиране"</string>
<string name="screenrecord_pause_label" msgid="6004054907104549857">"Поставяне на пауза"</string>
<string name="screenrecord_resume_label" msgid="4972223043729555575">"Възобновяване"</string>
@@ -388,6 +390,8 @@
<string name="quick_settings_dark_mode_secondary_label_battery_saver" msgid="4990712734503013251">"Запазв. на батерията"</string>
<string name="quick_settings_dark_mode_secondary_label_on_at_sunset" msgid="6017379738102015710">"Ще се вкл. по залез"</string>
<string name="quick_settings_dark_mode_secondary_label_until_sunrise" msgid="4404885070316716472">"До изгрев"</string>
+ <string name="quick_settings_dark_mode_secondary_label_on_at" msgid="5128758823486361279">"Ще се включи в <xliff:g id="TIME">%s</xliff:g>"</string>
+ <string name="quick_settings_dark_mode_secondary_label_until" msgid="2289774641256492437">"До <xliff:g id="TIME">%s</xliff:g>"</string>
<string name="quick_settings_nfc_label" msgid="1054317416221168085">"КБП"</string>
<string name="quick_settings_nfc_off" msgid="3465000058515424663">"КБП е деактивирана"</string>
<string name="quick_settings_nfc_on" msgid="1004976611203202230">"КБП е активирана"</string>
diff --git a/packages/SystemUI/res/values-bn/strings.xml b/packages/SystemUI/res/values-bn/strings.xml
index 10479b134c89..f1e49ce57ce8 100644
--- a/packages/SystemUI/res/values-bn/strings.xml
+++ b/packages/SystemUI/res/values-bn/strings.xml
@@ -71,6 +71,7 @@
<string name="compat_mode_on" msgid="4963711187149440884">"স্ক্রীণ পূরণ করতে জুম করুন"</string>
<string name="compat_mode_off" msgid="7682459748279487945">"ফুল স্ক্রিন করুন"</string>
<string name="global_action_screenshot" msgid="2760267567509131654">"স্ক্রিনশট"</string>
+ <string name="remote_input_image_insertion_text" msgid="4613177882724332877">"ছবি যোগ করা হয়েছে"</string>
<string name="screenshot_saving_ticker" msgid="6519186952674544916">"স্ক্রিনশট সেভ করা হচ্ছে..."</string>
<string name="screenshot_saving_title" msgid="2298349784913287333">"স্ক্রিনশট সেভ করা হচ্ছে..."</string>
<string name="screenshot_saved_title" msgid="8893267638659083153">"স্ক্রিনশট সেভ করা হয়েছে"</string>
@@ -84,6 +85,7 @@
<string name="screenrecord_start_label" msgid="1539048263178882562">"রেকর্ডিং শুরু করুন"</string>
<string name="screenrecord_mic_label" msgid="6134198080740031632">"ভয়েসওভার রেকর্ড করুন"</string>
<string name="screenrecord_taps_label" msgid="2518244240225925076">"ট্যাপগুলি দেখুন"</string>
+ <string name="screenrecord_stop_text" msgid="6549288689506057686">"বন্ধ করতে ট্যাপ করুন"</string>
<string name="screenrecord_stop_label" msgid="72699670052087989">"বন্ধ করুন"</string>
<string name="screenrecord_pause_label" msgid="6004054907104549857">"পজ করুন"</string>
<string name="screenrecord_resume_label" msgid="4972223043729555575">"আবার চালু করুন"</string>
@@ -388,15 +390,14 @@
<string name="quick_settings_dark_mode_secondary_label_battery_saver" msgid="4990712734503013251">"ব্যাটারি সেভার"</string>
<string name="quick_settings_dark_mode_secondary_label_on_at_sunset" msgid="6017379738102015710">"সূর্যাস্তে চালু হবে"</string>
<string name="quick_settings_dark_mode_secondary_label_until_sunrise" msgid="4404885070316716472">"সূর্যোদয় পর্যন্ত"</string>
+ <string name="quick_settings_dark_mode_secondary_label_on_at" msgid="5128758823486361279">"<xliff:g id="TIME">%s</xliff:g>-এ চালু হবে"</string>
+ <string name="quick_settings_dark_mode_secondary_label_until" msgid="2289774641256492437">"<xliff:g id="TIME">%s</xliff:g> পর্যন্ত"</string>
<string name="quick_settings_nfc_label" msgid="1054317416221168085">"NFC"</string>
<string name="quick_settings_nfc_off" msgid="3465000058515424663">"NFC অক্ষম করা আছে"</string>
<string name="quick_settings_nfc_on" msgid="1004976611203202230">"NFC সক্ষম করা আছে"</string>
- <!-- no translation found for quick_settings_screen_record_label (1594046461509776676) -->
- <skip />
- <!-- no translation found for quick_settings_screen_record_start (1574725369331638985) -->
- <skip />
- <!-- no translation found for quick_settings_screen_record_stop (8087348522976412119) -->
- <skip />
+ <string name="quick_settings_screen_record_label" msgid="1594046461509776676">"স্ক্রিন রেকর্ড"</string>
+ <string name="quick_settings_screen_record_start" msgid="1574725369331638985">"শুরু করুন"</string>
+ <string name="quick_settings_screen_record_stop" msgid="8087348522976412119">"বন্ধ করুন"</string>
<string name="recents_swipe_up_onboarding" msgid="2820265886420993995">"অন্য অ্যাপে যেতে উপরের দিকে সোয়াইপ করুন"</string>
<string name="recents_quick_scrub_onboarding" msgid="765934300283514912">"একটি অ্যাপ ছেড়ে দ্রুত অন্য অ্যাপে যেতে ডান দিকে টেনে আনুন"</string>
<string name="quick_step_accessibility_toggle_overview" msgid="7908949976727578403">"\'এক নজরে\' বৈশিষ্ট্যটি চালু বা বন্ধ করুন"</string>
@@ -696,22 +697,14 @@
<string name="notification_app_settings" msgid="8963648463858039377">"কাস্টমাইজ করুন"</string>
<string name="notification_done" msgid="6215117625922713976">"সম্পন্ন"</string>
<string name="inline_undo" msgid="9026953267645116526">"আগের অবস্থায় ফিরে যান"</string>
- <!-- no translation found for demote (6225813324237153980) -->
- <skip />
- <!-- no translation found for notification_conversation_favorite (8252976467488182853) -->
- <skip />
- <!-- no translation found for notification_conversation_unfavorite (633301300443356176) -->
- <skip />
- <!-- no translation found for notification_conversation_mute (477431709687199671) -->
- <skip />
- <!-- no translation found for notification_conversation_unmute (410885000669775294) -->
- <skip />
- <!-- no translation found for notification_conversation_bubble (4598142032706190028) -->
- <skip />
- <!-- no translation found for notification_conversation_unbubble (2303087159802926401) -->
- <skip />
- <!-- no translation found for notification_conversation_home_screen (8347136037958438935) -->
- <skip />
+ <string name="demote" msgid="6225813324237153980">"কথোপকথন হিসেবে এই বিজ্ঞপ্তি চিহ্নিত করবেন না"</string>
+ <string name="notification_conversation_favorite" msgid="8252976467488182853">"পছন্দসই"</string>
+ <string name="notification_conversation_unfavorite" msgid="633301300443356176">"পছন্দসই থেকে সরান"</string>
+ <string name="notification_conversation_mute" msgid="477431709687199671">"মিউট করুন"</string>
+ <string name="notification_conversation_unmute" msgid="410885000669775294">"আনমিউট করুন"</string>
+ <string name="notification_conversation_bubble" msgid="4598142032706190028">"পপ-আপ হিসেবে দেখুন"</string>
+ <string name="notification_conversation_unbubble" msgid="2303087159802926401">"পপ-আপ বন্ধ করুন"</string>
+ <string name="notification_conversation_home_screen" msgid="8347136037958438935">"হোম স্ক্রিনে যোগ করুন"</string>
<string name="notification_menu_accessibility" msgid="8984166825879886773">"<xliff:g id="APP_NAME">%1$s</xliff:g> <xliff:g id="MENU_DESCRIPTION">%2$s</xliff:g>"</string>
<string name="notification_menu_gear_description" msgid="6429668976593634862">"বিজ্ঞপ্তির নিয়ন্ত্রণগুলি"</string>
<string name="notification_menu_snooze_description" msgid="4740133348901973244">"বিজ্ঞপ্তি মনে করিয়ে দেওয়ার বিকল্পগুলি"</string>
diff --git a/packages/SystemUI/res/values-bs/strings.xml b/packages/SystemUI/res/values-bs/strings.xml
index 6c89d976731a..cabe87f4a0b0 100644
--- a/packages/SystemUI/res/values-bs/strings.xml
+++ b/packages/SystemUI/res/values-bs/strings.xml
@@ -71,6 +71,7 @@
<string name="compat_mode_on" msgid="4963711187149440884">"Uvećaj prikaz na ekran"</string>
<string name="compat_mode_off" msgid="7682459748279487945">"Razvuci prikaz na ekran"</string>
<string name="global_action_screenshot" msgid="2760267567509131654">"Snimak ekrana"</string>
+ <string name="remote_input_image_insertion_text" msgid="4613177882724332877">"Slika je umetnuta"</string>
<string name="screenshot_saving_ticker" msgid="6519186952674544916">"Spašavanje snimka ekrana..."</string>
<string name="screenshot_saving_title" msgid="2298349784913287333">"Spašavanje snimka ekrana..."</string>
<string name="screenshot_saved_title" msgid="8893267638659083153">"Snimak ekrana je sačuvan"</string>
@@ -84,6 +85,7 @@
<string name="screenrecord_start_label" msgid="1539048263178882562">"Započni snimanje"</string>
<string name="screenrecord_mic_label" msgid="6134198080740031632">"Govor snimka"</string>
<string name="screenrecord_taps_label" msgid="2518244240225925076">"Prikaži dodire"</string>
+ <string name="screenrecord_stop_text" msgid="6549288689506057686">"Dodirnite za zaustavljanje"</string>
<string name="screenrecord_stop_label" msgid="72699670052087989">"Zaustavi"</string>
<string name="screenrecord_pause_label" msgid="6004054907104549857">"Pauza"</string>
<string name="screenrecord_resume_label" msgid="4972223043729555575">"Nastavi"</string>
@@ -390,6 +392,8 @@
<string name="quick_settings_dark_mode_secondary_label_battery_saver" msgid="4990712734503013251">"Ušteda baterije"</string>
<string name="quick_settings_dark_mode_secondary_label_on_at_sunset" msgid="6017379738102015710">"Uključuje se u sumrak"</string>
<string name="quick_settings_dark_mode_secondary_label_until_sunrise" msgid="4404885070316716472">"Do svitanja"</string>
+ <string name="quick_settings_dark_mode_secondary_label_on_at" msgid="5128758823486361279">"Uključuje se u <xliff:g id="TIME">%s</xliff:g>"</string>
+ <string name="quick_settings_dark_mode_secondary_label_until" msgid="2289774641256492437">"Do <xliff:g id="TIME">%s</xliff:g>"</string>
<string name="quick_settings_nfc_label" msgid="1054317416221168085">"NFC"</string>
<string name="quick_settings_nfc_off" msgid="3465000058515424663">"NFC je onemogućen"</string>
<string name="quick_settings_nfc_on" msgid="1004976611203202230">"NFC je omogućen"</string>
diff --git a/packages/SystemUI/res/values-ca/strings.xml b/packages/SystemUI/res/values-ca/strings.xml
index f675e20e7dfd..3e257952050a 100644
--- a/packages/SystemUI/res/values-ca/strings.xml
+++ b/packages/SystemUI/res/values-ca/strings.xml
@@ -71,6 +71,7 @@
<string name="compat_mode_on" msgid="4963711187149440884">"Zoom per omplir pantalla"</string>
<string name="compat_mode_off" msgid="7682459748279487945">"Estira per omplir pant."</string>
<string name="global_action_screenshot" msgid="2760267567509131654">"Captura de pantalla"</string>
+ <string name="remote_input_image_insertion_text" msgid="4613177882724332877">"Imatge inserida"</string>
<string name="screenshot_saving_ticker" msgid="6519186952674544916">"S\'està desant captura de pantalla..."</string>
<string name="screenshot_saving_title" msgid="2298349784913287333">"S\'està desant la captura de pantalla..."</string>
<string name="screenshot_saved_title" msgid="8893267638659083153">"S\'ha desat la captura de pantalla"</string>
@@ -84,6 +85,7 @@
<string name="screenrecord_start_label" msgid="1539048263178882562">"Inicia la gravació"</string>
<string name="screenrecord_mic_label" msgid="6134198080740031632">"Grava la veu en off"</string>
<string name="screenrecord_taps_label" msgid="2518244240225925076">"Mostra els tocs"</string>
+ <string name="screenrecord_stop_text" msgid="6549288689506057686">"Toca per aturar"</string>
<string name="screenrecord_stop_label" msgid="72699670052087989">"Atura"</string>
<string name="screenrecord_pause_label" msgid="6004054907104549857">"Posa en pausa"</string>
<string name="screenrecord_resume_label" msgid="4972223043729555575">"Reprèn"</string>
@@ -388,6 +390,8 @@
<string name="quick_settings_dark_mode_secondary_label_battery_saver" msgid="4990712734503013251">"Estalvi de bateria"</string>
<string name="quick_settings_dark_mode_secondary_label_on_at_sunset" msgid="6017379738102015710">"Al vespre"</string>
<string name="quick_settings_dark_mode_secondary_label_until_sunrise" msgid="4404885070316716472">"Fins a l\'alba"</string>
+ <string name="quick_settings_dark_mode_secondary_label_on_at" msgid="5128758823486361279">"S\'activarà a les <xliff:g id="TIME">%s</xliff:g>"</string>
+ <string name="quick_settings_dark_mode_secondary_label_until" msgid="2289774641256492437">"Fins a les <xliff:g id="TIME">%s</xliff:g>"</string>
<string name="quick_settings_nfc_label" msgid="1054317416221168085">"NFC"</string>
<string name="quick_settings_nfc_off" msgid="3465000058515424663">"L\'NFC està desactivada"</string>
<string name="quick_settings_nfc_on" msgid="1004976611203202230">"L\'NFC està activada"</string>
diff --git a/packages/SystemUI/res/values-cs/strings.xml b/packages/SystemUI/res/values-cs/strings.xml
index 354ccf44af39..3c85880eaaf8 100644
--- a/packages/SystemUI/res/values-cs/strings.xml
+++ b/packages/SystemUI/res/values-cs/strings.xml
@@ -71,6 +71,7 @@
<string name="compat_mode_on" msgid="4963711187149440884">"Přiblížit na celou obrazovku"</string>
<string name="compat_mode_off" msgid="7682459748279487945">"Na celou obrazovku"</string>
<string name="global_action_screenshot" msgid="2760267567509131654">"Snímek obrazovky"</string>
+ <string name="remote_input_image_insertion_text" msgid="4613177882724332877">"Vložen obrázek"</string>
<string name="screenshot_saving_ticker" msgid="6519186952674544916">"Ukládání snímku obrazovky..."</string>
<string name="screenshot_saving_title" msgid="2298349784913287333">"Ukládání snímku obrazovky..."</string>
<string name="screenshot_saved_title" msgid="8893267638659083153">"Snímek obrazovky byl uložen"</string>
@@ -84,6 +85,7 @@
<string name="screenrecord_start_label" msgid="1539048263178882562">"Spustit nahrávání"</string>
<string name="screenrecord_mic_label" msgid="6134198080740031632">"Nahrávat komentář"</string>
<string name="screenrecord_taps_label" msgid="2518244240225925076">"Zobrazovat klepnutí"</string>
+ <string name="screenrecord_stop_text" msgid="6549288689506057686">"Klepnutím zastavíte"</string>
<string name="screenrecord_stop_label" msgid="72699670052087989">"Zastavit"</string>
<string name="screenrecord_pause_label" msgid="6004054907104549857">"Pozastavit"</string>
<string name="screenrecord_resume_label" msgid="4972223043729555575">"Obnovit"</string>
@@ -392,6 +394,8 @@
<string name="quick_settings_dark_mode_secondary_label_battery_saver" msgid="4990712734503013251">"Spořič baterie"</string>
<string name="quick_settings_dark_mode_secondary_label_on_at_sunset" msgid="6017379738102015710">"Při soumraku"</string>
<string name="quick_settings_dark_mode_secondary_label_until_sunrise" msgid="4404885070316716472">"Do svítání"</string>
+ <string name="quick_settings_dark_mode_secondary_label_on_at" msgid="5128758823486361279">"Zapnout v <xliff:g id="TIME">%s</xliff:g>"</string>
+ <string name="quick_settings_dark_mode_secondary_label_until" msgid="2289774641256492437">"Do <xliff:g id="TIME">%s</xliff:g>"</string>
<string name="quick_settings_nfc_label" msgid="1054317416221168085">"NFC"</string>
<string name="quick_settings_nfc_off" msgid="3465000058515424663">"NFC je vypnuto"</string>
<string name="quick_settings_nfc_on" msgid="1004976611203202230">"NFC je zapnuto"</string>
diff --git a/packages/SystemUI/res/values-da/strings.xml b/packages/SystemUI/res/values-da/strings.xml
index 04e83d4b62ea..f2ffdaac0e37 100644
--- a/packages/SystemUI/res/values-da/strings.xml
+++ b/packages/SystemUI/res/values-da/strings.xml
@@ -71,6 +71,7 @@
<string name="compat_mode_on" msgid="4963711187149440884">"Zoom til fuld skærm"</string>
<string name="compat_mode_off" msgid="7682459748279487945">"Stræk til fuld skærm"</string>
<string name="global_action_screenshot" msgid="2760267567509131654">"Screenshot"</string>
+ <string name="remote_input_image_insertion_text" msgid="4613177882724332877">"Billedet blev indsat"</string>
<string name="screenshot_saving_ticker" msgid="6519186952674544916">"Gemmer screenshot..."</string>
<string name="screenshot_saving_title" msgid="2298349784913287333">"Gemmer screenshot..."</string>
<string name="screenshot_saved_title" msgid="8893267638659083153">"Screenshottet blev gemt"</string>
@@ -84,6 +85,7 @@
<string name="screenrecord_start_label" msgid="1539048263178882562">"Start optagelse"</string>
<string name="screenrecord_mic_label" msgid="6134198080740031632">"Optag voiceover"</string>
<string name="screenrecord_taps_label" msgid="2518244240225925076">"Vis tryk"</string>
+ <string name="screenrecord_stop_text" msgid="6549288689506057686">"Tryk for at stoppe"</string>
<string name="screenrecord_stop_label" msgid="72699670052087989">"Stop"</string>
<string name="screenrecord_pause_label" msgid="6004054907104549857">"Sæt på pause"</string>
<string name="screenrecord_resume_label" msgid="4972223043729555575">"Genoptag"</string>
@@ -388,6 +390,8 @@
<string name="quick_settings_dark_mode_secondary_label_battery_saver" msgid="4990712734503013251">"Batterisparefunktion"</string>
<string name="quick_settings_dark_mode_secondary_label_on_at_sunset" msgid="6017379738102015710">"Til ved solnedgang"</string>
<string name="quick_settings_dark_mode_secondary_label_until_sunrise" msgid="4404885070316716472">"Indtil solopgang"</string>
+ <string name="quick_settings_dark_mode_secondary_label_on_at" msgid="5128758823486361279">"Tænd kl. <xliff:g id="TIME">%s</xliff:g>"</string>
+ <string name="quick_settings_dark_mode_secondary_label_until" msgid="2289774641256492437">"Indtil kl. <xliff:g id="TIME">%s</xliff:g>"</string>
<string name="quick_settings_nfc_label" msgid="1054317416221168085">"NFC"</string>
<string name="quick_settings_nfc_off" msgid="3465000058515424663">"NFC er deaktiveret"</string>
<string name="quick_settings_nfc_on" msgid="1004976611203202230">"NFC er aktiveret"</string>
diff --git a/packages/SystemUI/res/values-de/strings.xml b/packages/SystemUI/res/values-de/strings.xml
index baf3e7c0006e..58caef2b46bb 100644
--- a/packages/SystemUI/res/values-de/strings.xml
+++ b/packages/SystemUI/res/values-de/strings.xml
@@ -71,6 +71,7 @@
<string name="compat_mode_on" msgid="4963711187149440884">"Zoom auf Bildschirmgröße"</string>
<string name="compat_mode_off" msgid="7682459748279487945">"Auf Bildschirmgröße anpassen"</string>
<string name="global_action_screenshot" msgid="2760267567509131654">"Screenshot"</string>
+ <string name="remote_input_image_insertion_text" msgid="4613177882724332877">"Bild eingefügt"</string>
<string name="screenshot_saving_ticker" msgid="6519186952674544916">"Screenshot wird gespeichert..."</string>
<string name="screenshot_saving_title" msgid="2298349784913287333">"Screenshot wird gespeichert..."</string>
<string name="screenshot_saved_title" msgid="8893267638659083153">"Screenshot gespeichert"</string>
@@ -84,6 +85,7 @@
<string name="screenrecord_start_label" msgid="1539048263178882562">"Aufzeichnung starten"</string>
<string name="screenrecord_mic_label" msgid="6134198080740031632">"Voice-over aufnehmen"</string>
<string name="screenrecord_taps_label" msgid="2518244240225925076">"Fingertipps anzeigen"</string>
+ <string name="screenrecord_stop_text" msgid="6549288689506057686">"Zum Stoppen tippen"</string>
<string name="screenrecord_stop_label" msgid="72699670052087989">"Anhalten"</string>
<string name="screenrecord_pause_label" msgid="6004054907104549857">"Pausieren"</string>
<string name="screenrecord_resume_label" msgid="4972223043729555575">"Fortsetzen"</string>
@@ -392,15 +394,14 @@
<string name="quick_settings_dark_mode_secondary_label_battery_saver" msgid="4990712734503013251">"Energiesparmodus"</string>
<string name="quick_settings_dark_mode_secondary_label_on_at_sunset" msgid="6017379738102015710">"An bei Sonnenuntergang"</string>
<string name="quick_settings_dark_mode_secondary_label_until_sunrise" msgid="4404885070316716472">"Bis Sonnenaufgang"</string>
+ <string name="quick_settings_dark_mode_secondary_label_on_at" msgid="5128758823486361279">"An um <xliff:g id="TIME">%s</xliff:g>"</string>
+ <string name="quick_settings_dark_mode_secondary_label_until" msgid="2289774641256492437">"Bis <xliff:g id="TIME">%s</xliff:g>"</string>
<string name="quick_settings_nfc_label" msgid="1054317416221168085">"NFC"</string>
<string name="quick_settings_nfc_off" msgid="3465000058515424663">"NFC ist deaktiviert"</string>
<string name="quick_settings_nfc_on" msgid="1004976611203202230">"NFC ist aktiviert"</string>
- <!-- no translation found for quick_settings_screen_record_label (1594046461509776676) -->
- <skip />
- <!-- no translation found for quick_settings_screen_record_start (1574725369331638985) -->
- <skip />
- <!-- no translation found for quick_settings_screen_record_stop (8087348522976412119) -->
- <skip />
+ <string name="quick_settings_screen_record_label" msgid="1594046461509776676">"Bildschirmaufnahme"</string>
+ <string name="quick_settings_screen_record_start" msgid="1574725369331638985">"Starten"</string>
+ <string name="quick_settings_screen_record_stop" msgid="8087348522976412119">"Beenden"</string>
<string name="recents_swipe_up_onboarding" msgid="2820265886420993995">"Nach oben wischen, um Apps zu wechseln"</string>
<string name="recents_quick_scrub_onboarding" msgid="765934300283514912">"Zum schnellen Wechseln der Apps nach rechts ziehen"</string>
<string name="quick_step_accessibility_toggle_overview" msgid="7908949976727578403">"Übersicht ein-/ausblenden"</string>
@@ -700,22 +701,14 @@
<string name="notification_app_settings" msgid="8963648463858039377">"Anpassen"</string>
<string name="notification_done" msgid="6215117625922713976">"Fertig"</string>
<string name="inline_undo" msgid="9026953267645116526">"Rückgängig machen"</string>
- <!-- no translation found for demote (6225813324237153980) -->
- <skip />
- <!-- no translation found for notification_conversation_favorite (8252976467488182853) -->
- <skip />
- <!-- no translation found for notification_conversation_unfavorite (633301300443356176) -->
- <skip />
- <!-- no translation found for notification_conversation_mute (477431709687199671) -->
- <skip />
- <!-- no translation found for notification_conversation_unmute (410885000669775294) -->
- <skip />
- <!-- no translation found for notification_conversation_bubble (4598142032706190028) -->
- <skip />
- <!-- no translation found for notification_conversation_unbubble (2303087159802926401) -->
- <skip />
- <!-- no translation found for notification_conversation_home_screen (8347136037958438935) -->
- <skip />
+ <string name="demote" msgid="6225813324237153980">"Diese Benachrichtigung als keine Unterhaltung markieren"</string>
+ <string name="notification_conversation_favorite" msgid="8252976467488182853">"Favorit"</string>
+ <string name="notification_conversation_unfavorite" msgid="633301300443356176">"Aus Favoriten entfernen"</string>
+ <string name="notification_conversation_mute" msgid="477431709687199671">"Stummschalten"</string>
+ <string name="notification_conversation_unmute" msgid="410885000669775294">"Stummschaltung aufheben"</string>
+ <string name="notification_conversation_bubble" msgid="4598142032706190028">"Als Infofeld anzeigen"</string>
+ <string name="notification_conversation_unbubble" msgid="2303087159802926401">"Infofelder deaktivieren"</string>
+ <string name="notification_conversation_home_screen" msgid="8347136037958438935">"Zum Startbildschirm hinzufügen"</string>
<string name="notification_menu_accessibility" msgid="8984166825879886773">"<xliff:g id="MENU_DESCRIPTION">%2$s</xliff:g> – <xliff:g id="APP_NAME">%1$s</xliff:g>"</string>
<string name="notification_menu_gear_description" msgid="6429668976593634862">"Benachrichtigungseinstellungen"</string>
<string name="notification_menu_snooze_description" msgid="4740133348901973244">"Optionen für spätere Erinnerung bei Benachrichtigungen"</string>
diff --git a/packages/SystemUI/res/values-el/strings.xml b/packages/SystemUI/res/values-el/strings.xml
index 45ad30244812..d3686c834882 100644
--- a/packages/SystemUI/res/values-el/strings.xml
+++ b/packages/SystemUI/res/values-el/strings.xml
@@ -71,6 +71,7 @@
<string name="compat_mode_on" msgid="4963711187149440884">"Ζουμ σε πλήρη οθόνη"</string>
<string name="compat_mode_off" msgid="7682459748279487945">"Προβoλή σε πλήρη οθ."</string>
<string name="global_action_screenshot" msgid="2760267567509131654">"Στιγμιότυπο οθόνης"</string>
+ <string name="remote_input_image_insertion_text" msgid="4613177882724332877">"Έγινε εισαγωγή εικόνας"</string>
<string name="screenshot_saving_ticker" msgid="6519186952674544916">"Αποθήκ. στιγμιότυπου οθόνης..."</string>
<string name="screenshot_saving_title" msgid="2298349784913287333">"Αποθήκευση στιγμιότυπου οθόνης..."</string>
<string name="screenshot_saved_title" msgid="8893267638659083153">"Το στιγμιότυπο οθόνης αποθηκεύτηκε"</string>
@@ -84,6 +85,7 @@
<string name="screenrecord_start_label" msgid="1539048263178882562">"Έναρξη εγγραφής"</string>
<string name="screenrecord_mic_label" msgid="6134198080740031632">"Εγγραφή σπικάζ"</string>
<string name="screenrecord_taps_label" msgid="2518244240225925076">"Εμφάνιση πατημάτων"</string>
+ <string name="screenrecord_stop_text" msgid="6549288689506057686">"Πατήστε για διακοπή"</string>
<string name="screenrecord_stop_label" msgid="72699670052087989">"Διακοπή"</string>
<string name="screenrecord_pause_label" msgid="6004054907104549857">"Παύση"</string>
<string name="screenrecord_resume_label" msgid="4972223043729555575">"Συνέχιση"</string>
@@ -388,6 +390,8 @@
<string name="quick_settings_dark_mode_secondary_label_battery_saver" msgid="4990712734503013251">"Εξοικονόμ. μπαταρίας"</string>
<string name="quick_settings_dark_mode_secondary_label_on_at_sunset" msgid="6017379738102015710">"Κατά τη δύση"</string>
<string name="quick_settings_dark_mode_secondary_label_until_sunrise" msgid="4404885070316716472">"Μέχρι την ανατολή"</string>
+ <string name="quick_settings_dark_mode_secondary_label_on_at" msgid="5128758823486361279">"Ενεργοποίηση στις <xliff:g id="TIME">%s</xliff:g>"</string>
+ <string name="quick_settings_dark_mode_secondary_label_until" msgid="2289774641256492437">"Έως <xliff:g id="TIME">%s</xliff:g>"</string>
<string name="quick_settings_nfc_label" msgid="1054317416221168085">"NFC"</string>
<string name="quick_settings_nfc_off" msgid="3465000058515424663">"Το NFC είναι απενεργοποιημένο"</string>
<string name="quick_settings_nfc_on" msgid="1004976611203202230">"Το NFC είναι ενεργοποιημένο"</string>
diff --git a/packages/SystemUI/res/values-en-rAU/strings.xml b/packages/SystemUI/res/values-en-rAU/strings.xml
index b6a5d1573acc..4f61daa2bf18 100644
--- a/packages/SystemUI/res/values-en-rAU/strings.xml
+++ b/packages/SystemUI/res/values-en-rAU/strings.xml
@@ -71,6 +71,7 @@
<string name="compat_mode_on" msgid="4963711187149440884">"Zoom to fill screen"</string>
<string name="compat_mode_off" msgid="7682459748279487945">"Stretch to fill screen"</string>
<string name="global_action_screenshot" msgid="2760267567509131654">"Screenshot"</string>
+ <string name="remote_input_image_insertion_text" msgid="4613177882724332877">"Image inserted"</string>
<string name="screenshot_saving_ticker" msgid="6519186952674544916">"Saving screenshot…"</string>
<string name="screenshot_saving_title" msgid="2298349784913287333">"Saving screenshot…"</string>
<string name="screenshot_saved_title" msgid="8893267638659083153">"Screenshot saved"</string>
@@ -84,6 +85,7 @@
<string name="screenrecord_start_label" msgid="1539048263178882562">"Start Recording"</string>
<string name="screenrecord_mic_label" msgid="6134198080740031632">"Record voiceover"</string>
<string name="screenrecord_taps_label" msgid="2518244240225925076">"Show taps"</string>
+ <string name="screenrecord_stop_text" msgid="6549288689506057686">"Tap to stop"</string>
<string name="screenrecord_stop_label" msgid="72699670052087989">"Stop"</string>
<string name="screenrecord_pause_label" msgid="6004054907104549857">"Pause"</string>
<string name="screenrecord_resume_label" msgid="4972223043729555575">"Resume"</string>
@@ -388,6 +390,8 @@
<string name="quick_settings_dark_mode_secondary_label_battery_saver" msgid="4990712734503013251">"Battery Saver"</string>
<string name="quick_settings_dark_mode_secondary_label_on_at_sunset" msgid="6017379738102015710">"On at sunset"</string>
<string name="quick_settings_dark_mode_secondary_label_until_sunrise" msgid="4404885070316716472">"Until sunrise"</string>
+ <string name="quick_settings_dark_mode_secondary_label_on_at" msgid="5128758823486361279">"On at <xliff:g id="TIME">%s</xliff:g>"</string>
+ <string name="quick_settings_dark_mode_secondary_label_until" msgid="2289774641256492437">"Until <xliff:g id="TIME">%s</xliff:g>"</string>
<string name="quick_settings_nfc_label" msgid="1054317416221168085">"NFC"</string>
<string name="quick_settings_nfc_off" msgid="3465000058515424663">"NFC is disabled"</string>
<string name="quick_settings_nfc_on" msgid="1004976611203202230">"NFC is enabled"</string>
diff --git a/packages/SystemUI/res/values-en-rCA/strings.xml b/packages/SystemUI/res/values-en-rCA/strings.xml
index e194e69c9299..34a2f1975d75 100644
--- a/packages/SystemUI/res/values-en-rCA/strings.xml
+++ b/packages/SystemUI/res/values-en-rCA/strings.xml
@@ -71,6 +71,7 @@
<string name="compat_mode_on" msgid="4963711187149440884">"Zoom to fill screen"</string>
<string name="compat_mode_off" msgid="7682459748279487945">"Stretch to fill screen"</string>
<string name="global_action_screenshot" msgid="2760267567509131654">"Screenshot"</string>
+ <string name="remote_input_image_insertion_text" msgid="4613177882724332877">"Image inserted"</string>
<string name="screenshot_saving_ticker" msgid="6519186952674544916">"Saving screenshot…"</string>
<string name="screenshot_saving_title" msgid="2298349784913287333">"Saving screenshot…"</string>
<string name="screenshot_saved_title" msgid="8893267638659083153">"Screenshot saved"</string>
@@ -84,6 +85,7 @@
<string name="screenrecord_start_label" msgid="1539048263178882562">"Start Recording"</string>
<string name="screenrecord_mic_label" msgid="6134198080740031632">"Record voiceover"</string>
<string name="screenrecord_taps_label" msgid="2518244240225925076">"Show taps"</string>
+ <string name="screenrecord_stop_text" msgid="6549288689506057686">"Tap to stop"</string>
<string name="screenrecord_stop_label" msgid="72699670052087989">"Stop"</string>
<string name="screenrecord_pause_label" msgid="6004054907104549857">"Pause"</string>
<string name="screenrecord_resume_label" msgid="4972223043729555575">"Resume"</string>
@@ -388,6 +390,8 @@
<string name="quick_settings_dark_mode_secondary_label_battery_saver" msgid="4990712734503013251">"Battery Saver"</string>
<string name="quick_settings_dark_mode_secondary_label_on_at_sunset" msgid="6017379738102015710">"On at sunset"</string>
<string name="quick_settings_dark_mode_secondary_label_until_sunrise" msgid="4404885070316716472">"Until sunrise"</string>
+ <string name="quick_settings_dark_mode_secondary_label_on_at" msgid="5128758823486361279">"On at <xliff:g id="TIME">%s</xliff:g>"</string>
+ <string name="quick_settings_dark_mode_secondary_label_until" msgid="2289774641256492437">"Until <xliff:g id="TIME">%s</xliff:g>"</string>
<string name="quick_settings_nfc_label" msgid="1054317416221168085">"NFC"</string>
<string name="quick_settings_nfc_off" msgid="3465000058515424663">"NFC is disabled"</string>
<string name="quick_settings_nfc_on" msgid="1004976611203202230">"NFC is enabled"</string>
diff --git a/packages/SystemUI/res/values-en-rGB/strings.xml b/packages/SystemUI/res/values-en-rGB/strings.xml
index b6a5d1573acc..4f61daa2bf18 100644
--- a/packages/SystemUI/res/values-en-rGB/strings.xml
+++ b/packages/SystemUI/res/values-en-rGB/strings.xml
@@ -71,6 +71,7 @@
<string name="compat_mode_on" msgid="4963711187149440884">"Zoom to fill screen"</string>
<string name="compat_mode_off" msgid="7682459748279487945">"Stretch to fill screen"</string>
<string name="global_action_screenshot" msgid="2760267567509131654">"Screenshot"</string>
+ <string name="remote_input_image_insertion_text" msgid="4613177882724332877">"Image inserted"</string>
<string name="screenshot_saving_ticker" msgid="6519186952674544916">"Saving screenshot…"</string>
<string name="screenshot_saving_title" msgid="2298349784913287333">"Saving screenshot…"</string>
<string name="screenshot_saved_title" msgid="8893267638659083153">"Screenshot saved"</string>
@@ -84,6 +85,7 @@
<string name="screenrecord_start_label" msgid="1539048263178882562">"Start Recording"</string>
<string name="screenrecord_mic_label" msgid="6134198080740031632">"Record voiceover"</string>
<string name="screenrecord_taps_label" msgid="2518244240225925076">"Show taps"</string>
+ <string name="screenrecord_stop_text" msgid="6549288689506057686">"Tap to stop"</string>
<string name="screenrecord_stop_label" msgid="72699670052087989">"Stop"</string>
<string name="screenrecord_pause_label" msgid="6004054907104549857">"Pause"</string>
<string name="screenrecord_resume_label" msgid="4972223043729555575">"Resume"</string>
@@ -388,6 +390,8 @@
<string name="quick_settings_dark_mode_secondary_label_battery_saver" msgid="4990712734503013251">"Battery Saver"</string>
<string name="quick_settings_dark_mode_secondary_label_on_at_sunset" msgid="6017379738102015710">"On at sunset"</string>
<string name="quick_settings_dark_mode_secondary_label_until_sunrise" msgid="4404885070316716472">"Until sunrise"</string>
+ <string name="quick_settings_dark_mode_secondary_label_on_at" msgid="5128758823486361279">"On at <xliff:g id="TIME">%s</xliff:g>"</string>
+ <string name="quick_settings_dark_mode_secondary_label_until" msgid="2289774641256492437">"Until <xliff:g id="TIME">%s</xliff:g>"</string>
<string name="quick_settings_nfc_label" msgid="1054317416221168085">"NFC"</string>
<string name="quick_settings_nfc_off" msgid="3465000058515424663">"NFC is disabled"</string>
<string name="quick_settings_nfc_on" msgid="1004976611203202230">"NFC is enabled"</string>
diff --git a/packages/SystemUI/res/values-en-rIN/strings.xml b/packages/SystemUI/res/values-en-rIN/strings.xml
index b6a5d1573acc..4f61daa2bf18 100644
--- a/packages/SystemUI/res/values-en-rIN/strings.xml
+++ b/packages/SystemUI/res/values-en-rIN/strings.xml
@@ -71,6 +71,7 @@
<string name="compat_mode_on" msgid="4963711187149440884">"Zoom to fill screen"</string>
<string name="compat_mode_off" msgid="7682459748279487945">"Stretch to fill screen"</string>
<string name="global_action_screenshot" msgid="2760267567509131654">"Screenshot"</string>
+ <string name="remote_input_image_insertion_text" msgid="4613177882724332877">"Image inserted"</string>
<string name="screenshot_saving_ticker" msgid="6519186952674544916">"Saving screenshot…"</string>
<string name="screenshot_saving_title" msgid="2298349784913287333">"Saving screenshot…"</string>
<string name="screenshot_saved_title" msgid="8893267638659083153">"Screenshot saved"</string>
@@ -84,6 +85,7 @@
<string name="screenrecord_start_label" msgid="1539048263178882562">"Start Recording"</string>
<string name="screenrecord_mic_label" msgid="6134198080740031632">"Record voiceover"</string>
<string name="screenrecord_taps_label" msgid="2518244240225925076">"Show taps"</string>
+ <string name="screenrecord_stop_text" msgid="6549288689506057686">"Tap to stop"</string>
<string name="screenrecord_stop_label" msgid="72699670052087989">"Stop"</string>
<string name="screenrecord_pause_label" msgid="6004054907104549857">"Pause"</string>
<string name="screenrecord_resume_label" msgid="4972223043729555575">"Resume"</string>
@@ -388,6 +390,8 @@
<string name="quick_settings_dark_mode_secondary_label_battery_saver" msgid="4990712734503013251">"Battery Saver"</string>
<string name="quick_settings_dark_mode_secondary_label_on_at_sunset" msgid="6017379738102015710">"On at sunset"</string>
<string name="quick_settings_dark_mode_secondary_label_until_sunrise" msgid="4404885070316716472">"Until sunrise"</string>
+ <string name="quick_settings_dark_mode_secondary_label_on_at" msgid="5128758823486361279">"On at <xliff:g id="TIME">%s</xliff:g>"</string>
+ <string name="quick_settings_dark_mode_secondary_label_until" msgid="2289774641256492437">"Until <xliff:g id="TIME">%s</xliff:g>"</string>
<string name="quick_settings_nfc_label" msgid="1054317416221168085">"NFC"</string>
<string name="quick_settings_nfc_off" msgid="3465000058515424663">"NFC is disabled"</string>
<string name="quick_settings_nfc_on" msgid="1004976611203202230">"NFC is enabled"</string>
diff --git a/packages/SystemUI/res/values-en-rXC/strings.xml b/packages/SystemUI/res/values-en-rXC/strings.xml
index e3a569a29cae..2bb51311aa0a 100644
--- a/packages/SystemUI/res/values-en-rXC/strings.xml
+++ b/packages/SystemUI/res/values-en-rXC/strings.xml
@@ -71,6 +71,7 @@
<string name="compat_mode_on" msgid="4963711187149440884">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‎‎‎‏‎‎‏‏‏‎‎‎‏‎‏‎‏‎‎‏‎‏‎‎‎‎‎‏‏‎‏‎‎‏‏‏‎‏‏‎‏‎‏‏‎‎‏‏‎‎‎‏‏‏‎‏‏‏‎‏‎‎‎Zoom to fill screen‎‏‎‎‏‎"</string>
<string name="compat_mode_off" msgid="7682459748279487945">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‏‎‏‎‏‎‏‎‎‏‏‏‎‏‏‎‎‏‎‏‎‎‎‏‏‏‎‎‎‎‎‎‎‎‏‎‎‏‎‏‏‎‏‏‏‏‏‎‎‎‏‏‎‏‏‏‎‎‏‎‎‏‎Stretch to fill screen‎‏‎‎‏‎"</string>
<string name="global_action_screenshot" msgid="2760267567509131654">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‎‏‎‎‏‏‎‎‏‎‎‏‏‏‎‎‏‏‏‎‎‎‎‏‎‏‏‏‎‏‎‎‏‏‎‎‏‏‎‎‏‏‎‏‎‎‏‏‏‎‏‎‎‎‏‏‎‎‎‎‏‏‎‎Screenshot‎‏‎‎‏‎"</string>
+ <string name="remote_input_image_insertion_text" msgid="4613177882724332877">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‎‎‎‎‎‎‎‎‎‎‎‏‎‏‎‏‎‎‏‏‎‎‏‏‎‏‎‏‏‏‏‎‏‏‏‎‎‏‎‏‏‏‏‎‎‎‏‏‏‏‎‏‎‏‎‏‎‎‏‏‎‏‎Image inserted‎‏‎‎‏‎"</string>
<string name="screenshot_saving_ticker" msgid="6519186952674544916">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‎‏‏‎‏‎‎‏‏‏‏‎‎‎‏‏‎‎‏‏‎‏‏‏‏‏‏‏‏‎‎‏‎‎‏‎‏‎‏‎‎‎‎‏‏‏‎‎‎‎‏‏‎‏‎‎‎‏‎‏‎‎‎Saving screenshot…‎‏‎‎‏‎"</string>
<string name="screenshot_saving_title" msgid="2298349784913287333">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‎‏‏‏‏‏‏‏‏‏‎‎‏‎‏‎‏‏‎‎‎‎‎‏‏‏‏‎‎‏‏‏‏‎‎‎‎‏‏‎‏‎‏‏‎‏‎‎‎‎‏‏‎‎‎‏‎‏‎‎‏‎‏‎Saving screenshot…‎‏‎‎‏‎"</string>
<string name="screenshot_saved_title" msgid="8893267638659083153">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‏‏‏‎‏‏‎‏‏‎‏‎‏‏‎‎‏‏‏‎‏‏‏‏‎‎‏‏‎‎‎‏‏‏‏‏‏‎‏‏‎‎‎‎‎‎‎‏‏‎‏‎‏‏‏‎‎‏‎‎‎‏‎Screenshot saved‎‏‎‎‏‎"</string>
@@ -84,6 +85,7 @@
<string name="screenrecord_start_label" msgid="1539048263178882562">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‎‏‏‎‏‎‏‎‏‎‏‏‎‏‏‏‏‎‎‏‏‎‎‎‎‏‏‏‏‎‏‏‎‏‏‏‎‏‎‏‏‏‏‎‏‎‏‏‎‎‎‏‏‏‎‎‎‎‎‎‎‏‎‎Start Recording‎‏‎‎‏‎"</string>
<string name="screenrecord_mic_label" msgid="6134198080740031632">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‎‏‎‏‎‏‎‎‏‎‎‎‎‏‎‎‎‎‏‏‎‎‏‎‏‎‎‏‏‏‎‏‏‏‎‎‎‎‎‏‏‎‎‏‎‎‎‏‏‏‏‏‎‎‏‎‎‏‎‎‎‎‎Record voiceover‎‏‎‎‏‎"</string>
<string name="screenrecord_taps_label" msgid="2518244240225925076">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‎‏‎‎‎‏‎‏‏‏‏‎‎‏‎‏‎‎‏‏‎‎‏‏‏‎‎‏‎‏‏‎‏‎‏‎‏‎‎‎‏‎‏‎‏‏‎‏‏‏‏‎‏‏‏‏‏‎‏‎‏‎‎‎Show taps‎‏‎‎‏‎"</string>
+ <string name="screenrecord_stop_text" msgid="6549288689506057686">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‎‏‏‎‏‎‏‏‏‎‎‎‏‏‏‎‏‏‏‏‏‏‎‏‎‏‏‏‎‏‎‎‎‎‏‏‎‏‎‏‏‏‏‏‎‏‏‏‏‏‏‏‎‏‏‏‎‏‎‏‏‎‎Tap to stop‎‏‎‎‏‎"</string>
<string name="screenrecord_stop_label" msgid="72699670052087989">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‎‎‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‏‏‏‏‏‏‏‎‏‏‎‏‏‏‏‏‎‎‎‏‎‏‏‏‏‏‎‎‎‏‎‏‎‎‎‏‎‏‏‎‏‎‏‎Stop‎‏‎‎‏‎"</string>
<string name="screenrecord_pause_label" msgid="6004054907104549857">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‎‏‎‎‏‏‎‏‎‏‎‎‏‎‏‎‏‏‎‎‎‎‎‎‏‎‎‎‎‎‏‎‎‎‏‏‎‏‏‏‏‎‏‎‎‎‎‏‎‏‏‎‏‏‏‏‏‎‎‎‎‏‎Pause‎‏‎‎‏‎"</string>
<string name="screenrecord_resume_label" msgid="4972223043729555575">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‎‎‎‏‎‏‎‎‎‎‎‎‎‎‏‏‏‎‎‎‏‎‏‎‎‎‎‎‏‏‏‎‏‎‏‎‎‏‏‎‎‎‎‎‏‏‎‎‎‎‎‎‎‎‎‏‏‏‎‏‏‏‎Resume‎‏‎‎‏‎"</string>
@@ -388,6 +390,8 @@
<string name="quick_settings_dark_mode_secondary_label_battery_saver" msgid="4990712734503013251">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‎‎‎‏‎‏‎‏‎‎‎‎‏‎‏‎‎‏‎‎‏‎‏‏‎‎‏‎‏‎‎‏‎‎‎‏‎‎‎‎‎‏‎‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‏‏‎Battery Saver‎‏‎‎‏‎"</string>
<string name="quick_settings_dark_mode_secondary_label_on_at_sunset" msgid="6017379738102015710">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‎‏‎‎‏‏‏‎‎‎‎‎‏‎‎‎‎‎‎‏‏‎‏‏‏‏‏‏‎‏‏‎‎‎‏‎‎‎‎‎‏‏‎‏‎‎‎‎‏‎‎‏‏‎‏‏‎‏‏‏‏‎‎On at sunset‎‏‎‎‏‎"</string>
<string name="quick_settings_dark_mode_secondary_label_until_sunrise" msgid="4404885070316716472">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‎‏‏‏‏‎‏‎‎‏‎‎‎‎‏‎‏‎‎‏‎‏‏‏‎‏‎‎‎‏‎‎‎‏‎‎‏‎‎‏‎‏‎‎‏‏‎‎‏‎‎‎‎‎‏‏‎‏‏‏‎‎‎‎Until sunrise‎‏‎‎‏‎"</string>
+ <string name="quick_settings_dark_mode_secondary_label_on_at" msgid="5128758823486361279">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‎‎‎‏‏‏‎‎‏‎‏‏‎‏‎‎‎‎‎‎‏‎‏‏‏‏‏‎‏‎‎‎‎‎‎‏‎‎‏‎‎‎‏‏‏‎‎‎‎‎‏‏‏‎‏‎‏‏‏‏‏‏‎On at ‎‏‎‎‏‏‎<xliff:g id="TIME">%s</xliff:g>‎‏‎‎‏‏‏‎‎‏‎‎‏‎"</string>
+ <string name="quick_settings_dark_mode_secondary_label_until" msgid="2289774641256492437">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‎‏‏‏‏‏‏‏‏‎‎‎‏‏‎‏‏‏‎‏‎‎‏‏‏‏‎‎‏‏‏‏‎‎‎‏‎‏‏‏‏‎‎‏‎‏‎‎‏‏‎‏‏‎‏‏‎‎‏‎‏‎‏‎Until ‎‏‎‎‏‏‎<xliff:g id="TIME">%s</xliff:g>‎‏‎‎‏‏‏‎‎‏‎‎‏‎"</string>
<string name="quick_settings_nfc_label" msgid="1054317416221168085">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‎‎‏‏‏‎‏‎‏‎‎‎‎‏‏‎‏‏‎‎‎‎‎‎‎‏‏‎‏‎‏‎‏‎‎‎‏‎‎‏‎‏‏‏‏‎‏‏‎‏‎‎‎‏‏‏‎‏‎‏‎‏‎NFC‎‏‎‎‏‎"</string>
<string name="quick_settings_nfc_off" msgid="3465000058515424663">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‎‏‏‎‎‎‎‎‎‎‏‎‏‏‎‎‎‏‎‎‏‏‏‎‎‏‏‎‎‎‏‏‏‏‎‎‎‏‎‏‎‎‎‎‏‎‏‎‎‎‎‎‏‎‏‏‎‎‏‎‏‏‏‎NFC is disabled‎‏‎‎‏‎"</string>
<string name="quick_settings_nfc_on" msgid="1004976611203202230">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‎‎‏‏‎‏‏‏‏‏‎‎‏‎‎‏‏‎‎‏‎‎‏‏‏‎‎‏‏‏‎‎‏‏‏‏‏‎‎‏‎‏‎‏‏‏‏‎‏‏‏‏‎‎‏‎‏‏‎‏‏‎‎NFC is enabled‎‏‎‎‏‎"</string>
diff --git a/packages/SystemUI/res/values-es-rUS/strings.xml b/packages/SystemUI/res/values-es-rUS/strings.xml
index 255db9229657..e2bf6efa538e 100644
--- a/packages/SystemUI/res/values-es-rUS/strings.xml
+++ b/packages/SystemUI/res/values-es-rUS/strings.xml
@@ -71,6 +71,7 @@
<string name="compat_mode_on" msgid="4963711187149440884">"Zoom para ocupar la pantalla"</string>
<string name="compat_mode_off" msgid="7682459748279487945">"Estirar p/ ocupar la pantalla"</string>
<string name="global_action_screenshot" msgid="2760267567509131654">"Captura de pantalla"</string>
+ <string name="remote_input_image_insertion_text" msgid="4613177882724332877">"Se insertó la imagen"</string>
<string name="screenshot_saving_ticker" msgid="6519186952674544916">"Guardando captura de pantalla"</string>
<string name="screenshot_saving_title" msgid="2298349784913287333">"Guardando la captura de pantalla..."</string>
<string name="screenshot_saved_title" msgid="8893267638659083153">"Se guardó la captura de pantalla"</string>
@@ -84,6 +85,7 @@
<string name="screenrecord_start_label" msgid="1539048263178882562">"Iniciar grabación"</string>
<string name="screenrecord_mic_label" msgid="6134198080740031632">"Grabar voz superpuesta"</string>
<string name="screenrecord_taps_label" msgid="2518244240225925076">"Mostrar toques"</string>
+ <string name="screenrecord_stop_text" msgid="6549288689506057686">"Presiona para detener"</string>
<string name="screenrecord_stop_label" msgid="72699670052087989">"Detener"</string>
<string name="screenrecord_pause_label" msgid="6004054907104549857">"Pausar"</string>
<string name="screenrecord_resume_label" msgid="4972223043729555575">"Reanudar"</string>
@@ -388,6 +390,8 @@
<string name="quick_settings_dark_mode_secondary_label_battery_saver" msgid="4990712734503013251">"Ahorro de batería"</string>
<string name="quick_settings_dark_mode_secondary_label_on_at_sunset" msgid="6017379738102015710">"Al atardecer"</string>
<string name="quick_settings_dark_mode_secondary_label_until_sunrise" msgid="4404885070316716472">"Hasta el amanecer"</string>
+ <string name="quick_settings_dark_mode_secondary_label_on_at" msgid="5128758823486361279">"A la(s) <xliff:g id="TIME">%s</xliff:g>"</string>
+ <string name="quick_settings_dark_mode_secondary_label_until" msgid="2289774641256492437">"Hasta la(s) <xliff:g id="TIME">%s</xliff:g>"</string>
<string name="quick_settings_nfc_label" msgid="1054317416221168085">"NFC"</string>
<string name="quick_settings_nfc_off" msgid="3465000058515424663">"La tecnología NFC está inhabilitada"</string>
<string name="quick_settings_nfc_on" msgid="1004976611203202230">"La tecnología NFC está habilitada"</string>
diff --git a/packages/SystemUI/res/values-es/strings.xml b/packages/SystemUI/res/values-es/strings.xml
index d1d959c84099..40d7fe3bdf25 100644
--- a/packages/SystemUI/res/values-es/strings.xml
+++ b/packages/SystemUI/res/values-es/strings.xml
@@ -71,6 +71,7 @@
<string name="compat_mode_on" msgid="4963711187149440884">"Zoom para ajustar"</string>
<string name="compat_mode_off" msgid="7682459748279487945">"Expandir para ajustar"</string>
<string name="global_action_screenshot" msgid="2760267567509131654">"Captura de pantalla"</string>
+ <string name="remote_input_image_insertion_text" msgid="4613177882724332877">"Se ha insertado la imagen"</string>
<string name="screenshot_saving_ticker" msgid="6519186952674544916">"Guardando captura..."</string>
<string name="screenshot_saving_title" msgid="2298349784913287333">"Guardando captura..."</string>
<string name="screenshot_saved_title" msgid="8893267638659083153">"Se ha guardado la captura de pantalla"</string>
@@ -84,6 +85,7 @@
<string name="screenrecord_start_label" msgid="1539048263178882562">"Iniciar grabación"</string>
<string name="screenrecord_mic_label" msgid="6134198080740031632">"Grabar voz en off"</string>
<string name="screenrecord_taps_label" msgid="2518244240225925076">"Mostrar toques"</string>
+ <string name="screenrecord_stop_text" msgid="6549288689506057686">"Toca para detener"</string>
<string name="screenrecord_stop_label" msgid="72699670052087989">"Detener"</string>
<string name="screenrecord_pause_label" msgid="6004054907104549857">"Pausar"</string>
<string name="screenrecord_resume_label" msgid="4972223043729555575">"Seguir"</string>
@@ -388,6 +390,8 @@
<string name="quick_settings_dark_mode_secondary_label_battery_saver" msgid="4990712734503013251">"Ahorro de batería"</string>
<string name="quick_settings_dark_mode_secondary_label_on_at_sunset" msgid="6017379738102015710">"Al anochecer"</string>
<string name="quick_settings_dark_mode_secondary_label_until_sunrise" msgid="4404885070316716472">"Hasta el amanecer"</string>
+ <string name="quick_settings_dark_mode_secondary_label_on_at" msgid="5128758823486361279">"A las <xliff:g id="TIME">%s</xliff:g>"</string>
+ <string name="quick_settings_dark_mode_secondary_label_until" msgid="2289774641256492437">"Hasta las <xliff:g id="TIME">%s</xliff:g>"</string>
<string name="quick_settings_nfc_label" msgid="1054317416221168085">"NFC"</string>
<string name="quick_settings_nfc_off" msgid="3465000058515424663">"El NFC está desactivado"</string>
<string name="quick_settings_nfc_on" msgid="1004976611203202230">"El NFC está activado"</string>
diff --git a/packages/SystemUI/res/values-et/strings.xml b/packages/SystemUI/res/values-et/strings.xml
index 6f69a17f35f0..d8f546ce48d4 100644
--- a/packages/SystemUI/res/values-et/strings.xml
+++ b/packages/SystemUI/res/values-et/strings.xml
@@ -71,6 +71,7 @@
<string name="compat_mode_on" msgid="4963711187149440884">"Suumi ekraani täitmiseks"</string>
<string name="compat_mode_off" msgid="7682459748279487945">"Venita ekraani täitmiseks"</string>
<string name="global_action_screenshot" msgid="2760267567509131654">"Ekraanipilt"</string>
+ <string name="remote_input_image_insertion_text" msgid="4613177882724332877">"Pilt on sisestatud"</string>
<string name="screenshot_saving_ticker" msgid="6519186952674544916">"Kuvatõmmise salvestamine ..."</string>
<string name="screenshot_saving_title" msgid="2298349784913287333">"Kuvatõmmise salvestamine ..."</string>
<string name="screenshot_saved_title" msgid="8893267638659083153">"Ekraanipilt salvestati"</string>
@@ -84,6 +85,7 @@
<string name="screenrecord_start_label" msgid="1539048263178882562">"Alusta salvestamist"</string>
<string name="screenrecord_mic_label" msgid="6134198080740031632">"Salvesta hääl"</string>
<string name="screenrecord_taps_label" msgid="2518244240225925076">"Kuva puudutused"</string>
+ <string name="screenrecord_stop_text" msgid="6549288689506057686">"Puudutage peatamiseks"</string>
<string name="screenrecord_stop_label" msgid="72699670052087989">"Peata"</string>
<string name="screenrecord_pause_label" msgid="6004054907104549857">"Peata"</string>
<string name="screenrecord_resume_label" msgid="4972223043729555575">"Jätka"</string>
@@ -388,15 +390,14 @@
<string name="quick_settings_dark_mode_secondary_label_battery_saver" msgid="4990712734503013251">"Akusäästja"</string>
<string name="quick_settings_dark_mode_secondary_label_on_at_sunset" msgid="6017379738102015710">"Sisse päikeselooj."</string>
<string name="quick_settings_dark_mode_secondary_label_until_sunrise" msgid="4404885070316716472">"Kuni päikesetõusuni"</string>
+ <string name="quick_settings_dark_mode_secondary_label_on_at" msgid="5128758823486361279">"Sisse kell <xliff:g id="TIME">%s</xliff:g>"</string>
+ <string name="quick_settings_dark_mode_secondary_label_until" msgid="2289774641256492437">"Kuni <xliff:g id="TIME">%s</xliff:g>"</string>
<string name="quick_settings_nfc_label" msgid="1054317416221168085">"NFC"</string>
<string name="quick_settings_nfc_off" msgid="3465000058515424663">"NFC on keelatud"</string>
<string name="quick_settings_nfc_on" msgid="1004976611203202230">"NFC on lubatud"</string>
- <!-- no translation found for quick_settings_screen_record_label (1594046461509776676) -->
- <skip />
- <!-- no translation found for quick_settings_screen_record_start (1574725369331638985) -->
- <skip />
- <!-- no translation found for quick_settings_screen_record_stop (8087348522976412119) -->
- <skip />
+ <string name="quick_settings_screen_record_label" msgid="1594046461509776676">"Ekraanikirje"</string>
+ <string name="quick_settings_screen_record_start" msgid="1574725369331638985">"Alustage"</string>
+ <string name="quick_settings_screen_record_stop" msgid="8087348522976412119">"Peatage"</string>
<string name="recents_swipe_up_onboarding" msgid="2820265886420993995">"Rakenduste vahetamiseks pühkige üles"</string>
<string name="recents_quick_scrub_onboarding" msgid="765934300283514912">"Lohistage paremale, et rakendusi kiiresti vahetada"</string>
<string name="quick_step_accessibility_toggle_overview" msgid="7908949976727578403">"Lehe Ülevaade sisse- ja väljalülitamine"</string>
@@ -696,22 +697,14 @@
<string name="notification_app_settings" msgid="8963648463858039377">"Kohandamine"</string>
<string name="notification_done" msgid="6215117625922713976">"Valmis"</string>
<string name="inline_undo" msgid="9026953267645116526">"Võta tagasi"</string>
- <!-- no translation found for demote (6225813324237153980) -->
- <skip />
- <!-- no translation found for notification_conversation_favorite (8252976467488182853) -->
- <skip />
- <!-- no translation found for notification_conversation_unfavorite (633301300443356176) -->
- <skip />
- <!-- no translation found for notification_conversation_mute (477431709687199671) -->
- <skip />
- <!-- no translation found for notification_conversation_unmute (410885000669775294) -->
- <skip />
- <!-- no translation found for notification_conversation_bubble (4598142032706190028) -->
- <skip />
- <!-- no translation found for notification_conversation_unbubble (2303087159802926401) -->
- <skip />
- <!-- no translation found for notification_conversation_home_screen (8347136037958438935) -->
- <skip />
+ <string name="demote" msgid="6225813324237153980">"Eemalda see meeldetuletus vestlustest"</string>
+ <string name="notification_conversation_favorite" msgid="8252976467488182853">"Lisa lemmikutesse"</string>
+ <string name="notification_conversation_unfavorite" msgid="633301300443356176">"Tühista lemmikutesse lisamine"</string>
+ <string name="notification_conversation_mute" msgid="477431709687199671">"Vaigista"</string>
+ <string name="notification_conversation_unmute" msgid="410885000669775294">"Tühista vaigistus"</string>
+ <string name="notification_conversation_bubble" msgid="4598142032706190028">"Kuva mullina"</string>
+ <string name="notification_conversation_unbubble" msgid="2303087159802926401">"Lülita mullid välja"</string>
+ <string name="notification_conversation_home_screen" msgid="8347136037958438935">"Lisa avakuvale"</string>
<string name="notification_menu_accessibility" msgid="8984166825879886773">"<xliff:g id="APP_NAME">%1$s</xliff:g>, <xliff:g id="MENU_DESCRIPTION">%2$s</xliff:g>"</string>
<string name="notification_menu_gear_description" msgid="6429668976593634862">"märguannete juhtnupud"</string>
<string name="notification_menu_snooze_description" msgid="4740133348901973244">"märguannete edasilükkamise valikud"</string>
diff --git a/packages/SystemUI/res/values-eu/strings.xml b/packages/SystemUI/res/values-eu/strings.xml
index 1b89c5aa92d2..9dceb5fe3f05 100644
--- a/packages/SystemUI/res/values-eu/strings.xml
+++ b/packages/SystemUI/res/values-eu/strings.xml
@@ -71,6 +71,7 @@
<string name="compat_mode_on" msgid="4963711187149440884">"Handiagotu pantaila betetzeko"</string>
<string name="compat_mode_off" msgid="7682459748279487945">"Luzatu pantaila betetzeko"</string>
<string name="global_action_screenshot" msgid="2760267567509131654">"Pantaila-argazkia"</string>
+ <string name="remote_input_image_insertion_text" msgid="4613177882724332877">"Irudi bat txertatu da"</string>
<string name="screenshot_saving_ticker" msgid="6519186952674544916">"Pantaila-argazkia gordetzen…"</string>
<string name="screenshot_saving_title" msgid="2298349784913287333">"Pantaila-argazkia gordetzen…"</string>
<string name="screenshot_saved_title" msgid="8893267638659083153">"Gorde da pantaila-argazkia"</string>
@@ -84,6 +85,7 @@
<string name="screenrecord_start_label" msgid="1539048263178882562">"Hasi grabatzen"</string>
<string name="screenrecord_mic_label" msgid="6134198080740031632">"Grabatu off ahotsa"</string>
<string name="screenrecord_taps_label" msgid="2518244240225925076">"Erakutsi sakatzeak"</string>
+ <string name="screenrecord_stop_text" msgid="6549288689506057686">"Sakatu gelditzeko"</string>
<string name="screenrecord_stop_label" msgid="72699670052087989">"Gelditu"</string>
<string name="screenrecord_pause_label" msgid="6004054907104549857">"Pausatu"</string>
<string name="screenrecord_resume_label" msgid="4972223043729555575">"Berrekin"</string>
@@ -388,15 +390,14 @@
<string name="quick_settings_dark_mode_secondary_label_battery_saver" msgid="4990712734503013251">"Bateria-aurrezlea"</string>
<string name="quick_settings_dark_mode_secondary_label_on_at_sunset" msgid="6017379738102015710">"Ilunabarrean aktibatuko da"</string>
<string name="quick_settings_dark_mode_secondary_label_until_sunrise" msgid="4404885070316716472">"Egunsentira arte"</string>
+ <string name="quick_settings_dark_mode_secondary_label_on_at" msgid="5128758823486361279">"Aktibatze-ordua: <xliff:g id="TIME">%s</xliff:g>"</string>
+ <string name="quick_settings_dark_mode_secondary_label_until" msgid="2289774641256492437">"Desaktibatze-ordua: <xliff:g id="TIME">%s</xliff:g>"</string>
<string name="quick_settings_nfc_label" msgid="1054317416221168085">"NFC"</string>
<string name="quick_settings_nfc_off" msgid="3465000058515424663">"Desgaituta dago NFC"</string>
<string name="quick_settings_nfc_on" msgid="1004976611203202230">"Gaituta dago NFC"</string>
- <!-- no translation found for quick_settings_screen_record_label (1594046461509776676) -->
- <skip />
- <!-- no translation found for quick_settings_screen_record_start (1574725369331638985) -->
- <skip />
- <!-- no translation found for quick_settings_screen_record_stop (8087348522976412119) -->
- <skip />
+ <string name="quick_settings_screen_record_label" msgid="1594046461509776676">"Pantaila-grabaketa"</string>
+ <string name="quick_settings_screen_record_start" msgid="1574725369331638985">"Hasi"</string>
+ <string name="quick_settings_screen_record_stop" msgid="8087348522976412119">"Gelditu"</string>
<string name="recents_swipe_up_onboarding" msgid="2820265886420993995">"Egin gora aplikazioa aldatzeko"</string>
<string name="recents_quick_scrub_onboarding" msgid="765934300283514912">"Arrastatu eskuinera aplikazioa azkar aldatzeko"</string>
<string name="quick_step_accessibility_toggle_overview" msgid="7908949976727578403">"Aldatu ikuspegi orokorra"</string>
@@ -696,22 +697,14 @@
<string name="notification_app_settings" msgid="8963648463858039377">"Pertsonalizatu"</string>
<string name="notification_done" msgid="6215117625922713976">"Eginda"</string>
<string name="inline_undo" msgid="9026953267645116526">"Desegin"</string>
- <!-- no translation found for demote (6225813324237153980) -->
- <skip />
- <!-- no translation found for notification_conversation_favorite (8252976467488182853) -->
- <skip />
- <!-- no translation found for notification_conversation_unfavorite (633301300443356176) -->
- <skip />
- <!-- no translation found for notification_conversation_mute (477431709687199671) -->
- <skip />
- <!-- no translation found for notification_conversation_unmute (410885000669775294) -->
- <skip />
- <!-- no translation found for notification_conversation_bubble (4598142032706190028) -->
- <skip />
- <!-- no translation found for notification_conversation_unbubble (2303087159802926401) -->
- <skip />
- <!-- no translation found for notification_conversation_home_screen (8347136037958438935) -->
- <skip />
+ <string name="demote" msgid="6225813324237153980">"Markatu jakinarazpen hau ez dela elkarrizketa bat"</string>
+ <string name="notification_conversation_favorite" msgid="8252976467488182853">"Markatu gogoko gisa"</string>
+ <string name="notification_conversation_unfavorite" msgid="633301300443356176">"Kendu gogokoetatik"</string>
+ <string name="notification_conversation_mute" msgid="477431709687199671">"Ezkutatu"</string>
+ <string name="notification_conversation_unmute" msgid="410885000669775294">"Erakutsi"</string>
+ <string name="notification_conversation_bubble" msgid="4598142032706190028">"Markatu burbuila gisa"</string>
+ <string name="notification_conversation_unbubble" msgid="2303087159802926401">"Desaktibatu burbuilak"</string>
+ <string name="notification_conversation_home_screen" msgid="8347136037958438935">"Gehitu hasierako pantailan"</string>
<string name="notification_menu_accessibility" msgid="8984166825879886773">"<xliff:g id="APP_NAME">%1$s</xliff:g> <xliff:g id="MENU_DESCRIPTION">%2$s</xliff:g>"</string>
<string name="notification_menu_gear_description" msgid="6429668976593634862">"jakinarazpena kontrolatzeko aukerak"</string>
<string name="notification_menu_snooze_description" msgid="4740133348901973244">"jakinarazpena atzeratzeko aukerak"</string>
diff --git a/packages/SystemUI/res/values-fa/strings.xml b/packages/SystemUI/res/values-fa/strings.xml
index 2b765573ac06..d27b420c0e9c 100644
--- a/packages/SystemUI/res/values-fa/strings.xml
+++ b/packages/SystemUI/res/values-fa/strings.xml
@@ -71,6 +71,7 @@
<string name="compat_mode_on" msgid="4963711187149440884">"بزرگ‌نمایی برای پر کردن صفحه"</string>
<string name="compat_mode_off" msgid="7682459748279487945">"گسترده کردن برای پر کردن صفحه"</string>
<string name="global_action_screenshot" msgid="2760267567509131654">"عکس صفحه‌نمایش"</string>
+ <string name="remote_input_image_insertion_text" msgid="4613177882724332877">"تصویر درج شد"</string>
<string name="screenshot_saving_ticker" msgid="6519186952674544916">"در حال ذخیره عکس صفحه‌نمایش..."</string>
<string name="screenshot_saving_title" msgid="2298349784913287333">"درحال ذخیره عکس صفحه‌نمایش…"</string>
<string name="screenshot_saved_title" msgid="8893267638659083153">"عکس صفحه‌نمایش ذخیره شد"</string>
@@ -84,6 +85,7 @@
<string name="screenrecord_start_label" msgid="1539048263178882562">"شروع ضبط"</string>
<string name="screenrecord_mic_label" msgid="6134198080740031632">"ضبط صدا روی تصویر"</string>
<string name="screenrecord_taps_label" msgid="2518244240225925076">"نمایش ضربه‌ها"</string>
+ <string name="screenrecord_stop_text" msgid="6549288689506057686">"ضربه برای توقف"</string>
<string name="screenrecord_stop_label" msgid="72699670052087989">"توقف"</string>
<string name="screenrecord_pause_label" msgid="6004054907104549857">"مکث"</string>
<string name="screenrecord_resume_label" msgid="4972223043729555575">"ازسرگیری"</string>
@@ -388,15 +390,14 @@
<string name="quick_settings_dark_mode_secondary_label_battery_saver" msgid="4990712734503013251">"بهینه‌سازی باتری"</string>
<string name="quick_settings_dark_mode_secondary_label_on_at_sunset" msgid="6017379738102015710">"غروب روشن می‌شود"</string>
<string name="quick_settings_dark_mode_secondary_label_until_sunrise" msgid="4404885070316716472">"تا طلوع آفتاب"</string>
+ <string name="quick_settings_dark_mode_secondary_label_on_at" msgid="5128758823486361279">"ساعت <xliff:g id="TIME">%s</xliff:g> روشن می‌شود"</string>
+ <string name="quick_settings_dark_mode_secondary_label_until" msgid="2289774641256492437">"تا<xliff:g id="TIME">%s</xliff:g>"</string>
<string name="quick_settings_nfc_label" msgid="1054317416221168085">"NFC"</string>
<string name="quick_settings_nfc_off" msgid="3465000058515424663">"‏NFC غیرفعال است"</string>
<string name="quick_settings_nfc_on" msgid="1004976611203202230">"‏NFC فعال است"</string>
- <!-- no translation found for quick_settings_screen_record_label (1594046461509776676) -->
- <skip />
- <!-- no translation found for quick_settings_screen_record_start (1574725369331638985) -->
- <skip />
- <!-- no translation found for quick_settings_screen_record_stop (8087348522976412119) -->
- <skip />
+ <string name="quick_settings_screen_record_label" msgid="1594046461509776676">"ضبط کردن صفحه‌نمایش"</string>
+ <string name="quick_settings_screen_record_start" msgid="1574725369331638985">"شروع"</string>
+ <string name="quick_settings_screen_record_stop" msgid="8087348522976412119">"توقف"</string>
<string name="recents_swipe_up_onboarding" msgid="2820265886420993995">"برای تغییر برنامه‌ها،‌ تند به بالا بکشید"</string>
<string name="recents_quick_scrub_onboarding" msgid="765934300283514912">"برای جابه‌جایی سریع میان برنامه‌ها، به چپ بکشید"</string>
<string name="quick_step_accessibility_toggle_overview" msgid="7908949976727578403">"تغییر وضعیت نمای کلی"</string>
@@ -696,22 +697,14 @@
<string name="notification_app_settings" msgid="8963648463858039377">"سفارشی کردن"</string>
<string name="notification_done" msgid="6215117625922713976">"تمام"</string>
<string name="inline_undo" msgid="9026953267645116526">"واگرد"</string>
- <!-- no translation found for demote (6225813324237153980) -->
- <skip />
- <!-- no translation found for notification_conversation_favorite (8252976467488182853) -->
- <skip />
- <!-- no translation found for notification_conversation_unfavorite (633301300443356176) -->
- <skip />
- <!-- no translation found for notification_conversation_mute (477431709687199671) -->
- <skip />
- <!-- no translation found for notification_conversation_unmute (410885000669775294) -->
- <skip />
- <!-- no translation found for notification_conversation_bubble (4598142032706190028) -->
- <skip />
- <!-- no translation found for notification_conversation_unbubble (2303087159802926401) -->
- <skip />
- <!-- no translation found for notification_conversation_home_screen (8347136037958438935) -->
- <skip />
+ <string name="demote" msgid="6225813324237153980">"علامت‌گذاری این اعلان به‌عنوان غیرمکالمه"</string>
+ <string name="notification_conversation_favorite" msgid="8252976467488182853">"مورد دلخواه"</string>
+ <string name="notification_conversation_unfavorite" msgid="633301300443356176">"حذف از موارد دلخواه"</string>
+ <string name="notification_conversation_mute" msgid="477431709687199671">"صامت کردن"</string>
+ <string name="notification_conversation_unmute" msgid="410885000669775294">"باصدا کردن"</string>
+ <string name="notification_conversation_bubble" msgid="4598142032706190028">"نمایش به‌شکل ابزارک اعلان"</string>
+ <string name="notification_conversation_unbubble" msgid="2303087159802926401">"خاموش کردن ابزارک‌های اعلان"</string>
+ <string name="notification_conversation_home_screen" msgid="8347136037958438935">"افزودن به صفحه اصلی"</string>
<string name="notification_menu_accessibility" msgid="8984166825879886773">"<xliff:g id="APP_NAME">%1$s</xliff:g> <xliff:g id="MENU_DESCRIPTION">%2$s</xliff:g>"</string>
<string name="notification_menu_gear_description" msgid="6429668976593634862">"کنترل‌های اعلان"</string>
<string name="notification_menu_snooze_description" msgid="4740133348901973244">"گزینه‌های تعویق اعلان"</string>
diff --git a/packages/SystemUI/res/values-fi/strings.xml b/packages/SystemUI/res/values-fi/strings.xml
index 9b76f39175ab..cf01efd933d0 100644
--- a/packages/SystemUI/res/values-fi/strings.xml
+++ b/packages/SystemUI/res/values-fi/strings.xml
@@ -71,6 +71,7 @@
<string name="compat_mode_on" msgid="4963711187149440884">"Zoomaa koko näyttöön"</string>
<string name="compat_mode_off" msgid="7682459748279487945">"Venytä koko näyttöön"</string>
<string name="global_action_screenshot" msgid="2760267567509131654">"Kuvakaappaus"</string>
+ <string name="remote_input_image_insertion_text" msgid="4613177882724332877">"Kuva lisätty"</string>
<string name="screenshot_saving_ticker" msgid="6519186952674544916">"Tallennetaan kuvakaappausta..."</string>
<string name="screenshot_saving_title" msgid="2298349784913287333">"Tallennetaan kuvakaappausta..."</string>
<string name="screenshot_saved_title" msgid="8893267638659083153">"Kuvakaappaus tallennettu"</string>
@@ -84,6 +85,7 @@
<string name="screenrecord_start_label" msgid="1539048263178882562">"Aloita tallennus"</string>
<string name="screenrecord_mic_label" msgid="6134198080740031632">"Äänitä taustaselostus"</string>
<string name="screenrecord_taps_label" msgid="2518244240225925076">"Näytä napautukset"</string>
+ <string name="screenrecord_stop_text" msgid="6549288689506057686">"Lopeta napauttamalla"</string>
<string name="screenrecord_stop_label" msgid="72699670052087989">"Lopeta"</string>
<string name="screenrecord_pause_label" msgid="6004054907104549857">"Keskeytä"</string>
<string name="screenrecord_resume_label" msgid="4972223043729555575">"Jatka"</string>
@@ -388,15 +390,14 @@
<string name="quick_settings_dark_mode_secondary_label_battery_saver" msgid="4990712734503013251">"Virransäästö"</string>
<string name="quick_settings_dark_mode_secondary_label_on_at_sunset" msgid="6017379738102015710">"Auringon laskiessa"</string>
<string name="quick_settings_dark_mode_secondary_label_until_sunrise" msgid="4404885070316716472">"Auringonnousuun"</string>
+ <string name="quick_settings_dark_mode_secondary_label_on_at" msgid="5128758823486361279">"Päälle klo <xliff:g id="TIME">%s</xliff:g>"</string>
+ <string name="quick_settings_dark_mode_secondary_label_until" msgid="2289774641256492437">"<xliff:g id="TIME">%s</xliff:g> asti"</string>
<string name="quick_settings_nfc_label" msgid="1054317416221168085">"NFC"</string>
<string name="quick_settings_nfc_off" msgid="3465000058515424663">"NFC on poistettu käytöstä"</string>
<string name="quick_settings_nfc_on" msgid="1004976611203202230">"NFC on käytössä"</string>
- <!-- no translation found for quick_settings_screen_record_label (1594046461509776676) -->
- <skip />
- <!-- no translation found for quick_settings_screen_record_start (1574725369331638985) -->
- <skip />
- <!-- no translation found for quick_settings_screen_record_stop (8087348522976412119) -->
- <skip />
+ <string name="quick_settings_screen_record_label" msgid="1594046461509776676">"Näytön tallentaminen"</string>
+ <string name="quick_settings_screen_record_start" msgid="1574725369331638985">"Aloita"</string>
+ <string name="quick_settings_screen_record_stop" msgid="8087348522976412119">"Lopeta"</string>
<string name="recents_swipe_up_onboarding" msgid="2820265886420993995">"Vaihda sovellusta pyyhkäisemällä ylös"</string>
<string name="recents_quick_scrub_onboarding" msgid="765934300283514912">"Vaihda sovellusta nopeasti vetämällä oikealle"</string>
<string name="quick_step_accessibility_toggle_overview" msgid="7908949976727578403">"Näytä/piilota viimeisimmät"</string>
@@ -696,22 +697,14 @@
<string name="notification_app_settings" msgid="8963648463858039377">"Muokkaa"</string>
<string name="notification_done" msgid="6215117625922713976">"Valmis"</string>
<string name="inline_undo" msgid="9026953267645116526">"Kumoa"</string>
- <!-- no translation found for demote (6225813324237153980) -->
- <skip />
- <!-- no translation found for notification_conversation_favorite (8252976467488182853) -->
- <skip />
- <!-- no translation found for notification_conversation_unfavorite (633301300443356176) -->
- <skip />
- <!-- no translation found for notification_conversation_mute (477431709687199671) -->
- <skip />
- <!-- no translation found for notification_conversation_unmute (410885000669775294) -->
- <skip />
- <!-- no translation found for notification_conversation_bubble (4598142032706190028) -->
- <skip />
- <!-- no translation found for notification_conversation_unbubble (2303087159802926401) -->
- <skip />
- <!-- no translation found for notification_conversation_home_screen (8347136037958438935) -->
- <skip />
+ <string name="demote" msgid="6225813324237153980">"Merkitse, että tämä ilmoitus ei ole keskustelu"</string>
+ <string name="notification_conversation_favorite" msgid="8252976467488182853">"Suosikki"</string>
+ <string name="notification_conversation_unfavorite" msgid="633301300443356176">"Poista suosikeista"</string>
+ <string name="notification_conversation_mute" msgid="477431709687199671">"Ohita"</string>
+ <string name="notification_conversation_unmute" msgid="410885000669775294">"Poista ohitus"</string>
+ <string name="notification_conversation_bubble" msgid="4598142032706190028">"Näytä ohjekuplana"</string>
+ <string name="notification_conversation_unbubble" msgid="2303087159802926401">"Laita ohjekuplat pois päältä"</string>
+ <string name="notification_conversation_home_screen" msgid="8347136037958438935">"Lisää aloitusnäytölle"</string>
<string name="notification_menu_accessibility" msgid="8984166825879886773">"<xliff:g id="APP_NAME">%1$s</xliff:g> <xliff:g id="MENU_DESCRIPTION">%2$s</xliff:g>"</string>
<string name="notification_menu_gear_description" msgid="6429668976593634862">"Ilmoitusten hallinta"</string>
<string name="notification_menu_snooze_description" msgid="4740133348901973244">"Ilmoitusten torkkuasetukset"</string>
diff --git a/packages/SystemUI/res/values-fr-rCA/strings.xml b/packages/SystemUI/res/values-fr-rCA/strings.xml
index 55eb39ee69f2..5f7a358239c7 100644
--- a/packages/SystemUI/res/values-fr-rCA/strings.xml
+++ b/packages/SystemUI/res/values-fr-rCA/strings.xml
@@ -71,6 +71,7 @@
<string name="compat_mode_on" msgid="4963711187149440884">"Zoomer pour remplir l\'écran"</string>
<string name="compat_mode_off" msgid="7682459748279487945">"Étirer pour remplir l\'écran"</string>
<string name="global_action_screenshot" msgid="2760267567509131654">"Capture d\'écran"</string>
+ <string name="remote_input_image_insertion_text" msgid="4613177882724332877">"Image insérée"</string>
<string name="screenshot_saving_ticker" msgid="6519186952674544916">"Enregistrement capture écran…"</string>
<string name="screenshot_saving_title" msgid="2298349784913287333">"Enregistrement capture écran…"</string>
<string name="screenshot_saved_title" msgid="8893267638659083153">"Capture d\'écran enregistrée"</string>
@@ -84,6 +85,7 @@
<string name="screenrecord_start_label" msgid="1539048263178882562">"Commencer l\'enregistrement"</string>
<string name="screenrecord_mic_label" msgid="6134198080740031632">"Enregistrer la voix hors champ"</string>
<string name="screenrecord_taps_label" msgid="2518244240225925076">"Afficher les éléments sélectionnés"</string>
+ <string name="screenrecord_stop_text" msgid="6549288689506057686">"Toucher pour arrêter"</string>
<string name="screenrecord_stop_label" msgid="72699670052087989">"Arrêter"</string>
<string name="screenrecord_pause_label" msgid="6004054907104549857">"Pause"</string>
<string name="screenrecord_resume_label" msgid="4972223043729555575">"Reprendre"</string>
@@ -388,6 +390,8 @@
<string name="quick_settings_dark_mode_secondary_label_battery_saver" msgid="4990712734503013251">"Économiseur de pile"</string>
<string name="quick_settings_dark_mode_secondary_label_on_at_sunset" msgid="6017379738102015710">"Activé la nuit"</string>
<string name="quick_settings_dark_mode_secondary_label_until_sunrise" msgid="4404885070316716472">"Jusqu\'à l\'aube"</string>
+ <string name="quick_settings_dark_mode_secondary_label_on_at" msgid="5128758823486361279">"Actif à <xliff:g id="TIME">%s</xliff:g>"</string>
+ <string name="quick_settings_dark_mode_secondary_label_until" msgid="2289774641256492437">"Jusqu\'à <xliff:g id="TIME">%s</xliff:g>"</string>
<string name="quick_settings_nfc_label" msgid="1054317416221168085">"NFC"</string>
<string name="quick_settings_nfc_off" msgid="3465000058515424663">"NFC désactivée"</string>
<string name="quick_settings_nfc_on" msgid="1004976611203202230">"NFC activée"</string>
diff --git a/packages/SystemUI/res/values-fr/strings.xml b/packages/SystemUI/res/values-fr/strings.xml
index 5b126129311e..3c12f668d2b9 100644
--- a/packages/SystemUI/res/values-fr/strings.xml
+++ b/packages/SystemUI/res/values-fr/strings.xml
@@ -71,6 +71,7 @@
<string name="compat_mode_on" msgid="4963711187149440884">"Zoomer pour remplir l\'écran"</string>
<string name="compat_mode_off" msgid="7682459748279487945">"Étirer pour remplir l\'écran"</string>
<string name="global_action_screenshot" msgid="2760267567509131654">"Capture d\'écran"</string>
+ <string name="remote_input_image_insertion_text" msgid="4613177882724332877">"Image insérée"</string>
<string name="screenshot_saving_ticker" msgid="6519186952674544916">"Enregistrement capture écran…"</string>
<string name="screenshot_saving_title" msgid="2298349784913287333">"Enregistrement de la capture d\'écran…"</string>
<string name="screenshot_saved_title" msgid="8893267638659083153">"Capture d\'écran enregistrée"</string>
@@ -84,6 +85,7 @@
<string name="screenrecord_start_label" msgid="1539048263178882562">"Démarrer l\'enregistrement"</string>
<string name="screenrecord_mic_label" msgid="6134198080740031632">"Enregistrer une voix off"</string>
<string name="screenrecord_taps_label" msgid="2518244240225925076">"Afficher les éléments sélectionnés"</string>
+ <string name="screenrecord_stop_text" msgid="6549288689506057686">"Appuyez ici pour arrêter"</string>
<string name="screenrecord_stop_label" msgid="72699670052087989">"Arrêter"</string>
<string name="screenrecord_pause_label" msgid="6004054907104549857">"Pause"</string>
<string name="screenrecord_resume_label" msgid="4972223043729555575">"Reprendre"</string>
@@ -388,6 +390,8 @@
<string name="quick_settings_dark_mode_secondary_label_battery_saver" msgid="4990712734503013251">"Économiseur batterie"</string>
<string name="quick_settings_dark_mode_secondary_label_on_at_sunset" msgid="6017379738102015710">"Activé la nuit"</string>
<string name="quick_settings_dark_mode_secondary_label_until_sunrise" msgid="4404885070316716472">"Jusqu\'à l\'aube"</string>
+ <string name="quick_settings_dark_mode_secondary_label_on_at" msgid="5128758823486361279">"À partir de <xliff:g id="TIME">%s</xliff:g>"</string>
+ <string name="quick_settings_dark_mode_secondary_label_until" msgid="2289774641256492437">"Jusqu\'à <xliff:g id="TIME">%s</xliff:g>"</string>
<string name="quick_settings_nfc_label" msgid="1054317416221168085">"NFC"</string>
<string name="quick_settings_nfc_off" msgid="3465000058515424663">"NFC désactivée"</string>
<string name="quick_settings_nfc_on" msgid="1004976611203202230">"La technologie NFC est activée"</string>
diff --git a/packages/SystemUI/res/values-gl/strings.xml b/packages/SystemUI/res/values-gl/strings.xml
index 74ee202f9926..0c444e48aee3 100644
--- a/packages/SystemUI/res/values-gl/strings.xml
+++ b/packages/SystemUI/res/values-gl/strings.xml
@@ -71,6 +71,7 @@
<string name="compat_mode_on" msgid="4963711187149440884">"Ampliar ata ocupar todo"</string>
<string name="compat_mode_off" msgid="7682459748279487945">"Estirar ata ocupar todo"</string>
<string name="global_action_screenshot" msgid="2760267567509131654">"Crear captura"</string>
+ <string name="remote_input_image_insertion_text" msgid="4613177882724332877">"Engadiuse a imaxe"</string>
<string name="screenshot_saving_ticker" msgid="6519186952674544916">"Gardando captura de pantalla…"</string>
<string name="screenshot_saving_title" msgid="2298349784913287333">"Gardando captura de pantalla…"</string>
<string name="screenshot_saved_title" msgid="8893267638659083153">"Gardouse a captura de pantalla"</string>
@@ -84,6 +85,7 @@
<string name="screenrecord_start_label" msgid="1539048263178882562">"Iniciar gravación"</string>
<string name="screenrecord_mic_label" msgid="6134198080740031632">"Gravar voz en off"</string>
<string name="screenrecord_taps_label" msgid="2518244240225925076">"Mostrar toques"</string>
+ <string name="screenrecord_stop_text" msgid="6549288689506057686">"Toca para deter a gravación"</string>
<string name="screenrecord_stop_label" msgid="72699670052087989">"Deter"</string>
<string name="screenrecord_pause_label" msgid="6004054907104549857">"Pór en pausa"</string>
<string name="screenrecord_resume_label" msgid="4972223043729555575">"Retomar"</string>
@@ -388,6 +390,8 @@
<string name="quick_settings_dark_mode_secondary_label_battery_saver" msgid="4990712734503013251">"Aforro de batería"</string>
<string name="quick_settings_dark_mode_secondary_label_on_at_sunset" msgid="6017379738102015710">"Activación ao solpor"</string>
<string name="quick_settings_dark_mode_secondary_label_until_sunrise" msgid="4404885070316716472">"Ata o amencer"</string>
+ <string name="quick_settings_dark_mode_secondary_label_on_at" msgid="5128758823486361279">"Activarase ás: <xliff:g id="TIME">%s</xliff:g>"</string>
+ <string name="quick_settings_dark_mode_secondary_label_until" msgid="2289774641256492437">"Utilizarase ata as: <xliff:g id="TIME">%s</xliff:g>"</string>
<string name="quick_settings_nfc_label" msgid="1054317416221168085">"NFC"</string>
<string name="quick_settings_nfc_off" msgid="3465000058515424663">"A opción NFC está desactivada"</string>
<string name="quick_settings_nfc_on" msgid="1004976611203202230">"A opción NFC está activada"</string>
diff --git a/packages/SystemUI/res/values-gu/strings.xml b/packages/SystemUI/res/values-gu/strings.xml
index 4b5dd2519055..9dde523d9a89 100644
--- a/packages/SystemUI/res/values-gu/strings.xml
+++ b/packages/SystemUI/res/values-gu/strings.xml
@@ -71,6 +71,7 @@
<string name="compat_mode_on" msgid="4963711187149440884">"સ્ક્રીન ભરવા માટે ઝૂમ કરો"</string>
<string name="compat_mode_off" msgid="7682459748279487945">"સ્ક્રીન ભરવા માટે ખેંચો"</string>
<string name="global_action_screenshot" msgid="2760267567509131654">"સ્ક્રીનશૉટ"</string>
+ <string name="remote_input_image_insertion_text" msgid="4613177882724332877">"છબી શામેલ કરી"</string>
<string name="screenshot_saving_ticker" msgid="6519186952674544916">"સ્ક્રીનશોટ સાચવી રહ્યું છે…"</string>
<string name="screenshot_saving_title" msgid="2298349784913287333">"સ્ક્રીનશોટ સાચવી રહ્યું છે…"</string>
<string name="screenshot_saved_title" msgid="8893267638659083153">"સ્ક્રીનશૉટ સાચવ્યો"</string>
@@ -84,6 +85,7 @@
<string name="screenrecord_start_label" msgid="1539048263178882562">"રેકોર્ડિંગ શરૂ કરો"</string>
<string name="screenrecord_mic_label" msgid="6134198080740031632">"વૉઇસઓવર રેકોર્ડ કરો"</string>
<string name="screenrecord_taps_label" msgid="2518244240225925076">"ટૅપ કર્યાની સંખ્યા બતાવો"</string>
+ <string name="screenrecord_stop_text" msgid="6549288689506057686">"રોકવા માટે ટૅપ કરો"</string>
<string name="screenrecord_stop_label" msgid="72699670052087989">"રોકો"</string>
<string name="screenrecord_pause_label" msgid="6004054907104549857">"થોભાવો"</string>
<string name="screenrecord_resume_label" msgid="4972223043729555575">"ફરી શરૂ કરો"</string>
@@ -388,15 +390,14 @@
<string name="quick_settings_dark_mode_secondary_label_battery_saver" msgid="4990712734503013251">"બૅટરી સેવર"</string>
<string name="quick_settings_dark_mode_secondary_label_on_at_sunset" msgid="6017379738102015710">"સૂર્યાસ્ત વખતે"</string>
<string name="quick_settings_dark_mode_secondary_label_until_sunrise" msgid="4404885070316716472">"સૂર્યોદય સુધી"</string>
+ <string name="quick_settings_dark_mode_secondary_label_on_at" msgid="5128758823486361279">"<xliff:g id="TIME">%s</xliff:g> વાગ્યે ચાલુ"</string>
+ <string name="quick_settings_dark_mode_secondary_label_until" msgid="2289774641256492437">"<xliff:g id="TIME">%s</xliff:g> વાગ્યા સુધી"</string>
<string name="quick_settings_nfc_label" msgid="1054317416221168085">"NFC"</string>
<string name="quick_settings_nfc_off" msgid="3465000058515424663">"NFC અક્ષમ કરેલ છે"</string>
<string name="quick_settings_nfc_on" msgid="1004976611203202230">"NFC સક્ષમ કરેલ છે"</string>
- <!-- no translation found for quick_settings_screen_record_label (1594046461509776676) -->
- <skip />
- <!-- no translation found for quick_settings_screen_record_start (1574725369331638985) -->
- <skip />
- <!-- no translation found for quick_settings_screen_record_stop (8087348522976412119) -->
- <skip />
+ <string name="quick_settings_screen_record_label" msgid="1594046461509776676">"સ્ક્રીન રેકૉર્ડ કરો"</string>
+ <string name="quick_settings_screen_record_start" msgid="1574725369331638985">"શરૂ કરો"</string>
+ <string name="quick_settings_screen_record_stop" msgid="8087348522976412119">"રોકો"</string>
<string name="recents_swipe_up_onboarding" msgid="2820265886420993995">"ઍપ સ્વિચ કરવા માટે ઉપરની તરફ સ્વાઇપ કરો"</string>
<string name="recents_quick_scrub_onboarding" msgid="765934300283514912">"ઍપને ઝડપથી સ્વિચ કરવા માટે જમણે ખેંચો"</string>
<string name="quick_step_accessibility_toggle_overview" msgid="7908949976727578403">"ઝલકને ટૉગલ કરો"</string>
@@ -696,22 +697,14 @@
<string name="notification_app_settings" msgid="8963648463858039377">"કસ્ટમાઇઝ કરો"</string>
<string name="notification_done" msgid="6215117625922713976">"થઈ ગયું"</string>
<string name="inline_undo" msgid="9026953267645116526">"રદ કરો"</string>
- <!-- no translation found for demote (6225813324237153980) -->
- <skip />
- <!-- no translation found for notification_conversation_favorite (8252976467488182853) -->
- <skip />
- <!-- no translation found for notification_conversation_unfavorite (633301300443356176) -->
- <skip />
- <!-- no translation found for notification_conversation_mute (477431709687199671) -->
- <skip />
- <!-- no translation found for notification_conversation_unmute (410885000669775294) -->
- <skip />
- <!-- no translation found for notification_conversation_bubble (4598142032706190028) -->
- <skip />
- <!-- no translation found for notification_conversation_unbubble (2303087159802926401) -->
- <skip />
- <!-- no translation found for notification_conversation_home_screen (8347136037958438935) -->
- <skip />
+ <string name="demote" msgid="6225813324237153980">"આ નોટિફિકેશન વાતચીત ન હોવા તરીકે માર્ક કરો"</string>
+ <string name="notification_conversation_favorite" msgid="8252976467488182853">"મનપસંદ"</string>
+ <string name="notification_conversation_unfavorite" msgid="633301300443356176">"મનપસંદમાંથી કાઢી નાખો"</string>
+ <string name="notification_conversation_mute" msgid="477431709687199671">"મ્યૂટ કરો"</string>
+ <string name="notification_conversation_unmute" msgid="410885000669775294">"અનમ્યૂટ કરો"</string>
+ <string name="notification_conversation_bubble" msgid="4598142032706190028">"બબલ તરીકે બતાવો"</string>
+ <string name="notification_conversation_unbubble" msgid="2303087159802926401">"બબલ બંધ કરો"</string>
+ <string name="notification_conversation_home_screen" msgid="8347136037958438935">"હોમ સ્ક્રીન પર ઉમેરો"</string>
<string name="notification_menu_accessibility" msgid="8984166825879886773">"<xliff:g id="APP_NAME">%1$s</xliff:g> <xliff:g id="MENU_DESCRIPTION">%2$s</xliff:g>"</string>
<string name="notification_menu_gear_description" msgid="6429668976593634862">"સૂચના નિયંત્રણો"</string>
<string name="notification_menu_snooze_description" msgid="4740133348901973244">"સૂચના સ્નૂઝ કરવાના વિકલ્પો"</string>
diff --git a/packages/SystemUI/res/values-hi/strings.xml b/packages/SystemUI/res/values-hi/strings.xml
index c22d234bedd9..5522605cd0f4 100644
--- a/packages/SystemUI/res/values-hi/strings.xml
+++ b/packages/SystemUI/res/values-hi/strings.xml
@@ -71,6 +71,7 @@
<string name="compat_mode_on" msgid="4963711187149440884">"स्‍क्रीन भरने के लिए ज़ूम करें"</string>
<string name="compat_mode_off" msgid="7682459748279487945">"स्‍क्रीन भरने के लिए खींचें"</string>
<string name="global_action_screenshot" msgid="2760267567509131654">"स्क्रीनशॉट"</string>
+ <string name="remote_input_image_insertion_text" msgid="4613177882724332877">"इमेज डाली गई"</string>
<string name="screenshot_saving_ticker" msgid="6519186952674544916">"स्क्रीनशॉट सहेजा जा रहा है..."</string>
<string name="screenshot_saving_title" msgid="2298349784913287333">"स्क्रीनशॉट सहेजा जा रहा है..."</string>
<string name="screenshot_saved_title" msgid="8893267638659083153">"स्क्रीनशॉट सेव किया गया"</string>
@@ -84,6 +85,7 @@
<string name="screenrecord_start_label" msgid="1539048263178882562">"रिकॉर्डिंग शुरू करें"</string>
<string name="screenrecord_mic_label" msgid="6134198080740031632">"वॉइसओवर रिकॉर्ड करें"</string>
<string name="screenrecord_taps_label" msgid="2518244240225925076">"टैप दिखाएं"</string>
+ <string name="screenrecord_stop_text" msgid="6549288689506057686">"रोकने के लिए टैप करें"</string>
<string name="screenrecord_stop_label" msgid="72699670052087989">"रोकें"</string>
<string name="screenrecord_pause_label" msgid="6004054907104549857">"रोकें"</string>
<string name="screenrecord_resume_label" msgid="4972223043729555575">"फिर से शुरू करें"</string>
@@ -388,15 +390,14 @@
<string name="quick_settings_dark_mode_secondary_label_battery_saver" msgid="4990712734503013251">"बैटरी सेवर"</string>
<string name="quick_settings_dark_mode_secondary_label_on_at_sunset" msgid="6017379738102015710">"शाम को चालू होगा"</string>
<string name="quick_settings_dark_mode_secondary_label_until_sunrise" msgid="4404885070316716472">"सुबह तक चालू रहेगी"</string>
+ <string name="quick_settings_dark_mode_secondary_label_on_at" msgid="5128758823486361279">"<xliff:g id="TIME">%s</xliff:g> बजे चालू हाेगी"</string>
+ <string name="quick_settings_dark_mode_secondary_label_until" msgid="2289774641256492437">"<xliff:g id="TIME">%s</xliff:g> बजे तक चालू रहेगी"</string>
<string name="quick_settings_nfc_label" msgid="1054317416221168085">"एनएफ़सी"</string>
<string name="quick_settings_nfc_off" msgid="3465000058515424663">"NFC बंद है"</string>
<string name="quick_settings_nfc_on" msgid="1004976611203202230">"NFC चालू है"</string>
- <!-- no translation found for quick_settings_screen_record_label (1594046461509776676) -->
- <skip />
- <!-- no translation found for quick_settings_screen_record_start (1574725369331638985) -->
- <skip />
- <!-- no translation found for quick_settings_screen_record_stop (8087348522976412119) -->
- <skip />
+ <string name="quick_settings_screen_record_label" msgid="1594046461509776676">"स्क्रीन रिकॉर्ड"</string>
+ <string name="quick_settings_screen_record_start" msgid="1574725369331638985">"शुरू करें"</string>
+ <string name="quick_settings_screen_record_stop" msgid="8087348522976412119">"रोकें"</string>
<string name="recents_swipe_up_onboarding" msgid="2820265886420993995">"ऐप्लिकेशन बदलने के लिए ऊपर स्वाइप करें"</string>
<string name="recents_quick_scrub_onboarding" msgid="765934300283514912">"ऐप्लिकेशन को झटपट स्विच करने के लिए उसे दाईं ओर खींचें और छोड़ें"</string>
<string name="quick_step_accessibility_toggle_overview" msgid="7908949976727578403">"खास जानकारी टॉगल करें"</string>
@@ -696,22 +697,14 @@
<string name="notification_app_settings" msgid="8963648463858039377">"पसंद के मुताबिक बनाएं"</string>
<string name="notification_done" msgid="6215117625922713976">"हो गया"</string>
<string name="inline_undo" msgid="9026953267645116526">"पहले जैसा करें"</string>
- <!-- no translation found for demote (6225813324237153980) -->
- <skip />
- <!-- no translation found for notification_conversation_favorite (8252976467488182853) -->
- <skip />
- <!-- no translation found for notification_conversation_unfavorite (633301300443356176) -->
- <skip />
- <!-- no translation found for notification_conversation_mute (477431709687199671) -->
- <skip />
- <!-- no translation found for notification_conversation_unmute (410885000669775294) -->
- <skip />
- <!-- no translation found for notification_conversation_bubble (4598142032706190028) -->
- <skip />
- <!-- no translation found for notification_conversation_unbubble (2303087159802926401) -->
- <skip />
- <!-- no translation found for notification_conversation_home_screen (8347136037958438935) -->
- <skip />
+ <string name="demote" msgid="6225813324237153980">"इस सूचना को \'बातचीत नहीं\' के रूप में मार्क करें"</string>
+ <string name="notification_conversation_favorite" msgid="8252976467488182853">"पसंदीदा के रूप में मार्क करें"</string>
+ <string name="notification_conversation_unfavorite" msgid="633301300443356176">"पसंदीदा से हटाएं"</string>
+ <string name="notification_conversation_mute" msgid="477431709687199671">"म्यूट करें"</string>
+ <string name="notification_conversation_unmute" msgid="410885000669775294">"अनम्यूट करें"</string>
+ <string name="notification_conversation_bubble" msgid="4598142032706190028">"बबल के रूप में दिखाएं"</string>
+ <string name="notification_conversation_unbubble" msgid="2303087159802926401">"बबल्स को बंद करें"</string>
+ <string name="notification_conversation_home_screen" msgid="8347136037958438935">"होम स्क्रीन पर जोड़ें"</string>
<string name="notification_menu_accessibility" msgid="8984166825879886773">"<xliff:g id="APP_NAME">%1$s</xliff:g> <xliff:g id="MENU_DESCRIPTION">%2$s</xliff:g>"</string>
<string name="notification_menu_gear_description" msgid="6429668976593634862">"सूचना नियंत्रण"</string>
<string name="notification_menu_snooze_description" msgid="4740133348901973244">"सूचना को स्नूज़ (थोड़ी देर के लिए चुप करना) करने के विकल्प"</string>
diff --git a/packages/SystemUI/res/values-hr/strings.xml b/packages/SystemUI/res/values-hr/strings.xml
index 128670923f1c..55a0e788581a 100644
--- a/packages/SystemUI/res/values-hr/strings.xml
+++ b/packages/SystemUI/res/values-hr/strings.xml
@@ -71,6 +71,7 @@
<string name="compat_mode_on" msgid="4963711187149440884">"Zumiraj i ispuni zaslon"</string>
<string name="compat_mode_off" msgid="7682459748279487945">"Rastegni i ispuni zaslon"</string>
<string name="global_action_screenshot" msgid="2760267567509131654">"Snimka zaslona"</string>
+ <string name="remote_input_image_insertion_text" msgid="4613177882724332877">"Umetnuta je slika"</string>
<string name="screenshot_saving_ticker" msgid="6519186952674544916">"Spremanje snimke zaslona..."</string>
<string name="screenshot_saving_title" msgid="2298349784913287333">"Spremanje snimke zaslona..."</string>
<string name="screenshot_saved_title" msgid="8893267638659083153">"Snimka zaslona spremljena"</string>
@@ -84,6 +85,7 @@
<string name="screenrecord_start_label" msgid="1539048263178882562">"Započni snimanje"</string>
<string name="screenrecord_mic_label" msgid="6134198080740031632">"Snimi glasovni zapis"</string>
<string name="screenrecord_taps_label" msgid="2518244240225925076">"Prikaži dodire"</string>
+ <string name="screenrecord_stop_text" msgid="6549288689506057686">"Dodirnite da biste zaustavili"</string>
<string name="screenrecord_stop_label" msgid="72699670052087989">"Zaustavi"</string>
<string name="screenrecord_pause_label" msgid="6004054907104549857">"Pauza"</string>
<string name="screenrecord_resume_label" msgid="4972223043729555575">"Nastavi"</string>
@@ -390,6 +392,8 @@
<string name="quick_settings_dark_mode_secondary_label_battery_saver" msgid="4990712734503013251">"Štednja baterije"</string>
<string name="quick_settings_dark_mode_secondary_label_on_at_sunset" msgid="6017379738102015710">"Uključuje se u suton"</string>
<string name="quick_settings_dark_mode_secondary_label_until_sunrise" msgid="4404885070316716472">"Do izlaska sunca"</string>
+ <string name="quick_settings_dark_mode_secondary_label_on_at" msgid="5128758823486361279">"Uključuje se u <xliff:g id="TIME">%s</xliff:g>"</string>
+ <string name="quick_settings_dark_mode_secondary_label_until" msgid="2289774641256492437">"Do <xliff:g id="TIME">%s</xliff:g>"</string>
<string name="quick_settings_nfc_label" msgid="1054317416221168085">"NFC"</string>
<string name="quick_settings_nfc_off" msgid="3465000058515424663">"NFC je onemogućen"</string>
<string name="quick_settings_nfc_on" msgid="1004976611203202230">"NFC je omogućen"</string>
diff --git a/packages/SystemUI/res/values-hu/strings.xml b/packages/SystemUI/res/values-hu/strings.xml
index a3b6d3fe1480..a9b2d036ebcc 100644
--- a/packages/SystemUI/res/values-hu/strings.xml
+++ b/packages/SystemUI/res/values-hu/strings.xml
@@ -71,6 +71,7 @@
<string name="compat_mode_on" msgid="4963711187149440884">"Nagyítás a kitöltéshez"</string>
<string name="compat_mode_off" msgid="7682459748279487945">"Nyújtás kitöltéshez"</string>
<string name="global_action_screenshot" msgid="2760267567509131654">"Képernyőkép"</string>
+ <string name="remote_input_image_insertion_text" msgid="4613177882724332877">"Kép beszúrva"</string>
<string name="screenshot_saving_ticker" msgid="6519186952674544916">"Képernyőkép mentése..."</string>
<string name="screenshot_saving_title" msgid="2298349784913287333">"Képernyőkép mentése..."</string>
<string name="screenshot_saved_title" msgid="8893267638659083153">"A képernyőkép mentése sikerült"</string>
@@ -84,6 +85,7 @@
<string name="screenrecord_start_label" msgid="1539048263178882562">"Rögzítés indítása"</string>
<string name="screenrecord_mic_label" msgid="6134198080740031632">"Hang rögzítése"</string>
<string name="screenrecord_taps_label" msgid="2518244240225925076">"Koppintások megjelenítése"</string>
+ <string name="screenrecord_stop_text" msgid="6549288689506057686">"Koppintson a leállításhoz"</string>
<string name="screenrecord_stop_label" msgid="72699670052087989">"Leállítás"</string>
<string name="screenrecord_pause_label" msgid="6004054907104549857">"Szünet"</string>
<string name="screenrecord_resume_label" msgid="4972223043729555575">"Folytatás"</string>
@@ -388,6 +390,8 @@
<string name="quick_settings_dark_mode_secondary_label_battery_saver" msgid="4990712734503013251">"Akkumulátorkímélő"</string>
<string name="quick_settings_dark_mode_secondary_label_on_at_sunset" msgid="6017379738102015710">"Be: napnyugta"</string>
<string name="quick_settings_dark_mode_secondary_label_until_sunrise" msgid="4404885070316716472">"Napfelkeltéig"</string>
+ <string name="quick_settings_dark_mode_secondary_label_on_at" msgid="5128758823486361279">"Be: <xliff:g id="TIME">%s</xliff:g>"</string>
+ <string name="quick_settings_dark_mode_secondary_label_until" msgid="2289774641256492437">"Eddig: <xliff:g id="TIME">%s</xliff:g>"</string>
<string name="quick_settings_nfc_label" msgid="1054317416221168085">"NFC"</string>
<string name="quick_settings_nfc_off" msgid="3465000058515424663">"Az NFC ki van kapcsolva"</string>
<string name="quick_settings_nfc_on" msgid="1004976611203202230">"Az NFC be van kapcsolva"</string>
diff --git a/packages/SystemUI/res/values-hy/strings.xml b/packages/SystemUI/res/values-hy/strings.xml
index 5c73ed18d73f..78acb2ed164e 100644
--- a/packages/SystemUI/res/values-hy/strings.xml
+++ b/packages/SystemUI/res/values-hy/strings.xml
@@ -71,6 +71,7 @@
<string name="compat_mode_on" msgid="4963711187149440884">"Խոշորացնել` էկրանը լցնելու համար"</string>
<string name="compat_mode_off" msgid="7682459748279487945">"Ձգել` էկրանը լցնելու համար"</string>
<string name="global_action_screenshot" msgid="2760267567509131654">"Սքրինշոթ"</string>
+ <string name="remote_input_image_insertion_text" msgid="4613177882724332877">"Պատկերը զետեղվեց"</string>
<string name="screenshot_saving_ticker" msgid="6519186952674544916">"Սքրինշոթը պահվում է…"</string>
<string name="screenshot_saving_title" msgid="2298349784913287333">"Սքրինշոթը պահվում է..."</string>
<string name="screenshot_saved_title" msgid="8893267638659083153">"Սքրինշոթը պահվեց"</string>
@@ -84,6 +85,7 @@
<string name="screenrecord_start_label" msgid="1539048263178882562">"Սկսել տեսագրումը"</string>
<string name="screenrecord_mic_label" msgid="6134198080740031632">"Ձայնագրել ուղեկցող ձայները"</string>
<string name="screenrecord_taps_label" msgid="2518244240225925076">"Ցույց տալ հպումները"</string>
+ <string name="screenrecord_stop_text" msgid="6549288689506057686">"Հպեք՝ դադարեցնելու համար"</string>
<string name="screenrecord_stop_label" msgid="72699670052087989">"Կանգնեցնել"</string>
<string name="screenrecord_pause_label" msgid="6004054907104549857">"Դադարեցնել"</string>
<string name="screenrecord_resume_label" msgid="4972223043729555575">"Վերսկսել"</string>
@@ -388,15 +390,14 @@
<string name="quick_settings_dark_mode_secondary_label_battery_saver" msgid="4990712734503013251">"Մարտկոցի տնտեսում"</string>
<string name="quick_settings_dark_mode_secondary_label_on_at_sunset" msgid="6017379738102015710">"Կմիացվի մայրամուտին"</string>
<string name="quick_settings_dark_mode_secondary_label_until_sunrise" msgid="4404885070316716472">"Մինչև լուսաբաց"</string>
+ <string name="quick_settings_dark_mode_secondary_label_on_at" msgid="5128758823486361279">"Կմիանա՝ <xliff:g id="TIME">%s</xliff:g>"</string>
+ <string name="quick_settings_dark_mode_secondary_label_until" msgid="2289774641256492437">"Մինչև <xliff:g id="TIME">%s</xliff:g>"</string>
<string name="quick_settings_nfc_label" msgid="1054317416221168085">"NFC"</string>
<string name="quick_settings_nfc_off" msgid="3465000058515424663">"NFC-ն անջատված է"</string>
<string name="quick_settings_nfc_on" msgid="1004976611203202230">"NFC-ն միացված է"</string>
- <!-- no translation found for quick_settings_screen_record_label (1594046461509776676) -->
- <skip />
- <!-- no translation found for quick_settings_screen_record_start (1574725369331638985) -->
- <skip />
- <!-- no translation found for quick_settings_screen_record_stop (8087348522976412119) -->
- <skip />
+ <string name="quick_settings_screen_record_label" msgid="1594046461509776676">"Էկրանի ձայնագրում"</string>
+ <string name="quick_settings_screen_record_start" msgid="1574725369331638985">"Սկսել"</string>
+ <string name="quick_settings_screen_record_stop" msgid="8087348522976412119">"Կանգնեցնել"</string>
<string name="recents_swipe_up_onboarding" msgid="2820265886420993995">"Սահեցրեք վերև՝ մյուս հավելվածին անցնելու համար"</string>
<string name="recents_quick_scrub_onboarding" msgid="765934300283514912">"Քաշեք աջ՝ հավելվածների միջև անցնելու համար"</string>
<string name="quick_step_accessibility_toggle_overview" msgid="7908949976727578403">"Միացնել/անջատել համատեսքը"</string>
@@ -696,22 +697,14 @@
<string name="notification_app_settings" msgid="8963648463858039377">"Կարգավորել"</string>
<string name="notification_done" msgid="6215117625922713976">"Պատրաստ է"</string>
<string name="inline_undo" msgid="9026953267645116526">"Հետարկել"</string>
- <!-- no translation found for demote (6225813324237153980) -->
- <skip />
- <!-- no translation found for notification_conversation_favorite (8252976467488182853) -->
- <skip />
- <!-- no translation found for notification_conversation_unfavorite (633301300443356176) -->
- <skip />
- <!-- no translation found for notification_conversation_mute (477431709687199671) -->
- <skip />
- <!-- no translation found for notification_conversation_unmute (410885000669775294) -->
- <skip />
- <!-- no translation found for notification_conversation_bubble (4598142032706190028) -->
- <skip />
- <!-- no translation found for notification_conversation_unbubble (2303087159802926401) -->
- <skip />
- <!-- no translation found for notification_conversation_home_screen (8347136037958438935) -->
- <skip />
+ <string name="demote" msgid="6225813324237153980">"Նշել այս ծանուցումը որպես ոչ խոսակցություն"</string>
+ <string name="notification_conversation_favorite" msgid="8252976467488182853">"Ավելացնել ընտրանիում"</string>
+ <string name="notification_conversation_unfavorite" msgid="633301300443356176">"Հեռացնել ընտրանուց"</string>
+ <string name="notification_conversation_mute" msgid="477431709687199671">"Անջատել ծանուցումները"</string>
+ <string name="notification_conversation_unmute" msgid="410885000669775294">"Միացնել ծանուցումները"</string>
+ <string name="notification_conversation_bubble" msgid="4598142032706190028">"Ցուցադրել որպես ամպիկ"</string>
+ <string name="notification_conversation_unbubble" msgid="2303087159802926401">"Անջատել ամպիկները"</string>
+ <string name="notification_conversation_home_screen" msgid="8347136037958438935">"Ավելացնել հիմնական էկրանին"</string>
<string name="notification_menu_accessibility" msgid="8984166825879886773">"<xliff:g id="APP_NAME">%1$s</xliff:g> <xliff:g id="MENU_DESCRIPTION">%2$s</xliff:g>"</string>
<string name="notification_menu_gear_description" msgid="6429668976593634862">"ծանուցման կառավարներ"</string>
<string name="notification_menu_snooze_description" msgid="4740133348901973244">"ծանուցման հետաձգման ընտրանքներ"</string>
diff --git a/packages/SystemUI/res/values-in/strings.xml b/packages/SystemUI/res/values-in/strings.xml
index bad3e677af0d..95c16c87dc1a 100644
--- a/packages/SystemUI/res/values-in/strings.xml
+++ b/packages/SystemUI/res/values-in/strings.xml
@@ -71,6 +71,7 @@
<string name="compat_mode_on" msgid="4963711187149440884">"Perbesar utk mengisi layar"</string>
<string name="compat_mode_off" msgid="7682459748279487945">"Rentangkn utk mngisi layar"</string>
<string name="global_action_screenshot" msgid="2760267567509131654">"Screenshot"</string>
+ <string name="remote_input_image_insertion_text" msgid="4613177882724332877">"Gambar disisipkan"</string>
<string name="screenshot_saving_ticker" msgid="6519186952674544916">"Menyimpan screenshot..."</string>
<string name="screenshot_saving_title" msgid="2298349784913287333">"Menyimpan screenshot..."</string>
<string name="screenshot_saved_title" msgid="8893267638659083153">"Screenshot disimpan"</string>
@@ -84,6 +85,7 @@
<string name="screenrecord_start_label" msgid="1539048263178882562">"Mulai Merekam"</string>
<string name="screenrecord_mic_label" msgid="6134198080740031632">"Rekam voiceover"</string>
<string name="screenrecord_taps_label" msgid="2518244240225925076">"Tampilkan sentuhan"</string>
+ <string name="screenrecord_stop_text" msgid="6549288689506057686">"Ketuk untuk menghentikan"</string>
<string name="screenrecord_stop_label" msgid="72699670052087989">"Stop"</string>
<string name="screenrecord_pause_label" msgid="6004054907104549857">"Jeda"</string>
<string name="screenrecord_resume_label" msgid="4972223043729555575">"Lanjutkan"</string>
@@ -388,6 +390,8 @@
<string name="quick_settings_dark_mode_secondary_label_battery_saver" msgid="4990712734503013251">"Penghemat Baterai"</string>
<string name="quick_settings_dark_mode_secondary_label_on_at_sunset" msgid="6017379738102015710">"Aktif saat malam"</string>
<string name="quick_settings_dark_mode_secondary_label_until_sunrise" msgid="4404885070316716472">"Sampai pagi"</string>
+ <string name="quick_settings_dark_mode_secondary_label_on_at" msgid="5128758823486361279">"Aktif pada <xliff:g id="TIME">%s</xliff:g>"</string>
+ <string name="quick_settings_dark_mode_secondary_label_until" msgid="2289774641256492437">"Sampai <xliff:g id="TIME">%s</xliff:g>"</string>
<string name="quick_settings_nfc_label" msgid="1054317416221168085">"NFC"</string>
<string name="quick_settings_nfc_off" msgid="3465000058515424663">"NFC dinonaktifkan"</string>
<string name="quick_settings_nfc_on" msgid="1004976611203202230">"NFC diaktifkan"</string>
diff --git a/packages/SystemUI/res/values-is/strings.xml b/packages/SystemUI/res/values-is/strings.xml
index 68f04fb506fc..985f62d3e936 100644
--- a/packages/SystemUI/res/values-is/strings.xml
+++ b/packages/SystemUI/res/values-is/strings.xml
@@ -71,6 +71,7 @@
<string name="compat_mode_on" msgid="4963711187149440884">"Fylla skjá með aðdrætti"</string>
<string name="compat_mode_off" msgid="7682459748279487945">"Teygja yfir allan skjáinn"</string>
<string name="global_action_screenshot" msgid="2760267567509131654">"Skjámynd"</string>
+ <string name="remote_input_image_insertion_text" msgid="4613177882724332877">"Mynd sett inn"</string>
<string name="screenshot_saving_ticker" msgid="6519186952674544916">"Vistar skjámynd…"</string>
<string name="screenshot_saving_title" msgid="2298349784913287333">"Vistar skjámynd…"</string>
<string name="screenshot_saved_title" msgid="8893267638659083153">"Skjámynd vistuð"</string>
@@ -84,6 +85,7 @@
<string name="screenrecord_start_label" msgid="1539048263178882562">"Hefja upptöku"</string>
<string name="screenrecord_mic_label" msgid="6134198080740031632">"Taka upp talsetningu"</string>
<string name="screenrecord_taps_label" msgid="2518244240225925076">"Sýna snertingar"</string>
+ <string name="screenrecord_stop_text" msgid="6549288689506057686">"Ýttu til að stöðva"</string>
<string name="screenrecord_stop_label" msgid="72699670052087989">"Stöðva"</string>
<string name="screenrecord_pause_label" msgid="6004054907104549857">"Hlé"</string>
<string name="screenrecord_resume_label" msgid="4972223043729555575">"Halda áfram"</string>
@@ -388,6 +390,8 @@
<string name="quick_settings_dark_mode_secondary_label_battery_saver" msgid="4990712734503013251">"Rafhlöðusparnaður"</string>
<string name="quick_settings_dark_mode_secondary_label_on_at_sunset" msgid="6017379738102015710">"Kveikt við sólsetur"</string>
<string name="quick_settings_dark_mode_secondary_label_until_sunrise" msgid="4404885070316716472">"Til sólarupprásar"</string>
+ <string name="quick_settings_dark_mode_secondary_label_on_at" msgid="5128758823486361279">"Virkt kl. <xliff:g id="TIME">%s</xliff:g>"</string>
+ <string name="quick_settings_dark_mode_secondary_label_until" msgid="2289774641256492437">"Til <xliff:g id="TIME">%s</xliff:g>"</string>
<string name="quick_settings_nfc_label" msgid="1054317416221168085">"NFC"</string>
<string name="quick_settings_nfc_off" msgid="3465000058515424663">"Slökkt á NFC"</string>
<string name="quick_settings_nfc_on" msgid="1004976611203202230">"Kveikt á NFC"</string>
diff --git a/packages/SystemUI/res/values-it/strings.xml b/packages/SystemUI/res/values-it/strings.xml
index bdf93fa93e15..870e6b7a75e7 100644
--- a/packages/SystemUI/res/values-it/strings.xml
+++ b/packages/SystemUI/res/values-it/strings.xml
@@ -71,6 +71,7 @@
<string name="compat_mode_on" msgid="4963711187149440884">"Zoom per riempire schermo"</string>
<string name="compat_mode_off" msgid="7682459748279487945">"Estendi per riemp. schermo"</string>
<string name="global_action_screenshot" msgid="2760267567509131654">"Screenshot"</string>
+ <string name="remote_input_image_insertion_text" msgid="4613177882724332877">"Immagine inserita"</string>
<string name="screenshot_saving_ticker" msgid="6519186952674544916">"Salvataggio screenshot..."</string>
<string name="screenshot_saving_title" msgid="2298349784913287333">"Salvataggio screenshot..."</string>
<string name="screenshot_saved_title" msgid="8893267638659083153">"Screenshot salvato"</string>
@@ -84,6 +85,7 @@
<string name="screenrecord_start_label" msgid="1539048263178882562">"Avvia registrazione"</string>
<string name="screenrecord_mic_label" msgid="6134198080740031632">"Registra voce fuori campo"</string>
<string name="screenrecord_taps_label" msgid="2518244240225925076">"Mostra tocchi"</string>
+ <string name="screenrecord_stop_text" msgid="6549288689506057686">"Tocca per interrompere"</string>
<string name="screenrecord_stop_label" msgid="72699670052087989">"Interrompi"</string>
<string name="screenrecord_pause_label" msgid="6004054907104549857">"Pausa"</string>
<string name="screenrecord_resume_label" msgid="4972223043729555575">"Riprendi"</string>
@@ -388,6 +390,8 @@
<string name="quick_settings_dark_mode_secondary_label_battery_saver" msgid="4990712734503013251">"Risparmio energetico"</string>
<string name="quick_settings_dark_mode_secondary_label_on_at_sunset" msgid="6017379738102015710">"Attivato al tramonto"</string>
<string name="quick_settings_dark_mode_secondary_label_until_sunrise" msgid="4404885070316716472">"Fino all\'alba"</string>
+ <string name="quick_settings_dark_mode_secondary_label_on_at" msgid="5128758823486361279">"Attivazione alle <xliff:g id="TIME">%s</xliff:g>"</string>
+ <string name="quick_settings_dark_mode_secondary_label_until" msgid="2289774641256492437">"Fino alle <xliff:g id="TIME">%s</xliff:g>"</string>
<string name="quick_settings_nfc_label" msgid="1054317416221168085">"NFC"</string>
<string name="quick_settings_nfc_off" msgid="3465000058515424663">"NFC non attiva"</string>
<string name="quick_settings_nfc_on" msgid="1004976611203202230">"NFC attiva"</string>
diff --git a/packages/SystemUI/res/values-iw/strings.xml b/packages/SystemUI/res/values-iw/strings.xml
index 9cfd0b3a22f6..775be02e20e9 100644
--- a/packages/SystemUI/res/values-iw/strings.xml
+++ b/packages/SystemUI/res/values-iw/strings.xml
@@ -71,6 +71,7 @@
<string name="compat_mode_on" msgid="4963711187149440884">"הגדל תצוגה כדי למלא את המסך"</string>
<string name="compat_mode_off" msgid="7682459748279487945">"מתח כדי למלא את המסך"</string>
<string name="global_action_screenshot" msgid="2760267567509131654">"צילום מסך"</string>
+ <string name="remote_input_image_insertion_text" msgid="4613177882724332877">"התמונה התווספה"</string>
<string name="screenshot_saving_ticker" msgid="6519186952674544916">"שומר צילום מסך..."</string>
<string name="screenshot_saving_title" msgid="2298349784913287333">"שומר צילום מסך..."</string>
<string name="screenshot_saved_title" msgid="8893267638659083153">"צילום המסך נשמר"</string>
@@ -84,6 +85,7 @@
<string name="screenrecord_start_label" msgid="1539048263178882562">"הפעלת ההקלטה"</string>
<string name="screenrecord_mic_label" msgid="6134198080740031632">"‏הקלטת voiceover"</string>
<string name="screenrecord_taps_label" msgid="2518244240225925076">"הצגת הקשות"</string>
+ <string name="screenrecord_stop_text" msgid="6549288689506057686">"אפשר להקיש כדי להפסיק"</string>
<string name="screenrecord_stop_label" msgid="72699670052087989">"עצירה"</string>
<string name="screenrecord_pause_label" msgid="6004054907104549857">"השהיה"</string>
<string name="screenrecord_resume_label" msgid="4972223043729555575">"המשך"</string>
@@ -392,15 +394,14 @@
<string name="quick_settings_dark_mode_secondary_label_battery_saver" msgid="4990712734503013251">"חיסכון בסוללה"</string>
<string name="quick_settings_dark_mode_secondary_label_on_at_sunset" msgid="6017379738102015710">"מופעל בשקיעה"</string>
<string name="quick_settings_dark_mode_secondary_label_until_sunrise" msgid="4404885070316716472">"עד הזריחה"</string>
+ <string name="quick_settings_dark_mode_secondary_label_on_at" msgid="5128758823486361279">"יתחיל בשעה <xliff:g id="TIME">%s</xliff:g>"</string>
+ <string name="quick_settings_dark_mode_secondary_label_until" msgid="2289774641256492437">"עד <xliff:g id="TIME">%s</xliff:g>"</string>
<string name="quick_settings_nfc_label" msgid="1054317416221168085">"NFC"</string>
<string name="quick_settings_nfc_off" msgid="3465000058515424663">"‏NFC מושבת"</string>
<string name="quick_settings_nfc_on" msgid="1004976611203202230">"‏NFC מופעל"</string>
- <!-- no translation found for quick_settings_screen_record_label (1594046461509776676) -->
- <skip />
- <!-- no translation found for quick_settings_screen_record_start (1574725369331638985) -->
- <skip />
- <!-- no translation found for quick_settings_screen_record_stop (8087348522976412119) -->
- <skip />
+ <string name="quick_settings_screen_record_label" msgid="1594046461509776676">"הקלטת המסך"</string>
+ <string name="quick_settings_screen_record_start" msgid="1574725369331638985">"התחלה"</string>
+ <string name="quick_settings_screen_record_stop" msgid="8087348522976412119">"עצירה"</string>
<string name="recents_swipe_up_onboarding" msgid="2820265886420993995">"יש להחליק מעלה כדי להחליף אפליקציות"</string>
<string name="recents_quick_scrub_onboarding" msgid="765934300283514912">"יש לגרור ימינה כדי לעבור במהירות בין אפליקציות"</string>
<string name="quick_step_accessibility_toggle_overview" msgid="7908949976727578403">"החלפת מצב של מסכים אחרונים"</string>
@@ -702,22 +703,14 @@
<string name="notification_app_settings" msgid="8963648463858039377">"התאמה אישית"</string>
<string name="notification_done" msgid="6215117625922713976">"סיום"</string>
<string name="inline_undo" msgid="9026953267645116526">"ביטול"</string>
- <!-- no translation found for demote (6225813324237153980) -->
- <skip />
- <!-- no translation found for notification_conversation_favorite (8252976467488182853) -->
- <skip />
- <!-- no translation found for notification_conversation_unfavorite (633301300443356176) -->
- <skip />
- <!-- no translation found for notification_conversation_mute (477431709687199671) -->
- <skip />
- <!-- no translation found for notification_conversation_unmute (410885000669775294) -->
- <skip />
- <!-- no translation found for notification_conversation_bubble (4598142032706190028) -->
- <skip />
- <!-- no translation found for notification_conversation_unbubble (2303087159802926401) -->
- <skip />
- <!-- no translation found for notification_conversation_home_screen (8347136037958438935) -->
- <skip />
+ <string name="demote" msgid="6225813324237153980">"סימון ההתראה הזו כהתראה שאינה משיחה"</string>
+ <string name="notification_conversation_favorite" msgid="8252976467488182853">"סימון כמועדפת"</string>
+ <string name="notification_conversation_unfavorite" msgid="633301300443356176">"הסרה מהמועדפים"</string>
+ <string name="notification_conversation_mute" msgid="477431709687199671">"השתקה"</string>
+ <string name="notification_conversation_unmute" msgid="410885000669775294">"ביטול ההשתקה"</string>
+ <string name="notification_conversation_bubble" msgid="4598142032706190028">"הצגה בתור בועה"</string>
+ <string name="notification_conversation_unbubble" msgid="2303087159802926401">"כיבוי הבועות"</string>
+ <string name="notification_conversation_home_screen" msgid="8347136037958438935">"הוספה למסך הבית"</string>
<string name="notification_menu_accessibility" msgid="8984166825879886773">"<xliff:g id="APP_NAME">%1$s</xliff:g> <xliff:g id="MENU_DESCRIPTION">%2$s</xliff:g>"</string>
<string name="notification_menu_gear_description" msgid="6429668976593634862">"בקרת התראות"</string>
<string name="notification_menu_snooze_description" msgid="4740133348901973244">"אפשרויות של דחיית התראות לטיפול בהמשך"</string>
diff --git a/packages/SystemUI/res/values-ja/strings.xml b/packages/SystemUI/res/values-ja/strings.xml
index 0c57372f3ba9..7af3f8631f03 100644
--- a/packages/SystemUI/res/values-ja/strings.xml
+++ b/packages/SystemUI/res/values-ja/strings.xml
@@ -71,6 +71,7 @@
<string name="compat_mode_on" msgid="4963711187149440884">"画面サイズに合わせて拡大"</string>
<string name="compat_mode_off" msgid="7682459748279487945">"画面サイズに合わせて拡大"</string>
<string name="global_action_screenshot" msgid="2760267567509131654">"スクリーンショット"</string>
+ <string name="remote_input_image_insertion_text" msgid="4613177882724332877">"画像を挿入しました"</string>
<string name="screenshot_saving_ticker" msgid="6519186952674544916">"スクリーンショットを保存中..."</string>
<string name="screenshot_saving_title" msgid="2298349784913287333">"スクリーンショットを保存しています..."</string>
<string name="screenshot_saved_title" msgid="8893267638659083153">"スクリーンショットを保存しました"</string>
@@ -84,6 +85,7 @@
<string name="screenrecord_start_label" msgid="1539048263178882562">"録画を開始"</string>
<string name="screenrecord_mic_label" msgid="6134198080740031632">"ナレーションの録音"</string>
<string name="screenrecord_taps_label" msgid="2518244240225925076">"タップの表示"</string>
+ <string name="screenrecord_stop_text" msgid="6549288689506057686">"タップして停止"</string>
<string name="screenrecord_stop_label" msgid="72699670052087989">"停止"</string>
<string name="screenrecord_pause_label" msgid="6004054907104549857">"一時停止"</string>
<string name="screenrecord_resume_label" msgid="4972223043729555575">"再開"</string>
@@ -388,15 +390,14 @@
<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">"日の入りに ON"</string>
<string name="quick_settings_dark_mode_secondary_label_until_sunrise" msgid="4404885070316716472">"日の出まで"</string>
+ <string name="quick_settings_dark_mode_secondary_label_on_at" msgid="5128758823486361279">"<xliff:g id="TIME">%s</xliff:g>にオン"</string>
+ <string name="quick_settings_dark_mode_secondary_label_until" msgid="2289774641256492437">"<xliff:g id="TIME">%s</xliff:g>まで"</string>
<string name="quick_settings_nfc_label" msgid="1054317416221168085">"NFC"</string>
<string name="quick_settings_nfc_off" msgid="3465000058515424663">"NFC は無効です"</string>
<string name="quick_settings_nfc_on" msgid="1004976611203202230">"NFC は有効です"</string>
- <!-- no translation found for quick_settings_screen_record_label (1594046461509776676) -->
- <skip />
- <!-- no translation found for quick_settings_screen_record_start (1574725369331638985) -->
- <skip />
- <!-- no translation found for quick_settings_screen_record_stop (8087348522976412119) -->
- <skip />
+ <string name="quick_settings_screen_record_label" msgid="1594046461509776676">"スクリーン レコード"</string>
+ <string name="quick_settings_screen_record_start" msgid="1574725369331638985">"開始"</string>
+ <string name="quick_settings_screen_record_stop" msgid="8087348522976412119">"停止"</string>
<string name="recents_swipe_up_onboarding" msgid="2820265886420993995">"アプリを切り替えるには上にスワイプ"</string>
<string name="recents_quick_scrub_onboarding" msgid="765934300283514912">"右にドラッグするとアプリを素早く切り替えることができます"</string>
<string name="quick_step_accessibility_toggle_overview" msgid="7908949976727578403">"概要を切り替え"</string>
@@ -696,22 +697,14 @@
<string name="notification_app_settings" msgid="8963648463858039377">"カスタマイズ"</string>
<string name="notification_done" msgid="6215117625922713976">"完了"</string>
<string name="inline_undo" msgid="9026953267645116526">"元に戻す"</string>
- <!-- no translation found for demote (6225813324237153980) -->
- <skip />
- <!-- no translation found for notification_conversation_favorite (8252976467488182853) -->
- <skip />
- <!-- no translation found for notification_conversation_unfavorite (633301300443356176) -->
- <skip />
- <!-- no translation found for notification_conversation_mute (477431709687199671) -->
- <skip />
- <!-- no translation found for notification_conversation_unmute (410885000669775294) -->
- <skip />
- <!-- no translation found for notification_conversation_bubble (4598142032706190028) -->
- <skip />
- <!-- no translation found for notification_conversation_unbubble (2303087159802926401) -->
- <skip />
- <!-- no translation found for notification_conversation_home_screen (8347136037958438935) -->
- <skip />
+ <string name="demote" msgid="6225813324237153980">"この通知を会話ではないとマーク"</string>
+ <string name="notification_conversation_favorite" msgid="8252976467488182853">"お気に入り"</string>
+ <string name="notification_conversation_unfavorite" msgid="633301300443356176">"お気に入りから削除"</string>
+ <string name="notification_conversation_mute" msgid="477431709687199671">"ミュート"</string>
+ <string name="notification_conversation_unmute" msgid="410885000669775294">"ミュートを解除"</string>
+ <string name="notification_conversation_bubble" msgid="4598142032706190028">"ふきだしとして表示"</string>
+ <string name="notification_conversation_unbubble" msgid="2303087159802926401">"ふきだしを OFF にする"</string>
+ <string name="notification_conversation_home_screen" msgid="8347136037958438935">"ホーム画面に追加"</string>
<string name="notification_menu_accessibility" msgid="8984166825879886773">"<xliff:g id="APP_NAME">%1$s</xliff:g> <xliff:g id="MENU_DESCRIPTION">%2$s</xliff:g>"</string>
<string name="notification_menu_gear_description" msgid="6429668976593634862">"通知管理"</string>
<string name="notification_menu_snooze_description" msgid="4740133348901973244">"通知スヌーズ設定"</string>
diff --git a/packages/SystemUI/res/values-ka/strings.xml b/packages/SystemUI/res/values-ka/strings.xml
index a3827d2a183b..2433f9e32e8f 100644
--- a/packages/SystemUI/res/values-ka/strings.xml
+++ b/packages/SystemUI/res/values-ka/strings.xml
@@ -71,6 +71,7 @@
<string name="compat_mode_on" msgid="4963711187149440884">"მასშტაბი შეცვალეთ ეკრანის შესავსებად."</string>
<string name="compat_mode_off" msgid="7682459748279487945">"გაწიეთ ეკრანის შესავსებად."</string>
<string name="global_action_screenshot" msgid="2760267567509131654">"ეკრანის ანაბეჭდი"</string>
+ <string name="remote_input_image_insertion_text" msgid="4613177882724332877">"სურათი ჩასმულია"</string>
<string name="screenshot_saving_ticker" msgid="6519186952674544916">"სკრინშოტის შენახვა…"</string>
<string name="screenshot_saving_title" msgid="2298349784913287333">"ეკრანის სურათის შენახვა…"</string>
<string name="screenshot_saved_title" msgid="8893267638659083153">"ეკრანის ანაბეჭდი შენახულია"</string>
@@ -84,6 +85,7 @@
<string name="screenrecord_start_label" msgid="1539048263178882562">"ჩაწერის დაწყება"</string>
<string name="screenrecord_mic_label" msgid="6134198080740031632">"ხმის ჩაწერა"</string>
<string name="screenrecord_taps_label" msgid="2518244240225925076">"შეხებების ჩვენება"</string>
+ <string name="screenrecord_stop_text" msgid="6549288689506057686">"შეეხეთ შესაწყვეტად"</string>
<string name="screenrecord_stop_label" msgid="72699670052087989">"შეწყვეტა"</string>
<string name="screenrecord_pause_label" msgid="6004054907104549857">"პაუზა"</string>
<string name="screenrecord_resume_label" msgid="4972223043729555575">"გაგრძელება"</string>
@@ -388,15 +390,14 @@
<string name="quick_settings_dark_mode_secondary_label_battery_saver" msgid="4990712734503013251">"ბატარეის დამზოგი"</string>
<string name="quick_settings_dark_mode_secondary_label_on_at_sunset" msgid="6017379738102015710">"ჩაირთოს მზის ჩასვლისას"</string>
<string name="quick_settings_dark_mode_secondary_label_until_sunrise" msgid="4404885070316716472">"მზის ამოსვლამდე"</string>
+ <string name="quick_settings_dark_mode_secondary_label_on_at" msgid="5128758823486361279">"ჩაირთოს <xliff:g id="TIME">%s</xliff:g>-ზე"</string>
+ <string name="quick_settings_dark_mode_secondary_label_until" msgid="2289774641256492437">"<xliff:g id="TIME">%s</xliff:g>-მდე"</string>
<string name="quick_settings_nfc_label" msgid="1054317416221168085">"NFC"</string>
<string name="quick_settings_nfc_off" msgid="3465000058515424663">"NFC გათიშულია"</string>
<string name="quick_settings_nfc_on" msgid="1004976611203202230">"NFC ჩართულია"</string>
- <!-- no translation found for quick_settings_screen_record_label (1594046461509776676) -->
- <skip />
- <!-- no translation found for quick_settings_screen_record_start (1574725369331638985) -->
- <skip />
- <!-- no translation found for quick_settings_screen_record_stop (8087348522976412119) -->
- <skip />
+ <string name="quick_settings_screen_record_label" msgid="1594046461509776676">"ეკრანის ჩანაწერი"</string>
+ <string name="quick_settings_screen_record_start" msgid="1574725369331638985">"დაწყება"</string>
+ <string name="quick_settings_screen_record_stop" msgid="8087348522976412119">"შეწყვეტა"</string>
<string name="recents_swipe_up_onboarding" msgid="2820265886420993995">"გადაფურცლეთ ზემოთ აპების გადასართავად"</string>
<string name="recents_quick_scrub_onboarding" msgid="765934300283514912">"აპების სწრაფად გადასართავად ჩავლებით გადაიტანეთ მარჯვნივ"</string>
<string name="quick_step_accessibility_toggle_overview" msgid="7908949976727578403">"მიმოხილვის გადართვა"</string>
@@ -696,22 +697,14 @@
<string name="notification_app_settings" msgid="8963648463858039377">"მორგება"</string>
<string name="notification_done" msgid="6215117625922713976">"მზადაა"</string>
<string name="inline_undo" msgid="9026953267645116526">"მოქმედების გაუქმება"</string>
- <!-- no translation found for demote (6225813324237153980) -->
- <skip />
- <!-- no translation found for notification_conversation_favorite (8252976467488182853) -->
- <skip />
- <!-- no translation found for notification_conversation_unfavorite (633301300443356176) -->
- <skip />
- <!-- no translation found for notification_conversation_mute (477431709687199671) -->
- <skip />
- <!-- no translation found for notification_conversation_unmute (410885000669775294) -->
- <skip />
- <!-- no translation found for notification_conversation_bubble (4598142032706190028) -->
- <skip />
- <!-- no translation found for notification_conversation_unbubble (2303087159802926401) -->
- <skip />
- <!-- no translation found for notification_conversation_home_screen (8347136037958438935) -->
- <skip />
+ <string name="demote" msgid="6225813324237153980">"ამ შეტყობინების მონიშვნა, როგორც არა მიმოწერის"</string>
+ <string name="notification_conversation_favorite" msgid="8252976467488182853">"რჩეული"</string>
+ <string name="notification_conversation_unfavorite" msgid="633301300443356176">"რჩეულებიდან ამოღება"</string>
+ <string name="notification_conversation_mute" msgid="477431709687199671">"დადუმება"</string>
+ <string name="notification_conversation_unmute" msgid="410885000669775294">"დადუმების მოხსნა"</string>
+ <string name="notification_conversation_bubble" msgid="4598142032706190028">"ბუშტის სახით ჩვენება"</string>
+ <string name="notification_conversation_unbubble" msgid="2303087159802926401">"ბუშტების გამორთვა"</string>
+ <string name="notification_conversation_home_screen" msgid="8347136037958438935">"მთავარ ეკრანზე დამატება"</string>
<string name="notification_menu_accessibility" msgid="8984166825879886773">"<xliff:g id="APP_NAME">%1$s</xliff:g> <xliff:g id="MENU_DESCRIPTION">%2$s</xliff:g>"</string>
<string name="notification_menu_gear_description" msgid="6429668976593634862">"შეტყობინებების მართვის საშუალებები"</string>
<string name="notification_menu_snooze_description" msgid="4740133348901973244">"შეტყობინებების ჩაჩუმების ვარიანტები"</string>
diff --git a/packages/SystemUI/res/values-kk/strings.xml b/packages/SystemUI/res/values-kk/strings.xml
index 19ae4e2a02d7..be93c18006b9 100644
--- a/packages/SystemUI/res/values-kk/strings.xml
+++ b/packages/SystemUI/res/values-kk/strings.xml
@@ -71,6 +71,7 @@
<string name="compat_mode_on" msgid="4963711187149440884">"Экранды толтыру үшін ұлғайту"</string>
<string name="compat_mode_off" msgid="7682459748279487945">"Экранды толтыру үшін созу"</string>
<string name="global_action_screenshot" msgid="2760267567509131654">"Скриншот"</string>
+ <string name="remote_input_image_insertion_text" msgid="4613177882724332877">"Сурет енгізілді"</string>
<string name="screenshot_saving_ticker" msgid="6519186952674544916">"Скриншотты сақтауда…"</string>
<string name="screenshot_saving_title" msgid="2298349784913287333">"Скриншотты сақтауда…"</string>
<string name="screenshot_saved_title" msgid="8893267638659083153">"Скриншот сақталды"</string>
@@ -84,6 +85,7 @@
<string name="screenrecord_start_label" msgid="1539048263178882562">"Жазуды бастау"</string>
<string name="screenrecord_mic_label" msgid="6134198080740031632">"Кадр сыртындағы дыбысты жазу"</string>
<string name="screenrecord_taps_label" msgid="2518244240225925076">"Түрту әрекеттерін көрсету"</string>
+ <string name="screenrecord_stop_text" msgid="6549288689506057686">"Тоқтату үшін түртіңіз"</string>
<string name="screenrecord_stop_label" msgid="72699670052087989">"Тоқтату"</string>
<string name="screenrecord_pause_label" msgid="6004054907104549857">"Тоқтата тұру"</string>
<string name="screenrecord_resume_label" msgid="4972223043729555575">"Жалғастыру"</string>
@@ -388,15 +390,14 @@
<string name="quick_settings_dark_mode_secondary_label_battery_saver" msgid="4990712734503013251">"Battery Saver"</string>
<string name="quick_settings_dark_mode_secondary_label_on_at_sunset" msgid="6017379738102015710">"Күн батқанда қосу"</string>
<string name="quick_settings_dark_mode_secondary_label_until_sunrise" msgid="4404885070316716472">"Күн шыққанға дейін"</string>
+ <string name="quick_settings_dark_mode_secondary_label_on_at" msgid="5128758823486361279">"Қосылу уақыты: <xliff:g id="TIME">%s</xliff:g>"</string>
+ <string name="quick_settings_dark_mode_secondary_label_until" msgid="2289774641256492437">"<xliff:g id="TIME">%s</xliff:g> дейін"</string>
<string name="quick_settings_nfc_label" msgid="1054317416221168085">"NFC"</string>
<string name="quick_settings_nfc_off" msgid="3465000058515424663">"NFC өшірулі"</string>
<string name="quick_settings_nfc_on" msgid="1004976611203202230">"NFC қосулы"</string>
- <!-- no translation found for quick_settings_screen_record_label (1594046461509776676) -->
- <skip />
- <!-- no translation found for quick_settings_screen_record_start (1574725369331638985) -->
- <skip />
- <!-- no translation found for quick_settings_screen_record_stop (8087348522976412119) -->
- <skip />
+ <string name="quick_settings_screen_record_label" msgid="1594046461509776676">"Экранды жазу"</string>
+ <string name="quick_settings_screen_record_start" msgid="1574725369331638985">"Бастау"</string>
+ <string name="quick_settings_screen_record_stop" msgid="8087348522976412119">"Тоқтату"</string>
<string name="recents_swipe_up_onboarding" msgid="2820265886420993995">"Қолданбалар арасында ауысу үшін жоғары сырғытыңыз"</string>
<string name="recents_quick_scrub_onboarding" msgid="765934300283514912">"Қолданбаларды жылдам ауыстырып қосу үшін оңға қарай сүйреңіз"</string>
<string name="quick_step_accessibility_toggle_overview" msgid="7908949976727578403">"Шолуды қосу/өшіру"</string>
@@ -696,22 +697,14 @@
<string name="notification_app_settings" msgid="8963648463858039377">"Реттеу"</string>
<string name="notification_done" msgid="6215117625922713976">"Дайын"</string>
<string name="inline_undo" msgid="9026953267645116526">"Қайтару"</string>
- <!-- no translation found for demote (6225813324237153980) -->
- <skip />
- <!-- no translation found for notification_conversation_favorite (8252976467488182853) -->
- <skip />
- <!-- no translation found for notification_conversation_unfavorite (633301300443356176) -->
- <skip />
- <!-- no translation found for notification_conversation_mute (477431709687199671) -->
- <skip />
- <!-- no translation found for notification_conversation_unmute (410885000669775294) -->
- <skip />
- <!-- no translation found for notification_conversation_bubble (4598142032706190028) -->
- <skip />
- <!-- no translation found for notification_conversation_unbubble (2303087159802926401) -->
- <skip />
- <!-- no translation found for notification_conversation_home_screen (8347136037958438935) -->
- <skip />
+ <string name="demote" msgid="6225813324237153980">"Хабарландыруды сөйлесу емес деп белгілеу"</string>
+ <string name="notification_conversation_favorite" msgid="8252976467488182853">"Таңдаулы"</string>
+ <string name="notification_conversation_unfavorite" msgid="633301300443356176">"Таңдаулылар тізімінен шығару"</string>
+ <string name="notification_conversation_mute" msgid="477431709687199671">"Дыбысын өшіру"</string>
+ <string name="notification_conversation_unmute" msgid="410885000669775294">"Дыбысын қосу"</string>
+ <string name="notification_conversation_bubble" msgid="4598142032706190028">"Қалқымалы анықтама ретінде көрсету"</string>
+ <string name="notification_conversation_unbubble" msgid="2303087159802926401">"Қалқымалы анықтамаларды өшіру"</string>
+ <string name="notification_conversation_home_screen" msgid="8347136037958438935">"Негізгі экранға қосу"</string>
<string name="notification_menu_accessibility" msgid="8984166825879886773">"<xliff:g id="APP_NAME">%1$s</xliff:g> <xliff:g id="MENU_DESCRIPTION">%2$s</xliff:g>"</string>
<string name="notification_menu_gear_description" msgid="6429668976593634862">"хабарландыруларды басқару элементтері"</string>
<string name="notification_menu_snooze_description" msgid="4740133348901973244">"хабарландыруды кідірту опциялары"</string>
diff --git a/packages/SystemUI/res/values-km/strings.xml b/packages/SystemUI/res/values-km/strings.xml
index d6ec177d3909..413125cefb92 100644
--- a/packages/SystemUI/res/values-km/strings.xml
+++ b/packages/SystemUI/res/values-km/strings.xml
@@ -71,6 +71,7 @@
<string name="compat_mode_on" msgid="4963711187149440884">"ពង្រីក​​ដើម្បី​ឲ្យ​ពេញ​អេក្រង់"</string>
<string name="compat_mode_off" msgid="7682459748279487945">"ទាញ​ដើម្បី​ឲ្យ​ពេញ​អេក្រង់"</string>
<string name="global_action_screenshot" msgid="2760267567509131654">"រូបថតអេក្រង់"</string>
+ <string name="remote_input_image_insertion_text" msgid="4613177882724332877">"បាន​បញ្ចូល​រូបភាព"</string>
<string name="screenshot_saving_ticker" msgid="6519186952674544916">"កំពុង​រក្សាទុក​រូបថត​អេក្រង់…"</string>
<string name="screenshot_saving_title" msgid="2298349784913287333">"កំពុង​រក្សាទុក​រូបថត​អេក្រង់..."</string>
<string name="screenshot_saved_title" msgid="8893267638659083153">"បានរក្សាទុក​រូបថតអេក្រង់"</string>
@@ -84,6 +85,7 @@
<string name="screenrecord_start_label" msgid="1539048263178882562">"ចាប់​ផ្តើមថត"</string>
<string name="screenrecord_mic_label" msgid="6134198080740031632">"ថត​ការបញ្ចូល​សំឡេង"</string>
<string name="screenrecord_taps_label" msgid="2518244240225925076">"បង្ហាញការចុច"</string>
+ <string name="screenrecord_stop_text" msgid="6549288689506057686">"ចុច ដើម្បីបញ្ឈប់"</string>
<string name="screenrecord_stop_label" msgid="72699670052087989">"ឈប់"</string>
<string name="screenrecord_pause_label" msgid="6004054907104549857">"ផ្អាក"</string>
<string name="screenrecord_resume_label" msgid="4972223043729555575">"បន្ត"</string>
@@ -388,15 +390,14 @@
<string name="quick_settings_dark_mode_secondary_label_battery_saver" msgid="4990712734503013251">"កម្មវិធីសន្សំថ្ម"</string>
<string name="quick_settings_dark_mode_secondary_label_on_at_sunset" msgid="6017379738102015710">"បើក​នៅពេល​ថ្ងៃលិច"</string>
<string name="quick_settings_dark_mode_secondary_label_until_sunrise" msgid="4404885070316716472">"រហូត​ដល់​ពេល​ថ្ងៃរះ"</string>
+ <string name="quick_settings_dark_mode_secondary_label_on_at" msgid="5128758823486361279">"បើកនៅម៉ោង <xliff:g id="TIME">%s</xliff:g>"</string>
+ <string name="quick_settings_dark_mode_secondary_label_until" msgid="2289774641256492437">"រហូតដល់ម៉ោង <xliff:g id="TIME">%s</xliff:g>"</string>
<string name="quick_settings_nfc_label" msgid="1054317416221168085">"NFC"</string>
<string name="quick_settings_nfc_off" msgid="3465000058515424663">"បាន​បិទ NFC"</string>
<string name="quick_settings_nfc_on" msgid="1004976611203202230">"បាន​បើក NFC"</string>
- <!-- no translation found for quick_settings_screen_record_label (1594046461509776676) -->
- <skip />
- <!-- no translation found for quick_settings_screen_record_start (1574725369331638985) -->
- <skip />
- <!-- no translation found for quick_settings_screen_record_stop (8087348522976412119) -->
- <skip />
+ <string name="quick_settings_screen_record_label" msgid="1594046461509776676">"ការថត​អេក្រង់"</string>
+ <string name="quick_settings_screen_record_start" msgid="1574725369331638985">"ចាប់ផ្ដើម"</string>
+ <string name="quick_settings_screen_record_stop" msgid="8087348522976412119">"ឈប់"</string>
<string name="recents_swipe_up_onboarding" msgid="2820265886420993995">"អូស​ឡើង​លើ​ដើម្បី​ប្តូរ​កម្មវិធី"</string>
<string name="recents_quick_scrub_onboarding" msgid="765934300283514912">"អូសទៅស្ដាំដើម្បីប្ដូរកម្មវិធីបានរហ័ស"</string>
<string name="quick_step_accessibility_toggle_overview" msgid="7908949976727578403">"បិទ/បើក​ទិដ្ឋភាពរួម"</string>
@@ -696,22 +697,14 @@
<string name="notification_app_settings" msgid="8963648463858039377">"ប្ដូរតាមបំណង"</string>
<string name="notification_done" msgid="6215117625922713976">"រួចរាល់"</string>
<string name="inline_undo" msgid="9026953267645116526">"ត្រឡប់វិញ"</string>
- <!-- no translation found for demote (6225813324237153980) -->
- <skip />
- <!-- no translation found for notification_conversation_favorite (8252976467488182853) -->
- <skip />
- <!-- no translation found for notification_conversation_unfavorite (633301300443356176) -->
- <skip />
- <!-- no translation found for notification_conversation_mute (477431709687199671) -->
- <skip />
- <!-- no translation found for notification_conversation_unmute (410885000669775294) -->
- <skip />
- <!-- no translation found for notification_conversation_bubble (4598142032706190028) -->
- <skip />
- <!-- no translation found for notification_conversation_unbubble (2303087159802926401) -->
- <skip />
- <!-- no translation found for notification_conversation_home_screen (8347136037958438935) -->
- <skip />
+ <string name="demote" msgid="6225813324237153980">"សម្គាល់​ការជូន​ដំណឹង​នេះ​ថា​មិនមែនជា​ការសន្ទនា"</string>
+ <string name="notification_conversation_favorite" msgid="8252976467488182853">"សំណព្វ"</string>
+ <string name="notification_conversation_unfavorite" msgid="633301300443356176">"ដក​ចេញ​ពី​សំណព្វ"</string>
+ <string name="notification_conversation_mute" msgid="477431709687199671">"បិទ​សំឡេង"</string>
+ <string name="notification_conversation_unmute" msgid="410885000669775294">"បើក​សំឡេង"</string>
+ <string name="notification_conversation_bubble" msgid="4598142032706190028">"បង្ហាញ​ជា​សារ​លេចឡើង"</string>
+ <string name="notification_conversation_unbubble" msgid="2303087159802926401">"បិទ​សារ​លេចឡើង"</string>
+ <string name="notification_conversation_home_screen" msgid="8347136037958438935">"បញ្ចូល​ទៅក្នុង​អេក្រង់​ដើម"</string>
<string name="notification_menu_accessibility" msgid="8984166825879886773">"<xliff:g id="APP_NAME">%1$s</xliff:g> <xliff:g id="MENU_DESCRIPTION">%2$s</xliff:g>"</string>
<string name="notification_menu_gear_description" msgid="6429668976593634862">"ការគ្រប់គ្រង​ការជូន​ដំណឹង"</string>
<string name="notification_menu_snooze_description" msgid="4740133348901973244">"ជម្រើស​ផ្អាកការ​ជូនដំណឹង"</string>
diff --git a/packages/SystemUI/res/values-kn/strings.xml b/packages/SystemUI/res/values-kn/strings.xml
index fc0f8e6c8093..0b98e0655a7e 100644
--- a/packages/SystemUI/res/values-kn/strings.xml
+++ b/packages/SystemUI/res/values-kn/strings.xml
@@ -71,6 +71,7 @@
<string name="compat_mode_on" msgid="4963711187149440884">"ಪರದೆ ತುಂಬಿಸಲು ಝೂಮ್ ಮಾಡು"</string>
<string name="compat_mode_off" msgid="7682459748279487945">"ಪರದೆ ತುಂಬಿಸಲು ವಿಸ್ತಾರಗೊಳಿಸು"</string>
<string name="global_action_screenshot" msgid="2760267567509131654">"ಸ್ಕ್ರೀನ್‌ಶಾಟ್"</string>
+ <string name="remote_input_image_insertion_text" msgid="4613177882724332877">"ಚಿತ್ರವನ್ನು ಸೇರಿಸಲಾಗಿದೆ"</string>
<string name="screenshot_saving_ticker" msgid="6519186952674544916">"ಸ್ಕ್ರೀನ್‌ಶಾಟ್ ಉಳಿಸಲಾಗುತ್ತಿದೆ…"</string>
<string name="screenshot_saving_title" msgid="2298349784913287333">"ಸ್ಕ್ರೀನ್‌ಶಾಟ್ ಉಳಿಸಲಾಗುತ್ತಿದೆ…"</string>
<string name="screenshot_saved_title" msgid="8893267638659083153">"ಸ್ಕ್ರೀನ್‌ಶಾಟ್‌ ಅನ್ನು ಉಳಿಸಲಾಗಿದೆ"</string>
@@ -84,6 +85,7 @@
<string name="screenrecord_start_label" msgid="1539048263178882562">"ರೆಕಾರ್ಡಿಂಗ್ ಆರಂಭಿಸಿ"</string>
<string name="screenrecord_mic_label" msgid="6134198080740031632">"voiceover ಅನ್ನು ರೆಕಾರ್ಡ್ ಮಾಡಿ"</string>
<string name="screenrecord_taps_label" msgid="2518244240225925076">"ಟ್ಯಾಪ್‌ಗಳನ್ನು ತೋರಿಸಿ"</string>
+ <string name="screenrecord_stop_text" msgid="6549288689506057686">"ನಿಲ್ಲಿಸಲು ಟ್ಯಾಪ್ ಮಾಡಿ"</string>
<string name="screenrecord_stop_label" msgid="72699670052087989">"ನಿಲ್ಲಿಸಿ"</string>
<string name="screenrecord_pause_label" msgid="6004054907104549857">"ವಿರಾಮಗೊಳಿಸಿ"</string>
<string name="screenrecord_resume_label" msgid="4972223043729555575">"ಮುಂದುವರಿಸಿ"</string>
@@ -388,15 +390,14 @@
<string name="quick_settings_dark_mode_secondary_label_battery_saver" msgid="4990712734503013251">"ಬ್ಯಾಟರಿ ಸೇವರ್"</string>
<string name="quick_settings_dark_mode_secondary_label_on_at_sunset" msgid="6017379738102015710">"ಸೂರ್ಯಾಸ್ತ ಸಮಯದಲ್ಲಿ"</string>
<string name="quick_settings_dark_mode_secondary_label_until_sunrise" msgid="4404885070316716472">"ಸೂರ್ಯೋದಯದವರೆಗೆ"</string>
+ <string name="quick_settings_dark_mode_secondary_label_on_at" msgid="5128758823486361279">"<xliff:g id="TIME">%s</xliff:g> ಸಮಯದಲ್ಲಿ"</string>
+ <string name="quick_settings_dark_mode_secondary_label_until" msgid="2289774641256492437">"<xliff:g id="TIME">%s</xliff:g> ವರೆಗೂ"</string>
<string name="quick_settings_nfc_label" msgid="1054317416221168085">"NFC"</string>
<string name="quick_settings_nfc_off" msgid="3465000058515424663">"NFC ನಿಷ್ಕ್ರಿಯಗೊಂಡಿದೆ"</string>
<string name="quick_settings_nfc_on" msgid="1004976611203202230">"NFC ಸಕ್ರಿಯಗೊಂಡಿದೆ"</string>
- <!-- no translation found for quick_settings_screen_record_label (1594046461509776676) -->
- <skip />
- <!-- no translation found for quick_settings_screen_record_start (1574725369331638985) -->
- <skip />
- <!-- no translation found for quick_settings_screen_record_stop (8087348522976412119) -->
- <skip />
+ <string name="quick_settings_screen_record_label" msgid="1594046461509776676">"ಸ್ಕ್ರೀನ್ ರೆಕಾರ್ಡ್"</string>
+ <string name="quick_settings_screen_record_start" msgid="1574725369331638985">"ಪ್ರಾರಂಭಿಸಿ"</string>
+ <string name="quick_settings_screen_record_stop" msgid="8087348522976412119">"ನಿಲ್ಲಿಸಿ"</string>
<string name="recents_swipe_up_onboarding" msgid="2820265886420993995">"ಅಪ್ಲಿಕೇಶನ್‌ಗಳನ್ನು ಬದಲಾಯಿಸಲು ಸ್ವೈಪ್ ಮಾಡಿ"</string>
<string name="recents_quick_scrub_onboarding" msgid="765934300283514912">"ಅಪ್ಲಿಕೇಶನ್‌ಗಳನ್ನು ಬದಲಿಸಲು ತ್ವರಿತವಾಗಿ ಬಲಕ್ಕೆ ಡ್ರ್ಯಾಗ್ ಮಾಡಿ"</string>
<string name="quick_step_accessibility_toggle_overview" msgid="7908949976727578403">"ಟಾಗಲ್ ನ ಅವಲೋಕನ"</string>
@@ -696,22 +697,14 @@
<string name="notification_app_settings" msgid="8963648463858039377">"ಕಸ್ಟಮೈಜ್‌ ಮಾಡಿ"</string>
<string name="notification_done" msgid="6215117625922713976">"ಮುಗಿದಿದೆ"</string>
<string name="inline_undo" msgid="9026953267645116526">"ರದ್ದುಮಾಡಿ"</string>
- <!-- no translation found for demote (6225813324237153980) -->
- <skip />
- <!-- no translation found for notification_conversation_favorite (8252976467488182853) -->
- <skip />
- <!-- no translation found for notification_conversation_unfavorite (633301300443356176) -->
- <skip />
- <!-- no translation found for notification_conversation_mute (477431709687199671) -->
- <skip />
- <!-- no translation found for notification_conversation_unmute (410885000669775294) -->
- <skip />
- <!-- no translation found for notification_conversation_bubble (4598142032706190028) -->
- <skip />
- <!-- no translation found for notification_conversation_unbubble (2303087159802926401) -->
- <skip />
- <!-- no translation found for notification_conversation_home_screen (8347136037958438935) -->
- <skip />
+ <string name="demote" msgid="6225813324237153980">"ಈ ಅಧಿಸೂಚನೆಯನ್ನು ಸಂಭಾಷಣೆಯಲ್ಲ ಎಂಬುದಾಗಿ ಗುರುತಿಸಿ"</string>
+ <string name="notification_conversation_favorite" msgid="8252976467488182853">"ಮೆಚ್ಚಿನದು"</string>
+ <string name="notification_conversation_unfavorite" msgid="633301300443356176">"ಮೆಚ್ಚಿನದಲ್ಲದ್ದು"</string>
+ <string name="notification_conversation_mute" msgid="477431709687199671">"ಮ್ಯೂಟ್ ಮಾಡಿ"</string>
+ <string name="notification_conversation_unmute" msgid="410885000669775294">"ಅನ್‌ಮ್ಯೂಟ್ ಮಾಡಿ"</string>
+ <string name="notification_conversation_bubble" msgid="4598142032706190028">"ಗುಳ್ಳೆಯಾಗಿ ತೋರಿಸಿ"</string>
+ <string name="notification_conversation_unbubble" msgid="2303087159802926401">"ಗುಳ್ಳೆಗಳನ್ನು ಆಫ್ ಮಾಡಿ"</string>
+ <string name="notification_conversation_home_screen" msgid="8347136037958438935">"ಮುಖಪುಟದ ಪರದೆಗೆ ಸೇರಿಸಿ"</string>
<string name="notification_menu_accessibility" msgid="8984166825879886773">"<xliff:g id="APP_NAME">%1$s</xliff:g> <xliff:g id="MENU_DESCRIPTION">%2$s</xliff:g>"</string>
<string name="notification_menu_gear_description" msgid="6429668976593634862">"ಅಧಿಸೂಚನೆ ನಿಯಂತ್ರಣಗಳು"</string>
<string name="notification_menu_snooze_description" msgid="4740133348901973244">"ಅಧಿಸೂಚನೆ ಸ್ನೂಜ್ ಆಯ್ಕೆಗಳು"</string>
diff --git a/packages/SystemUI/res/values-ko/strings.xml b/packages/SystemUI/res/values-ko/strings.xml
index eb3ebc918edb..54b055dd674b 100644
--- a/packages/SystemUI/res/values-ko/strings.xml
+++ b/packages/SystemUI/res/values-ko/strings.xml
@@ -71,6 +71,7 @@
<string name="compat_mode_on" msgid="4963711187149440884">"전체화면 모드로 확대"</string>
<string name="compat_mode_off" msgid="7682459748279487945">"전체화면 모드로 확대"</string>
<string name="global_action_screenshot" msgid="2760267567509131654">"스크린샷"</string>
+ <string name="remote_input_image_insertion_text" msgid="4613177882724332877">"이미지 삽입됨"</string>
<string name="screenshot_saving_ticker" msgid="6519186952674544916">"캡쳐화면 저장 중..."</string>
<string name="screenshot_saving_title" msgid="2298349784913287333">"캡쳐화면 저장 중..."</string>
<string name="screenshot_saved_title" msgid="8893267638659083153">"스크린샷 저장됨"</string>
@@ -84,6 +85,7 @@
<string name="screenrecord_start_label" msgid="1539048263178882562">"녹화 시작"</string>
<string name="screenrecord_mic_label" msgid="6134198080740031632">"음성 해설 녹음"</string>
<string name="screenrecord_taps_label" msgid="2518244240225925076">"탭한 항목 표시"</string>
+ <string name="screenrecord_stop_text" msgid="6549288689506057686">"탭하여 중지하기"</string>
<string name="screenrecord_stop_label" msgid="72699670052087989">"중지"</string>
<string name="screenrecord_pause_label" msgid="6004054907104549857">"일시중지"</string>
<string name="screenrecord_resume_label" msgid="4972223043729555575">"재개"</string>
@@ -388,15 +390,14 @@
<string name="quick_settings_dark_mode_secondary_label_battery_saver" msgid="4990712734503013251">"절전 모드"</string>
<string name="quick_settings_dark_mode_secondary_label_on_at_sunset" msgid="6017379738102015710">"일몰에"</string>
<string name="quick_settings_dark_mode_secondary_label_until_sunrise" msgid="4404885070316716472">"일출까지"</string>
+ <string name="quick_settings_dark_mode_secondary_label_on_at" msgid="5128758823486361279">"<xliff:g id="TIME">%s</xliff:g>에"</string>
+ <string name="quick_settings_dark_mode_secondary_label_until" msgid="2289774641256492437">"<xliff:g id="TIME">%s</xliff:g>까지"</string>
<string name="quick_settings_nfc_label" msgid="1054317416221168085">"NFC"</string>
<string name="quick_settings_nfc_off" msgid="3465000058515424663">"NFC 사용 중지됨"</string>
<string name="quick_settings_nfc_on" msgid="1004976611203202230">"NFC 사용 설정됨"</string>
- <!-- no translation found for quick_settings_screen_record_label (1594046461509776676) -->
- <skip />
- <!-- no translation found for quick_settings_screen_record_start (1574725369331638985) -->
- <skip />
- <!-- no translation found for quick_settings_screen_record_stop (8087348522976412119) -->
- <skip />
+ <string name="quick_settings_screen_record_label" msgid="1594046461509776676">"화면 녹화"</string>
+ <string name="quick_settings_screen_record_start" msgid="1574725369331638985">"시작"</string>
+ <string name="quick_settings_screen_record_stop" msgid="8087348522976412119">"중지"</string>
<string name="recents_swipe_up_onboarding" msgid="2820265886420993995">"위로 스와이프하여 앱 전환"</string>
<string name="recents_quick_scrub_onboarding" msgid="765934300283514912">"앱을 빠르게 전환하려면 오른쪽으로 드래그"</string>
<string name="quick_step_accessibility_toggle_overview" msgid="7908949976727578403">"최근 사용 버튼 전환"</string>
@@ -696,22 +697,14 @@
<string name="notification_app_settings" msgid="8963648463858039377">"맞춤설정"</string>
<string name="notification_done" msgid="6215117625922713976">"완료"</string>
<string name="inline_undo" msgid="9026953267645116526">"실행취소"</string>
- <!-- no translation found for demote (6225813324237153980) -->
- <skip />
- <!-- no translation found for notification_conversation_favorite (8252976467488182853) -->
- <skip />
- <!-- no translation found for notification_conversation_unfavorite (633301300443356176) -->
- <skip />
- <!-- no translation found for notification_conversation_mute (477431709687199671) -->
- <skip />
- <!-- no translation found for notification_conversation_unmute (410885000669775294) -->
- <skip />
- <!-- no translation found for notification_conversation_bubble (4598142032706190028) -->
- <skip />
- <!-- no translation found for notification_conversation_unbubble (2303087159802926401) -->
- <skip />
- <!-- no translation found for notification_conversation_home_screen (8347136037958438935) -->
- <skip />
+ <string name="demote" msgid="6225813324237153980">"이 알림을 대화가 아닌 일반 알림으로 표시"</string>
+ <string name="notification_conversation_favorite" msgid="8252976467488182853">"즐겨찾기"</string>
+ <string name="notification_conversation_unfavorite" msgid="633301300443356176">"즐겨찾기 해제"</string>
+ <string name="notification_conversation_mute" msgid="477431709687199671">"숨기기"</string>
+ <string name="notification_conversation_unmute" msgid="410885000669775294">"숨기기 취소"</string>
+ <string name="notification_conversation_bubble" msgid="4598142032706190028">"버블로 표시"</string>
+ <string name="notification_conversation_unbubble" msgid="2303087159802926401">"버블 사용 중지"</string>
+ <string name="notification_conversation_home_screen" msgid="8347136037958438935">"홈 화면에 추가"</string>
<string name="notification_menu_accessibility" msgid="8984166825879886773">"<xliff:g id="APP_NAME">%1$s</xliff:g> <xliff:g id="MENU_DESCRIPTION">%2$s</xliff:g>"</string>
<string name="notification_menu_gear_description" msgid="6429668976593634862">"알림 관리"</string>
<string name="notification_menu_snooze_description" msgid="4740133348901973244">"알림 일시 중지 옵션"</string>
diff --git a/packages/SystemUI/res/values-ky/strings.xml b/packages/SystemUI/res/values-ky/strings.xml
index 06f81614d1e2..ed6b9186cdba 100644
--- a/packages/SystemUI/res/values-ky/strings.xml
+++ b/packages/SystemUI/res/values-ky/strings.xml
@@ -71,6 +71,7 @@
<string name="compat_mode_on" msgid="4963711187149440884">"Экрнд тлтр ү. чен өлч өзг"</string>
<string name="compat_mode_off" msgid="7682459748279487945">"Экранды толтуруу ү-н чоюу"</string>
<string name="global_action_screenshot" msgid="2760267567509131654">"Скриншот"</string>
+ <string name="remote_input_image_insertion_text" msgid="4613177882724332877">"Сүрөт киргизилди"</string>
<string name="screenshot_saving_ticker" msgid="6519186952674544916">"Скриншот сакталууда…"</string>
<string name="screenshot_saving_title" msgid="2298349784913287333">"Скриншот сакталууда..."</string>
<string name="screenshot_saved_title" msgid="8893267638659083153">"Скриншот сакталды"</string>
@@ -84,6 +85,7 @@
<string name="screenrecord_start_label" msgid="1539048263178882562">"Жаздырып баштоо"</string>
<string name="screenrecord_mic_label" msgid="6134198080740031632">"Үн коштоону жаздыруу"</string>
<string name="screenrecord_taps_label" msgid="2518244240225925076">"Басылган жерди көрсөтүү"</string>
+ <string name="screenrecord_stop_text" msgid="6549288689506057686">"Токтотуш үчүн басыңыз"</string>
<string name="screenrecord_stop_label" msgid="72699670052087989">"Токтотуу"</string>
<string name="screenrecord_pause_label" msgid="6004054907104549857">"Тындыруу"</string>
<string name="screenrecord_resume_label" msgid="4972223043729555575">"Улантуу"</string>
@@ -388,15 +390,14 @@
<string name="quick_settings_dark_mode_secondary_label_battery_saver" msgid="4990712734503013251">"Батареяны үнөмдөгүч"</string>
<string name="quick_settings_dark_mode_secondary_label_on_at_sunset" msgid="6017379738102015710">"Күн батканда күйөт"</string>
<string name="quick_settings_dark_mode_secondary_label_until_sunrise" msgid="4404885070316716472">"Күн чыкканга чейин"</string>
+ <string name="quick_settings_dark_mode_secondary_label_on_at" msgid="5128758823486361279">"Саат <xliff:g id="TIME">%s</xliff:g> күйөт"</string>
+ <string name="quick_settings_dark_mode_secondary_label_until" msgid="2289774641256492437">"<xliff:g id="TIME">%s</xliff:g> чейин"</string>
<string name="quick_settings_nfc_label" msgid="1054317416221168085">"NFC"</string>
<string name="quick_settings_nfc_off" msgid="3465000058515424663">"NFC өчүрүлгөн"</string>
<string name="quick_settings_nfc_on" msgid="1004976611203202230">"NFC иштетилген"</string>
- <!-- no translation found for quick_settings_screen_record_label (1594046461509776676) -->
- <skip />
- <!-- no translation found for quick_settings_screen_record_start (1574725369331638985) -->
- <skip />
- <!-- no translation found for quick_settings_screen_record_stop (8087348522976412119) -->
- <skip />
+ <string name="quick_settings_screen_record_label" msgid="1594046461509776676">"Экранды жаздыруу"</string>
+ <string name="quick_settings_screen_record_start" msgid="1574725369331638985">"Старт"</string>
+ <string name="quick_settings_screen_record_stop" msgid="8087348522976412119">"Токтотуу"</string>
<string name="recents_swipe_up_onboarding" msgid="2820265886420993995">"Колдонмолорду которуштуруу үчүн өйдө сүрүңүз"</string>
<string name="recents_quick_scrub_onboarding" msgid="765934300283514912">"Колдонмолорду тез которуштуруу үчүн оңго сүйрөңүз"</string>
<string name="quick_step_accessibility_toggle_overview" msgid="7908949976727578403">"Сереп салууну өчүрүү/күйгүзүү"</string>
@@ -444,7 +445,7 @@
<string name="guest_new_guest" msgid="962155336259570156">"Конок кошуу"</string>
<string name="guest_exit_guest" msgid="4030840507598850886">"Конокту алып салуу"</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_message" msgid="8183450985628495709">"Бул сеанстагы бардык колдонмолор жана дайындар өчүрүлөт."</string>
<string name="guest_exit_guest_dialog_remove" msgid="7505817591242703757">"Алып салуу"</string>
<string name="guest_wipe_session_title" msgid="7147965814683990944">"Кайтып келишиңиз менен, конок!"</string>
<string name="guest_wipe_session_message" msgid="3393823610257065457">"Сеансыңызды улантасызбы?"</string>
@@ -464,7 +465,7 @@
<item quantity="one">Бир колдонуучуну гана кошууга болот.</item>
</plurals>
<string name="user_remove_user_title" msgid="9124124694835811874">"Колдонуучу алынып салынсынбы?"</string>
- <string name="user_remove_user_message" msgid="6702834122128031833">"Бул колдонуучунун бардык колдонмолору жана дайындары жок кылынат."</string>
+ <string name="user_remove_user_message" msgid="6702834122128031833">"Бул колдонуучунун бардык колдонмолору жана дайындары өчүрүлөт."</string>
<string name="user_remove_user_remove" msgid="8387386066949061256">"Алып салуу"</string>
<string name="battery_saver_notification_title" msgid="8419266546034372562">"Батареяны үнөмдөгүч режими күйүк"</string>
<string name="battery_saver_notification_text" msgid="2617841636449016951">"Иштин майнаптуулугун начарлатып, фондук дайындарды чектейт"</string>
@@ -696,22 +697,14 @@
<string name="notification_app_settings" msgid="8963648463858039377">"Ыңгайлаштыруу"</string>
<string name="notification_done" msgid="6215117625922713976">"Бүттү"</string>
<string name="inline_undo" msgid="9026953267645116526">"Кайтаруу"</string>
- <!-- no translation found for demote (6225813324237153980) -->
- <skip />
- <!-- no translation found for notification_conversation_favorite (8252976467488182853) -->
- <skip />
- <!-- no translation found for notification_conversation_unfavorite (633301300443356176) -->
- <skip />
- <!-- no translation found for notification_conversation_mute (477431709687199671) -->
- <skip />
- <!-- no translation found for notification_conversation_unmute (410885000669775294) -->
- <skip />
- <!-- no translation found for notification_conversation_bubble (4598142032706190028) -->
- <skip />
- <!-- no translation found for notification_conversation_unbubble (2303087159802926401) -->
- <skip />
- <!-- no translation found for notification_conversation_home_screen (8347136037958438935) -->
- <skip />
+ <string name="demote" msgid="6225813324237153980">"Бул билдирме \"жазышуу эмес\" катары белгиленсин"</string>
+ <string name="notification_conversation_favorite" msgid="8252976467488182853">"Сүйүктүүлөргө кошуу"</string>
+ <string name="notification_conversation_unfavorite" msgid="633301300443356176">"Сүйүктүүлөрдөн чыгаруу"</string>
+ <string name="notification_conversation_mute" msgid="477431709687199671">"Үнүн басуу"</string>
+ <string name="notification_conversation_unmute" msgid="410885000669775294">"Үнүн чыгаруу"</string>
+ <string name="notification_conversation_bubble" msgid="4598142032706190028">"Көбүк катары көрсөтүү"</string>
+ <string name="notification_conversation_unbubble" msgid="2303087159802926401">"Көбүктөрдү өчүрүү"</string>
+ <string name="notification_conversation_home_screen" msgid="8347136037958438935">"Башкы экранга кошуу"</string>
<string name="notification_menu_accessibility" msgid="8984166825879886773">"<xliff:g id="APP_NAME">%1$s</xliff:g> <xliff:g id="MENU_DESCRIPTION">%2$s</xliff:g>"</string>
<string name="notification_menu_gear_description" msgid="6429668976593634862">"эскертмелерди башкаруу каражаттары"</string>
<string name="notification_menu_snooze_description" msgid="4740133348901973244">"эскертмени тындыруу опциялары"</string>
diff --git a/packages/SystemUI/res/values-lo/strings.xml b/packages/SystemUI/res/values-lo/strings.xml
index 3013e1e94c34..11c4f7c45433 100644
--- a/packages/SystemUI/res/values-lo/strings.xml
+++ b/packages/SystemUI/res/values-lo/strings.xml
@@ -71,6 +71,7 @@
<string name="compat_mode_on" msgid="4963711187149440884">"ຊູມໃຫ້ເຕັມໜ້າຈໍ"</string>
<string name="compat_mode_off" msgid="7682459748279487945">"ປັບໃຫ້ເຕັມໜ້າຈໍ"</string>
<string name="global_action_screenshot" msgid="2760267567509131654">"ພາບໜ້າຈໍ"</string>
+ <string name="remote_input_image_insertion_text" msgid="4613177882724332877">"ແຊກຮູບແລ້ວ"</string>
<string name="screenshot_saving_ticker" msgid="6519186952674544916">"ກຳລັງບັນທຶກຮູບໜ້າຈໍ"</string>
<string name="screenshot_saving_title" msgid="2298349784913287333">"ກຳລັງບັນທຶກພາບໜ້າຈໍ..."</string>
<string name="screenshot_saved_title" msgid="8893267638659083153">"ບັນທຶກຮູບໜ້າຈໍໄວ້ແລ້ວ"</string>
@@ -84,6 +85,7 @@
<string name="screenrecord_start_label" msgid="1539048263178882562">"ເລີ່ມການບັນທຶກ"</string>
<string name="screenrecord_mic_label" msgid="6134198080740031632">"ບັນທຶກສຽງພາກ"</string>
<string name="screenrecord_taps_label" msgid="2518244240225925076">"ສະແດງການແຕະ"</string>
+ <string name="screenrecord_stop_text" msgid="6549288689506057686">"ແຕະ​ເພື່ອ​ຢຸດ"</string>
<string name="screenrecord_stop_label" msgid="72699670052087989">"ຢຸດ"</string>
<string name="screenrecord_pause_label" msgid="6004054907104549857">"ຢຸດຊົ່ວຄາວ"</string>
<string name="screenrecord_resume_label" msgid="4972223043729555575">"ສືບຕໍ່"</string>
@@ -388,15 +390,14 @@
<string name="quick_settings_dark_mode_secondary_label_battery_saver" msgid="4990712734503013251">"ຕົວປະຢັດແບັດເຕີຣີ"</string>
<string name="quick_settings_dark_mode_secondary_label_on_at_sunset" msgid="6017379738102015710">"ເປີດຕອນຕາເວັນຕົກ"</string>
<string name="quick_settings_dark_mode_secondary_label_until_sunrise" msgid="4404885070316716472">"ຈົນກວ່າຕາເວັນຂຶ້ນ"</string>
+ <string name="quick_settings_dark_mode_secondary_label_on_at" msgid="5128758823486361279">"ເປີດເວລາ <xliff:g id="TIME">%s</xliff:g>"</string>
+ <string name="quick_settings_dark_mode_secondary_label_until" msgid="2289774641256492437">"ຈົນຮອດ <xliff:g id="TIME">%s</xliff:g>"</string>
<string name="quick_settings_nfc_label" msgid="1054317416221168085">"NFC"</string>
<string name="quick_settings_nfc_off" msgid="3465000058515424663">"NFC is disabled"</string>
<string name="quick_settings_nfc_on" msgid="1004976611203202230">"NFC is enabled"</string>
- <!-- no translation found for quick_settings_screen_record_label (1594046461509776676) -->
- <skip />
- <!-- no translation found for quick_settings_screen_record_start (1574725369331638985) -->
- <skip />
- <!-- no translation found for quick_settings_screen_record_stop (8087348522976412119) -->
- <skip />
+ <string name="quick_settings_screen_record_label" msgid="1594046461509776676">"ການບັນທຶກໜ້າຈໍ"</string>
+ <string name="quick_settings_screen_record_start" msgid="1574725369331638985">"ເລີ່ມ"</string>
+ <string name="quick_settings_screen_record_stop" msgid="8087348522976412119">"ຢຸດ"</string>
<string name="recents_swipe_up_onboarding" msgid="2820265886420993995">"ປັດຂື້ນເພື່ອສະຫຼັບແອັບ"</string>
<string name="recents_quick_scrub_onboarding" msgid="765934300283514912">"ລາກໄປຂວາເພື່ອສະຫຼັບແອັບດ່ວນ"</string>
<string name="quick_step_accessibility_toggle_overview" msgid="7908949976727578403">"ສະຫຼັບພາບຮວມ"</string>
@@ -696,22 +697,14 @@
<string name="notification_app_settings" msgid="8963648463858039377">"ປັບແຕ່ງ"</string>
<string name="notification_done" msgid="6215117625922713976">"ສຳເລັດແລ້ວ"</string>
<string name="inline_undo" msgid="9026953267645116526">"ຍົກເລີກ"</string>
- <!-- no translation found for demote (6225813324237153980) -->
- <skip />
- <!-- no translation found for notification_conversation_favorite (8252976467488182853) -->
- <skip />
- <!-- no translation found for notification_conversation_unfavorite (633301300443356176) -->
- <skip />
- <!-- no translation found for notification_conversation_mute (477431709687199671) -->
- <skip />
- <!-- no translation found for notification_conversation_unmute (410885000669775294) -->
- <skip />
- <!-- no translation found for notification_conversation_bubble (4598142032706190028) -->
- <skip />
- <!-- no translation found for notification_conversation_unbubble (2303087159802926401) -->
- <skip />
- <!-- no translation found for notification_conversation_home_screen (8347136037958438935) -->
- <skip />
+ <string name="demote" msgid="6225813324237153980">"ໝາຍການແຈ້ງເຕືອນນີ້ວ່າບໍ່ແມ່ນການສົນທະນາ"</string>
+ <string name="notification_conversation_favorite" msgid="8252976467488182853">"ລາຍ​ການ​ທີ່​ມັກ"</string>
+ <string name="notification_conversation_unfavorite" msgid="633301300443356176">"ຍົກເລີກລາຍການທີ່ມັກ"</string>
+ <string name="notification_conversation_mute" msgid="477431709687199671">"ປິດສຽງ"</string>
+ <string name="notification_conversation_unmute" msgid="410885000669775294">"ເຊົາປິດສຽງ"</string>
+ <string name="notification_conversation_bubble" msgid="4598142032706190028">"ສະແດງເປັນ bubble"</string>
+ <string name="notification_conversation_unbubble" msgid="2303087159802926401">"ປິດການໃຊ້ bubble"</string>
+ <string name="notification_conversation_home_screen" msgid="8347136037958438935">"ເພີ່ມໃສ່ໜ້າຈໍຫຼັກ"</string>
<string name="notification_menu_accessibility" msgid="8984166825879886773">"<xliff:g id="APP_NAME">%1$s</xliff:g> <xliff:g id="MENU_DESCRIPTION">%2$s</xliff:g>"</string>
<string name="notification_menu_gear_description" msgid="6429668976593634862">"ການຄວບຄຸມການແຈ້ງເຕືອນ"</string>
<string name="notification_menu_snooze_description" msgid="4740133348901973244">"ຕົວເລືອກການເລື່ອນການແຈ້ງເຕືອນ"</string>
diff --git a/packages/SystemUI/res/values-lt/strings.xml b/packages/SystemUI/res/values-lt/strings.xml
index c6b50d92e3ed..7da1174c0878 100644
--- a/packages/SystemUI/res/values-lt/strings.xml
+++ b/packages/SystemUI/res/values-lt/strings.xml
@@ -71,6 +71,7 @@
<string name="compat_mode_on" msgid="4963711187149440884">"Keisti mast., kad atit. ekr."</string>
<string name="compat_mode_off" msgid="7682459748279487945">"Ištempti, kad atit. ekr."</string>
<string name="global_action_screenshot" msgid="2760267567509131654">"Ekrano kopija"</string>
+ <string name="remote_input_image_insertion_text" msgid="4613177882724332877">"Vaizdas įterptas"</string>
<string name="screenshot_saving_ticker" msgid="6519186952674544916">"Išsaugoma ekrano kopija..."</string>
<string name="screenshot_saving_title" msgid="2298349784913287333">"Išsaugoma ekrano kopija..."</string>
<string name="screenshot_saved_title" msgid="8893267638659083153">"Ekrano kopija išsaugota"</string>
@@ -84,6 +85,7 @@
<string name="screenrecord_start_label" msgid="1539048263178882562">"Pradėti įrašymą"</string>
<string name="screenrecord_mic_label" msgid="6134198080740031632">"Įrašyti balsą už kadro"</string>
<string name="screenrecord_taps_label" msgid="2518244240225925076">"Rodyti palietimus"</string>
+ <string name="screenrecord_stop_text" msgid="6549288689506057686">"Sustabdykite palietę"</string>
<string name="screenrecord_stop_label" msgid="72699670052087989">"Sustabdyti"</string>
<string name="screenrecord_pause_label" msgid="6004054907104549857">"Pristabdyti"</string>
<string name="screenrecord_resume_label" msgid="4972223043729555575">"Tęsti"</string>
@@ -392,6 +394,8 @@
<string name="quick_settings_dark_mode_secondary_label_battery_saver" msgid="4990712734503013251">"Akum. taus. priemonė"</string>
<string name="quick_settings_dark_mode_secondary_label_on_at_sunset" msgid="6017379738102015710">"Per saulėlydį"</string>
<string name="quick_settings_dark_mode_secondary_label_until_sunrise" msgid="4404885070316716472">"Iki saulėtekio"</string>
+ <string name="quick_settings_dark_mode_secondary_label_on_at" msgid="5128758823486361279">"<xliff:g id="TIME">%s</xliff:g>"</string>
+ <string name="quick_settings_dark_mode_secondary_label_until" msgid="2289774641256492437">"Iki <xliff:g id="TIME">%s</xliff:g>"</string>
<string name="quick_settings_nfc_label" msgid="1054317416221168085">"ALR"</string>
<string name="quick_settings_nfc_off" msgid="3465000058515424663">"ALR išjungtas"</string>
<string name="quick_settings_nfc_on" msgid="1004976611203202230">"ALR įjungtas"</string>
diff --git a/packages/SystemUI/res/values-lv/strings.xml b/packages/SystemUI/res/values-lv/strings.xml
index b519f053b1ce..fed86b324c78 100644
--- a/packages/SystemUI/res/values-lv/strings.xml
+++ b/packages/SystemUI/res/values-lv/strings.xml
@@ -71,6 +71,7 @@
<string name="compat_mode_on" msgid="4963711187149440884">"Tālumm., lai aizp. ekr."</string>
<string name="compat_mode_off" msgid="7682459748279487945">"Stiepiet, lai aizp. ekr."</string>
<string name="global_action_screenshot" msgid="2760267567509131654">"Ekrānuzņēmums"</string>
+ <string name="remote_input_image_insertion_text" msgid="4613177882724332877">"Attēls ir ievietots"</string>
<string name="screenshot_saving_ticker" msgid="6519186952674544916">"Saglabā ekrānuzņēmumu…"</string>
<string name="screenshot_saving_title" msgid="2298349784913287333">"Notiek ekrānuzņēmuma saglabāšana..."</string>
<string name="screenshot_saved_title" msgid="8893267638659083153">"Ekrānuzņēmums saglabāts"</string>
@@ -84,6 +85,7 @@
<string name="screenrecord_start_label" msgid="1539048263178882562">"Sākt ierakstīšanu"</string>
<string name="screenrecord_mic_label" msgid="6134198080740031632">"Ierakstīt balsi"</string>
<string name="screenrecord_taps_label" msgid="2518244240225925076">"Rādīt pieskārienus"</string>
+ <string name="screenrecord_stop_text" msgid="6549288689506057686">"Pieskarieties, lai apturētu"</string>
<string name="screenrecord_stop_label" msgid="72699670052087989">"Pārtraukt"</string>
<string name="screenrecord_pause_label" msgid="6004054907104549857">"Apturēt"</string>
<string name="screenrecord_resume_label" msgid="4972223043729555575">"Atsākt"</string>
@@ -390,15 +392,14 @@
<string name="quick_settings_dark_mode_secondary_label_battery_saver" msgid="4990712734503013251">"Jaudas taupīšana"</string>
<string name="quick_settings_dark_mode_secondary_label_on_at_sunset" msgid="6017379738102015710">"Saulrietā"</string>
<string name="quick_settings_dark_mode_secondary_label_until_sunrise" msgid="4404885070316716472">"Līdz saullēktam"</string>
+ <string name="quick_settings_dark_mode_secondary_label_on_at" msgid="5128758823486361279">"Plkst. <xliff:g id="TIME">%s</xliff:g>"</string>
+ <string name="quick_settings_dark_mode_secondary_label_until" msgid="2289774641256492437">"Līdz plkst. <xliff:g id="TIME">%s</xliff:g>"</string>
<string name="quick_settings_nfc_label" msgid="1054317416221168085">"NFC"</string>
<string name="quick_settings_nfc_off" msgid="3465000058515424663">"NFC ir atspējoti"</string>
<string name="quick_settings_nfc_on" msgid="1004976611203202230">"NFC ir iespējoti"</string>
- <!-- no translation found for quick_settings_screen_record_label (1594046461509776676) -->
- <skip />
- <!-- no translation found for quick_settings_screen_record_start (1574725369331638985) -->
- <skip />
- <!-- no translation found for quick_settings_screen_record_stop (8087348522976412119) -->
- <skip />
+ <string name="quick_settings_screen_record_label" msgid="1594046461509776676">"Ekrāna ierakstīšana"</string>
+ <string name="quick_settings_screen_record_start" msgid="1574725369331638985">"Sākt"</string>
+ <string name="quick_settings_screen_record_stop" msgid="8087348522976412119">"Apturēt"</string>
<string name="recents_swipe_up_onboarding" msgid="2820265886420993995">"Velciet augšup, lai pārslēgtu lietotnes"</string>
<string name="recents_quick_scrub_onboarding" msgid="765934300283514912">"Lai ātri pārslēgtu lietotnes, velciet pa labi"</string>
<string name="quick_step_accessibility_toggle_overview" msgid="7908949976727578403">"Pārskata pārslēgšana"</string>
@@ -699,22 +700,14 @@
<string name="notification_app_settings" msgid="8963648463858039377">"Pielāgot"</string>
<string name="notification_done" msgid="6215117625922713976">"Gatavs"</string>
<string name="inline_undo" msgid="9026953267645116526">"Atsaukt"</string>
- <!-- no translation found for demote (6225813324237153980) -->
- <skip />
- <!-- no translation found for notification_conversation_favorite (8252976467488182853) -->
- <skip />
- <!-- no translation found for notification_conversation_unfavorite (633301300443356176) -->
- <skip />
- <!-- no translation found for notification_conversation_mute (477431709687199671) -->
- <skip />
- <!-- no translation found for notification_conversation_unmute (410885000669775294) -->
- <skip />
- <!-- no translation found for notification_conversation_bubble (4598142032706190028) -->
- <skip />
- <!-- no translation found for notification_conversation_unbubble (2303087159802926401) -->
- <skip />
- <!-- no translation found for notification_conversation_home_screen (8347136037958438935) -->
- <skip />
+ <string name="demote" msgid="6225813324237153980">"Atzīmēt, ka šis paziņojums nav saruna"</string>
+ <string name="notification_conversation_favorite" msgid="8252976467488182853">"Pievienot izlasei"</string>
+ <string name="notification_conversation_unfavorite" msgid="633301300443356176">"Noņemt no izlases"</string>
+ <string name="notification_conversation_mute" msgid="477431709687199671">"Izslēgt skaņu"</string>
+ <string name="notification_conversation_unmute" msgid="410885000669775294">"Ieslēgt skaņu"</string>
+ <string name="notification_conversation_bubble" msgid="4598142032706190028">"Rādīt kā burbuli"</string>
+ <string name="notification_conversation_unbubble" msgid="2303087159802926401">"Izslēgt burbuļus"</string>
+ <string name="notification_conversation_home_screen" msgid="8347136037958438935">"Pievienot sākuma ekrānam"</string>
<string name="notification_menu_accessibility" msgid="8984166825879886773">"<xliff:g id="APP_NAME">%1$s</xliff:g> <xliff:g id="MENU_DESCRIPTION">%2$s</xliff:g>"</string>
<string name="notification_menu_gear_description" msgid="6429668976593634862">"paziņojumu vadīklas"</string>
<string name="notification_menu_snooze_description" msgid="4740133348901973244">"paziņojumu atlikšanas opcijas"</string>
diff --git a/packages/SystemUI/res/values-mk/strings.xml b/packages/SystemUI/res/values-mk/strings.xml
index 9080b576d4a9..a02f38ed267b 100644
--- a/packages/SystemUI/res/values-mk/strings.xml
+++ b/packages/SystemUI/res/values-mk/strings.xml
@@ -71,6 +71,7 @@
<string name="compat_mode_on" msgid="4963711187149440884">"Зумирај да се исполни екранот"</string>
<string name="compat_mode_off" msgid="7682459748279487945">"Растегни да се исполни екранот"</string>
<string name="global_action_screenshot" msgid="2760267567509131654">"Слика од екранот"</string>
+ <string name="remote_input_image_insertion_text" msgid="4613177882724332877">"Вметната е слика"</string>
<string name="screenshot_saving_ticker" msgid="6519186952674544916">"Сликата на екранот се зачувува..."</string>
<string name="screenshot_saving_title" msgid="2298349784913287333">"Сликата на екранот се зачувува..."</string>
<string name="screenshot_saved_title" msgid="8893267638659083153">"Сликата од екранот е зачувана"</string>
@@ -84,6 +85,7 @@
<string name="screenrecord_start_label" msgid="1539048263178882562">"Започни со снимање"</string>
<string name="screenrecord_mic_label" msgid="6134198080740031632">"Снимај коментар"</string>
<string name="screenrecord_taps_label" msgid="2518244240225925076">"Прикажувај допири"</string>
+ <string name="screenrecord_stop_text" msgid="6549288689506057686">"Допрете за запирање"</string>
<string name="screenrecord_stop_label" msgid="72699670052087989">"Сопри"</string>
<string name="screenrecord_pause_label" msgid="6004054907104549857">"Паузирај"</string>
<string name="screenrecord_resume_label" msgid="4972223043729555575">"Продолжи"</string>
@@ -388,6 +390,8 @@
<string name="quick_settings_dark_mode_secondary_label_battery_saver" msgid="4990712734503013251">"Штедач на батерија"</string>
<string name="quick_settings_dark_mode_secondary_label_on_at_sunset" msgid="6017379738102015710">"Вклуч. на зајдисонце"</string>
<string name="quick_settings_dark_mode_secondary_label_until_sunrise" msgid="4404885070316716472">"До изгрејсонце"</string>
+ <string name="quick_settings_dark_mode_secondary_label_on_at" msgid="5128758823486361279">"Се вклучува во <xliff:g id="TIME">%s</xliff:g>"</string>
+ <string name="quick_settings_dark_mode_secondary_label_until" msgid="2289774641256492437">"До <xliff:g id="TIME">%s</xliff:g>"</string>
<string name="quick_settings_nfc_label" msgid="1054317416221168085">"NFC"</string>
<string name="quick_settings_nfc_off" msgid="3465000058515424663">"NFC е оневозможено"</string>
<string name="quick_settings_nfc_on" msgid="1004976611203202230">"NFC е овозможено"</string>
diff --git a/packages/SystemUI/res/values-ml/strings.xml b/packages/SystemUI/res/values-ml/strings.xml
index b761c445297f..2d3f73fc5978 100644
--- a/packages/SystemUI/res/values-ml/strings.xml
+++ b/packages/SystemUI/res/values-ml/strings.xml
@@ -71,6 +71,7 @@
<string name="compat_mode_on" msgid="4963711187149440884">"സ്‌ക്രീനിൽ ഉൾക്കൊള്ളിക്കാൻ സൂം ചെയ്യുക"</string>
<string name="compat_mode_off" msgid="7682459748279487945">"സ്‌ക്രീനിൽ ഉൾക്കൊള്ളിക്കാൻ വലിച്ചുനീട്ടുക"</string>
<string name="global_action_screenshot" msgid="2760267567509131654">"സ്ക്രീൻഷോട്ട്"</string>
+ <string name="remote_input_image_insertion_text" msgid="4613177882724332877">"ചിത്രം ചേർത്തു"</string>
<string name="screenshot_saving_ticker" msgid="6519186952674544916">"സ്‌ക്രീൻഷോട്ട് സംരക്ഷിക്കുന്നു..."</string>
<string name="screenshot_saving_title" msgid="2298349784913287333">"സ്‌ക്രീൻഷോട്ട് സംരക്ഷിക്കുന്നു..."</string>
<string name="screenshot_saved_title" msgid="8893267638659083153">"സ്‌ക്രീൻഷോട്ട് സംരക്ഷിച്ചു"</string>
@@ -84,6 +85,7 @@
<string name="screenrecord_start_label" msgid="1539048263178882562">"റെക്കോര്‍ഡിംഗ് ആരംഭിക്കുക"</string>
<string name="screenrecord_mic_label" msgid="6134198080740031632">"വോയ്‌സ് ഓവർ റെക്കോർഡ് ചെയ്യുക"</string>
<string name="screenrecord_taps_label" msgid="2518244240225925076">"ടാപ്പുകൾ കാണിക്കുക"</string>
+ <string name="screenrecord_stop_text" msgid="6549288689506057686">"നിർത്താൻ ടാപ്പ് ചെയ്യുക"</string>
<string name="screenrecord_stop_label" msgid="72699670052087989">"നിർത്തുക"</string>
<string name="screenrecord_pause_label" msgid="6004054907104549857">"താൽക്കാലികമായി നിർത്തുക"</string>
<string name="screenrecord_resume_label" msgid="4972223043729555575">"പുനരാരംഭിക്കുക"</string>
@@ -388,15 +390,14 @@
<string name="quick_settings_dark_mode_secondary_label_battery_saver" msgid="4990712734503013251">"ബാറ്ററി ലാഭിക്കൽ"</string>
<string name="quick_settings_dark_mode_secondary_label_on_at_sunset" msgid="6017379738102015710">"സൂര്യാസ്‌തമയത്തിന്"</string>
<string name="quick_settings_dark_mode_secondary_label_until_sunrise" msgid="4404885070316716472">"സൂര്യോദയം വരെ"</string>
+ <string name="quick_settings_dark_mode_secondary_label_on_at" msgid="5128758823486361279">"<xliff:g id="TIME">%s</xliff:g>-ന്"</string>
+ <string name="quick_settings_dark_mode_secondary_label_until" msgid="2289774641256492437">"<xliff:g id="TIME">%s</xliff:g> വരെ"</string>
<string name="quick_settings_nfc_label" msgid="1054317416221168085">"NFC"</string>
<string name="quick_settings_nfc_off" msgid="3465000058515424663">"NFC പ്രവർത്തനരഹിതമാക്കി"</string>
<string name="quick_settings_nfc_on" msgid="1004976611203202230">"NFC പ്രവർത്തനക്ഷമമാക്കി"</string>
- <!-- no translation found for quick_settings_screen_record_label (1594046461509776676) -->
- <skip />
- <!-- no translation found for quick_settings_screen_record_start (1574725369331638985) -->
- <skip />
- <!-- no translation found for quick_settings_screen_record_stop (8087348522976412119) -->
- <skip />
+ <string name="quick_settings_screen_record_label" msgid="1594046461509776676">"സ്‌ക്രീൻ റെക്കോർഡ്"</string>
+ <string name="quick_settings_screen_record_start" msgid="1574725369331638985">"ആരംഭിക്കുക"</string>
+ <string name="quick_settings_screen_record_stop" msgid="8087348522976412119">"നിര്‍ത്തുക"</string>
<string name="recents_swipe_up_onboarding" msgid="2820265886420993995">"ആപ്പുകൾ മാറാൻ മുകളിലേക്ക് സ്വൈപ്പ് ചെയ്യുക"</string>
<string name="recents_quick_scrub_onboarding" msgid="765934300283514912">"ആപ്പുകൾ പെട്ടെന്ന് മാറാൻ വലത്തോട്ട് വലിച്ചിടുക"</string>
<string name="quick_step_accessibility_toggle_overview" msgid="7908949976727578403">"അവലോകനം മാറ്റുക"</string>
@@ -696,22 +697,14 @@
<string name="notification_app_settings" msgid="8963648463858039377">"ഇഷ്‌ടാനുസൃതമാക്കുക"</string>
<string name="notification_done" msgid="6215117625922713976">"പൂർത്തിയായി"</string>
<string name="inline_undo" msgid="9026953267645116526">"പഴയപടിയാക്കുക"</string>
- <!-- no translation found for demote (6225813324237153980) -->
- <skip />
- <!-- no translation found for notification_conversation_favorite (8252976467488182853) -->
- <skip />
- <!-- no translation found for notification_conversation_unfavorite (633301300443356176) -->
- <skip />
- <!-- no translation found for notification_conversation_mute (477431709687199671) -->
- <skip />
- <!-- no translation found for notification_conversation_unmute (410885000669775294) -->
- <skip />
- <!-- no translation found for notification_conversation_bubble (4598142032706190028) -->
- <skip />
- <!-- no translation found for notification_conversation_unbubble (2303087159802926401) -->
- <skip />
- <!-- no translation found for notification_conversation_home_screen (8347136037958438935) -->
- <skip />
+ <string name="demote" msgid="6225813324237153980">"ഈ അറിയിപ്പ് സംഭാഷണമല്ലെന്ന് അടയാളപ്പെടുത്തുക"</string>
+ <string name="notification_conversation_favorite" msgid="8252976467488182853">"പ്രിയപ്പെട്ടവ"</string>
+ <string name="notification_conversation_unfavorite" msgid="633301300443356176">"പ്രിയപ്പെട്ടതല്ലാതാക്കുക"</string>
+ <string name="notification_conversation_mute" msgid="477431709687199671">"മ്യൂട്ട് ചെയ്യുക"</string>
+ <string name="notification_conversation_unmute" msgid="410885000669775294">"അൺമ്യൂട്ട് ചെയ്യുക"</string>
+ <string name="notification_conversation_bubble" msgid="4598142032706190028">"ബബ്‌ൾ ആയി കാണിക്കുക"</string>
+ <string name="notification_conversation_unbubble" msgid="2303087159802926401">"ബബിളുകൾ ഓഫാക്കുക"</string>
+ <string name="notification_conversation_home_screen" msgid="8347136037958438935">"ഹോം സ്‌ക്രീനിലേക്ക് ചേർക്കുക"</string>
<string name="notification_menu_accessibility" msgid="8984166825879886773">"<xliff:g id="APP_NAME">%1$s</xliff:g> <xliff:g id="MENU_DESCRIPTION">%2$s</xliff:g>"</string>
<string name="notification_menu_gear_description" msgid="6429668976593634862">"അറിയിപ്പ് നിയന്ത്രണങ്ങൾ"</string>
<string name="notification_menu_snooze_description" msgid="4740133348901973244">"അറിയിപ്പ് സ്‌നൂസ് ഓപ്ഷനുകൾ"</string>
diff --git a/packages/SystemUI/res/values-mn/strings.xml b/packages/SystemUI/res/values-mn/strings.xml
index 4f9b447bcd69..85d7eadce349 100644
--- a/packages/SystemUI/res/values-mn/strings.xml
+++ b/packages/SystemUI/res/values-mn/strings.xml
@@ -71,6 +71,7 @@
<string name="compat_mode_on" msgid="4963711187149440884">"Дэлгэц дүүргэх бол өсгөнө үү"</string>
<string name="compat_mode_off" msgid="7682459748279487945">"Дэлгэц дүүргэх бол татна уу"</string>
<string name="global_action_screenshot" msgid="2760267567509131654">"Дэлгэцийн зураг дарах"</string>
+ <string name="remote_input_image_insertion_text" msgid="4613177882724332877">"Зургийг орууллаа"</string>
<string name="screenshot_saving_ticker" msgid="6519186952674544916">"Дэлгэцийн агшинг хадгалж байна…"</string>
<string name="screenshot_saving_title" msgid="2298349784913287333">"Дэлгэцийн агшинг хадгалж байна…"</string>
<string name="screenshot_saved_title" msgid="8893267638659083153">"Дэлгэцээс дарсан зургийг хадгалсан"</string>
@@ -84,6 +85,7 @@
<string name="screenrecord_start_label" msgid="1539048263178882562">"Бичлэгийг эхлүүлэх"</string>
<string name="screenrecord_mic_label" msgid="6134198080740031632">"Дуу оруулалтыг бичих"</string>
<string name="screenrecord_taps_label" msgid="2518244240225925076">"Товшилтыг харуулах"</string>
+ <string name="screenrecord_stop_text" msgid="6549288689506057686">"Зогсоохын тулд товшино уу"</string>
<string name="screenrecord_stop_label" msgid="72699670052087989">"Зогсоох"</string>
<string name="screenrecord_pause_label" msgid="6004054907104549857">"Түр зогсоох"</string>
<string name="screenrecord_resume_label" msgid="4972223043729555575">"Үргэлжлүүлэх"</string>
@@ -388,15 +390,14 @@
<string name="quick_settings_dark_mode_secondary_label_battery_saver" msgid="4990712734503013251">"Батарей хэмнэгч"</string>
<string name="quick_settings_dark_mode_secondary_label_on_at_sunset" msgid="6017379738102015710">"Нар жаргах үед"</string>
<string name="quick_settings_dark_mode_secondary_label_until_sunrise" msgid="4404885070316716472">"Нар мандах хүртэл"</string>
+ <string name="quick_settings_dark_mode_secondary_label_on_at" msgid="5128758823486361279">"<xliff:g id="TIME">%s</xliff:g>-д"</string>
+ <string name="quick_settings_dark_mode_secondary_label_until" msgid="2289774641256492437">"<xliff:g id="TIME">%s</xliff:g> хүртэл"</string>
<string name="quick_settings_nfc_label" msgid="1054317416221168085">"NFC"</string>
<string name="quick_settings_nfc_off" msgid="3465000058515424663">"NFC-г цуцалсан"</string>
<string name="quick_settings_nfc_on" msgid="1004976611203202230">"NFC-г идэвхжүүлсэн"</string>
- <!-- no translation found for quick_settings_screen_record_label (1594046461509776676) -->
- <skip />
- <!-- no translation found for quick_settings_screen_record_start (1574725369331638985) -->
- <skip />
- <!-- no translation found for quick_settings_screen_record_stop (8087348522976412119) -->
- <skip />
+ <string name="quick_settings_screen_record_label" msgid="1594046461509776676">"Дэлгэцийн бичлэг хийх"</string>
+ <string name="quick_settings_screen_record_start" msgid="1574725369331638985">"Эхлүүлэх"</string>
+ <string name="quick_settings_screen_record_stop" msgid="8087348522976412119">"Зогсоох"</string>
<string name="recents_swipe_up_onboarding" msgid="2820265886420993995">"Апп сэлгэхийн тулд дээш шударна уу"</string>
<string name="recents_quick_scrub_onboarding" msgid="765934300283514912">"Аппуудыг хурдан сэлгэхийн тулд баруун тийш чирнэ үү"</string>
<string name="quick_step_accessibility_toggle_overview" msgid="7908949976727578403">"Тоймыг унтраах/асаах"</string>
@@ -696,22 +697,14 @@
<string name="notification_app_settings" msgid="8963648463858039377">"Тохируулах"</string>
<string name="notification_done" msgid="6215117625922713976">"Дууссан"</string>
<string name="inline_undo" msgid="9026953267645116526">"Болих"</string>
- <!-- no translation found for demote (6225813324237153980) -->
- <skip />
- <!-- no translation found for notification_conversation_favorite (8252976467488182853) -->
- <skip />
- <!-- no translation found for notification_conversation_unfavorite (633301300443356176) -->
- <skip />
- <!-- no translation found for notification_conversation_mute (477431709687199671) -->
- <skip />
- <!-- no translation found for notification_conversation_unmute (410885000669775294) -->
- <skip />
- <!-- no translation found for notification_conversation_bubble (4598142032706190028) -->
- <skip />
- <!-- no translation found for notification_conversation_unbubble (2303087159802926401) -->
- <skip />
- <!-- no translation found for notification_conversation_home_screen (8347136037958438935) -->
- <skip />
+ <string name="demote" msgid="6225813324237153980">"Энэ мэдэгдлийг харилцаа биш гэж тэмдэглэх"</string>
+ <string name="notification_conversation_favorite" msgid="8252976467488182853">"Дуртай"</string>
+ <string name="notification_conversation_unfavorite" msgid="633301300443356176">"Дургүй"</string>
+ <string name="notification_conversation_mute" msgid="477431709687199671">"Дууг хаах"</string>
+ <string name="notification_conversation_unmute" msgid="410885000669775294">"Дууг нээх"</string>
+ <string name="notification_conversation_bubble" msgid="4598142032706190028">"Хөөс маягаар харуулах"</string>
+ <string name="notification_conversation_unbubble" msgid="2303087159802926401">"Хөөсийг унтраах"</string>
+ <string name="notification_conversation_home_screen" msgid="8347136037958438935">"Үндсэн нүүрэнд нэмэх"</string>
<string name="notification_menu_accessibility" msgid="8984166825879886773">"<xliff:g id="APP_NAME">%1$s</xliff:g> <xliff:g id="MENU_DESCRIPTION">%2$s</xliff:g>"</string>
<string name="notification_menu_gear_description" msgid="6429668976593634862">"мэдэгдлийн удирдлага"</string>
<string name="notification_menu_snooze_description" msgid="4740133348901973244">"мэдэгдэл түр хойшлуулагчийн сонголт"</string>
diff --git a/packages/SystemUI/res/values-mr/strings.xml b/packages/SystemUI/res/values-mr/strings.xml
index 99f48e6b4cb9..3406edf3f1bd 100644
--- a/packages/SystemUI/res/values-mr/strings.xml
+++ b/packages/SystemUI/res/values-mr/strings.xml
@@ -71,6 +71,7 @@
<string name="compat_mode_on" msgid="4963711187149440884">"स्क्रीन भरण्यासाठी झूम करा"</string>
<string name="compat_mode_off" msgid="7682459748279487945">"स्क्रीन भरण्यासाठी ताणा"</string>
<string name="global_action_screenshot" msgid="2760267567509131654">"स्क्रीनशॉट"</string>
+ <string name="remote_input_image_insertion_text" msgid="4613177882724332877">"इमेज घातली गेली"</string>
<string name="screenshot_saving_ticker" msgid="6519186952674544916">"स्क्रीनशॉट सेव्ह करत आहे…"</string>
<string name="screenshot_saving_title" msgid="2298349784913287333">"स्क्रीनशॉट सेव्ह करत आहे…"</string>
<string name="screenshot_saved_title" msgid="8893267638659083153">"स्क्रीनशॉट सेव्ह केला"</string>
@@ -84,6 +85,7 @@
<string name="screenrecord_start_label" msgid="1539048263178882562">"रेकॉर्डिंग सुरू करा"</string>
<string name="screenrecord_mic_label" msgid="6134198080740031632">"व्हॉइसओव्हर रेकॉर्ड करा"</string>
<string name="screenrecord_taps_label" msgid="2518244240225925076">"टॅप दाखवा"</string>
+ <string name="screenrecord_stop_text" msgid="6549288689506057686">"थांबवण्यासाठी टॅप करा"</string>
<string name="screenrecord_stop_label" msgid="72699670052087989">"थांबवा"</string>
<string name="screenrecord_pause_label" msgid="6004054907104549857">"थांबवा"</string>
<string name="screenrecord_resume_label" msgid="4972223043729555575">"पुन्हा सुरू करा"</string>
@@ -388,15 +390,14 @@
<string name="quick_settings_dark_mode_secondary_label_battery_saver" msgid="4990712734503013251">"बॅटरी सेव्‍हर"</string>
<string name="quick_settings_dark_mode_secondary_label_on_at_sunset" msgid="6017379738102015710">"संध्याकाळी सुरू होते"</string>
<string name="quick_settings_dark_mode_secondary_label_until_sunrise" msgid="4404885070316716472">"सूर्योदयापर्यंत"</string>
+ <string name="quick_settings_dark_mode_secondary_label_on_at" msgid="5128758823486361279">"<xliff:g id="TIME">%s</xliff:g> वाजता सुरू होते"</string>
+ <string name="quick_settings_dark_mode_secondary_label_until" msgid="2289774641256492437">"<xliff:g id="TIME">%s</xliff:g> पर्यंत"</string>
<string name="quick_settings_nfc_label" msgid="1054317416221168085">"NFC"</string>
<string name="quick_settings_nfc_off" msgid="3465000058515424663">"NFC अक्षम केले आहे"</string>
<string name="quick_settings_nfc_on" msgid="1004976611203202230">"NFC सक्षम केले आहे"</string>
- <!-- no translation found for quick_settings_screen_record_label (1594046461509776676) -->
- <skip />
- <!-- no translation found for quick_settings_screen_record_start (1574725369331638985) -->
- <skip />
- <!-- no translation found for quick_settings_screen_record_stop (8087348522976412119) -->
- <skip />
+ <string name="quick_settings_screen_record_label" msgid="1594046461509776676">"स्क्रीन रेकॉर्ड"</string>
+ <string name="quick_settings_screen_record_start" msgid="1574725369331638985">"सुरू"</string>
+ <string name="quick_settings_screen_record_stop" msgid="8087348522976412119">"थांबा"</string>
<string name="recents_swipe_up_onboarding" msgid="2820265886420993995">"अ‍ॅप्स स्विच करण्यासाठी वर स्वाइप करा"</string>
<string name="recents_quick_scrub_onboarding" msgid="765934300283514912">"अ‍ॅप्स वर झटपट स्विच करण्यासाठी उजवीकडे ड्रॅग करा"</string>
<string name="quick_step_accessibility_toggle_overview" msgid="7908949976727578403">"अवलोकन टॉगल करा."</string>
@@ -696,22 +697,14 @@
<string name="notification_app_settings" msgid="8963648463858039377">"कस्टमाइझ करा"</string>
<string name="notification_done" msgid="6215117625922713976">"पूर्ण झाले"</string>
<string name="inline_undo" msgid="9026953267645116526">"पहिल्यासारखे करा"</string>
- <!-- no translation found for demote (6225813324237153980) -->
- <skip />
- <!-- no translation found for notification_conversation_favorite (8252976467488182853) -->
- <skip />
- <!-- no translation found for notification_conversation_unfavorite (633301300443356176) -->
- <skip />
- <!-- no translation found for notification_conversation_mute (477431709687199671) -->
- <skip />
- <!-- no translation found for notification_conversation_unmute (410885000669775294) -->
- <skip />
- <!-- no translation found for notification_conversation_bubble (4598142032706190028) -->
- <skip />
- <!-- no translation found for notification_conversation_unbubble (2303087159802926401) -->
- <skip />
- <!-- no translation found for notification_conversation_home_screen (8347136037958438935) -->
- <skip />
+ <string name="demote" msgid="6225813324237153980">"ही सूचना संभाषण नाही म्हणून खूण करा"</string>
+ <string name="notification_conversation_favorite" msgid="8252976467488182853">"आवडते"</string>
+ <string name="notification_conversation_unfavorite" msgid="633301300443356176">"नावडते"</string>
+ <string name="notification_conversation_mute" msgid="477431709687199671">"म्यूट करा"</string>
+ <string name="notification_conversation_unmute" msgid="410885000669775294">"अनम्यूट करा"</string>
+ <string name="notification_conversation_bubble" msgid="4598142032706190028">"बबल असे दाखवा"</string>
+ <string name="notification_conversation_unbubble" msgid="2303087159802926401">"बुडबुडे बंद करा"</string>
+ <string name="notification_conversation_home_screen" msgid="8347136037958438935">"होम स्क्रीनवर जोडा"</string>
<string name="notification_menu_accessibility" msgid="8984166825879886773">"<xliff:g id="APP_NAME">%1$s</xliff:g> <xliff:g id="MENU_DESCRIPTION">%2$s</xliff:g>"</string>
<string name="notification_menu_gear_description" msgid="6429668976593634862">"सूचना नियंत्रणे"</string>
<string name="notification_menu_snooze_description" msgid="4740133348901973244">"सूचना स्नूझ पर्याय"</string>
diff --git a/packages/SystemUI/res/values-ms/strings.xml b/packages/SystemUI/res/values-ms/strings.xml
index 42c51b7844a6..eeaaceae0669 100644
--- a/packages/SystemUI/res/values-ms/strings.xml
+++ b/packages/SystemUI/res/values-ms/strings.xml
@@ -71,6 +71,7 @@
<string name="compat_mode_on" msgid="4963711187149440884">"Zum untuk memenuhi skrin"</string>
<string name="compat_mode_off" msgid="7682459748279487945">"Regang utk memenuhi skrin"</string>
<string name="global_action_screenshot" msgid="2760267567509131654">"Tangkapan skrin"</string>
+ <string name="remote_input_image_insertion_text" msgid="4613177882724332877">"Imej disisipkan"</string>
<string name="screenshot_saving_ticker" msgid="6519186952674544916">"Menyimpan tangkapan skrin..."</string>
<string name="screenshot_saving_title" msgid="2298349784913287333">"Menyimpan tangkapan skrin..."</string>
<string name="screenshot_saved_title" msgid="8893267638659083153">"Tangkapan skrin disimpan"</string>
@@ -84,6 +85,7 @@
<string name="screenrecord_start_label" msgid="1539048263178882562">"Mula Merakam"</string>
<string name="screenrecord_mic_label" msgid="6134198080740031632">"Rakam suara latar"</string>
<string name="screenrecord_taps_label" msgid="2518244240225925076">"Tunjukkan ketikan"</string>
+ <string name="screenrecord_stop_text" msgid="6549288689506057686">"Ketik untuk berhenti"</string>
<string name="screenrecord_stop_label" msgid="72699670052087989">"Berhenti"</string>
<string name="screenrecord_pause_label" msgid="6004054907104549857">"Jeda"</string>
<string name="screenrecord_resume_label" msgid="4972223043729555575">"Sambung semula"</string>
@@ -388,15 +390,14 @@
<string name="quick_settings_dark_mode_secondary_label_battery_saver" msgid="4990712734503013251">"Penjimat Bateri"</string>
<string name="quick_settings_dark_mode_secondary_label_on_at_sunset" msgid="6017379738102015710">"Dihidupkan pd senja"</string>
<string name="quick_settings_dark_mode_secondary_label_until_sunrise" msgid="4404885070316716472">"Hingga matahari trbt"</string>
+ <string name="quick_settings_dark_mode_secondary_label_on_at" msgid="5128758823486361279">"Dihidupkan pada <xliff:g id="TIME">%s</xliff:g>"</string>
+ <string name="quick_settings_dark_mode_secondary_label_until" msgid="2289774641256492437">"Hingga <xliff:g id="TIME">%s</xliff:g>"</string>
<string name="quick_settings_nfc_label" msgid="1054317416221168085">"NFC"</string>
<string name="quick_settings_nfc_off" msgid="3465000058515424663">"NFC dilumpuhkan"</string>
<string name="quick_settings_nfc_on" msgid="1004976611203202230">"NFC didayakan"</string>
- <!-- no translation found for quick_settings_screen_record_label (1594046461509776676) -->
- <skip />
- <!-- no translation found for quick_settings_screen_record_start (1574725369331638985) -->
- <skip />
- <!-- no translation found for quick_settings_screen_record_stop (8087348522976412119) -->
- <skip />
+ <string name="quick_settings_screen_record_label" msgid="1594046461509776676">"Saring Rekod"</string>
+ <string name="quick_settings_screen_record_start" msgid="1574725369331638985">"Mula"</string>
+ <string name="quick_settings_screen_record_stop" msgid="8087348522976412119">"Berhenti"</string>
<string name="recents_swipe_up_onboarding" msgid="2820265886420993995">"Leret ke atas untuk menukar apl"</string>
<string name="recents_quick_scrub_onboarding" msgid="765934300283514912">"Seret ke kanan untuk beralih apl dengan pantas"</string>
<string name="quick_step_accessibility_toggle_overview" msgid="7908949976727578403">"Togol Ikhtisar"</string>
@@ -696,22 +697,14 @@
<string name="notification_app_settings" msgid="8963648463858039377">"Sesuaikan"</string>
<string name="notification_done" msgid="6215117625922713976">"Selesai"</string>
<string name="inline_undo" msgid="9026953267645116526">"Buat asal"</string>
- <!-- no translation found for demote (6225813324237153980) -->
- <skip />
- <!-- no translation found for notification_conversation_favorite (8252976467488182853) -->
- <skip />
- <!-- no translation found for notification_conversation_unfavorite (633301300443356176) -->
- <skip />
- <!-- no translation found for notification_conversation_mute (477431709687199671) -->
- <skip />
- <!-- no translation found for notification_conversation_unmute (410885000669775294) -->
- <skip />
- <!-- no translation found for notification_conversation_bubble (4598142032706190028) -->
- <skip />
- <!-- no translation found for notification_conversation_unbubble (2303087159802926401) -->
- <skip />
- <!-- no translation found for notification_conversation_home_screen (8347136037958438935) -->
- <skip />
+ <string name="demote" msgid="6225813324237153980">"Tandai pemberitahuan ini sebagai bukan perbualan"</string>
+ <string name="notification_conversation_favorite" msgid="8252976467488182853">"Kegemaran"</string>
+ <string name="notification_conversation_unfavorite" msgid="633301300443356176">"Nyahgemari"</string>
+ <string name="notification_conversation_mute" msgid="477431709687199671">"Redam"</string>
+ <string name="notification_conversation_unmute" msgid="410885000669775294">"Nyahredam"</string>
+ <string name="notification_conversation_bubble" msgid="4598142032706190028">"Tunjukkan sebagai gelembung"</string>
+ <string name="notification_conversation_unbubble" msgid="2303087159802926401">"Matikan gelembung"</string>
+ <string name="notification_conversation_home_screen" msgid="8347136037958438935">"Tambahkan pada skrin utama"</string>
<string name="notification_menu_accessibility" msgid="8984166825879886773">"<xliff:g id="APP_NAME">%1$s</xliff:g> <xliff:g id="MENU_DESCRIPTION">%2$s</xliff:g>"</string>
<string name="notification_menu_gear_description" msgid="6429668976593634862">"kawalan pemberitahuan"</string>
<string name="notification_menu_snooze_description" msgid="4740133348901973244">"pilihan tunda pemberitahuan"</string>
diff --git a/packages/SystemUI/res/values-my/strings.xml b/packages/SystemUI/res/values-my/strings.xml
index dd5acfab0c21..5ae82eafbcf9 100644
--- a/packages/SystemUI/res/values-my/strings.xml
+++ b/packages/SystemUI/res/values-my/strings.xml
@@ -71,6 +71,7 @@
<string name="compat_mode_on" msgid="4963711187149440884">"ဇူးမ်အပြည့်ဆွဲခြင်း"</string>
<string name="compat_mode_off" msgid="7682459748279487945">"ဖန်သားပြင်အပြည့်ဆန့်ခြင်း"</string>
<string name="global_action_screenshot" msgid="2760267567509131654">"ဖန်သားပြင်ဓာတ်ပုံ"</string>
+ <string name="remote_input_image_insertion_text" msgid="4613177882724332877">"ပုံ ထည့်ထားသည်"</string>
<string name="screenshot_saving_ticker" msgid="6519186952674544916">"ဖန်သားပြင်ဓါတ်ပုံသိမ်းစဉ်.."</string>
<string name="screenshot_saving_title" msgid="2298349784913287333">"ဖန်သားပြင်ဓါတ်ပုံရိုက်ခြင်းအား သိမ်းဆည်းပါမည်"</string>
<string name="screenshot_saved_title" msgid="8893267638659083153">"ဖန်သားပြင်ဓာတ်ပုံကို သိမ်းပြီးပါပြီ"</string>
@@ -84,6 +85,7 @@
<string name="screenrecord_start_label" msgid="1539048263178882562">"စတင် ရိုက်ကူးရန်"</string>
<string name="screenrecord_mic_label" msgid="6134198080740031632">"နောက်ခံစကားပြော ကူးယူရန်"</string>
<string name="screenrecord_taps_label" msgid="2518244240225925076">"တို့ခြင်းများကို ပြရန်"</string>
+ <string name="screenrecord_stop_text" msgid="6549288689506057686">"ရပ်ရန် တို့ပါ"</string>
<string name="screenrecord_stop_label" msgid="72699670052087989">"ရပ်ရန်"</string>
<string name="screenrecord_pause_label" msgid="6004054907104549857">"ခဏရပ်ရန်"</string>
<string name="screenrecord_resume_label" msgid="4972223043729555575">"ဆက်လုပ်ရန်"</string>
@@ -388,15 +390,14 @@
<string name="quick_settings_dark_mode_secondary_label_battery_saver" msgid="4990712734503013251">"ဘက်ထရီ အားထိန်း"</string>
<string name="quick_settings_dark_mode_secondary_label_on_at_sunset" msgid="6017379738102015710">"နေဝင်ချိန်၌ ဖွင့်ရန်"</string>
<string name="quick_settings_dark_mode_secondary_label_until_sunrise" msgid="4404885070316716472">"နေထွက်ချိန် အထိ"</string>
+ <string name="quick_settings_dark_mode_secondary_label_on_at" msgid="5128758823486361279">"<xliff:g id="TIME">%s</xliff:g> တွင် ဖွင့်မည်"</string>
+ <string name="quick_settings_dark_mode_secondary_label_until" msgid="2289774641256492437">"<xliff:g id="TIME">%s</xliff:g> အထိ"</string>
<string name="quick_settings_nfc_label" msgid="1054317416221168085">"NFC"</string>
<string name="quick_settings_nfc_off" msgid="3465000058515424663">"NFC ကို ပိတ်ထားသည်"</string>
<string name="quick_settings_nfc_on" msgid="1004976611203202230">"NFC ကို ဖွင့်ထားသည်"</string>
- <!-- no translation found for quick_settings_screen_record_label (1594046461509776676) -->
- <skip />
- <!-- no translation found for quick_settings_screen_record_start (1574725369331638985) -->
- <skip />
- <!-- no translation found for quick_settings_screen_record_stop (8087348522976412119) -->
- <skip />
+ <string name="quick_settings_screen_record_label" msgid="1594046461509776676">"ဖန်သားပြင် မှတ်တမ်းတင်ရန်"</string>
+ <string name="quick_settings_screen_record_start" msgid="1574725369331638985">"စတင်ရန်"</string>
+ <string name="quick_settings_screen_record_stop" msgid="8087348522976412119">"ရပ်ရန်"</string>
<string name="recents_swipe_up_onboarding" msgid="2820265886420993995">"အက်ပ်များကို ဖွင့်ရန် အပေါ်သို့ ပွတ်ဆွဲပါ"</string>
<string name="recents_quick_scrub_onboarding" msgid="765934300283514912">"အက်ပ်များကို ပြောင်းရန် ညာဘက်သို့ ဖိဆွဲပါ"</string>
<string name="quick_step_accessibility_toggle_overview" msgid="7908949976727578403">"ဖွင့်၊ ပိတ် အနှစ်ချုပ်"</string>
@@ -696,22 +697,14 @@
<string name="notification_app_settings" msgid="8963648463858039377">"စိတ်ကြိုက်ပြုလုပ်ရန်"</string>
<string name="notification_done" msgid="6215117625922713976">"ပြီးပါပြီ"</string>
<string name="inline_undo" msgid="9026953267645116526">"တစ်ဆင့်နောက်ပြန်ရန်"</string>
- <!-- no translation found for demote (6225813324237153980) -->
- <skip />
- <!-- no translation found for notification_conversation_favorite (8252976467488182853) -->
- <skip />
- <!-- no translation found for notification_conversation_unfavorite (633301300443356176) -->
- <skip />
- <!-- no translation found for notification_conversation_mute (477431709687199671) -->
- <skip />
- <!-- no translation found for notification_conversation_unmute (410885000669775294) -->
- <skip />
- <!-- no translation found for notification_conversation_bubble (4598142032706190028) -->
- <skip />
- <!-- no translation found for notification_conversation_unbubble (2303087159802926401) -->
- <skip />
- <!-- no translation found for notification_conversation_home_screen (8347136037958438935) -->
- <skip />
+ <string name="demote" msgid="6225813324237153980">"ဤအကြောင်းကြားချက်ကို စကားဝိုင်းမဟုတ်ဟု မှတ်ထားရန်"</string>
+ <string name="notification_conversation_favorite" msgid="8252976467488182853">"အကြိုက်ဆုံး"</string>
+ <string name="notification_conversation_unfavorite" msgid="633301300443356176">"အကြိုက်ဆုံးမှ ဖယ်ရှားရန်"</string>
+ <string name="notification_conversation_mute" msgid="477431709687199671">"အသံတိတ်ရန်"</string>
+ <string name="notification_conversation_unmute" msgid="410885000669775294">"အသံဖွင့်ရန်"</string>
+ <string name="notification_conversation_bubble" msgid="4598142032706190028">"ပူဖောင်းကွက်အဖြစ် ပြရန်"</string>
+ <string name="notification_conversation_unbubble" msgid="2303087159802926401">"ပူဖောင်းကွက်များကို ပိတ်ရန်"</string>
+ <string name="notification_conversation_home_screen" msgid="8347136037958438935">"ပင်မစာမျက်နှာတွင် ထည့်ရန်"</string>
<string name="notification_menu_accessibility" msgid="8984166825879886773">"<xliff:g id="APP_NAME">%1$s</xliff:g> <xliff:g id="MENU_DESCRIPTION">%2$s</xliff:g>"</string>
<string name="notification_menu_gear_description" msgid="6429668976593634862">"အကြောင်းကြားချက် ထိန်းချုပ်မှုများ"</string>
<string name="notification_menu_snooze_description" msgid="4740133348901973244">"အကြောင်းကြားချက်များကို ဆိုင်းငံ့ရန် ရွေးချယ်စရာများ"</string>
diff --git a/packages/SystemUI/res/values-nb/strings.xml b/packages/SystemUI/res/values-nb/strings.xml
index 97d6792ee6bb..463b95c0107e 100644
--- a/packages/SystemUI/res/values-nb/strings.xml
+++ b/packages/SystemUI/res/values-nb/strings.xml
@@ -71,6 +71,7 @@
<string name="compat_mode_on" msgid="4963711187149440884">"Zoom for å fylle skjermen"</string>
<string name="compat_mode_off" msgid="7682459748279487945">"Strekk for å fylle skjerm"</string>
<string name="global_action_screenshot" msgid="2760267567509131654">"Skjermdump"</string>
+ <string name="remote_input_image_insertion_text" msgid="4613177882724332877">"Bildet er satt inn"</string>
<string name="screenshot_saving_ticker" msgid="6519186952674544916">"Lagrer skjermdumpen …"</string>
<string name="screenshot_saving_title" msgid="2298349784913287333">"Lagrer skjermdumpen …"</string>
<string name="screenshot_saved_title" msgid="8893267638659083153">"Skjermdumpen er lagret"</string>
@@ -84,6 +85,7 @@
<string name="screenrecord_start_label" msgid="1539048263178882562">"Start opptak"</string>
<string name="screenrecord_mic_label" msgid="6134198080740031632">"Ta opp et kommentarspor"</string>
<string name="screenrecord_taps_label" msgid="2518244240225925076">"Vis trykk"</string>
+ <string name="screenrecord_stop_text" msgid="6549288689506057686">"Trykk for å stoppe"</string>
<string name="screenrecord_stop_label" msgid="72699670052087989">"Stopp"</string>
<string name="screenrecord_pause_label" msgid="6004054907104549857">"Sett på pause"</string>
<string name="screenrecord_resume_label" msgid="4972223043729555575">"Gjenoppta"</string>
@@ -388,15 +390,14 @@
<string name="quick_settings_dark_mode_secondary_label_battery_saver" msgid="4990712734503013251">"Batterisparing"</string>
<string name="quick_settings_dark_mode_secondary_label_on_at_sunset" msgid="6017379738102015710">"På ved solnedgang"</string>
<string name="quick_settings_dark_mode_secondary_label_until_sunrise" msgid="4404885070316716472">"Til soloppgang"</string>
+ <string name="quick_settings_dark_mode_secondary_label_on_at" msgid="5128758823486361279">"Slås på klokken <xliff:g id="TIME">%s</xliff:g>"</string>
+ <string name="quick_settings_dark_mode_secondary_label_until" msgid="2289774641256492437">"Til <xliff:g id="TIME">%s</xliff:g>"</string>
<string name="quick_settings_nfc_label" msgid="1054317416221168085">"NFC"</string>
<string name="quick_settings_nfc_off" msgid="3465000058515424663">"NFC er slått av"</string>
<string name="quick_settings_nfc_on" msgid="1004976611203202230">"NFC er slått på"</string>
- <!-- no translation found for quick_settings_screen_record_label (1594046461509776676) -->
- <skip />
- <!-- no translation found for quick_settings_screen_record_start (1574725369331638985) -->
- <skip />
- <!-- no translation found for quick_settings_screen_record_stop (8087348522976412119) -->
- <skip />
+ <string name="quick_settings_screen_record_label" msgid="1594046461509776676">"Skjermopptak"</string>
+ <string name="quick_settings_screen_record_start" msgid="1574725369331638985">"Start"</string>
+ <string name="quick_settings_screen_record_stop" msgid="8087348522976412119">"Stopp"</string>
<string name="recents_swipe_up_onboarding" msgid="2820265886420993995">"Sveip opp for å bytte apper"</string>
<string name="recents_quick_scrub_onboarding" msgid="765934300283514912">"Dra til høyre for å bytte apper raskt"</string>
<string name="quick_step_accessibility_toggle_overview" msgid="7908949976727578403">"Slå oversikten av eller på"</string>
@@ -696,22 +697,14 @@
<string name="notification_app_settings" msgid="8963648463858039377">"Tilpass"</string>
<string name="notification_done" msgid="6215117625922713976">"Ferdig"</string>
<string name="inline_undo" msgid="9026953267645116526">"Angre"</string>
- <!-- no translation found for demote (6225813324237153980) -->
- <skip />
- <!-- no translation found for notification_conversation_favorite (8252976467488182853) -->
- <skip />
- <!-- no translation found for notification_conversation_unfavorite (633301300443356176) -->
- <skip />
- <!-- no translation found for notification_conversation_mute (477431709687199671) -->
- <skip />
- <!-- no translation found for notification_conversation_unmute (410885000669775294) -->
- <skip />
- <!-- no translation found for notification_conversation_bubble (4598142032706190028) -->
- <skip />
- <!-- no translation found for notification_conversation_unbubble (2303087159802926401) -->
- <skip />
- <!-- no translation found for notification_conversation_home_screen (8347136037958438935) -->
- <skip />
+ <string name="demote" msgid="6225813324237153980">"Marker dette varselet som ikke en samtale"</string>
+ <string name="notification_conversation_favorite" msgid="8252976467488182853">"Favoritt"</string>
+ <string name="notification_conversation_unfavorite" msgid="633301300443356176">"Fjern som favoritt"</string>
+ <string name="notification_conversation_mute" msgid="477431709687199671">"Ignorer"</string>
+ <string name="notification_conversation_unmute" msgid="410885000669775294">"Slutt å ignorere"</string>
+ <string name="notification_conversation_bubble" msgid="4598142032706190028">"Vis som boble"</string>
+ <string name="notification_conversation_unbubble" msgid="2303087159802926401">"Slå av bobler"</string>
+ <string name="notification_conversation_home_screen" msgid="8347136037958438935">"Legg til på startskjermen"</string>
<string name="notification_menu_accessibility" msgid="8984166825879886773">"<xliff:g id="APP_NAME">%1$s</xliff:g> <xliff:g id="MENU_DESCRIPTION">%2$s</xliff:g>"</string>
<string name="notification_menu_gear_description" msgid="6429668976593634862">"varselinnstillinger"</string>
<string name="notification_menu_snooze_description" msgid="4740133348901973244">"slumrealternativer for varsler"</string>
diff --git a/packages/SystemUI/res/values-ne/strings.xml b/packages/SystemUI/res/values-ne/strings.xml
index 1345b098f06e..21a76a3a86d0 100644
--- a/packages/SystemUI/res/values-ne/strings.xml
+++ b/packages/SystemUI/res/values-ne/strings.xml
@@ -71,6 +71,7 @@
<string name="compat_mode_on" msgid="4963711187149440884">"स्क्रिन भर्न जुम गर्नुहोस्"</string>
<string name="compat_mode_off" msgid="7682459748279487945">"स्क्रिन भर्न तन्काउनुहोस्"</string>
<string name="global_action_screenshot" msgid="2760267567509131654">"स्क्रिनसट"</string>
+ <string name="remote_input_image_insertion_text" msgid="4613177882724332877">"छवि सम्मिलित गरियो"</string>
<string name="screenshot_saving_ticker" msgid="6519186952674544916">"स्क्रिनसट बचत गर्दै…"</string>
<string name="screenshot_saving_title" msgid="2298349784913287333">"स्क्रिनसट बचत गर्दै…"</string>
<string name="screenshot_saved_title" msgid="8893267638659083153">"स्क्रिनसट सुरक्षित गरियो"</string>
@@ -84,6 +85,7 @@
<string name="screenrecord_start_label" msgid="1539048263178882562">"रेकर्डिङ सुरु गर्नुहोस्"</string>
<string name="screenrecord_mic_label" msgid="6134198080740031632">"भ्वाइसओवर रेकर्ड गर्नुहोस्"</string>
<string name="screenrecord_taps_label" msgid="2518244240225925076">"ट्यापहरू देखाउनुहोस्"</string>
+ <string name="screenrecord_stop_text" msgid="6549288689506057686">"रोक्न ट्याप गर्नुहोस्"</string>
<string name="screenrecord_stop_label" msgid="72699670052087989">"रोक्नुहोस्"</string>
<string name="screenrecord_pause_label" msgid="6004054907104549857">"पज गर्नुहोस्"</string>
<string name="screenrecord_resume_label" msgid="4972223043729555575">"जारी राख्नुहोस्"</string>
@@ -388,15 +390,14 @@
<string name="quick_settings_dark_mode_secondary_label_battery_saver" msgid="4990712734503013251">"ब्याट्री सेभर"</string>
<string name="quick_settings_dark_mode_secondary_label_on_at_sunset" msgid="6017379738102015710">"सूर्यास्तमा सक्रिय"</string>
<string name="quick_settings_dark_mode_secondary_label_until_sunrise" msgid="4404885070316716472">"सूर्योदयसम्म"</string>
+ <string name="quick_settings_dark_mode_secondary_label_on_at" msgid="5128758823486361279">"<xliff:g id="TIME">%s</xliff:g> मा सक्रिय"</string>
+ <string name="quick_settings_dark_mode_secondary_label_until" msgid="2289774641256492437">"<xliff:g id="TIME">%s</xliff:g> सम्म"</string>
<string name="quick_settings_nfc_label" msgid="1054317416221168085">"NFC"</string>
<string name="quick_settings_nfc_off" msgid="3465000058515424663">"NFC लाई असक्षम पारिएको छ"</string>
<string name="quick_settings_nfc_on" msgid="1004976611203202230">"NFC लाई सक्षम पारिएको छ"</string>
- <!-- no translation found for quick_settings_screen_record_label (1594046461509776676) -->
- <skip />
- <!-- no translation found for quick_settings_screen_record_start (1574725369331638985) -->
- <skip />
- <!-- no translation found for quick_settings_screen_record_stop (8087348522976412119) -->
- <skip />
+ <string name="quick_settings_screen_record_label" msgid="1594046461509776676">"स्रिनको रेकर्ड"</string>
+ <string name="quick_settings_screen_record_start" msgid="1574725369331638985">"सुरु गर्नुहोस्"</string>
+ <string name="quick_settings_screen_record_stop" msgid="8087348522976412119">"रोक्नुहोस्"</string>
<string name="recents_swipe_up_onboarding" msgid="2820265886420993995">"अनुप्रयोगहरू बदल्न माथितिर स्वाइप गर्नुहोस्"</string>
<string name="recents_quick_scrub_onboarding" msgid="765934300283514912">"अनुप्रयोगहरू बदल्न द्रुत गतिमा दायाँतिर ड्र्याग गर्नुहोस्"</string>
<string name="quick_step_accessibility_toggle_overview" msgid="7908949976727578403">"परिदृश्य टगल गर्नुहोस्"</string>
@@ -696,22 +697,14 @@
<string name="notification_app_settings" msgid="8963648463858039377">"आफू अनुकूल पार्नुहोस्"</string>
<string name="notification_done" msgid="6215117625922713976">"सम्पन्‍न भयो"</string>
<string name="inline_undo" msgid="9026953267645116526">"अन्डू गर्नुहोस्"</string>
- <!-- no translation found for demote (6225813324237153980) -->
- <skip />
- <!-- no translation found for notification_conversation_favorite (8252976467488182853) -->
- <skip />
- <!-- no translation found for notification_conversation_unfavorite (633301300443356176) -->
- <skip />
- <!-- no translation found for notification_conversation_mute (477431709687199671) -->
- <skip />
- <!-- no translation found for notification_conversation_unmute (410885000669775294) -->
- <skip />
- <!-- no translation found for notification_conversation_bubble (4598142032706190028) -->
- <skip />
- <!-- no translation found for notification_conversation_unbubble (2303087159802926401) -->
- <skip />
- <!-- no translation found for notification_conversation_home_screen (8347136037958438935) -->
- <skip />
+ <string name="demote" msgid="6225813324237153980">"यो सूचनालाई वार्तालाप होइन भनी चिन्ह लगाउनुहोस्"</string>
+ <string name="notification_conversation_favorite" msgid="8252976467488182853">"मन पर्ने"</string>
+ <string name="notification_conversation_unfavorite" msgid="633301300443356176">"मन पर्ने कुराहरूबाट हटाउनुहोस्"</string>
+ <string name="notification_conversation_mute" msgid="477431709687199671">"म्युट गर्नुहोस्"</string>
+ <string name="notification_conversation_unmute" msgid="410885000669775294">"अनम्युट गर्नुहोस्"</string>
+ <string name="notification_conversation_bubble" msgid="4598142032706190028">"बबलको रूपमा देखाउनुहोस्"</string>
+ <string name="notification_conversation_unbubble" msgid="2303087159802926401">"बबलहरूलाई निष्क्रिय पार्नुहोस्"</string>
+ <string name="notification_conversation_home_screen" msgid="8347136037958438935">"गृह स्क्रिनमा थप्नुहोस्"</string>
<string name="notification_menu_accessibility" msgid="8984166825879886773">"<xliff:g id="APP_NAME">%1$s</xliff:g> <xliff:g id="MENU_DESCRIPTION">%2$s</xliff:g>"</string>
<string name="notification_menu_gear_description" msgid="6429668976593634862">"सूचना सम्बन्धी नियन्त्रणहरू"</string>
<string name="notification_menu_snooze_description" msgid="4740133348901973244">"सूचना स्नुज गर्ने विकल्पहरू"</string>
diff --git a/packages/SystemUI/res/values-nl/strings.xml b/packages/SystemUI/res/values-nl/strings.xml
index 71d2388264fb..3c040154437c 100644
--- a/packages/SystemUI/res/values-nl/strings.xml
+++ b/packages/SystemUI/res/values-nl/strings.xml
@@ -71,6 +71,7 @@
<string name="compat_mode_on" msgid="4963711187149440884">"Zoom om scherm te vullen"</string>
<string name="compat_mode_off" msgid="7682459748279487945">"Rek uit v. schermvulling"</string>
<string name="global_action_screenshot" msgid="2760267567509131654">"Screenshot"</string>
+ <string name="remote_input_image_insertion_text" msgid="4613177882724332877">"Afbeelding ingevoegd"</string>
<string name="screenshot_saving_ticker" msgid="6519186952674544916">"Screenshot opslaan..."</string>
<string name="screenshot_saving_title" msgid="2298349784913287333">"Screenshot opslaan..."</string>
<string name="screenshot_saved_title" msgid="8893267638659083153">"Screenshot opgeslagen"</string>
@@ -84,6 +85,7 @@
<string name="screenrecord_start_label" msgid="1539048263178882562">"Opname starten"</string>
<string name="screenrecord_mic_label" msgid="6134198080740031632">"Voice-over opnemen"</string>
<string name="screenrecord_taps_label" msgid="2518244240225925076">"Tikken weergeven"</string>
+ <string name="screenrecord_stop_text" msgid="6549288689506057686">"Tik om te stoppen"</string>
<string name="screenrecord_stop_label" msgid="72699670052087989">"Stoppen"</string>
<string name="screenrecord_pause_label" msgid="6004054907104549857">"Pauzeren"</string>
<string name="screenrecord_resume_label" msgid="4972223043729555575">"Hervatten"</string>
@@ -388,6 +390,8 @@
<string name="quick_settings_dark_mode_secondary_label_battery_saver" msgid="4990712734503013251">"Batterijbesparing"</string>
<string name="quick_settings_dark_mode_secondary_label_on_at_sunset" msgid="6017379738102015710">"Aan bij zonsondergang"</string>
<string name="quick_settings_dark_mode_secondary_label_until_sunrise" msgid="4404885070316716472">"Tot zonsopgang"</string>
+ <string name="quick_settings_dark_mode_secondary_label_on_at" msgid="5128758823486361279">"Aan om <xliff:g id="TIME">%s</xliff:g>"</string>
+ <string name="quick_settings_dark_mode_secondary_label_until" msgid="2289774641256492437">"Tot <xliff:g id="TIME">%s</xliff:g>"</string>
<string name="quick_settings_nfc_label" msgid="1054317416221168085">"NFC"</string>
<string name="quick_settings_nfc_off" msgid="3465000058515424663">"NFC is uitgeschakeld"</string>
<string name="quick_settings_nfc_on" msgid="1004976611203202230">"NFC is ingeschakeld"</string>
diff --git a/packages/SystemUI/res/values-or/strings.xml b/packages/SystemUI/res/values-or/strings.xml
index 85841feeff14..42302756debd 100644
--- a/packages/SystemUI/res/values-or/strings.xml
+++ b/packages/SystemUI/res/values-or/strings.xml
@@ -71,6 +71,7 @@
<string name="compat_mode_on" msgid="4963711187149440884">"ସ୍କ୍ରୀନ ଭରିବା ପାଇଁ ଜୁମ୍ କରନ୍ତୁ"</string>
<string name="compat_mode_off" msgid="7682459748279487945">"ସ୍କ୍ରୀନ୍‌କୁ ଭରିବା ପାଇଁ ଟାଣନ୍ତୁ"</string>
<string name="global_action_screenshot" msgid="2760267567509131654">"ସ୍କ୍ରିନ୍‌ସଟ୍ ନିଅନ୍ତୁ"</string>
+ <string name="remote_input_image_insertion_text" msgid="4613177882724332877">"ଛବି ଭର୍ତ୍ତି କରାଯାଇଛି"</string>
<string name="screenshot_saving_ticker" msgid="6519186952674544916">"ସ୍କ୍ରୀନଶଟ୍‍ ସେଭ୍‍ କରାଯାଉଛି…"</string>
<string name="screenshot_saving_title" msgid="2298349784913287333">"ସ୍କ୍ରୀନଶଟ୍‍ ସେଭ୍‍ କରାଯାଉଛି…"</string>
<string name="screenshot_saved_title" msgid="8893267638659083153">"ସ୍କ୍ରୀନଶଟ୍ ସେଭ୍ ହୋଇଛି"</string>
@@ -84,6 +85,7 @@
<string name="screenrecord_start_label" msgid="1539048263178882562">"ରେକର୍ଡିଂ ଆରମ୍ଭ କରନ୍ତୁ"</string>
<string name="screenrecord_mic_label" msgid="6134198080740031632">"ଭଏସ୍‍‍‍‍‍‍‍‍‍ଓଭର୍‍ ରେକର୍ଡ କରନ୍ତୁ"</string>
<string name="screenrecord_taps_label" msgid="2518244240225925076">"ଟାପ୍‌ ଦେଖାନ୍ତୁ"</string>
+ <string name="screenrecord_stop_text" msgid="6549288689506057686">"ବନ୍ଦ କରିବା ପାଇଁ ଟାପ୍ କରନ୍ତୁ"</string>
<string name="screenrecord_stop_label" msgid="72699670052087989">"ବନ୍ଦ କରନ୍ତୁ"</string>
<string name="screenrecord_pause_label" msgid="6004054907104549857">"ବିରତି କରନ୍ତୁ"</string>
<string name="screenrecord_resume_label" msgid="4972223043729555575">"ପୁଣି ଚାଲୁ କରନ୍ତୁ"</string>
@@ -388,15 +390,14 @@
<string name="quick_settings_dark_mode_secondary_label_battery_saver" msgid="4990712734503013251">"ବ୍ୟାଟେରୀ ସେଭର୍"</string>
<string name="quick_settings_dark_mode_secondary_label_on_at_sunset" msgid="6017379738102015710">"ସନ୍ଧ୍ୟାରେ ଚାଲୁ ହେବ"</string>
<string name="quick_settings_dark_mode_secondary_label_until_sunrise" msgid="4404885070316716472">"ସକାଳ ପର୍ଯ୍ୟନ୍ତ"</string>
+ <string name="quick_settings_dark_mode_secondary_label_on_at" msgid="5128758823486361279">"<xliff:g id="TIME">%s</xliff:g>ରେ ଚାଲୁ ହେବ"</string>
+ <string name="quick_settings_dark_mode_secondary_label_until" msgid="2289774641256492437">"<xliff:g id="TIME">%s</xliff:g> ପର୍ଯ୍ୟନ୍ତ"</string>
<string name="quick_settings_nfc_label" msgid="1054317416221168085">"NFC"</string>
<string name="quick_settings_nfc_off" msgid="3465000058515424663">"NFC ଅକ୍ଷମ କରାଯାଇଛି"</string>
<string name="quick_settings_nfc_on" msgid="1004976611203202230">"NFC ସକ୍ଷମ କରାଯାଇଛି"</string>
- <!-- no translation found for quick_settings_screen_record_label (1594046461509776676) -->
- <skip />
- <!-- no translation found for quick_settings_screen_record_start (1574725369331638985) -->
- <skip />
- <!-- no translation found for quick_settings_screen_record_stop (8087348522976412119) -->
- <skip />
+ <string name="quick_settings_screen_record_label" msgid="1594046461509776676">"ସ୍କ୍ରିନ୍ ରେକର୍ଡ"</string>
+ <string name="quick_settings_screen_record_start" msgid="1574725369331638985">"ଆରମ୍ଭ କରନ୍ତୁ"</string>
+ <string name="quick_settings_screen_record_stop" msgid="8087348522976412119">"ବନ୍ଦ କରନ୍ତୁ"</string>
<string name="recents_swipe_up_onboarding" msgid="2820265886420993995">"ଆପ୍‌କୁ ବଦଳ କରିବା ପାଇଁ ସ୍ଵାଇପ୍ କରନ୍ତୁ"</string>
<string name="recents_quick_scrub_onboarding" msgid="765934300283514912">"ଆପ୍‌ଗୁଡ଼ିକ ମଧ୍ୟରେ ଶୀଘ୍ର ବଦଳ କରିବା ପାଇଁ ଡାହାଣକୁ ଡ୍ରାଗ୍ କରନ୍ତୁ"</string>
<string name="quick_step_accessibility_toggle_overview" msgid="7908949976727578403">"ସଂକ୍ଷିପ୍ତ ବିବରଣୀକୁ ଟୋଗଲ୍ କରନ୍ତୁ"</string>
@@ -696,22 +697,14 @@
<string name="notification_app_settings" msgid="8963648463858039377">"କଷ୍ଟମାଇଜ୍‌ କରନ୍ତୁ"</string>
<string name="notification_done" msgid="6215117625922713976">"ହୋଇଗଲା"</string>
<string name="inline_undo" msgid="9026953267645116526">"ପୂର୍ବସ୍ଥାନକୁ ଫେରାଇଆଣନ୍ତୁ"</string>
- <!-- no translation found for demote (6225813324237153980) -->
- <skip />
- <!-- no translation found for notification_conversation_favorite (8252976467488182853) -->
- <skip />
- <!-- no translation found for notification_conversation_unfavorite (633301300443356176) -->
- <skip />
- <!-- no translation found for notification_conversation_mute (477431709687199671) -->
- <skip />
- <!-- no translation found for notification_conversation_unmute (410885000669775294) -->
- <skip />
- <!-- no translation found for notification_conversation_bubble (4598142032706190028) -->
- <skip />
- <!-- no translation found for notification_conversation_unbubble (2303087159802926401) -->
- <skip />
- <!-- no translation found for notification_conversation_home_screen (8347136037958438935) -->
- <skip />
+ <string name="demote" msgid="6225813324237153980">"ଏହି ବିଜ୍ଞପ୍ତି କୌଣସି ବାର୍ତ୍ତାଳାପ ନୁହେଁ ବୋଲି ଚିହ୍ନଟ କରନ୍ତୁ"</string>
+ <string name="notification_conversation_favorite" msgid="8252976467488182853">"ପସନ୍ଦ ଭାବରେ ଚିହ୍ନଟ କରନ୍ତୁ"</string>
+ <string name="notification_conversation_unfavorite" msgid="633301300443356176">"ନାପସନ୍ଦ କରନ୍ତୁ"</string>
+ <string name="notification_conversation_mute" msgid="477431709687199671">"ମ୍ୟୁଟ୍ କରନ୍ତୁ"</string>
+ <string name="notification_conversation_unmute" msgid="410885000669775294">"ଅନମ୍ୟୁଟ୍ କରନ୍ତୁ"</string>
+ <string name="notification_conversation_bubble" msgid="4598142032706190028">"ବୁଦବୁଦ୍ ଭାବରେ ଦେଖାନ୍ତୁ"</string>
+ <string name="notification_conversation_unbubble" msgid="2303087159802926401">"ବୁଦବୁଦଗୁଡ଼ିକ ବନ୍ଦ କରନ୍ତୁ"</string>
+ <string name="notification_conversation_home_screen" msgid="8347136037958438935">"ମୂଳ ସ୍କ୍ରିନରେ ଯୋଗ କରନ୍ତୁ"</string>
<string name="notification_menu_accessibility" msgid="8984166825879886773">"<xliff:g id="APP_NAME">%1$s</xliff:g> <xliff:g id="MENU_DESCRIPTION">%2$s</xliff:g>"</string>
<string name="notification_menu_gear_description" msgid="6429668976593634862">"ବିଜ୍ଞପ୍ତି ନିୟନ୍ତ୍ରଣ"</string>
<string name="notification_menu_snooze_description" msgid="4740133348901973244">"ବିଜ୍ଞପ୍ତି ସ୍ନୁଜ୍‍ ବିକଳ୍ପ"</string>
diff --git a/packages/SystemUI/res/values-pa/strings.xml b/packages/SystemUI/res/values-pa/strings.xml
index 4ea51ddbde9e..f345d6a8f7aa 100644
--- a/packages/SystemUI/res/values-pa/strings.xml
+++ b/packages/SystemUI/res/values-pa/strings.xml
@@ -71,6 +71,7 @@
<string name="compat_mode_on" msgid="4963711187149440884">"ਸਕ੍ਰੀਨ ਭਰਨ ਲਈ ਜ਼ੂਮ ਕਰੋ"</string>
<string name="compat_mode_off" msgid="7682459748279487945">"ਸਕ੍ਰੀਨ ਭਰਨ ਲਈ ਸਟ੍ਰੈਚ ਕਰੋ"</string>
<string name="global_action_screenshot" msgid="2760267567509131654">"ਸਕ੍ਰੀਨਸ਼ਾਟ"</string>
+ <string name="remote_input_image_insertion_text" msgid="4613177882724332877">"ਚਿੱਤਰ ਸ਼ਾਮਲ ਕੀਤਾ ਗਿਆ"</string>
<string name="screenshot_saving_ticker" msgid="6519186952674544916">"ਸਕ੍ਰੀਨਸ਼ਾਟ ਸੁਰੱਖਿਅਤ ਕਰ ਰਿਹਾ ਹੈ…"</string>
<string name="screenshot_saving_title" msgid="2298349784913287333">"ਸਕ੍ਰੀਨਸ਼ਾਟ ਸੁਰੱਖਿਅਤ ਕਰ ਰਿਹਾ ਹੈ…"</string>
<string name="screenshot_saved_title" msgid="8893267638659083153">"ਸਕ੍ਰੀਨਸ਼ਾਟ ਰੱਖਿਅਤ ਕੀਤਾ ਗਿਆ"</string>
@@ -84,6 +85,7 @@
<string name="screenrecord_start_label" msgid="1539048263178882562">"ਰਿਕਾਰਡਿੰਗ ਸ਼ੁਰੂ ਕਰੋ"</string>
<string name="screenrecord_mic_label" msgid="6134198080740031632">"ਅਵਾਜ਼ ਰਿਕਾਰਡ ਕਰੋ"</string>
<string name="screenrecord_taps_label" msgid="2518244240225925076">"ਟੈਪਾਂ ਦਿਖਾਓ"</string>
+ <string name="screenrecord_stop_text" msgid="6549288689506057686">"ਰੋਕਣ ਲਈ ਟੈਪ ਕਰੋ"</string>
<string name="screenrecord_stop_label" msgid="72699670052087989">"ਬੰਦ ਕਰੋ"</string>
<string name="screenrecord_pause_label" msgid="6004054907104549857">"ਰੋਕੋ"</string>
<string name="screenrecord_resume_label" msgid="4972223043729555575">"ਮੁੜ-ਚਾਲੂ ਕਰੋ"</string>
@@ -388,15 +390,14 @@
<string name="quick_settings_dark_mode_secondary_label_battery_saver" msgid="4990712734503013251">"ਬੈਟਰੀ ਸੇਵਰ"</string>
<string name="quick_settings_dark_mode_secondary_label_on_at_sunset" msgid="6017379738102015710">"ਸੂਰਜ ਛਿਪਣ \'ਤੇ ਚਾਲੂ"</string>
<string name="quick_settings_dark_mode_secondary_label_until_sunrise" msgid="4404885070316716472">"ਸੂਰਜ ਚੜ੍ਹਨ ਤੱਕ"</string>
+ <string name="quick_settings_dark_mode_secondary_label_on_at" msgid="5128758823486361279">"<xliff:g id="TIME">%s</xliff:g> ਵਜੇ ਚਾਲੂ"</string>
+ <string name="quick_settings_dark_mode_secondary_label_until" msgid="2289774641256492437">"<xliff:g id="TIME">%s</xliff:g> ਵਜੇ ਤੱਕ"</string>
<string name="quick_settings_nfc_label" msgid="1054317416221168085">"NFC"</string>
<string name="quick_settings_nfc_off" msgid="3465000058515424663">"NFC ਨੂੰ ਅਯੋਗ ਬਣਾਇਆ ਗਿਆ ਹੈ"</string>
<string name="quick_settings_nfc_on" msgid="1004976611203202230">"NFC ਨੂੰ ਯੋਗ ਬਣਾਇਆ ਗਿਆ ਹੈ"</string>
- <!-- no translation found for quick_settings_screen_record_label (1594046461509776676) -->
- <skip />
- <!-- no translation found for quick_settings_screen_record_start (1574725369331638985) -->
- <skip />
- <!-- no translation found for quick_settings_screen_record_stop (8087348522976412119) -->
- <skip />
+ <string name="quick_settings_screen_record_label" msgid="1594046461509776676">"ਸਕ੍ਰੀਨ ਰਿਕਾਰਡਰ"</string>
+ <string name="quick_settings_screen_record_start" msgid="1574725369331638985">"ਸ਼ੁਰੂ ਕਰੋ"</string>
+ <string name="quick_settings_screen_record_stop" msgid="8087348522976412119">"ਰੋਕੋ"</string>
<string name="recents_swipe_up_onboarding" msgid="2820265886420993995">"ਐਪਾਂ ਵਿਚਾਲੇ ਅਦਲਾ-ਬਦਲੀ ਕਰਨ ਲਈ ਉੱਪਰ ਵੱਲ ਸਵਾਈਪ ਕਰੋ"</string>
<string name="recents_quick_scrub_onboarding" msgid="765934300283514912">"ਐਪਾਂ ਵਿਚਾਲੇ ਤੇਜ਼ੀ ਨਾਲ ਅਦਲਾ-ਬਦਲੀ ਕਰਨ ਲਈ ਸੱਜੇ ਪਾਸੇ ਵੱਲ ਘਸੀਟੋ"</string>
<string name="quick_step_accessibility_toggle_overview" msgid="7908949976727578403">"ਰੂਪ-ਰੇਖਾ ਨੂੰ ਟੌਗਲ ਕਰੋ"</string>
@@ -696,22 +697,14 @@
<string name="notification_app_settings" msgid="8963648463858039377">"ਵਿਉਂਤਬੱਧ ਕਰੋ"</string>
<string name="notification_done" msgid="6215117625922713976">"ਹੋ ਗਿਆ"</string>
<string name="inline_undo" msgid="9026953267645116526">"ਅਣਕੀਤਾ ਕਰੋ"</string>
- <!-- no translation found for demote (6225813324237153980) -->
- <skip />
- <!-- no translation found for notification_conversation_favorite (8252976467488182853) -->
- <skip />
- <!-- no translation found for notification_conversation_unfavorite (633301300443356176) -->
- <skip />
- <!-- no translation found for notification_conversation_mute (477431709687199671) -->
- <skip />
- <!-- no translation found for notification_conversation_unmute (410885000669775294) -->
- <skip />
- <!-- no translation found for notification_conversation_bubble (4598142032706190028) -->
- <skip />
- <!-- no translation found for notification_conversation_unbubble (2303087159802926401) -->
- <skip />
- <!-- no translation found for notification_conversation_home_screen (8347136037958438935) -->
- <skip />
+ <string name="demote" msgid="6225813324237153980">"ਇਸ ਸੂਚਨਾ ਦੀ ਗੱਲ-ਬਾਤ ਨਹੀਂ ਵਜੋਂ ਨਿਸ਼ਾਨਦੇਹੀ ਕਰੋ"</string>
+ <string name="notification_conversation_favorite" msgid="8252976467488182853">"ਮਨਪਸੰਦ"</string>
+ <string name="notification_conversation_unfavorite" msgid="633301300443356176">"ਨਾਪਸੰਦ"</string>
+ <string name="notification_conversation_mute" msgid="477431709687199671">"ਮਿਊਟ ਕਰੋ"</string>
+ <string name="notification_conversation_unmute" msgid="410885000669775294">"ਅਣਮਿਊਟ ਕਰੋ"</string>
+ <string name="notification_conversation_bubble" msgid="4598142032706190028">"ਬੁਲਬੁਲੇ ਵਜੋਂ ਦਿਖਾਓ"</string>
+ <string name="notification_conversation_unbubble" msgid="2303087159802926401">"ਬੁਲਬੁਲੇ ਬੰਦ ਕਰੋ"</string>
+ <string name="notification_conversation_home_screen" msgid="8347136037958438935">"ਹੋਮ ਸਕ੍ਰੀਨ \'ਤੇ ਸ਼ਾਮਲ ਕਰੋ"</string>
<string name="notification_menu_accessibility" msgid="8984166825879886773">"<xliff:g id="APP_NAME">%1$s</xliff:g> <xliff:g id="MENU_DESCRIPTION">%2$s</xliff:g>"</string>
<string name="notification_menu_gear_description" msgid="6429668976593634862">"ਸੂਚਨਾ ਕੰਟਰੋਲ"</string>
<string name="notification_menu_snooze_description" msgid="4740133348901973244">"ਸੂਚਨਾ ਸਨੂਜ਼ ਵਿਕਲਪ"</string>
diff --git a/packages/SystemUI/res/values-pl/strings.xml b/packages/SystemUI/res/values-pl/strings.xml
index 515d659d2c5b..68cb14eb32b3 100644
--- a/packages/SystemUI/res/values-pl/strings.xml
+++ b/packages/SystemUI/res/values-pl/strings.xml
@@ -71,6 +71,7 @@
<string name="compat_mode_on" msgid="4963711187149440884">"Powiększ, aby wypełnić ekran"</string>
<string name="compat_mode_off" msgid="7682459748279487945">"Rozciągnij, aby wypełnić ekran"</string>
<string name="global_action_screenshot" msgid="2760267567509131654">"Zrzut ekranu"</string>
+ <string name="remote_input_image_insertion_text" msgid="4613177882724332877">"Wstawiono obraz"</string>
<string name="screenshot_saving_ticker" msgid="6519186952674544916">"Zapisywanie zrzutu ekranu..."</string>
<string name="screenshot_saving_title" msgid="2298349784913287333">"Zapisywanie zrzutu ekranu..."</string>
<string name="screenshot_saved_title" msgid="8893267638659083153">"Zrzut ekranu został zapisany"</string>
@@ -84,6 +85,7 @@
<string name="screenrecord_start_label" msgid="1539048263178882562">"Rozpocznij rejestrowanie"</string>
<string name="screenrecord_mic_label" msgid="6134198080740031632">"Nagraj tekst lektora"</string>
<string name="screenrecord_taps_label" msgid="2518244240225925076">"Pokaż dotknięcia"</string>
+ <string name="screenrecord_stop_text" msgid="6549288689506057686">"Kliknij, by zatrzymać"</string>
<string name="screenrecord_stop_label" msgid="72699670052087989">"Zatrzymaj"</string>
<string name="screenrecord_pause_label" msgid="6004054907104549857">"Wstrzymaj"</string>
<string name="screenrecord_resume_label" msgid="4972223043729555575">"Wznów"</string>
@@ -392,15 +394,14 @@
<string name="quick_settings_dark_mode_secondary_label_battery_saver" msgid="4990712734503013251">"Oszczędzanie baterii"</string>
<string name="quick_settings_dark_mode_secondary_label_on_at_sunset" msgid="6017379738102015710">"Włącz o zachodzie"</string>
<string name="quick_settings_dark_mode_secondary_label_until_sunrise" msgid="4404885070316716472">"Do wschodu słońca"</string>
+ <string name="quick_settings_dark_mode_secondary_label_on_at" msgid="5128758823486361279">"Włącz o <xliff:g id="TIME">%s</xliff:g>"</string>
+ <string name="quick_settings_dark_mode_secondary_label_until" msgid="2289774641256492437">"Do <xliff:g id="TIME">%s</xliff:g>"</string>
<string name="quick_settings_nfc_label" msgid="1054317416221168085">"Komunikacja NFC"</string>
<string name="quick_settings_nfc_off" msgid="3465000058515424663">"Komunikacja NFC jest wyłączona"</string>
<string name="quick_settings_nfc_on" msgid="1004976611203202230">"Komunikacja NFC jest włączona"</string>
- <!-- no translation found for quick_settings_screen_record_label (1594046461509776676) -->
- <skip />
- <!-- no translation found for quick_settings_screen_record_start (1574725369331638985) -->
- <skip />
- <!-- no translation found for quick_settings_screen_record_stop (8087348522976412119) -->
- <skip />
+ <string name="quick_settings_screen_record_label" msgid="1594046461509776676">"Zapis ekranu"</string>
+ <string name="quick_settings_screen_record_start" msgid="1574725369331638985">"Rozpocznij"</string>
+ <string name="quick_settings_screen_record_stop" msgid="8087348522976412119">"Zatrzymaj"</string>
<string name="recents_swipe_up_onboarding" msgid="2820265886420993995">"Przesuń w górę, by przełączyć aplikacje"</string>
<string name="recents_quick_scrub_onboarding" msgid="765934300283514912">"Szybko przeciągnij w prawo, by przełączyć aplikacje"</string>
<string name="quick_step_accessibility_toggle_overview" msgid="7908949976727578403">"Przełącz Przegląd"</string>
@@ -702,22 +703,14 @@
<string name="notification_app_settings" msgid="8963648463858039377">"Dostosuj"</string>
<string name="notification_done" msgid="6215117625922713976">"Gotowe"</string>
<string name="inline_undo" msgid="9026953267645116526">"Cofnij"</string>
- <!-- no translation found for demote (6225813324237153980) -->
- <skip />
- <!-- no translation found for notification_conversation_favorite (8252976467488182853) -->
- <skip />
- <!-- no translation found for notification_conversation_unfavorite (633301300443356176) -->
- <skip />
- <!-- no translation found for notification_conversation_mute (477431709687199671) -->
- <skip />
- <!-- no translation found for notification_conversation_unmute (410885000669775294) -->
- <skip />
- <!-- no translation found for notification_conversation_bubble (4598142032706190028) -->
- <skip />
- <!-- no translation found for notification_conversation_unbubble (2303087159802926401) -->
- <skip />
- <!-- no translation found for notification_conversation_home_screen (8347136037958438935) -->
- <skip />
+ <string name="demote" msgid="6225813324237153980">"Nie oznaczaj tego powiadomienia jako wątku"</string>
+ <string name="notification_conversation_favorite" msgid="8252976467488182853">"Dodaj do ulubionych"</string>
+ <string name="notification_conversation_unfavorite" msgid="633301300443356176">"Usuń z ulubionych"</string>
+ <string name="notification_conversation_mute" msgid="477431709687199671">"Wycisz"</string>
+ <string name="notification_conversation_unmute" msgid="410885000669775294">"Wyłącz wyciszenie"</string>
+ <string name="notification_conversation_bubble" msgid="4598142032706190028">"Pokaż jako dymek"</string>
+ <string name="notification_conversation_unbubble" msgid="2303087159802926401">"Wyłącz dymki"</string>
+ <string name="notification_conversation_home_screen" msgid="8347136037958438935">"Dodaj do ekranu głównego"</string>
<string name="notification_menu_accessibility" msgid="8984166825879886773">"<xliff:g id="APP_NAME">%1$s</xliff:g> <xliff:g id="MENU_DESCRIPTION">%2$s</xliff:g>"</string>
<string name="notification_menu_gear_description" msgid="6429668976593634862">"sterowanie powiadomieniami"</string>
<string name="notification_menu_snooze_description" msgid="4740133348901973244">"opcje odkładania powiadomień"</string>
diff --git a/packages/SystemUI/res/values-pt-rBR/strings.xml b/packages/SystemUI/res/values-pt-rBR/strings.xml
index d486b9b2a7ed..a9101d74706f 100644
--- a/packages/SystemUI/res/values-pt-rBR/strings.xml
+++ b/packages/SystemUI/res/values-pt-rBR/strings.xml
@@ -71,6 +71,7 @@
<string name="compat_mode_on" msgid="4963711187149440884">"Zoom p/ preencher a tela"</string>
<string name="compat_mode_off" msgid="7682459748279487945">"Ampliar p/ preencher tela"</string>
<string name="global_action_screenshot" msgid="2760267567509131654">"Capturar tela"</string>
+ <string name="remote_input_image_insertion_text" msgid="4613177882724332877">"Imagem inserida"</string>
<string name="screenshot_saving_ticker" msgid="6519186952674544916">"Salvando captura de tela..."</string>
<string name="screenshot_saving_title" msgid="2298349784913287333">"Salvando captura de tela..."</string>
<string name="screenshot_saved_title" msgid="8893267638659083153">"Captura de tela salva"</string>
@@ -84,6 +85,7 @@
<string name="screenrecord_start_label" msgid="1539048263178882562">"Iniciar gravação"</string>
<string name="screenrecord_mic_label" msgid="6134198080740031632">"Gravar narração"</string>
<string name="screenrecord_taps_label" msgid="2518244240225925076">"Mostrar toques"</string>
+ <string name="screenrecord_stop_text" msgid="6549288689506057686">"Toque para parar"</string>
<string name="screenrecord_stop_label" msgid="72699670052087989">"Parar"</string>
<string name="screenrecord_pause_label" msgid="6004054907104549857">"Pausar"</string>
<string name="screenrecord_resume_label" msgid="4972223043729555575">"Retomar"</string>
@@ -388,6 +390,8 @@
<string name="quick_settings_dark_mode_secondary_label_battery_saver" msgid="4990712734503013251">"Economia de bateria"</string>
<string name="quick_settings_dark_mode_secondary_label_on_at_sunset" msgid="6017379738102015710">"Ativ. ao pôr do sol"</string>
<string name="quick_settings_dark_mode_secondary_label_until_sunrise" msgid="4404885070316716472">"Até o nascer do sol"</string>
+ <string name="quick_settings_dark_mode_secondary_label_on_at" msgid="5128758823486361279">"Ativar: <xliff:g id="TIME">%s</xliff:g>"</string>
+ <string name="quick_settings_dark_mode_secondary_label_until" msgid="2289774641256492437">"Até: <xliff:g id="TIME">%s</xliff:g>"</string>
<string name="quick_settings_nfc_label" msgid="1054317416221168085">"NFC"</string>
<string name="quick_settings_nfc_off" msgid="3465000058515424663">"A NFC está desativada"</string>
<string name="quick_settings_nfc_on" msgid="1004976611203202230">"A NFC está ativada"</string>
diff --git a/packages/SystemUI/res/values-pt-rPT/strings.xml b/packages/SystemUI/res/values-pt-rPT/strings.xml
index d17b0026802f..1a6e6739e7ff 100644
--- a/packages/SystemUI/res/values-pt-rPT/strings.xml
+++ b/packages/SystemUI/res/values-pt-rPT/strings.xml
@@ -71,6 +71,7 @@
<string name="compat_mode_on" msgid="4963711187149440884">"Zoom para preencher o ecrã"</string>
<string name="compat_mode_off" msgid="7682459748279487945">"Esticar p. caber em ec. int."</string>
<string name="global_action_screenshot" msgid="2760267567509131654">"Captura de ecrã"</string>
+ <string name="remote_input_image_insertion_text" msgid="4613177882724332877">"Imagem inserida"</string>
<string name="screenshot_saving_ticker" msgid="6519186952674544916">"A guardar captura de ecrã..."</string>
<string name="screenshot_saving_title" msgid="2298349784913287333">"A guardar captura de ecrã..."</string>
<string name="screenshot_saved_title" msgid="8893267638659083153">"Captura de ecrã guardada"</string>
@@ -84,6 +85,7 @@
<string name="screenrecord_start_label" msgid="1539048263178882562">"Iniciar gravação"</string>
<string name="screenrecord_mic_label" msgid="6134198080740031632">"Gravar voz-off"</string>
<string name="screenrecord_taps_label" msgid="2518244240225925076">"Mostrar toques"</string>
+ <string name="screenrecord_stop_text" msgid="6549288689506057686">"Tocar para parar"</string>
<string name="screenrecord_stop_label" msgid="72699670052087989">"Parar"</string>
<string name="screenrecord_pause_label" msgid="6004054907104549857">"Colocar em pausa"</string>
<string name="screenrecord_resume_label" msgid="4972223043729555575">"Retomar"</string>
@@ -388,6 +390,8 @@
<string name="quick_settings_dark_mode_secondary_label_battery_saver" msgid="4990712734503013251">"Poupança de bateria"</string>
<string name="quick_settings_dark_mode_secondary_label_on_at_sunset" msgid="6017379738102015710">"Ativ. ao pôr do sol"</string>
<string name="quick_settings_dark_mode_secondary_label_until_sunrise" msgid="4404885070316716472">"Até ao amanhecer"</string>
+ <string name="quick_settings_dark_mode_secondary_label_on_at" msgid="5128758823486361279">"Ativado à(s) <xliff:g id="TIME">%s</xliff:g>."</string>
+ <string name="quick_settings_dark_mode_secondary_label_until" msgid="2289774641256492437">"Até à(s) <xliff:g id="TIME">%s</xliff:g>."</string>
<string name="quick_settings_nfc_label" msgid="1054317416221168085">"NFC"</string>
<string name="quick_settings_nfc_off" msgid="3465000058515424663">"O NFC está desativado"</string>
<string name="quick_settings_nfc_on" msgid="1004976611203202230">"O NFC está ativado"</string>
diff --git a/packages/SystemUI/res/values-pt/strings.xml b/packages/SystemUI/res/values-pt/strings.xml
index d486b9b2a7ed..a9101d74706f 100644
--- a/packages/SystemUI/res/values-pt/strings.xml
+++ b/packages/SystemUI/res/values-pt/strings.xml
@@ -71,6 +71,7 @@
<string name="compat_mode_on" msgid="4963711187149440884">"Zoom p/ preencher a tela"</string>
<string name="compat_mode_off" msgid="7682459748279487945">"Ampliar p/ preencher tela"</string>
<string name="global_action_screenshot" msgid="2760267567509131654">"Capturar tela"</string>
+ <string name="remote_input_image_insertion_text" msgid="4613177882724332877">"Imagem inserida"</string>
<string name="screenshot_saving_ticker" msgid="6519186952674544916">"Salvando captura de tela..."</string>
<string name="screenshot_saving_title" msgid="2298349784913287333">"Salvando captura de tela..."</string>
<string name="screenshot_saved_title" msgid="8893267638659083153">"Captura de tela salva"</string>
@@ -84,6 +85,7 @@
<string name="screenrecord_start_label" msgid="1539048263178882562">"Iniciar gravação"</string>
<string name="screenrecord_mic_label" msgid="6134198080740031632">"Gravar narração"</string>
<string name="screenrecord_taps_label" msgid="2518244240225925076">"Mostrar toques"</string>
+ <string name="screenrecord_stop_text" msgid="6549288689506057686">"Toque para parar"</string>
<string name="screenrecord_stop_label" msgid="72699670052087989">"Parar"</string>
<string name="screenrecord_pause_label" msgid="6004054907104549857">"Pausar"</string>
<string name="screenrecord_resume_label" msgid="4972223043729555575">"Retomar"</string>
@@ -388,6 +390,8 @@
<string name="quick_settings_dark_mode_secondary_label_battery_saver" msgid="4990712734503013251">"Economia de bateria"</string>
<string name="quick_settings_dark_mode_secondary_label_on_at_sunset" msgid="6017379738102015710">"Ativ. ao pôr do sol"</string>
<string name="quick_settings_dark_mode_secondary_label_until_sunrise" msgid="4404885070316716472">"Até o nascer do sol"</string>
+ <string name="quick_settings_dark_mode_secondary_label_on_at" msgid="5128758823486361279">"Ativar: <xliff:g id="TIME">%s</xliff:g>"</string>
+ <string name="quick_settings_dark_mode_secondary_label_until" msgid="2289774641256492437">"Até: <xliff:g id="TIME">%s</xliff:g>"</string>
<string name="quick_settings_nfc_label" msgid="1054317416221168085">"NFC"</string>
<string name="quick_settings_nfc_off" msgid="3465000058515424663">"A NFC está desativada"</string>
<string name="quick_settings_nfc_on" msgid="1004976611203202230">"A NFC está ativada"</string>
diff --git a/packages/SystemUI/res/values-ro/strings.xml b/packages/SystemUI/res/values-ro/strings.xml
index 2674504db62e..e34fa2de54d2 100644
--- a/packages/SystemUI/res/values-ro/strings.xml
+++ b/packages/SystemUI/res/values-ro/strings.xml
@@ -71,6 +71,7 @@
<string name="compat_mode_on" msgid="4963711187149440884">"Zoom pt. a umple ecranul"</string>
<string name="compat_mode_off" msgid="7682459748279487945">"Înt. pt. a umple ecranul"</string>
<string name="global_action_screenshot" msgid="2760267567509131654">"Captură de ecran"</string>
+ <string name="remote_input_image_insertion_text" msgid="4613177882724332877">"S-a inserat imaginea"</string>
<string name="screenshot_saving_ticker" msgid="6519186952674544916">"Se salv. captura de ecran..."</string>
<string name="screenshot_saving_title" msgid="2298349784913287333">"Se salvează captura de ecran..."</string>
<string name="screenshot_saved_title" msgid="8893267638659083153">"Captură de ecran salvată"</string>
@@ -84,6 +85,7 @@
<string name="screenrecord_start_label" msgid="1539048263178882562">"Începeți înregistrarea"</string>
<string name="screenrecord_mic_label" msgid="6134198080740031632">"Înregistrați vocal"</string>
<string name="screenrecord_taps_label" msgid="2518244240225925076">"Afișați atingerile"</string>
+ <string name="screenrecord_stop_text" msgid="6549288689506057686">"Atingeți pentru a opri"</string>
<string name="screenrecord_stop_label" msgid="72699670052087989">"Opriți"</string>
<string name="screenrecord_pause_label" msgid="6004054907104549857">"Întrerupeți"</string>
<string name="screenrecord_resume_label" msgid="4972223043729555575">"Reluați"</string>
@@ -390,15 +392,14 @@
<string name="quick_settings_dark_mode_secondary_label_battery_saver" msgid="4990712734503013251">"Economisire baterie"</string>
<string name="quick_settings_dark_mode_secondary_label_on_at_sunset" msgid="6017379738102015710">"Activată la apus"</string>
<string name="quick_settings_dark_mode_secondary_label_until_sunrise" msgid="4404885070316716472">"Până la răsărit"</string>
+ <string name="quick_settings_dark_mode_secondary_label_on_at" msgid="5128758823486361279">"Activată la <xliff:g id="TIME">%s</xliff:g>"</string>
+ <string name="quick_settings_dark_mode_secondary_label_until" msgid="2289774641256492437">"Până la <xliff:g id="TIME">%s</xliff:g>"</string>
<string name="quick_settings_nfc_label" msgid="1054317416221168085">"NFC"</string>
<string name="quick_settings_nfc_off" msgid="3465000058515424663">"Serviciul NFC este dezactivat"</string>
<string name="quick_settings_nfc_on" msgid="1004976611203202230">"Serviciul NFC este activat"</string>
- <!-- no translation found for quick_settings_screen_record_label (1594046461509776676) -->
- <skip />
- <!-- no translation found for quick_settings_screen_record_start (1574725369331638985) -->
- <skip />
- <!-- no translation found for quick_settings_screen_record_stop (8087348522976412119) -->
- <skip />
+ <string name="quick_settings_screen_record_label" msgid="1594046461509776676">"Înregistrarea ecranului"</string>
+ <string name="quick_settings_screen_record_start" msgid="1574725369331638985">"Începeți"</string>
+ <string name="quick_settings_screen_record_stop" msgid="8087348522976412119">"Opriți"</string>
<string name="recents_swipe_up_onboarding" msgid="2820265886420993995">"Glisați în sus pentru a comuta între aplicații"</string>
<string name="recents_quick_scrub_onboarding" msgid="765934300283514912">"Glisați la dreapta pentru a comuta rapid între aplicații"</string>
<string name="quick_step_accessibility_toggle_overview" msgid="7908949976727578403">"Comutați secțiunea Recente"</string>
@@ -699,22 +700,14 @@
<string name="notification_app_settings" msgid="8963648463858039377">"Personalizați"</string>
<string name="notification_done" msgid="6215117625922713976">"Terminat"</string>
<string name="inline_undo" msgid="9026953267645116526">"Anulați"</string>
- <!-- no translation found for demote (6225813324237153980) -->
- <skip />
- <!-- no translation found for notification_conversation_favorite (8252976467488182853) -->
- <skip />
- <!-- no translation found for notification_conversation_unfavorite (633301300443356176) -->
- <skip />
- <!-- no translation found for notification_conversation_mute (477431709687199671) -->
- <skip />
- <!-- no translation found for notification_conversation_unmute (410885000669775294) -->
- <skip />
- <!-- no translation found for notification_conversation_bubble (4598142032706190028) -->
- <skip />
- <!-- no translation found for notification_conversation_unbubble (2303087159802926401) -->
- <skip />
- <!-- no translation found for notification_conversation_home_screen (8347136037958438935) -->
- <skip />
+ <string name="demote" msgid="6225813324237153980">"Marcați această notificare ca nefiind o conversație"</string>
+ <string name="notification_conversation_favorite" msgid="8252976467488182853">"Preferată"</string>
+ <string name="notification_conversation_unfavorite" msgid="633301300443356176">"Anulați marcarea ca preferată"</string>
+ <string name="notification_conversation_mute" msgid="477431709687199671">"Ignorați"</string>
+ <string name="notification_conversation_unmute" msgid="410885000669775294">"Afișați"</string>
+ <string name="notification_conversation_bubble" msgid="4598142032706190028">"Afișează sub formă de balon"</string>
+ <string name="notification_conversation_unbubble" msgid="2303087159802926401">"Dezactivați baloanele"</string>
+ <string name="notification_conversation_home_screen" msgid="8347136037958438935">"Adăugați pe ecranul de pornire"</string>
<string name="notification_menu_accessibility" msgid="8984166825879886773">"<xliff:g id="APP_NAME">%1$s</xliff:g> <xliff:g id="MENU_DESCRIPTION">%2$s</xliff:g>"</string>
<string name="notification_menu_gear_description" msgid="6429668976593634862">"comenzile notificării"</string>
<string name="notification_menu_snooze_description" msgid="4740133348901973244">"opțiuni de amânare a notificării"</string>
diff --git a/packages/SystemUI/res/values-ru/strings.xml b/packages/SystemUI/res/values-ru/strings.xml
index 915698972541..0ee159d0c648 100644
--- a/packages/SystemUI/res/values-ru/strings.xml
+++ b/packages/SystemUI/res/values-ru/strings.xml
@@ -71,6 +71,7 @@
<string name="compat_mode_on" msgid="4963711187149440884">"Подогнать по размерам экрана"</string>
<string name="compat_mode_off" msgid="7682459748279487945">"Растянуть на весь экран"</string>
<string name="global_action_screenshot" msgid="2760267567509131654">"Скриншот"</string>
+ <string name="remote_input_image_insertion_text" msgid="4613177882724332877">"Изображение вставлено"</string>
<string name="screenshot_saving_ticker" msgid="6519186952674544916">"Сохранение..."</string>
<string name="screenshot_saving_title" msgid="2298349784913287333">"Сохранение..."</string>
<string name="screenshot_saved_title" msgid="8893267638659083153">"Скриншот сохранен"</string>
@@ -84,6 +85,7 @@
<string name="screenrecord_start_label" msgid="1539048263178882562">"Начать запись"</string>
<string name="screenrecord_mic_label" msgid="6134198080740031632">"Записать закадровую речь"</string>
<string name="screenrecord_taps_label" msgid="2518244240225925076">"Показывать нажатия"</string>
+ <string name="screenrecord_stop_text" msgid="6549288689506057686">"Нажмите, чтобы остановить"</string>
<string name="screenrecord_stop_label" msgid="72699670052087989">"Остановить"</string>
<string name="screenrecord_pause_label" msgid="6004054907104549857">"Приостановить"</string>
<string name="screenrecord_resume_label" msgid="4972223043729555575">"Возобновить"</string>
@@ -392,15 +394,14 @@
<string name="quick_settings_dark_mode_secondary_label_battery_saver" msgid="4990712734503013251">"Режим энергосбер."</string>
<string name="quick_settings_dark_mode_secondary_label_on_at_sunset" msgid="6017379738102015710">"Вкл. на закате"</string>
<string name="quick_settings_dark_mode_secondary_label_until_sunrise" msgid="4404885070316716472">"До рассвета"</string>
+ <string name="quick_settings_dark_mode_secondary_label_on_at" msgid="5128758823486361279">"Включить в <xliff:g id="TIME">%s</xliff:g>"</string>
+ <string name="quick_settings_dark_mode_secondary_label_until" msgid="2289774641256492437">"До <xliff:g id="TIME">%s</xliff:g>"</string>
<string name="quick_settings_nfc_label" msgid="1054317416221168085">"Модуль NFC"</string>
<string name="quick_settings_nfc_off" msgid="3465000058515424663">"Модуль NFC отключен"</string>
<string name="quick_settings_nfc_on" msgid="1004976611203202230">"Модуль NFC включен"</string>
- <!-- no translation found for quick_settings_screen_record_label (1594046461509776676) -->
- <skip />
- <!-- no translation found for quick_settings_screen_record_start (1574725369331638985) -->
- <skip />
- <!-- no translation found for quick_settings_screen_record_stop (8087348522976412119) -->
- <skip />
+ <string name="quick_settings_screen_record_label" msgid="1594046461509776676">"Запись экрана"</string>
+ <string name="quick_settings_screen_record_start" msgid="1574725369331638985">"Начать"</string>
+ <string name="quick_settings_screen_record_stop" msgid="8087348522976412119">"Остановить"</string>
<string name="recents_swipe_up_onboarding" msgid="2820265886420993995">"Чтобы переключиться между приложениями, проведите по экрану вверх."</string>
<string name="recents_quick_scrub_onboarding" msgid="765934300283514912">"Перетащите вправо, чтобы быстро переключиться между приложениями"</string>
<string name="quick_step_accessibility_toggle_overview" msgid="7908949976727578403">"Переключить режим обзора"</string>
@@ -702,22 +703,14 @@
<string name="notification_app_settings" msgid="8963648463858039377">"Настроить"</string>
<string name="notification_done" msgid="6215117625922713976">"Готово"</string>
<string name="inline_undo" msgid="9026953267645116526">"Отменить"</string>
- <!-- no translation found for demote (6225813324237153980) -->
- <skip />
- <!-- no translation found for notification_conversation_favorite (8252976467488182853) -->
- <skip />
- <!-- no translation found for notification_conversation_unfavorite (633301300443356176) -->
- <skip />
- <!-- no translation found for notification_conversation_mute (477431709687199671) -->
- <skip />
- <!-- no translation found for notification_conversation_unmute (410885000669775294) -->
- <skip />
- <!-- no translation found for notification_conversation_bubble (4598142032706190028) -->
- <skip />
- <!-- no translation found for notification_conversation_unbubble (2303087159802926401) -->
- <skip />
- <!-- no translation found for notification_conversation_home_screen (8347136037958438935) -->
- <skip />
+ <string name="demote" msgid="6225813324237153980">"Понизить приоритет уведомления (не считать чатом)"</string>
+ <string name="notification_conversation_favorite" msgid="8252976467488182853">"Добавить в избранное"</string>
+ <string name="notification_conversation_unfavorite" msgid="633301300443356176">"Удалить из избранного"</string>
+ <string name="notification_conversation_mute" msgid="477431709687199671">"Отключить звук"</string>
+ <string name="notification_conversation_unmute" msgid="410885000669775294">"Включить звук"</string>
+ <string name="notification_conversation_bubble" msgid="4598142032706190028">"Показывать как всплывающее уведомление"</string>
+ <string name="notification_conversation_unbubble" msgid="2303087159802926401">"Отключить показ всплывающих уведомлений"</string>
+ <string name="notification_conversation_home_screen" msgid="8347136037958438935">"Добавить на главный экран"</string>
<string name="notification_menu_accessibility" msgid="8984166825879886773">"<xliff:g id="APP_NAME">%1$s</xliff:g>: <xliff:g id="MENU_DESCRIPTION">%2$s</xliff:g>"</string>
<string name="notification_menu_gear_description" msgid="6429668976593634862">"настройки уведомлений"</string>
<string name="notification_menu_snooze_description" msgid="4740133348901973244">"параметры отсрочки уведомлений"</string>
diff --git a/packages/SystemUI/res/values-si/strings.xml b/packages/SystemUI/res/values-si/strings.xml
index 84e01d093f3d..2c5e1d8ba117 100644
--- a/packages/SystemUI/res/values-si/strings.xml
+++ b/packages/SystemUI/res/values-si/strings.xml
@@ -71,6 +71,7 @@
<string name="compat_mode_on" msgid="4963711187149440884">"තිරය පිරවීමට විශාලනය කරන්න"</string>
<string name="compat_mode_off" msgid="7682459748279487945">"තිරය පිරවීමට අදින්න"</string>
<string name="global_action_screenshot" msgid="2760267567509131654">"තිර රුව"</string>
+ <string name="remote_input_image_insertion_text" msgid="4613177882724332877">"රූපය ඇතුල් කරන ලදී"</string>
<string name="screenshot_saving_ticker" msgid="6519186952674544916">"තිර රුව සුරකිමින්…"</string>
<string name="screenshot_saving_title" msgid="2298349784913287333">"තිර රුව සුරැකෙමින් පවතී…"</string>
<string name="screenshot_saved_title" msgid="8893267638659083153">"තිර රුව සුරකින ලදී"</string>
@@ -84,6 +85,7 @@
<string name="screenrecord_start_label" msgid="1539048263178882562">"පටිගත කිරීම ආරම්භ කරන්න"</string>
<string name="screenrecord_mic_label" msgid="6134198080740031632">"පසුබිම් කථනය පටිගත කරන්න"</string>
<string name="screenrecord_taps_label" msgid="2518244240225925076">"තට්ටු කිරීම් පෙන්වන්න"</string>
+ <string name="screenrecord_stop_text" msgid="6549288689506057686">"නතර කිරීමට තට්ටු කරන්න"</string>
<string name="screenrecord_stop_label" msgid="72699670052087989">"නතර කරන්න"</string>
<string name="screenrecord_pause_label" msgid="6004054907104549857">"විරාම කරන්න"</string>
<string name="screenrecord_resume_label" msgid="4972223043729555575">"නැවත අරඹන්න"</string>
@@ -388,15 +390,14 @@
<string name="quick_settings_dark_mode_secondary_label_battery_saver" msgid="4990712734503013251">"බැටරි සුරැකුම"</string>
<string name="quick_settings_dark_mode_secondary_label_on_at_sunset" msgid="6017379738102015710">"හිරු බැසීමේදී ඔන්ය"</string>
<string name="quick_settings_dark_mode_secondary_label_until_sunrise" msgid="4404885070316716472">"හිරු නගින තෙක්"</string>
+ <string name="quick_settings_dark_mode_secondary_label_on_at" msgid="5128758823486361279">"<xliff:g id="TIME">%s</xliff:g>ට ක්‍රියාත්මකයි"</string>
+ <string name="quick_settings_dark_mode_secondary_label_until" msgid="2289774641256492437">"<xliff:g id="TIME">%s</xliff:g> තෙක්"</string>
<string name="quick_settings_nfc_label" msgid="1054317416221168085">"NFC"</string>
<string name="quick_settings_nfc_off" msgid="3465000058515424663">"NFC අබලයි"</string>
<string name="quick_settings_nfc_on" msgid="1004976611203202230">"NFC සබලයි"</string>
- <!-- no translation found for quick_settings_screen_record_label (1594046461509776676) -->
- <skip />
- <!-- no translation found for quick_settings_screen_record_start (1574725369331638985) -->
- <skip />
- <!-- no translation found for quick_settings_screen_record_stop (8087348522976412119) -->
- <skip />
+ <string name="quick_settings_screen_record_label" msgid="1594046461509776676">"තිර පටිගත කිරීම"</string>
+ <string name="quick_settings_screen_record_start" msgid="1574725369331638985">"ආරම්භ කරන්න"</string>
+ <string name="quick_settings_screen_record_stop" msgid="8087348522976412119">"නතර කරන්න"</string>
<string name="recents_swipe_up_onboarding" msgid="2820265886420993995">"යෙදුම් මාරු කිරීමට ස්වයිප් කරන්න"</string>
<string name="recents_quick_scrub_onboarding" msgid="765934300283514912">"ඉක්මනින් යෙදුම් මාරු කිරීමට දකුණට අදින්න"</string>
<string name="quick_step_accessibility_toggle_overview" msgid="7908949976727578403">"දළ විශ්ලේෂණය ටොගල කරන්න"</string>
@@ -696,22 +697,14 @@
<string name="notification_app_settings" msgid="8963648463858039377">"අභිරුචිකරණය"</string>
<string name="notification_done" msgid="6215117625922713976">"නිමයි"</string>
<string name="inline_undo" msgid="9026953267645116526">"පසුගමනය කරන්න"</string>
- <!-- no translation found for demote (6225813324237153980) -->
- <skip />
- <!-- no translation found for notification_conversation_favorite (8252976467488182853) -->
- <skip />
- <!-- no translation found for notification_conversation_unfavorite (633301300443356176) -->
- <skip />
- <!-- no translation found for notification_conversation_mute (477431709687199671) -->
- <skip />
- <!-- no translation found for notification_conversation_unmute (410885000669775294) -->
- <skip />
- <!-- no translation found for notification_conversation_bubble (4598142032706190028) -->
- <skip />
- <!-- no translation found for notification_conversation_unbubble (2303087159802926401) -->
- <skip />
- <!-- no translation found for notification_conversation_home_screen (8347136037958438935) -->
- <skip />
+ <string name="demote" msgid="6225813324237153980">"මෙම දැනුම් දීම සංවාදයක් නොවේ ලෙස ලකුණු කරන්න"</string>
+ <string name="notification_conversation_favorite" msgid="8252976467488182853">"ප්‍රියතම"</string>
+ <string name="notification_conversation_unfavorite" msgid="633301300443356176">"ප්‍රියතම වෙතින් ඉවත් කරන්න"</string>
+ <string name="notification_conversation_mute" msgid="477431709687199671">"නිහඬ කරන්න"</string>
+ <string name="notification_conversation_unmute" msgid="410885000669775294">"නිහඬ වෙතින් ඉවත් කරන්න"</string>
+ <string name="notification_conversation_bubble" msgid="4598142032706190028">"බුබුලක් ලෙස පෙන්වන්න"</string>
+ <string name="notification_conversation_unbubble" msgid="2303087159802926401">"බුබුලු ක්‍රියාවිරහිත කරන්න"</string>
+ <string name="notification_conversation_home_screen" msgid="8347136037958438935">"මුල් තිරය වෙත එක් කරන්න"</string>
<string name="notification_menu_accessibility" msgid="8984166825879886773">"<xliff:g id="APP_NAME">%1$s</xliff:g> <xliff:g id="MENU_DESCRIPTION">%2$s</xliff:g>"</string>
<string name="notification_menu_gear_description" msgid="6429668976593634862">"දැනුම්දීම් පාලන"</string>
<string name="notification_menu_snooze_description" msgid="4740133348901973244">"දැනුම්දීම් මදක් නතර කිරීමේ විකල්ප"</string>
diff --git a/packages/SystemUI/res/values-sk/strings.xml b/packages/SystemUI/res/values-sk/strings.xml
index fced8a42acf4..93f93a2860a9 100644
--- a/packages/SystemUI/res/values-sk/strings.xml
+++ b/packages/SystemUI/res/values-sk/strings.xml
@@ -71,6 +71,7 @@
<string name="compat_mode_on" msgid="4963711187149440884">"Priblížiť na celú obrazovku"</string>
<string name="compat_mode_off" msgid="7682459748279487945">"Na celú obrazovku"</string>
<string name="global_action_screenshot" msgid="2760267567509131654">"Snímka obrazovky"</string>
+ <string name="remote_input_image_insertion_text" msgid="4613177882724332877">"Bol vložený obrázok"</string>
<string name="screenshot_saving_ticker" msgid="6519186952674544916">"Prebieha ukladanie snímky obrazovky..."</string>
<string name="screenshot_saving_title" msgid="2298349784913287333">"Prebieha ukladanie snímky obrazovky..."</string>
<string name="screenshot_saved_title" msgid="8893267638659083153">"Snímka obrazovky bola uložená"</string>
@@ -84,6 +85,7 @@
<string name="screenrecord_start_label" msgid="1539048263178882562">"Spustiť zaznamenávanie"</string>
<string name="screenrecord_mic_label" msgid="6134198080740031632">"Hlasový vstup počas záznamu"</string>
<string name="screenrecord_taps_label" msgid="2518244240225925076">"Zobrazovať klepnutia"</string>
+ <string name="screenrecord_stop_text" msgid="6549288689506057686">"Zastavte klepnutím"</string>
<string name="screenrecord_stop_label" msgid="72699670052087989">"Ukončiť"</string>
<string name="screenrecord_pause_label" msgid="6004054907104549857">"Pozastaviť"</string>
<string name="screenrecord_resume_label" msgid="4972223043729555575">"Obnoviť"</string>
@@ -392,6 +394,8 @@
<string name="quick_settings_dark_mode_secondary_label_battery_saver" msgid="4990712734503013251">"Šetrič batérie"</string>
<string name="quick_settings_dark_mode_secondary_label_on_at_sunset" msgid="6017379738102015710">"Zap. pri záp. slnka"</string>
<string name="quick_settings_dark_mode_secondary_label_until_sunrise" msgid="4404885070316716472">"Do východu slnka"</string>
+ <string name="quick_settings_dark_mode_secondary_label_on_at" msgid="5128758823486361279">"Zapne sa o <xliff:g id="TIME">%s</xliff:g>"</string>
+ <string name="quick_settings_dark_mode_secondary_label_until" msgid="2289774641256492437">"Do <xliff:g id="TIME">%s</xliff:g>"</string>
<string name="quick_settings_nfc_label" msgid="1054317416221168085">"NFC"</string>
<string name="quick_settings_nfc_off" msgid="3465000058515424663">"NFC je deaktivované"</string>
<string name="quick_settings_nfc_on" msgid="1004976611203202230">"NFC je aktivované"</string>
diff --git a/packages/SystemUI/res/values-sl/strings.xml b/packages/SystemUI/res/values-sl/strings.xml
index 4816a6634dd7..452827fa362f 100644
--- a/packages/SystemUI/res/values-sl/strings.xml
+++ b/packages/SystemUI/res/values-sl/strings.xml
@@ -71,6 +71,7 @@
<string name="compat_mode_on" msgid="4963711187149440884">"Povečava čez cel zaslon"</string>
<string name="compat_mode_off" msgid="7682459748279487945">"Raztegnitev čez zaslon"</string>
<string name="global_action_screenshot" msgid="2760267567509131654">"Posnetek zaslona"</string>
+ <string name="remote_input_image_insertion_text" msgid="4613177882724332877">"Slika je vstavljena"</string>
<string name="screenshot_saving_ticker" msgid="6519186952674544916">"Shranjev. posnetka zaslona ..."</string>
<string name="screenshot_saving_title" msgid="2298349784913287333">"Shranjevanje posnetka zaslona ..."</string>
<string name="screenshot_saved_title" msgid="8893267638659083153">"Posnetek zaslona je shranjen"</string>
@@ -84,6 +85,7 @@
<string name="screenrecord_start_label" msgid="1539048263178882562">"Začni snemanje"</string>
<string name="screenrecord_mic_label" msgid="6134198080740031632">"Snemanje spremnega govora"</string>
<string name="screenrecord_taps_label" msgid="2518244240225925076">"Prikaz dotikov"</string>
+ <string name="screenrecord_stop_text" msgid="6549288689506057686">"Dotaknite se, da ustavite"</string>
<string name="screenrecord_stop_label" msgid="72699670052087989">"Ustavi"</string>
<string name="screenrecord_pause_label" msgid="6004054907104549857">"Začasno ustavi"</string>
<string name="screenrecord_resume_label" msgid="4972223043729555575">"Nadaljuj"</string>
@@ -392,15 +394,14 @@
<string name="quick_settings_dark_mode_secondary_label_battery_saver" msgid="4990712734503013251">"Varč. z ener. bater."</string>
<string name="quick_settings_dark_mode_secondary_label_on_at_sunset" msgid="6017379738102015710">"Ob sončnem zahodu"</string>
<string name="quick_settings_dark_mode_secondary_label_until_sunrise" msgid="4404885070316716472">"Do sončnega vzhoda"</string>
+ <string name="quick_settings_dark_mode_secondary_label_on_at" msgid="5128758823486361279">"Vklop ob <xliff:g id="TIME">%s</xliff:g>"</string>
+ <string name="quick_settings_dark_mode_secondary_label_until" msgid="2289774641256492437">"Do <xliff:g id="TIME">%s</xliff:g>"</string>
<string name="quick_settings_nfc_label" msgid="1054317416221168085">"NFC"</string>
<string name="quick_settings_nfc_off" msgid="3465000058515424663">"Tehnologija NFC je onemogočena"</string>
<string name="quick_settings_nfc_on" msgid="1004976611203202230">"Tehnologija NFC je omogočena"</string>
- <!-- no translation found for quick_settings_screen_record_label (1594046461509776676) -->
- <skip />
- <!-- no translation found for quick_settings_screen_record_start (1574725369331638985) -->
- <skip />
- <!-- no translation found for quick_settings_screen_record_stop (8087348522976412119) -->
- <skip />
+ <string name="quick_settings_screen_record_label" msgid="1594046461509776676">"Snemanje zaslona"</string>
+ <string name="quick_settings_screen_record_start" msgid="1574725369331638985">"Začni"</string>
+ <string name="quick_settings_screen_record_stop" msgid="8087348522976412119">"Ustavi"</string>
<string name="recents_swipe_up_onboarding" msgid="2820265886420993995">"Za preklop aplikacij povlecite navzgor"</string>
<string name="recents_quick_scrub_onboarding" msgid="765934300283514912">"Povlecite v desno za hiter preklop med aplikacijami"</string>
<string name="quick_step_accessibility_toggle_overview" msgid="7908949976727578403">"Vklop/izklop pregleda"</string>
@@ -702,22 +703,14 @@
<string name="notification_app_settings" msgid="8963648463858039377">"Prilagodi"</string>
<string name="notification_done" msgid="6215117625922713976">"Dokončano"</string>
<string name="inline_undo" msgid="9026953267645116526">"Razveljavi"</string>
- <!-- no translation found for demote (6225813324237153980) -->
- <skip />
- <!-- no translation found for notification_conversation_favorite (8252976467488182853) -->
- <skip />
- <!-- no translation found for notification_conversation_unfavorite (633301300443356176) -->
- <skip />
- <!-- no translation found for notification_conversation_mute (477431709687199671) -->
- <skip />
- <!-- no translation found for notification_conversation_unmute (410885000669775294) -->
- <skip />
- <!-- no translation found for notification_conversation_bubble (4598142032706190028) -->
- <skip />
- <!-- no translation found for notification_conversation_unbubble (2303087159802926401) -->
- <skip />
- <!-- no translation found for notification_conversation_home_screen (8347136037958438935) -->
- <skip />
+ <string name="demote" msgid="6225813324237153980">"Označi, da to obvestilo ni pogovor"</string>
+ <string name="notification_conversation_favorite" msgid="8252976467488182853">"Priljubljeno"</string>
+ <string name="notification_conversation_unfavorite" msgid="633301300443356176">"Odstrani iz priljubljenih"</string>
+ <string name="notification_conversation_mute" msgid="477431709687199671">"Prezri"</string>
+ <string name="notification_conversation_unmute" msgid="410885000669775294">"Znova prikaži"</string>
+ <string name="notification_conversation_bubble" msgid="4598142032706190028">"Pokaži kot oblaček"</string>
+ <string name="notification_conversation_unbubble" msgid="2303087159802926401">"Izklopi oblačke"</string>
+ <string name="notification_conversation_home_screen" msgid="8347136037958438935">"Dodaj na začetni zaslon"</string>
<string name="notification_menu_accessibility" msgid="8984166825879886773">"<xliff:g id="APP_NAME">%1$s</xliff:g> <xliff:g id="MENU_DESCRIPTION">%2$s</xliff:g>"</string>
<string name="notification_menu_gear_description" msgid="6429668976593634862">"kontrolniki obvestil"</string>
<string name="notification_menu_snooze_description" msgid="4740133348901973244">"možnosti preložitve obvestil"</string>
diff --git a/packages/SystemUI/res/values-sq/strings.xml b/packages/SystemUI/res/values-sq/strings.xml
index 14d72b4e1675..2dc3cc8b1cd2 100644
--- a/packages/SystemUI/res/values-sq/strings.xml
+++ b/packages/SystemUI/res/values-sq/strings.xml
@@ -71,6 +71,7 @@
<string name="compat_mode_on" msgid="4963711187149440884">"Zmadho për të mbushur ekranin"</string>
<string name="compat_mode_off" msgid="7682459748279487945">"Shtrije për të mbushur ekranin"</string>
<string name="global_action_screenshot" msgid="2760267567509131654">"Pamja e ekranit"</string>
+ <string name="remote_input_image_insertion_text" msgid="4613177882724332877">"Imazhi është futur"</string>
<string name="screenshot_saving_ticker" msgid="6519186952674544916">"Po ruan pamjen e ekranit..."</string>
<string name="screenshot_saving_title" msgid="2298349784913287333">"Po ruan pamjen e ekranit…"</string>
<string name="screenshot_saved_title" msgid="8893267638659083153">"Pamja e ekranit u ruajt"</string>
@@ -84,6 +85,7 @@
<string name="screenrecord_start_label" msgid="1539048263178882562">"Nis regjistrimin"</string>
<string name="screenrecord_mic_label" msgid="6134198080740031632">"Regjistro zërin e mikrofonit"</string>
<string name="screenrecord_taps_label" msgid="2518244240225925076">"Shfaq trokitjet"</string>
+ <string name="screenrecord_stop_text" msgid="6549288689506057686">"Trokit për të ndaluar"</string>
<string name="screenrecord_stop_label" msgid="72699670052087989">"Ndalo"</string>
<string name="screenrecord_pause_label" msgid="6004054907104549857">"Ndërprit"</string>
<string name="screenrecord_resume_label" msgid="4972223043729555575">"Vazhdo"</string>
@@ -388,15 +390,14 @@
<string name="quick_settings_dark_mode_secondary_label_battery_saver" msgid="4990712734503013251">"Kursyesi i baterisë"</string>
<string name="quick_settings_dark_mode_secondary_label_on_at_sunset" msgid="6017379738102015710">"Aktiv në perëndim"</string>
<string name="quick_settings_dark_mode_secondary_label_until_sunrise" msgid="4404885070316716472">"Deri në lindje të diellit"</string>
+ <string name="quick_settings_dark_mode_secondary_label_on_at" msgid="5128758823486361279">"Aktiv në <xliff:g id="TIME">%s</xliff:g>"</string>
+ <string name="quick_settings_dark_mode_secondary_label_until" msgid="2289774641256492437">"Deri në <xliff:g id="TIME">%s</xliff:g>"</string>
<string name="quick_settings_nfc_label" msgid="1054317416221168085">"NFC"</string>
<string name="quick_settings_nfc_off" msgid="3465000058515424663">"NFC është çaktivizuar"</string>
<string name="quick_settings_nfc_on" msgid="1004976611203202230">"NFC është aktivizuar"</string>
- <!-- no translation found for quick_settings_screen_record_label (1594046461509776676) -->
- <skip />
- <!-- no translation found for quick_settings_screen_record_start (1574725369331638985) -->
- <skip />
- <!-- no translation found for quick_settings_screen_record_stop (8087348522976412119) -->
- <skip />
+ <string name="quick_settings_screen_record_label" msgid="1594046461509776676">"Regjistrimi i ekranit"</string>
+ <string name="quick_settings_screen_record_start" msgid="1574725369331638985">"Nis"</string>
+ <string name="quick_settings_screen_record_stop" msgid="8087348522976412119">"Ndalo"</string>
<string name="recents_swipe_up_onboarding" msgid="2820265886420993995">"Rrëshqit shpejt lart për të ndërruar aplikacionet"</string>
<string name="recents_quick_scrub_onboarding" msgid="765934300283514912">"Zvarrit djathtas për të ndërruar aplikacionet me shpejtësi"</string>
<string name="quick_step_accessibility_toggle_overview" msgid="7908949976727578403">"Kalo te përmbledhja"</string>
@@ -696,22 +697,14 @@
<string name="notification_app_settings" msgid="8963648463858039377">"Personalizo"</string>
<string name="notification_done" msgid="6215117625922713976">"U krye"</string>
<string name="inline_undo" msgid="9026953267645116526">"Zhbëj"</string>
- <!-- no translation found for demote (6225813324237153980) -->
- <skip />
- <!-- no translation found for notification_conversation_favorite (8252976467488182853) -->
- <skip />
- <!-- no translation found for notification_conversation_unfavorite (633301300443356176) -->
- <skip />
- <!-- no translation found for notification_conversation_mute (477431709687199671) -->
- <skip />
- <!-- no translation found for notification_conversation_unmute (410885000669775294) -->
- <skip />
- <!-- no translation found for notification_conversation_bubble (4598142032706190028) -->
- <skip />
- <!-- no translation found for notification_conversation_unbubble (2303087159802926401) -->
- <skip />
- <!-- no translation found for notification_conversation_home_screen (8347136037958438935) -->
- <skip />
+ <string name="demote" msgid="6225813324237153980">"Shëno se ky njoftim nuk është një bisedë"</string>
+ <string name="notification_conversation_favorite" msgid="8252976467488182853">"Shëno si të preferuar"</string>
+ <string name="notification_conversation_unfavorite" msgid="633301300443356176">"Hiq nga të preferuarat"</string>
+ <string name="notification_conversation_mute" msgid="477431709687199671">"Kalo në heshtje"</string>
+ <string name="notification_conversation_unmute" msgid="410885000669775294">"Aktivizo"</string>
+ <string name="notification_conversation_bubble" msgid="4598142032706190028">"Shfaqe si flluskë"</string>
+ <string name="notification_conversation_unbubble" msgid="2303087159802926401">"Çaktivizo flluskat"</string>
+ <string name="notification_conversation_home_screen" msgid="8347136037958438935">"Shto në ekranin bazë"</string>
<string name="notification_menu_accessibility" msgid="8984166825879886773">"<xliff:g id="APP_NAME">%1$s</xliff:g> <xliff:g id="MENU_DESCRIPTION">%2$s</xliff:g>"</string>
<string name="notification_menu_gear_description" msgid="6429668976593634862">"kontrollet e njoftimit"</string>
<string name="notification_menu_snooze_description" msgid="4740133348901973244">"opsionet e shtyrjes së njoftimit"</string>
diff --git a/packages/SystemUI/res/values-sr/strings.xml b/packages/SystemUI/res/values-sr/strings.xml
index 29e7e1044d61..4127806f9cd3 100644
--- a/packages/SystemUI/res/values-sr/strings.xml
+++ b/packages/SystemUI/res/values-sr/strings.xml
@@ -71,6 +71,7 @@
<string name="compat_mode_on" msgid="4963711187149440884">"Зумирај на целом екрану"</string>
<string name="compat_mode_off" msgid="7682459748279487945">"Развуци на цео екран"</string>
<string name="global_action_screenshot" msgid="2760267567509131654">"Снимак екрана"</string>
+ <string name="remote_input_image_insertion_text" msgid="4613177882724332877">"Слика је уметнута"</string>
<string name="screenshot_saving_ticker" msgid="6519186952674544916">"Чување снимка екрана..."</string>
<string name="screenshot_saving_title" msgid="2298349784913287333">"Чување снимка екрана..."</string>
<string name="screenshot_saved_title" msgid="8893267638659083153">"Снимак екрана је сачуван"</string>
@@ -84,6 +85,7 @@
<string name="screenrecord_start_label" msgid="1539048263178882562">"Започни снимање"</string>
<string name="screenrecord_mic_label" msgid="6134198080740031632">"Сними пренос гласа"</string>
<string name="screenrecord_taps_label" msgid="2518244240225925076">"Приказуј додире"</string>
+ <string name="screenrecord_stop_text" msgid="6549288689506057686">"Додирните да бисте зауставили"</string>
<string name="screenrecord_stop_label" msgid="72699670052087989">"Заустави"</string>
<string name="screenrecord_pause_label" msgid="6004054907104549857">"Паузирај"</string>
<string name="screenrecord_resume_label" msgid="4972223043729555575">"Настави"</string>
@@ -390,6 +392,8 @@
<string name="quick_settings_dark_mode_secondary_label_battery_saver" msgid="4990712734503013251">"Уштеда батерије"</string>
<string name="quick_settings_dark_mode_secondary_label_on_at_sunset" msgid="6017379738102015710">"Укључује се по заласку сунца"</string>
<string name="quick_settings_dark_mode_secondary_label_until_sunrise" msgid="4404885070316716472">"До изласка сунца"</string>
+ <string name="quick_settings_dark_mode_secondary_label_on_at" msgid="5128758823486361279">"Укључује се у <xliff:g id="TIME">%s</xliff:g>"</string>
+ <string name="quick_settings_dark_mode_secondary_label_until" msgid="2289774641256492437">"До <xliff:g id="TIME">%s</xliff:g>"</string>
<string name="quick_settings_nfc_label" msgid="1054317416221168085">"NFC"</string>
<string name="quick_settings_nfc_off" msgid="3465000058515424663">"NFC је онемогућен"</string>
<string name="quick_settings_nfc_on" msgid="1004976611203202230">"NFC је омогућен"</string>
diff --git a/packages/SystemUI/res/values-sv/strings.xml b/packages/SystemUI/res/values-sv/strings.xml
index b0d78779daad..3e29fa6e6749 100644
--- a/packages/SystemUI/res/values-sv/strings.xml
+++ b/packages/SystemUI/res/values-sv/strings.xml
@@ -71,6 +71,7 @@
<string name="compat_mode_on" msgid="4963711187149440884">"Zooma för att fylla skärm"</string>
<string name="compat_mode_off" msgid="7682459748279487945">"Dra för att fylla skärmen"</string>
<string name="global_action_screenshot" msgid="2760267567509131654">"Skärmdump"</string>
+ <string name="remote_input_image_insertion_text" msgid="4613177882724332877">"Bilden har infogats"</string>
<string name="screenshot_saving_ticker" msgid="6519186952674544916">"Skärmdumpen sparas ..."</string>
<string name="screenshot_saving_title" msgid="2298349784913287333">"Skärmdumpen sparas ..."</string>
<string name="screenshot_saved_title" msgid="8893267638659083153">"Skärmdumpen har sparats"</string>
@@ -84,6 +85,7 @@
<string name="screenrecord_start_label" msgid="1539048263178882562">"Börja spela in"</string>
<string name="screenrecord_mic_label" msgid="6134198080740031632">"Spela in med mikrofondubbning"</string>
<string name="screenrecord_taps_label" msgid="2518244240225925076">"Visa tryck"</string>
+ <string name="screenrecord_stop_text" msgid="6549288689506057686">"Tryck för att stoppa"</string>
<string name="screenrecord_stop_label" msgid="72699670052087989">"Stoppa"</string>
<string name="screenrecord_pause_label" msgid="6004054907104549857">"Pausa"</string>
<string name="screenrecord_resume_label" msgid="4972223043729555575">"Återuppta"</string>
@@ -388,6 +390,8 @@
<string name="quick_settings_dark_mode_secondary_label_battery_saver" msgid="4990712734503013251">"Batterisparläge"</string>
<string name="quick_settings_dark_mode_secondary_label_on_at_sunset" msgid="6017379738102015710">"På från solnedgången"</string>
<string name="quick_settings_dark_mode_secondary_label_until_sunrise" msgid="4404885070316716472">"Till soluppgången"</string>
+ <string name="quick_settings_dark_mode_secondary_label_on_at" msgid="5128758823486361279">"Aktivera kl. <xliff:g id="TIME">%s</xliff:g>"</string>
+ <string name="quick_settings_dark_mode_secondary_label_until" msgid="2289774641256492437">"Till <xliff:g id="TIME">%s</xliff:g>"</string>
<string name="quick_settings_nfc_label" msgid="1054317416221168085">"NFC"</string>
<string name="quick_settings_nfc_off" msgid="3465000058515424663">"NFC är inaktiverat"</string>
<string name="quick_settings_nfc_on" msgid="1004976611203202230">"NFC är aktiverat"</string>
diff --git a/packages/SystemUI/res/values-sw/strings.xml b/packages/SystemUI/res/values-sw/strings.xml
index ecaefb0c0e4e..9fd4284710d1 100644
--- a/packages/SystemUI/res/values-sw/strings.xml
+++ b/packages/SystemUI/res/values-sw/strings.xml
@@ -71,6 +71,7 @@
<string name="compat_mode_on" msgid="4963711187149440884">"Kuza ili kujaza skrini"</string>
<string name="compat_mode_off" msgid="7682459748279487945">"Tanua ili kujaza skrini"</string>
<string name="global_action_screenshot" msgid="2760267567509131654">"Picha ya skrini"</string>
+ <string name="remote_input_image_insertion_text" msgid="4613177882724332877">"Picha imewekwa"</string>
<string name="screenshot_saving_ticker" msgid="6519186952674544916">"Inahifadhi picha ya skrini..."</string>
<string name="screenshot_saving_title" msgid="2298349784913287333">"Inahifadhi picha ya skrini..."</string>
<string name="screenshot_saved_title" msgid="8893267638659083153">"Imehifadhi picha ya skrini"</string>
@@ -84,6 +85,7 @@
<string name="screenrecord_start_label" msgid="1539048263178882562">"Anza Kurekodi"</string>
<string name="screenrecord_mic_label" msgid="6134198080740031632">"Rekodi sauti"</string>
<string name="screenrecord_taps_label" msgid="2518244240225925076">"Onyesha unapogusa"</string>
+ <string name="screenrecord_stop_text" msgid="6549288689506057686">"Gusa ili ukomeshe"</string>
<string name="screenrecord_stop_label" msgid="72699670052087989">"Acha"</string>
<string name="screenrecord_pause_label" msgid="6004054907104549857">"Sitisha"</string>
<string name="screenrecord_resume_label" msgid="4972223043729555575">"Endelea"</string>
@@ -388,15 +390,14 @@
<string name="quick_settings_dark_mode_secondary_label_battery_saver" msgid="4990712734503013251">"Kiokoa betri"</string>
<string name="quick_settings_dark_mode_secondary_label_on_at_sunset" msgid="6017379738102015710">"Itawashwa machweo"</string>
<string name="quick_settings_dark_mode_secondary_label_until_sunrise" msgid="4404885070316716472">"Hadi macheo"</string>
+ <string name="quick_settings_dark_mode_secondary_label_on_at" msgid="5128758823486361279">"Itawashwa saa <xliff:g id="TIME">%s</xliff:g>"</string>
+ <string name="quick_settings_dark_mode_secondary_label_until" msgid="2289774641256492437">"Hadi saa <xliff:g id="TIME">%s</xliff:g>"</string>
<string name="quick_settings_nfc_label" msgid="1054317416221168085">"NFC"</string>
<string name="quick_settings_nfc_off" msgid="3465000058515424663">"NFC imezimwa"</string>
<string name="quick_settings_nfc_on" msgid="1004976611203202230">"NFC imewashwa"</string>
- <!-- no translation found for quick_settings_screen_record_label (1594046461509776676) -->
- <skip />
- <!-- no translation found for quick_settings_screen_record_start (1574725369331638985) -->
- <skip />
- <!-- no translation found for quick_settings_screen_record_stop (8087348522976412119) -->
- <skip />
+ <string name="quick_settings_screen_record_label" msgid="1594046461509776676">"Rekodi ya Skrini"</string>
+ <string name="quick_settings_screen_record_start" msgid="1574725369331638985">"Anza kurekodi"</string>
+ <string name="quick_settings_screen_record_stop" msgid="8087348522976412119">"Acha kurekodi"</string>
<string name="recents_swipe_up_onboarding" msgid="2820265886420993995">"Telezesha kidole juu ili ubadilishe programu"</string>
<string name="recents_quick_scrub_onboarding" msgid="765934300283514912">"Buruta kulia ili ubadilishe programu haraka"</string>
<string name="quick_step_accessibility_toggle_overview" msgid="7908949976727578403">"Washa Muhtasari"</string>
@@ -696,22 +697,14 @@
<string name="notification_app_settings" msgid="8963648463858039377">"Weka mapendeleo"</string>
<string name="notification_done" msgid="6215117625922713976">"Nimemaliza"</string>
<string name="inline_undo" msgid="9026953267645116526">"Tendua"</string>
- <!-- no translation found for demote (6225813324237153980) -->
- <skip />
- <!-- no translation found for notification_conversation_favorite (8252976467488182853) -->
- <skip />
- <!-- no translation found for notification_conversation_unfavorite (633301300443356176) -->
- <skip />
- <!-- no translation found for notification_conversation_mute (477431709687199671) -->
- <skip />
- <!-- no translation found for notification_conversation_unmute (410885000669775294) -->
- <skip />
- <!-- no translation found for notification_conversation_bubble (4598142032706190028) -->
- <skip />
- <!-- no translation found for notification_conversation_unbubble (2303087159802926401) -->
- <skip />
- <!-- no translation found for notification_conversation_home_screen (8347136037958438935) -->
- <skip />
+ <string name="demote" msgid="6225813324237153980">"Tia alama arifa hii kuwa si mazungumzo"</string>
+ <string name="notification_conversation_favorite" msgid="8252976467488182853">"Tia alama ya kipendwa kwenye mazungumzo haya"</string>
+ <string name="notification_conversation_unfavorite" msgid="633301300443356176">"Ondoa alama ya kipendwa kwenye mazungumzo haya"</string>
+ <string name="notification_conversation_mute" msgid="477431709687199671">"Komesha mazungumzo"</string>
+ <string name="notification_conversation_unmute" msgid="410885000669775294">"Ruhusu mazungumzo"</string>
+ <string name="notification_conversation_bubble" msgid="4598142032706190028">"Onyesha kuwa kiputo"</string>
+ <string name="notification_conversation_unbubble" msgid="2303087159802926401">"Zima viputo"</string>
+ <string name="notification_conversation_home_screen" msgid="8347136037958438935">"Ongeza kwenye skrini ya kwanza"</string>
<string name="notification_menu_accessibility" msgid="8984166825879886773">"<xliff:g id="APP_NAME">%1$s</xliff:g> <xliff:g id="MENU_DESCRIPTION">%2$s</xliff:g>"</string>
<string name="notification_menu_gear_description" msgid="6429668976593634862">"vidhibiti vya arifa"</string>
<string name="notification_menu_snooze_description" msgid="4740133348901973244">"chaguo za kuahirisha arifa"</string>
diff --git a/packages/SystemUI/res/values-ta/strings.xml b/packages/SystemUI/res/values-ta/strings.xml
index 0bea7d6305d6..55bd472a7889 100644
--- a/packages/SystemUI/res/values-ta/strings.xml
+++ b/packages/SystemUI/res/values-ta/strings.xml
@@ -71,6 +71,7 @@
<string name="compat_mode_on" msgid="4963711187149440884">"திரையை நிரப்ப அளவை மாற்று"</string>
<string name="compat_mode_off" msgid="7682459748279487945">"திரையை நிரப்ப இழு"</string>
<string name="global_action_screenshot" msgid="2760267567509131654">"ஸ்கிரீன்ஷாட்"</string>
+ <string name="remote_input_image_insertion_text" msgid="4613177882724332877">"படம் செருகப்பட்டது"</string>
<string name="screenshot_saving_ticker" msgid="6519186952674544916">"ஸ்க்ரீன் ஷாட்டைச் சேமிக்கிறது…"</string>
<string name="screenshot_saving_title" msgid="2298349784913287333">"ஸ்க்ரீன் ஷாட்டைச் சேமிக்கிறது…"</string>
<string name="screenshot_saved_title" msgid="8893267638659083153">"ஸ்கிரீன்ஷாட் சேமிக்கப்பட்டது"</string>
@@ -84,6 +85,7 @@
<string name="screenrecord_start_label" msgid="1539048263178882562">"ரெக்கார்டிங்கைத் தொடங்கு"</string>
<string name="screenrecord_mic_label" msgid="6134198080740031632">"வாய்ஸ் ஓவரை ரெக்கார்டு செய்"</string>
<string name="screenrecord_taps_label" msgid="2518244240225925076">"தட்டல்களைக் காட்டு"</string>
+ <string name="screenrecord_stop_text" msgid="6549288689506057686">"நிறுத்த, தட்டவும்"</string>
<string name="screenrecord_stop_label" msgid="72699670052087989">"நிறுத்து"</string>
<string name="screenrecord_pause_label" msgid="6004054907104549857">"இடைநிறுத்து"</string>
<string name="screenrecord_resume_label" msgid="4972223043729555575">"மீண்டும் தொடங்கு"</string>
@@ -388,15 +390,14 @@
<string name="quick_settings_dark_mode_secondary_label_battery_saver" msgid="4990712734503013251">"பேட்டரி சேமிப்பான்"</string>
<string name="quick_settings_dark_mode_secondary_label_on_at_sunset" msgid="6017379738102015710">"மாலையில் ஆன் செய்"</string>
<string name="quick_settings_dark_mode_secondary_label_until_sunrise" msgid="4404885070316716472">"காலை வரை"</string>
+ <string name="quick_settings_dark_mode_secondary_label_on_at" msgid="5128758823486361279">"<xliff:g id="TIME">%s</xliff:g>க்கு ஆன் செய்"</string>
+ <string name="quick_settings_dark_mode_secondary_label_until" msgid="2289774641256492437">"<xliff:g id="TIME">%s</xliff:g> வரை"</string>
<string name="quick_settings_nfc_label" msgid="1054317416221168085">"NFC"</string>
<string name="quick_settings_nfc_off" msgid="3465000058515424663">"NFC முடக்கப்பட்டது"</string>
<string name="quick_settings_nfc_on" msgid="1004976611203202230">"NFC இயக்கப்பட்டது"</string>
- <!-- no translation found for quick_settings_screen_record_label (1594046461509776676) -->
- <skip />
- <!-- no translation found for quick_settings_screen_record_start (1574725369331638985) -->
- <skip />
- <!-- no translation found for quick_settings_screen_record_stop (8087348522976412119) -->
- <skip />
+ <string name="quick_settings_screen_record_label" msgid="1594046461509776676">"ஸ்கிரீன் ரெக்கார்டு"</string>
+ <string name="quick_settings_screen_record_start" msgid="1574725369331638985">"தொடங்கு"</string>
+ <string name="quick_settings_screen_record_stop" msgid="8087348522976412119">"நிறுத்து"</string>
<string name="recents_swipe_up_onboarding" msgid="2820265886420993995">"ஆப்ஸிற்கு இடையே மாற்றுவதற்கு, மேல்நோக்கி ஸ்வைப் செய்க"</string>
<string name="recents_quick_scrub_onboarding" msgid="765934300283514912">"ஆப்ஸை வேகமாக மாற்ற, வலப்புறம் இழுக்கவும்"</string>
<string name="quick_step_accessibility_toggle_overview" msgid="7908949976727578403">"மேலோட்டப் பார்வையை நிலைமாற்று"</string>
@@ -696,22 +697,14 @@
<string name="notification_app_settings" msgid="8963648463858039377">"தனிப்பயனாக்கு"</string>
<string name="notification_done" msgid="6215117625922713976">"முடிந்தது"</string>
<string name="inline_undo" msgid="9026953267645116526">"செயல்தவிர்"</string>
- <!-- no translation found for demote (6225813324237153980) -->
- <skip />
- <!-- no translation found for notification_conversation_favorite (8252976467488182853) -->
- <skip />
- <!-- no translation found for notification_conversation_unfavorite (633301300443356176) -->
- <skip />
- <!-- no translation found for notification_conversation_mute (477431709687199671) -->
- <skip />
- <!-- no translation found for notification_conversation_unmute (410885000669775294) -->
- <skip />
- <!-- no translation found for notification_conversation_bubble (4598142032706190028) -->
- <skip />
- <!-- no translation found for notification_conversation_unbubble (2303087159802926401) -->
- <skip />
- <!-- no translation found for notification_conversation_home_screen (8347136037958438935) -->
- <skip />
+ <string name="demote" msgid="6225813324237153980">"இந்த அறிவிப்பை உரையாடல் அல்லாததாகக் குறிக்கவும்"</string>
+ <string name="notification_conversation_favorite" msgid="8252976467488182853">"பிடித்தது"</string>
+ <string name="notification_conversation_unfavorite" msgid="633301300443356176">"பிடித்ததிலிருந்து அகற்று"</string>
+ <string name="notification_conversation_mute" msgid="477431709687199671">"அறிவிப்பைக் காட்டாதே"</string>
+ <string name="notification_conversation_unmute" msgid="410885000669775294">"அறிவிப்பைக் காட்டு"</string>
+ <string name="notification_conversation_bubble" msgid="4598142032706190028">"குமிழாகக் காட்டு"</string>
+ <string name="notification_conversation_unbubble" msgid="2303087159802926401">"குமிழ்களை ஆஃப் செய்"</string>
+ <string name="notification_conversation_home_screen" msgid="8347136037958438935">"முகப்புத் திரையில் சேர்"</string>
<string name="notification_menu_accessibility" msgid="8984166825879886773">"<xliff:g id="APP_NAME">%1$s</xliff:g> <xliff:g id="MENU_DESCRIPTION">%2$s</xliff:g>"</string>
<string name="notification_menu_gear_description" msgid="6429668976593634862">"அறிவிப்புக் கட்டுப்பாடுகள்"</string>
<string name="notification_menu_snooze_description" msgid="4740133348901973244">"அறிவிப்பை உறக்கநிலையாக்கும் விருப்பங்கள்"</string>
diff --git a/packages/SystemUI/res/values-te/strings.xml b/packages/SystemUI/res/values-te/strings.xml
index bb624902beb0..8b9c07ce0264 100644
--- a/packages/SystemUI/res/values-te/strings.xml
+++ b/packages/SystemUI/res/values-te/strings.xml
@@ -71,6 +71,7 @@
<string name="compat_mode_on" msgid="4963711187149440884">"స్క్రీన్‌కు నింపేలా జూమ్ చేయండి"</string>
<string name="compat_mode_off" msgid="7682459748279487945">"స్క్రీన్‌కు నింపేలా విస్తరించండి"</string>
<string name="global_action_screenshot" msgid="2760267567509131654">"స్క్రీన్‌షాట్"</string>
+ <string name="remote_input_image_insertion_text" msgid="4613177882724332877">"చిత్రం చొప్పించబడింది"</string>
<string name="screenshot_saving_ticker" msgid="6519186952674544916">"స్క్రీన్‌షాట్‌ను సేవ్ చేస్తోంది…"</string>
<string name="screenshot_saving_title" msgid="2298349784913287333">"స్క్రీన్‌షాట్‌ను సేవ్ చేస్తోంది…"</string>
<string name="screenshot_saved_title" msgid="8893267638659083153">"స్క్రీన్‌షాట్ సేవ్ చేయబడింది"</string>
@@ -84,6 +85,7 @@
<string name="screenrecord_start_label" msgid="1539048263178882562">"రికార్డింగ్‌ను ప్రారంభించు"</string>
<string name="screenrecord_mic_label" msgid="6134198080740031632">"వాయిస్ఓవర్‌ని రికార్డ్ చేయి"</string>
<string name="screenrecord_taps_label" msgid="2518244240225925076">"నొక్కినవి చూపు"</string>
+ <string name="screenrecord_stop_text" msgid="6549288689506057686">"ఆపడానికి నొక్కండి"</string>
<string name="screenrecord_stop_label" msgid="72699670052087989">"ఆపివేయి"</string>
<string name="screenrecord_pause_label" msgid="6004054907104549857">"పాజ్ చేయి"</string>
<string name="screenrecord_resume_label" msgid="4972223043729555575">"కొనసాగించు"</string>
@@ -388,15 +390,14 @@
<string name="quick_settings_dark_mode_secondary_label_battery_saver" msgid="4990712734503013251">"బ్యాటరీ సేవర్"</string>
<string name="quick_settings_dark_mode_secondary_label_on_at_sunset" msgid="6017379738102015710">"సూర్యాస్తమయానికి"</string>
<string name="quick_settings_dark_mode_secondary_label_until_sunrise" msgid="4404885070316716472">"సూర్యోదయం వరకు"</string>
+ <string name="quick_settings_dark_mode_secondary_label_on_at" msgid="5128758823486361279">"<xliff:g id="TIME">%s</xliff:g> వద్ద"</string>
+ <string name="quick_settings_dark_mode_secondary_label_until" msgid="2289774641256492437">"<xliff:g id="TIME">%s</xliff:g> వరకు"</string>
<string name="quick_settings_nfc_label" msgid="1054317416221168085">"NFC"</string>
<string name="quick_settings_nfc_off" msgid="3465000058515424663">"NFC నిలిపివేయబడింది"</string>
<string name="quick_settings_nfc_on" msgid="1004976611203202230">"NFC ప్రారంభించబడింది"</string>
- <!-- no translation found for quick_settings_screen_record_label (1594046461509776676) -->
- <skip />
- <!-- no translation found for quick_settings_screen_record_start (1574725369331638985) -->
- <skip />
- <!-- no translation found for quick_settings_screen_record_stop (8087348522976412119) -->
- <skip />
+ <string name="quick_settings_screen_record_label" msgid="1594046461509776676">"స్క్రీన్ రికార్డ్"</string>
+ <string name="quick_settings_screen_record_start" msgid="1574725369331638985">"ప్రారంభించు"</string>
+ <string name="quick_settings_screen_record_stop" msgid="8087348522976412119">"ఆపు"</string>
<string name="recents_swipe_up_onboarding" msgid="2820265886420993995">"యాప్‌లను మార్చడం కోసం ఎగువకు స్వైప్ చేయండి"</string>
<string name="recents_quick_scrub_onboarding" msgid="765934300283514912">"యాప్‌లను శీఘ్రంగా స్విచ్ చేయడానికి కుడి వైపుకు లాగండి"</string>
<string name="quick_step_accessibility_toggle_overview" msgid="7908949976727578403">"స్థూలదృష్టిని టోగుల్ చేయి"</string>
@@ -696,22 +697,14 @@
<string name="notification_app_settings" msgid="8963648463858039377">"అనుకూలపరచండి"</string>
<string name="notification_done" msgid="6215117625922713976">"పూర్తయింది"</string>
<string name="inline_undo" msgid="9026953267645116526">"చర్యరద్దు చేయి"</string>
- <!-- no translation found for demote (6225813324237153980) -->
- <skip />
- <!-- no translation found for notification_conversation_favorite (8252976467488182853) -->
- <skip />
- <!-- no translation found for notification_conversation_unfavorite (633301300443356176) -->
- <skip />
- <!-- no translation found for notification_conversation_mute (477431709687199671) -->
- <skip />
- <!-- no translation found for notification_conversation_unmute (410885000669775294) -->
- <skip />
- <!-- no translation found for notification_conversation_bubble (4598142032706190028) -->
- <skip />
- <!-- no translation found for notification_conversation_unbubble (2303087159802926401) -->
- <skip />
- <!-- no translation found for notification_conversation_home_screen (8347136037958438935) -->
- <skip />
+ <string name="demote" msgid="6225813324237153980">"ఈ నోటిఫికేషన్‌ను సంభాషణ కానిది అని గుర్తు పెట్టండి"</string>
+ <string name="notification_conversation_favorite" msgid="8252976467488182853">"ఇష్టమైనది"</string>
+ <string name="notification_conversation_unfavorite" msgid="633301300443356176">"ఇష్టమైనదిగా పెట్టిన గుర్తును తీసివేయి"</string>
+ <string name="notification_conversation_mute" msgid="477431709687199671">"మ్యూట్ చేయి"</string>
+ <string name="notification_conversation_unmute" msgid="410885000669775294">"అన్‌మ్యూట్ చేయి"</string>
+ <string name="notification_conversation_bubble" msgid="4598142032706190028">"బబుల్‌లా చూపించు"</string>
+ <string name="notification_conversation_unbubble" msgid="2303087159802926401">"బబుల్‌లను ఆఫ్ చేయి"</string>
+ <string name="notification_conversation_home_screen" msgid="8347136037958438935">"హోమ్ స్క్రీన్‌కు జోడించు"</string>
<string name="notification_menu_accessibility" msgid="8984166825879886773">"<xliff:g id="APP_NAME">%1$s</xliff:g> <xliff:g id="MENU_DESCRIPTION">%2$s</xliff:g>"</string>
<string name="notification_menu_gear_description" msgid="6429668976593634862">"నోటిఫికేషన్ నియంత్రణలు"</string>
<string name="notification_menu_snooze_description" msgid="4740133348901973244">"నోటిఫికేషన్ తాత్కాలిక ఆపివేత ఎంపికలు"</string>
diff --git a/packages/SystemUI/res/values-television/config.xml b/packages/SystemUI/res/values-television/config.xml
index 4cc5a6731108..dd4c3210922a 100644
--- a/packages/SystemUI/res/values-television/config.xml
+++ b/packages/SystemUI/res/values-television/config.xml
@@ -36,5 +36,6 @@
<item>com.android.systemui.SliceBroadcastRelayHandler</item>
<item>com.android.systemui.SizeCompatModeActivityController</item>
<item>com.android.systemui.statusbar.notification.InstantAppNotifier</item>
+ <item>com.android.systemui.toast.ToastUI</item>
</string-array>
</resources>
diff --git a/packages/SystemUI/res/values-th/strings.xml b/packages/SystemUI/res/values-th/strings.xml
index 54c3d7335728..c5e01236912d 100644
--- a/packages/SystemUI/res/values-th/strings.xml
+++ b/packages/SystemUI/res/values-th/strings.xml
@@ -71,6 +71,7 @@
<string name="compat_mode_on" msgid="4963711187149440884">"ขยายจนเต็มหน้าจอ"</string>
<string name="compat_mode_off" msgid="7682459748279487945">"ยืดจนเต็มหน้าจอ"</string>
<string name="global_action_screenshot" msgid="2760267567509131654">"ภาพหน้าจอ"</string>
+ <string name="remote_input_image_insertion_text" msgid="4613177882724332877">"รูปภาพที่แทรก"</string>
<string name="screenshot_saving_ticker" msgid="6519186952674544916">"กำลังบันทึกภาพหน้าจอ..."</string>
<string name="screenshot_saving_title" msgid="2298349784913287333">"กำลังบันทึกภาพหน้าจอ..."</string>
<string name="screenshot_saved_title" msgid="8893267638659083153">"บันทึกภาพหน้าจอแล้ว"</string>
@@ -84,6 +85,7 @@
<string name="screenrecord_start_label" msgid="1539048263178882562">"เริ่มต้นการบันทึก"</string>
<string name="screenrecord_mic_label" msgid="6134198080740031632">"บันทึกเสียงบรรยาย"</string>
<string name="screenrecord_taps_label" msgid="2518244240225925076">"แสดงการแตะ"</string>
+ <string name="screenrecord_stop_text" msgid="6549288689506057686">"แตะเพื่อหยุด"</string>
<string name="screenrecord_stop_label" msgid="72699670052087989">"หยุด"</string>
<string name="screenrecord_pause_label" msgid="6004054907104549857">"หยุด"</string>
<string name="screenrecord_resume_label" msgid="4972223043729555575">"ทำต่อ"</string>
@@ -388,15 +390,14 @@
<string name="quick_settings_dark_mode_secondary_label_battery_saver" msgid="4990712734503013251">"โหมดประหยัดแบตเตอรี่"</string>
<string name="quick_settings_dark_mode_secondary_label_on_at_sunset" msgid="6017379738102015710">"เปิดตอนพระอาทิตย์ตก"</string>
<string name="quick_settings_dark_mode_secondary_label_until_sunrise" msgid="4404885070316716472">"จนพระอาทิตย์ขึ้น"</string>
+ <string name="quick_settings_dark_mode_secondary_label_on_at" msgid="5128758823486361279">"เปิดเวลา <xliff:g id="TIME">%s</xliff:g>"</string>
+ <string name="quick_settings_dark_mode_secondary_label_until" msgid="2289774641256492437">"จนถึง <xliff:g id="TIME">%s</xliff:g>"</string>
<string name="quick_settings_nfc_label" msgid="1054317416221168085">"NFC"</string>
<string name="quick_settings_nfc_off" msgid="3465000058515424663">"NFC ถูกปิดใช้งาน"</string>
<string name="quick_settings_nfc_on" msgid="1004976611203202230">"เปิดใช้งาน NFC แล้ว"</string>
- <!-- no translation found for quick_settings_screen_record_label (1594046461509776676) -->
- <skip />
- <!-- no translation found for quick_settings_screen_record_start (1574725369331638985) -->
- <skip />
- <!-- no translation found for quick_settings_screen_record_stop (8087348522976412119) -->
- <skip />
+ <string name="quick_settings_screen_record_label" msgid="1594046461509776676">"บันทึกหน้าจอ"</string>
+ <string name="quick_settings_screen_record_start" msgid="1574725369331638985">"เริ่ม"</string>
+ <string name="quick_settings_screen_record_stop" msgid="8087348522976412119">"หยุด"</string>
<string name="recents_swipe_up_onboarding" msgid="2820265886420993995">"เลื่อนขึ้นเพื่อสลับแอป"</string>
<string name="recents_quick_scrub_onboarding" msgid="765934300283514912">"ลากไปทางขวาเพื่อสลับแอปอย่างรวดเร็ว"</string>
<string name="quick_step_accessibility_toggle_overview" msgid="7908949976727578403">"สลับภาพรวม"</string>
@@ -696,22 +697,14 @@
<string name="notification_app_settings" msgid="8963648463858039377">"ปรับแต่ง"</string>
<string name="notification_done" msgid="6215117625922713976">"เสร็จสิ้น"</string>
<string name="inline_undo" msgid="9026953267645116526">"เลิกทำ"</string>
- <!-- no translation found for demote (6225813324237153980) -->
- <skip />
- <!-- no translation found for notification_conversation_favorite (8252976467488182853) -->
- <skip />
- <!-- no translation found for notification_conversation_unfavorite (633301300443356176) -->
- <skip />
- <!-- no translation found for notification_conversation_mute (477431709687199671) -->
- <skip />
- <!-- no translation found for notification_conversation_unmute (410885000669775294) -->
- <skip />
- <!-- no translation found for notification_conversation_bubble (4598142032706190028) -->
- <skip />
- <!-- no translation found for notification_conversation_unbubble (2303087159802926401) -->
- <skip />
- <!-- no translation found for notification_conversation_home_screen (8347136037958438935) -->
- <skip />
+ <string name="demote" msgid="6225813324237153980">"ทำเครื่องหมายการแจ้งเตือนนี้ว่าไม่ใช่บทสนทนา"</string>
+ <string name="notification_conversation_favorite" msgid="8252976467488182853">"รายการโปรด"</string>
+ <string name="notification_conversation_unfavorite" msgid="633301300443356176">"นำออกจากรายการโปรด"</string>
+ <string name="notification_conversation_mute" msgid="477431709687199671">"ปิดเสียง"</string>
+ <string name="notification_conversation_unmute" msgid="410885000669775294">"เปิดเสียง"</string>
+ <string name="notification_conversation_bubble" msgid="4598142032706190028">"แสดงเป็นบับเบิล"</string>
+ <string name="notification_conversation_unbubble" msgid="2303087159802926401">"ปิดบับเบิล"</string>
+ <string name="notification_conversation_home_screen" msgid="8347136037958438935">"เพิ่มลงในหน้าจอหลัก"</string>
<string name="notification_menu_accessibility" msgid="8984166825879886773">"<xliff:g id="APP_NAME">%1$s</xliff:g> <xliff:g id="MENU_DESCRIPTION">%2$s</xliff:g>"</string>
<string name="notification_menu_gear_description" msgid="6429668976593634862">"ส่วนควบคุมการแจ้งเตือน"</string>
<string name="notification_menu_snooze_description" msgid="4740133348901973244">"ตัวเลือกการปิดเสียงแจ้งเตือนชั่วคราว"</string>
diff --git a/packages/SystemUI/res/values-tl/strings.xml b/packages/SystemUI/res/values-tl/strings.xml
index ef812fff45cd..3d343c5b4c0b 100644
--- a/packages/SystemUI/res/values-tl/strings.xml
+++ b/packages/SystemUI/res/values-tl/strings.xml
@@ -71,6 +71,7 @@
<string name="compat_mode_on" msgid="4963711187149440884">"I-zoom upang punan screen"</string>
<string name="compat_mode_off" msgid="7682459748279487945">"I-stretch upang mapuno screen"</string>
<string name="global_action_screenshot" msgid="2760267567509131654">"Screenshot"</string>
+ <string name="remote_input_image_insertion_text" msgid="4613177882724332877">"Inilagay ang larawan"</string>
<string name="screenshot_saving_ticker" msgid="6519186952674544916">"Sine-save ang screenshot…"</string>
<string name="screenshot_saving_title" msgid="2298349784913287333">"Sine-save ang screenshot…"</string>
<string name="screenshot_saved_title" msgid="8893267638659083153">"Na-save ang screenshot"</string>
@@ -84,6 +85,7 @@
<string name="screenrecord_start_label" msgid="1539048263178882562">"Simulan ang Pag-record"</string>
<string name="screenrecord_mic_label" msgid="6134198080740031632">"I-record ang voiceover"</string>
<string name="screenrecord_taps_label" msgid="2518244240225925076">"Ipakita ang mga pag-tap"</string>
+ <string name="screenrecord_stop_text" msgid="6549288689506057686">"I-tap para ihinto"</string>
<string name="screenrecord_stop_label" msgid="72699670052087989">"Ihinto"</string>
<string name="screenrecord_pause_label" msgid="6004054907104549857">"I-pause"</string>
<string name="screenrecord_resume_label" msgid="4972223043729555575">"Ituloy"</string>
@@ -388,15 +390,14 @@
<string name="quick_settings_dark_mode_secondary_label_battery_saver" msgid="4990712734503013251">"Pangtipid sa Baterya"</string>
<string name="quick_settings_dark_mode_secondary_label_on_at_sunset" msgid="6017379738102015710">"Mao-on sa sunset"</string>
<string name="quick_settings_dark_mode_secondary_label_until_sunrise" msgid="4404885070316716472">"Hanggang sunrise"</string>
+ <string name="quick_settings_dark_mode_secondary_label_on_at" msgid="5128758823486361279">"Ma-o-on nang <xliff:g id="TIME">%s</xliff:g>"</string>
+ <string name="quick_settings_dark_mode_secondary_label_until" msgid="2289774641256492437">"Hanggang <xliff:g id="TIME">%s</xliff:g>"</string>
<string name="quick_settings_nfc_label" msgid="1054317416221168085">"NFC"</string>
<string name="quick_settings_nfc_off" msgid="3465000058515424663">"Naka-disable ang NFC"</string>
<string name="quick_settings_nfc_on" msgid="1004976611203202230">"Naka-enable ang NFC"</string>
- <!-- no translation found for quick_settings_screen_record_label (1594046461509776676) -->
- <skip />
- <!-- no translation found for quick_settings_screen_record_start (1574725369331638985) -->
- <skip />
- <!-- no translation found for quick_settings_screen_record_stop (8087348522976412119) -->
- <skip />
+ <string name="quick_settings_screen_record_label" msgid="1594046461509776676">"Pag-record ng Screen"</string>
+ <string name="quick_settings_screen_record_start" msgid="1574725369331638985">"Magsimula"</string>
+ <string name="quick_settings_screen_record_stop" msgid="8087348522976412119">"Ihinto"</string>
<string name="recents_swipe_up_onboarding" msgid="2820265886420993995">"Mag-swipe pataas upang lumipat ng app"</string>
<string name="recents_quick_scrub_onboarding" msgid="765934300283514912">"I-drag pakanan para mabilisang magpalipat-lipat ng app"</string>
<string name="quick_step_accessibility_toggle_overview" msgid="7908949976727578403">"I-toggle ang Overview"</string>
@@ -696,22 +697,14 @@
<string name="notification_app_settings" msgid="8963648463858039377">"I-customize"</string>
<string name="notification_done" msgid="6215117625922713976">"Tapos Na"</string>
<string name="inline_undo" msgid="9026953267645116526">"I-undo"</string>
- <!-- no translation found for demote (6225813324237153980) -->
- <skip />
- <!-- no translation found for notification_conversation_favorite (8252976467488182853) -->
- <skip />
- <!-- no translation found for notification_conversation_unfavorite (633301300443356176) -->
- <skip />
- <!-- no translation found for notification_conversation_mute (477431709687199671) -->
- <skip />
- <!-- no translation found for notification_conversation_unmute (410885000669775294) -->
- <skip />
- <!-- no translation found for notification_conversation_bubble (4598142032706190028) -->
- <skip />
- <!-- no translation found for notification_conversation_unbubble (2303087159802926401) -->
- <skip />
- <!-- no translation found for notification_conversation_home_screen (8347136037958438935) -->
- <skip />
+ <string name="demote" msgid="6225813324237153980">"Markahan ang notification na ito bilang hindi pag-uusap"</string>
+ <string name="notification_conversation_favorite" msgid="8252976467488182853">"Gawing Paborito"</string>
+ <string name="notification_conversation_unfavorite" msgid="633301300443356176">"I-unfavorite"</string>
+ <string name="notification_conversation_mute" msgid="477431709687199671">"I-mute"</string>
+ <string name="notification_conversation_unmute" msgid="410885000669775294">"I-unmute"</string>
+ <string name="notification_conversation_bubble" msgid="4598142032706190028">"Ipakita bilang bubble"</string>
+ <string name="notification_conversation_unbubble" msgid="2303087159802926401">"I-off ang mga bubble"</string>
+ <string name="notification_conversation_home_screen" msgid="8347136037958438935">"Idagdag sa home screen"</string>
<string name="notification_menu_accessibility" msgid="8984166825879886773">"<xliff:g id="APP_NAME">%1$s</xliff:g> <xliff:g id="MENU_DESCRIPTION">%2$s</xliff:g>"</string>
<string name="notification_menu_gear_description" msgid="6429668976593634862">"mga kontrol ng notification"</string>
<string name="notification_menu_snooze_description" msgid="4740133348901973244">"mga opsyon sa pag-snooze ng notification"</string>
diff --git a/packages/SystemUI/res/values-tr/strings.xml b/packages/SystemUI/res/values-tr/strings.xml
index 470606f48981..46e583fb5124 100644
--- a/packages/SystemUI/res/values-tr/strings.xml
+++ b/packages/SystemUI/res/values-tr/strings.xml
@@ -71,6 +71,7 @@
<string name="compat_mode_on" msgid="4963711187149440884">"Yakınlaştır (ekranı kaplasın)"</string>
<string name="compat_mode_off" msgid="7682459748279487945">"Genişlet (ekran kapansın)"</string>
<string name="global_action_screenshot" msgid="2760267567509131654">"Ekran görüntüsü"</string>
+ <string name="remote_input_image_insertion_text" msgid="4613177882724332877">"Resim eklendi"</string>
<string name="screenshot_saving_ticker" msgid="6519186952674544916">"Ekran görüntüsü kaydediliyor..."</string>
<string name="screenshot_saving_title" msgid="2298349784913287333">"Ekran görüntüsü kaydediliyor..."</string>
<string name="screenshot_saved_title" msgid="8893267638659083153">"Ekran görüntüsü kaydedildi"</string>
@@ -84,6 +85,7 @@
<string name="screenrecord_start_label" msgid="1539048263178882562">"Kaydı Başlat"</string>
<string name="screenrecord_mic_label" msgid="6134198080740031632">"Seslendirme kaydet"</string>
<string name="screenrecord_taps_label" msgid="2518244240225925076">"Dokunmaları göster"</string>
+ <string name="screenrecord_stop_text" msgid="6549288689506057686">"Durdurmak için dokunun"</string>
<string name="screenrecord_stop_label" msgid="72699670052087989">"Durdur"</string>
<string name="screenrecord_pause_label" msgid="6004054907104549857">"Duraklat"</string>
<string name="screenrecord_resume_label" msgid="4972223043729555575">"Devam ettir"</string>
@@ -388,15 +390,14 @@
<string name="quick_settings_dark_mode_secondary_label_battery_saver" msgid="4990712734503013251">"Pil Tasarrufu"</string>
<string name="quick_settings_dark_mode_secondary_label_on_at_sunset" msgid="6017379738102015710">"Gün batımı açılacak"</string>
<string name="quick_settings_dark_mode_secondary_label_until_sunrise" msgid="4404885070316716472">"Sabaha kadar"</string>
+ <string name="quick_settings_dark_mode_secondary_label_on_at" msgid="5128758823486361279">"Açılacağı saat: <xliff:g id="TIME">%s</xliff:g>"</string>
+ <string name="quick_settings_dark_mode_secondary_label_until" msgid="2289774641256492437">"Şu saate kadar: <xliff:g id="TIME">%s</xliff:g>"</string>
<string name="quick_settings_nfc_label" msgid="1054317416221168085">"NFC"</string>
<string name="quick_settings_nfc_off" msgid="3465000058515424663">"NFC devre dışı"</string>
<string name="quick_settings_nfc_on" msgid="1004976611203202230">"NFC etkin"</string>
- <!-- no translation found for quick_settings_screen_record_label (1594046461509776676) -->
- <skip />
- <!-- no translation found for quick_settings_screen_record_start (1574725369331638985) -->
- <skip />
- <!-- no translation found for quick_settings_screen_record_stop (8087348522976412119) -->
- <skip />
+ <string name="quick_settings_screen_record_label" msgid="1594046461509776676">"Ekran Kaydı"</string>
+ <string name="quick_settings_screen_record_start" msgid="1574725369331638985">"Başlat"</string>
+ <string name="quick_settings_screen_record_stop" msgid="8087348522976412119">"Durdur"</string>
<string name="recents_swipe_up_onboarding" msgid="2820265886420993995">"Uygulamalar arasında geçiş yapmak için yukarı kaydırın"</string>
<string name="recents_quick_scrub_onboarding" msgid="765934300283514912">"Uygulamaları hızlıca değiştirmek için sağa kaydırın"</string>
<string name="quick_step_accessibility_toggle_overview" msgid="7908949976727578403">"Genel bakışı aç/kapat"</string>
@@ -696,22 +697,14 @@
<string name="notification_app_settings" msgid="8963648463858039377">"Özelleştir"</string>
<string name="notification_done" msgid="6215117625922713976">"Bitti"</string>
<string name="inline_undo" msgid="9026953267645116526">"Geri al"</string>
- <!-- no translation found for demote (6225813324237153980) -->
- <skip />
- <!-- no translation found for notification_conversation_favorite (8252976467488182853) -->
- <skip />
- <!-- no translation found for notification_conversation_unfavorite (633301300443356176) -->
- <skip />
- <!-- no translation found for notification_conversation_mute (477431709687199671) -->
- <skip />
- <!-- no translation found for notification_conversation_unmute (410885000669775294) -->
- <skip />
- <!-- no translation found for notification_conversation_bubble (4598142032706190028) -->
- <skip />
- <!-- no translation found for notification_conversation_unbubble (2303087159802926401) -->
- <skip />
- <!-- no translation found for notification_conversation_home_screen (8347136037958438935) -->
- <skip />
+ <string name="demote" msgid="6225813324237153980">"Bu bildirimi ileti dizisi değil olarak işaretle"</string>
+ <string name="notification_conversation_favorite" msgid="8252976467488182853">"Favori"</string>
+ <string name="notification_conversation_unfavorite" msgid="633301300443356176">"Favorilerden kaldır"</string>
+ <string name="notification_conversation_mute" msgid="477431709687199671">"Kapat"</string>
+ <string name="notification_conversation_unmute" msgid="410885000669775294">"Aç"</string>
+ <string name="notification_conversation_bubble" msgid="4598142032706190028">"Balon olarak göster"</string>
+ <string name="notification_conversation_unbubble" msgid="2303087159802926401">"Balonları kapat"</string>
+ <string name="notification_conversation_home_screen" msgid="8347136037958438935">"Ana ekrana ekle"</string>
<string name="notification_menu_accessibility" msgid="8984166825879886773">"<xliff:g id="APP_NAME">%1$s</xliff:g> <xliff:g id="MENU_DESCRIPTION">%2$s</xliff:g>"</string>
<string name="notification_menu_gear_description" msgid="6429668976593634862">"Bildirim kontrolleri"</string>
<string name="notification_menu_snooze_description" msgid="4740133348901973244">"bildirim erteleme seçenekleri"</string>
diff --git a/packages/SystemUI/res/values-uk/strings.xml b/packages/SystemUI/res/values-uk/strings.xml
index 496206a18668..7baad0df2e55 100644
--- a/packages/SystemUI/res/values-uk/strings.xml
+++ b/packages/SystemUI/res/values-uk/strings.xml
@@ -71,6 +71,7 @@
<string name="compat_mode_on" msgid="4963711187149440884">"Масштабув. на весь екран"</string>
<string name="compat_mode_off" msgid="7682459748279487945">"Розтягнути на весь екран"</string>
<string name="global_action_screenshot" msgid="2760267567509131654">"Знімок екрана"</string>
+ <string name="remote_input_image_insertion_text" msgid="4613177882724332877">"Вставлене зображення"</string>
<string name="screenshot_saving_ticker" msgid="6519186952674544916">"Збереження знімка екрана..."</string>
<string name="screenshot_saving_title" msgid="2298349784913287333">"Збереження знімка екрана..."</string>
<string name="screenshot_saved_title" msgid="8893267638659083153">"Знімок екрана збережено"</string>
@@ -84,6 +85,7 @@
<string name="screenrecord_start_label" msgid="1539048263178882562">"Почати запис"</string>
<string name="screenrecord_mic_label" msgid="6134198080740031632">"Записувати голосовий супровід"</string>
<string name="screenrecord_taps_label" msgid="2518244240225925076">"Показувати дотики"</string>
+ <string name="screenrecord_stop_text" msgid="6549288689506057686">"Натисніть, щоб зупинити"</string>
<string name="screenrecord_stop_label" msgid="72699670052087989">"Зупинити"</string>
<string name="screenrecord_pause_label" msgid="6004054907104549857">"Призупинити"</string>
<string name="screenrecord_resume_label" msgid="4972223043729555575">"Відновити"</string>
@@ -392,15 +394,14 @@
<string name="quick_settings_dark_mode_secondary_label_battery_saver" msgid="4990712734503013251">"Режим енергозбереження"</string>
<string name="quick_settings_dark_mode_secondary_label_on_at_sunset" msgid="6017379738102015710">"Увімкнеться ввечері"</string>
<string name="quick_settings_dark_mode_secondary_label_until_sunrise" msgid="4404885070316716472">"До сходу сонця"</string>
+ <string name="quick_settings_dark_mode_secondary_label_on_at" msgid="5128758823486361279">"Вмикається о <xliff:g id="TIME">%s</xliff:g>"</string>
+ <string name="quick_settings_dark_mode_secondary_label_until" msgid="2289774641256492437">"До <xliff:g id="TIME">%s</xliff:g>"</string>
<string name="quick_settings_nfc_label" msgid="1054317416221168085">"NFC"</string>
<string name="quick_settings_nfc_off" msgid="3465000058515424663">"NFC вимкнено"</string>
<string name="quick_settings_nfc_on" msgid="1004976611203202230">"NFC ввімкнено"</string>
- <!-- no translation found for quick_settings_screen_record_label (1594046461509776676) -->
- <skip />
- <!-- no translation found for quick_settings_screen_record_start (1574725369331638985) -->
- <skip />
- <!-- no translation found for quick_settings_screen_record_stop (8087348522976412119) -->
- <skip />
+ <string name="quick_settings_screen_record_label" msgid="1594046461509776676">"Запис екрана"</string>
+ <string name="quick_settings_screen_record_start" msgid="1574725369331638985">"Почати"</string>
+ <string name="quick_settings_screen_record_stop" msgid="8087348522976412119">"Зупинити"</string>
<string name="recents_swipe_up_onboarding" msgid="2820265886420993995">"Проводьте пальцем угору, щоб переходити між додатками"</string>
<string name="recents_quick_scrub_onboarding" msgid="765934300283514912">"Перетягуйте праворуч, щоб швидко переходити між додатками"</string>
<string name="quick_step_accessibility_toggle_overview" msgid="7908949976727578403">"Увімкнути або вимкнути огляд"</string>
@@ -702,22 +703,14 @@
<string name="notification_app_settings" msgid="8963648463858039377">"Налаштувати"</string>
<string name="notification_done" msgid="6215117625922713976">"Готово"</string>
<string name="inline_undo" msgid="9026953267645116526">"Відмінити"</string>
- <!-- no translation found for demote (6225813324237153980) -->
- <skip />
- <!-- no translation found for notification_conversation_favorite (8252976467488182853) -->
- <skip />
- <!-- no translation found for notification_conversation_unfavorite (633301300443356176) -->
- <skip />
- <!-- no translation found for notification_conversation_mute (477431709687199671) -->
- <skip />
- <!-- no translation found for notification_conversation_unmute (410885000669775294) -->
- <skip />
- <!-- no translation found for notification_conversation_bubble (4598142032706190028) -->
- <skip />
- <!-- no translation found for notification_conversation_unbubble (2303087159802926401) -->
- <skip />
- <!-- no translation found for notification_conversation_home_screen (8347136037958438935) -->
- <skip />
+ <string name="demote" msgid="6225813324237153980">"Перетворити з ланцюжка повідомлень на сповіщення"</string>
+ <string name="notification_conversation_favorite" msgid="8252976467488182853">"Вибране"</string>
+ <string name="notification_conversation_unfavorite" msgid="633301300443356176">"Видалити з вибраного"</string>
+ <string name="notification_conversation_mute" msgid="477431709687199671">"Ігнорувати"</string>
+ <string name="notification_conversation_unmute" msgid="410885000669775294">"Не ігнорувати"</string>
+ <string name="notification_conversation_bubble" msgid="4598142032706190028">"Показувати як спливаюче сповіщення"</string>
+ <string name="notification_conversation_unbubble" msgid="2303087159802926401">"Вимкнути спливаючі сповіщення"</string>
+ <string name="notification_conversation_home_screen" msgid="8347136037958438935">"Додати на головний екран"</string>
<string name="notification_menu_accessibility" msgid="8984166825879886773">"<xliff:g id="APP_NAME">%1$s</xliff:g> <xliff:g id="MENU_DESCRIPTION">%2$s</xliff:g>"</string>
<string name="notification_menu_gear_description" msgid="6429668976593634862">"елементи керування сповіщеннями"</string>
<string name="notification_menu_snooze_description" msgid="4740133348901973244">"параметри відкладення сповіщень"</string>
diff --git a/packages/SystemUI/res/values-ur/strings.xml b/packages/SystemUI/res/values-ur/strings.xml
index a6de5095e7e2..97d0a5d1d2a3 100644
--- a/packages/SystemUI/res/values-ur/strings.xml
+++ b/packages/SystemUI/res/values-ur/strings.xml
@@ -71,6 +71,7 @@
<string name="compat_mode_on" msgid="4963711187149440884">"پوری سکرین پر زوم کریں"</string>
<string name="compat_mode_off" msgid="7682459748279487945">"پوری سکرین پر پھیلائیں"</string>
<string name="global_action_screenshot" msgid="2760267567509131654">"اسکرین شاٹ"</string>
+ <string name="remote_input_image_insertion_text" msgid="4613177882724332877">"تصویر داخل کر دی گئی"</string>
<string name="screenshot_saving_ticker" msgid="6519186952674544916">"اسکرین شاٹ محفوظ ہو رہا ہے…"</string>
<string name="screenshot_saving_title" msgid="2298349784913287333">"اسکرین شاٹ محفوظ ہو رہا ہے…"</string>
<string name="screenshot_saved_title" msgid="8893267638659083153">"اسکرین شاٹ محفوظ ہو گیا"</string>
@@ -84,6 +85,7 @@
<string name="screenrecord_start_label" msgid="1539048263178882562">"ریکارڈنگ شروع کریں"</string>
<string name="screenrecord_mic_label" msgid="6134198080740031632">"وائس اوور ریکارڈ کریں"</string>
<string name="screenrecord_taps_label" msgid="2518244240225925076">"تھپتھپاہٹیں دکھائیں"</string>
+ <string name="screenrecord_stop_text" msgid="6549288689506057686">"روکنے کے لیے تھپتھپائیں"</string>
<string name="screenrecord_stop_label" msgid="72699670052087989">"روکیں"</string>
<string name="screenrecord_pause_label" msgid="6004054907104549857">"موقوف کریں"</string>
<string name="screenrecord_resume_label" msgid="4972223043729555575">"دوبارہ شروع کریں"</string>
@@ -388,15 +390,14 @@
<string name="quick_settings_dark_mode_secondary_label_battery_saver" msgid="4990712734503013251">"بیٹری سیور"</string>
<string name="quick_settings_dark_mode_secondary_label_on_at_sunset" msgid="6017379738102015710">"شام کے وقت آن ہوگی"</string>
<string name="quick_settings_dark_mode_secondary_label_until_sunrise" msgid="4404885070316716472">"طلوع آفتاب تک"</string>
+ <string name="quick_settings_dark_mode_secondary_label_on_at" msgid="5128758823486361279">"آن ہوگی بوقت <xliff:g id="TIME">%s</xliff:g>"</string>
+ <string name="quick_settings_dark_mode_secondary_label_until" msgid="2289774641256492437">"<xliff:g id="TIME">%s</xliff:g> تک"</string>
<string name="quick_settings_nfc_label" msgid="1054317416221168085">"NFC"</string>
<string name="quick_settings_nfc_off" msgid="3465000058515424663">"‏NFC غیر فعال ہے"</string>
<string name="quick_settings_nfc_on" msgid="1004976611203202230">"‏NFC فعال ہے"</string>
- <!-- no translation found for quick_settings_screen_record_label (1594046461509776676) -->
- <skip />
- <!-- no translation found for quick_settings_screen_record_start (1574725369331638985) -->
- <skip />
- <!-- no translation found for quick_settings_screen_record_stop (8087348522976412119) -->
- <skip />
+ <string name="quick_settings_screen_record_label" msgid="1594046461509776676">"اسکرین ریکارڈر کریں"</string>
+ <string name="quick_settings_screen_record_start" msgid="1574725369331638985">"آغاز"</string>
+ <string name="quick_settings_screen_record_stop" msgid="8087348522976412119">"روکیں"</string>
<string name="recents_swipe_up_onboarding" msgid="2820265886420993995">"ایپس سوئچ کرنے کیلئے اوپر سوائپ کریں"</string>
<string name="recents_quick_scrub_onboarding" msgid="765934300283514912">"تیزی سے ایپس کو سوئچ کرنے کے لیے دائیں طرف گھسیٹیں"</string>
<string name="quick_step_accessibility_toggle_overview" msgid="7908949976727578403">"مجموعی جائزہ ٹوگل کریں"</string>
@@ -696,22 +697,14 @@
<string name="notification_app_settings" msgid="8963648463858039377">"حسب ضرورت بنائیں"</string>
<string name="notification_done" msgid="6215117625922713976">"ہوگیا"</string>
<string name="inline_undo" msgid="9026953267645116526">"کالعدم کریں"</string>
- <!-- no translation found for demote (6225813324237153980) -->
- <skip />
- <!-- no translation found for notification_conversation_favorite (8252976467488182853) -->
- <skip />
- <!-- no translation found for notification_conversation_unfavorite (633301300443356176) -->
- <skip />
- <!-- no translation found for notification_conversation_mute (477431709687199671) -->
- <skip />
- <!-- no translation found for notification_conversation_unmute (410885000669775294) -->
- <skip />
- <!-- no translation found for notification_conversation_bubble (4598142032706190028) -->
- <skip />
- <!-- no translation found for notification_conversation_unbubble (2303087159802926401) -->
- <skip />
- <!-- no translation found for notification_conversation_home_screen (8347136037958438935) -->
- <skip />
+ <string name="demote" msgid="6225813324237153980">"اس اطلاع کو بطور گفتگو نشان زد نہ کریں"</string>
+ <string name="notification_conversation_favorite" msgid="8252976467488182853">"پسندیدہ"</string>
+ <string name="notification_conversation_unfavorite" msgid="633301300443356176">"پسندیدگی ختم کریں"</string>
+ <string name="notification_conversation_mute" msgid="477431709687199671">"خاموش کریں"</string>
+ <string name="notification_conversation_unmute" msgid="410885000669775294">"آواز چلائیں"</string>
+ <string name="notification_conversation_bubble" msgid="4598142032706190028">"بلبلہ کے بطور دکھائیں"</string>
+ <string name="notification_conversation_unbubble" msgid="2303087159802926401">"بلبلے آف کریں"</string>
+ <string name="notification_conversation_home_screen" msgid="8347136037958438935">"ہوم اسکرین میں شامل کریں"</string>
<string name="notification_menu_accessibility" msgid="8984166825879886773">"<xliff:g id="APP_NAME">%1$s</xliff:g> <xliff:g id="MENU_DESCRIPTION">%2$s</xliff:g>"</string>
<string name="notification_menu_gear_description" msgid="6429668976593634862">"اطلاع کے کنٹرولز"</string>
<string name="notification_menu_snooze_description" msgid="4740133348901973244">"اطلاع اسنوز کرنے کے اختیارات"</string>
diff --git a/packages/SystemUI/res/values-uz/strings.xml b/packages/SystemUI/res/values-uz/strings.xml
index 1eb02f3eafa7..b514f1e9754c 100644
--- a/packages/SystemUI/res/values-uz/strings.xml
+++ b/packages/SystemUI/res/values-uz/strings.xml
@@ -71,6 +71,7 @@
<string name="compat_mode_on" msgid="4963711187149440884">"Ekranga moslashtirish"</string>
<string name="compat_mode_off" msgid="7682459748279487945">"Ekran hajmida cho‘zish"</string>
<string name="global_action_screenshot" msgid="2760267567509131654">"Skrinshot"</string>
+ <string name="remote_input_image_insertion_text" msgid="4613177882724332877">"Rasm joylandi"</string>
<string name="screenshot_saving_ticker" msgid="6519186952674544916">"Skrinshot saqlanmoqda…"</string>
<string name="screenshot_saving_title" msgid="2298349784913287333">"Skrinshot saqlanmoqda…"</string>
<string name="screenshot_saved_title" msgid="8893267638659083153">"Skrinshot saqlandi"</string>
@@ -84,6 +85,7 @@
<string name="screenrecord_start_label" msgid="1539048263178882562">"Yozuvni boshlash"</string>
<string name="screenrecord_mic_label" msgid="6134198080740031632">"Kadrorti nutqini yozib olish"</string>
<string name="screenrecord_taps_label" msgid="2518244240225925076">"Bosishlarni ko‘rsatish"</string>
+ <string name="screenrecord_stop_text" msgid="6549288689506057686">"Toʻxtatish uchun bosing"</string>
<string name="screenrecord_stop_label" msgid="72699670052087989">"To‘xtatish"</string>
<string name="screenrecord_pause_label" msgid="6004054907104549857">"Pauza"</string>
<string name="screenrecord_resume_label" msgid="4972223043729555575">"Davom etish"</string>
@@ -388,6 +390,8 @@
<string name="quick_settings_dark_mode_secondary_label_battery_saver" msgid="4990712734503013251">"Quvvat tejash"</string>
<string name="quick_settings_dark_mode_secondary_label_on_at_sunset" msgid="6017379738102015710">"Kunbotarda yoqish"</string>
<string name="quick_settings_dark_mode_secondary_label_until_sunrise" msgid="4404885070316716472">"Quyosh chiqqunicha"</string>
+ <string name="quick_settings_dark_mode_secondary_label_on_at" msgid="5128758823486361279">"<xliff:g id="TIME">%s</xliff:g> da yoqish"</string>
+ <string name="quick_settings_dark_mode_secondary_label_until" msgid="2289774641256492437">"<xliff:g id="TIME">%s</xliff:g> gacha"</string>
<string name="quick_settings_nfc_label" msgid="1054317416221168085">"NFC"</string>
<string name="quick_settings_nfc_off" msgid="3465000058515424663">"NFC o‘chiq"</string>
<string name="quick_settings_nfc_on" msgid="1004976611203202230">"NFC yoniq"</string>
diff --git a/packages/SystemUI/res/values-vi/strings.xml b/packages/SystemUI/res/values-vi/strings.xml
index dc3882e53c3d..c4d76187c817 100644
--- a/packages/SystemUI/res/values-vi/strings.xml
+++ b/packages/SystemUI/res/values-vi/strings.xml
@@ -71,6 +71,7 @@
<string name="compat_mode_on" msgid="4963711187149440884">"T.phóng để lấp đầy m.hình"</string>
<string name="compat_mode_off" msgid="7682459748279487945">"Giãn ra để lấp đầy m.hình"</string>
<string name="global_action_screenshot" msgid="2760267567509131654">"Chụp ảnh màn hình"</string>
+ <string name="remote_input_image_insertion_text" msgid="4613177882724332877">"Đã chèn hình ảnh"</string>
<string name="screenshot_saving_ticker" msgid="6519186952674544916">"Đang lưu ảnh chụp màn hình..."</string>
<string name="screenshot_saving_title" msgid="2298349784913287333">"Đang lưu ảnh chụp màn hình..."</string>
<string name="screenshot_saved_title" msgid="8893267638659083153">"Đã lưu ảnh chụp màn hình"</string>
@@ -84,6 +85,7 @@
<string name="screenrecord_start_label" msgid="1539048263178882562">"Bắt đầu ghi"</string>
<string name="screenrecord_mic_label" msgid="6134198080740031632">"Ghi phần thuyết minh"</string>
<string name="screenrecord_taps_label" msgid="2518244240225925076">"Hiển thị số lần nhấn"</string>
+ <string name="screenrecord_stop_text" msgid="6549288689506057686">"Nhấn để dừng"</string>
<string name="screenrecord_stop_label" msgid="72699670052087989">"Dừng"</string>
<string name="screenrecord_pause_label" msgid="6004054907104549857">"Tạm dừng"</string>
<string name="screenrecord_resume_label" msgid="4972223043729555575">"Tiếp tục"</string>
@@ -388,15 +390,14 @@
<string name="quick_settings_dark_mode_secondary_label_battery_saver" msgid="4990712734503013251">"Trình tiết kiệm pin"</string>
<string name="quick_settings_dark_mode_secondary_label_on_at_sunset" msgid="6017379738102015710">"Bật khi trời tối"</string>
<string name="quick_settings_dark_mode_secondary_label_until_sunrise" msgid="4404885070316716472">"Cho đến khi trời sáng"</string>
+ <string name="quick_settings_dark_mode_secondary_label_on_at" msgid="5128758823486361279">"Bật vào lúc <xliff:g id="TIME">%s</xliff:g>"</string>
+ <string name="quick_settings_dark_mode_secondary_label_until" msgid="2289774641256492437">"Cho đến <xliff:g id="TIME">%s</xliff:g>"</string>
<string name="quick_settings_nfc_label" msgid="1054317416221168085">"NFC"</string>
<string name="quick_settings_nfc_off" msgid="3465000058515424663">"NFC đã được tắt"</string>
<string name="quick_settings_nfc_on" msgid="1004976611203202230">"NFC đã được bật"</string>
- <!-- no translation found for quick_settings_screen_record_label (1594046461509776676) -->
- <skip />
- <!-- no translation found for quick_settings_screen_record_start (1574725369331638985) -->
- <skip />
- <!-- no translation found for quick_settings_screen_record_stop (8087348522976412119) -->
- <skip />
+ <string name="quick_settings_screen_record_label" msgid="1594046461509776676">"Ghi lại nội dung trên màn hình"</string>
+ <string name="quick_settings_screen_record_start" msgid="1574725369331638985">"Bắt đầu"</string>
+ <string name="quick_settings_screen_record_stop" msgid="8087348522976412119">"Dừng"</string>
<string name="recents_swipe_up_onboarding" msgid="2820265886420993995">"Vuốt lên để chuyển đổi ứng dụng"</string>
<string name="recents_quick_scrub_onboarding" msgid="765934300283514912">"Kéo sang phải để chuyển đổi nhanh giữa các ứng dụng"</string>
<string name="quick_step_accessibility_toggle_overview" msgid="7908949976727578403">"Bật/tắt chế độ xem Tổng quan"</string>
@@ -696,22 +697,14 @@
<string name="notification_app_settings" msgid="8963648463858039377">"Tùy chỉnh"</string>
<string name="notification_done" msgid="6215117625922713976">"Xong"</string>
<string name="inline_undo" msgid="9026953267645116526">"Hoàn tác"</string>
- <!-- no translation found for demote (6225813324237153980) -->
- <skip />
- <!-- no translation found for notification_conversation_favorite (8252976467488182853) -->
- <skip />
- <!-- no translation found for notification_conversation_unfavorite (633301300443356176) -->
- <skip />
- <!-- no translation found for notification_conversation_mute (477431709687199671) -->
- <skip />
- <!-- no translation found for notification_conversation_unmute (410885000669775294) -->
- <skip />
- <!-- no translation found for notification_conversation_bubble (4598142032706190028) -->
- <skip />
- <!-- no translation found for notification_conversation_unbubble (2303087159802926401) -->
- <skip />
- <!-- no translation found for notification_conversation_home_screen (8347136037958438935) -->
- <skip />
+ <string name="demote" msgid="6225813324237153980">"Đánh dấu thông báo này không phải là cuộc trò chuyện"</string>
+ <string name="notification_conversation_favorite" msgid="8252976467488182853">"Đánh dấu là yêu thích"</string>
+ <string name="notification_conversation_unfavorite" msgid="633301300443356176">"Bỏ thích"</string>
+ <string name="notification_conversation_mute" msgid="477431709687199671">"Ẩn"</string>
+ <string name="notification_conversation_unmute" msgid="410885000669775294">"Hiển thị"</string>
+ <string name="notification_conversation_bubble" msgid="4598142032706190028">"Hiển thị dưới dạng bong bóng"</string>
+ <string name="notification_conversation_unbubble" msgid="2303087159802926401">"Tắt bong bóng"</string>
+ <string name="notification_conversation_home_screen" msgid="8347136037958438935">"Thêm vào màn hình chính"</string>
<string name="notification_menu_accessibility" msgid="8984166825879886773">"<xliff:g id="APP_NAME">%1$s</xliff:g> <xliff:g id="MENU_DESCRIPTION">%2$s</xliff:g>"</string>
<string name="notification_menu_gear_description" msgid="6429668976593634862">"điều khiển thông báo"</string>
<string name="notification_menu_snooze_description" msgid="4740133348901973244">"Tùy chọn báo lại thông báo"</string>
diff --git a/packages/SystemUI/res/values-zh-rCN/strings.xml b/packages/SystemUI/res/values-zh-rCN/strings.xml
index baacdae2da54..56edb64fd798 100644
--- a/packages/SystemUI/res/values-zh-rCN/strings.xml
+++ b/packages/SystemUI/res/values-zh-rCN/strings.xml
@@ -71,6 +71,7 @@
<string name="compat_mode_on" msgid="4963711187149440884">"缩放以填满屏幕"</string>
<string name="compat_mode_off" msgid="7682459748279487945">"拉伸以填满屏幕"</string>
<string name="global_action_screenshot" msgid="2760267567509131654">"屏幕截图"</string>
+ <string name="remote_input_image_insertion_text" msgid="4613177882724332877">"已插入图片"</string>
<string name="screenshot_saving_ticker" msgid="6519186952674544916">"正在保存屏幕截图..."</string>
<string name="screenshot_saving_title" msgid="2298349784913287333">"正在保存屏幕截图..."</string>
<string name="screenshot_saved_title" msgid="8893267638659083153">"已保存屏幕截图"</string>
@@ -84,6 +85,7 @@
<string name="screenrecord_start_label" msgid="1539048263178882562">"开始录制"</string>
<string name="screenrecord_mic_label" msgid="6134198080740031632">"录制旁白"</string>
<string name="screenrecord_taps_label" msgid="2518244240225925076">"显示点按操作反馈"</string>
+ <string name="screenrecord_stop_text" msgid="6549288689506057686">"点按即可停止"</string>
<string name="screenrecord_stop_label" msgid="72699670052087989">"停止"</string>
<string name="screenrecord_pause_label" msgid="6004054907104549857">"暂停"</string>
<string name="screenrecord_resume_label" msgid="4972223043729555575">"继续"</string>
@@ -388,15 +390,14 @@
<string name="quick_settings_dark_mode_secondary_label_battery_saver" msgid="4990712734503013251">"省电模式"</string>
<string name="quick_settings_dark_mode_secondary_label_on_at_sunset" msgid="6017379738102015710">"在日落时开启"</string>
<string name="quick_settings_dark_mode_secondary_label_until_sunrise" msgid="4404885070316716472">"在日出时关闭"</string>
+ <string name="quick_settings_dark_mode_secondary_label_on_at" msgid="5128758823486361279">"在<xliff:g id="TIME">%s</xliff:g> 开启"</string>
+ <string name="quick_settings_dark_mode_secondary_label_until" msgid="2289774641256492437">"直到<xliff:g id="TIME">%s</xliff:g>"</string>
<string name="quick_settings_nfc_label" msgid="1054317416221168085">"NFC"</string>
<string name="quick_settings_nfc_off" msgid="3465000058515424663">"NFC 已停用"</string>
<string name="quick_settings_nfc_on" msgid="1004976611203202230">"NFC 已启用"</string>
- <!-- no translation found for quick_settings_screen_record_label (1594046461509776676) -->
- <skip />
- <!-- no translation found for quick_settings_screen_record_start (1574725369331638985) -->
- <skip />
- <!-- no translation found for quick_settings_screen_record_stop (8087348522976412119) -->
- <skip />
+ <string name="quick_settings_screen_record_label" msgid="1594046461509776676">"屏幕录制"</string>
+ <string name="quick_settings_screen_record_start" msgid="1574725369331638985">"开始"</string>
+ <string name="quick_settings_screen_record_stop" msgid="8087348522976412119">"停止"</string>
<string name="recents_swipe_up_onboarding" msgid="2820265886420993995">"向上滑动可切换应用"</string>
<string name="recents_quick_scrub_onboarding" msgid="765934300283514912">"向右拖动可快速切换应用"</string>
<string name="quick_step_accessibility_toggle_overview" msgid="7908949976727578403">"切换概览"</string>
@@ -696,22 +697,14 @@
<string name="notification_app_settings" msgid="8963648463858039377">"自定义"</string>
<string name="notification_done" msgid="6215117625922713976">"完成"</string>
<string name="inline_undo" msgid="9026953267645116526">"撤消"</string>
- <!-- no translation found for demote (6225813324237153980) -->
- <skip />
- <!-- no translation found for notification_conversation_favorite (8252976467488182853) -->
- <skip />
- <!-- no translation found for notification_conversation_unfavorite (633301300443356176) -->
- <skip />
- <!-- no translation found for notification_conversation_mute (477431709687199671) -->
- <skip />
- <!-- no translation found for notification_conversation_unmute (410885000669775294) -->
- <skip />
- <!-- no translation found for notification_conversation_bubble (4598142032706190028) -->
- <skip />
- <!-- no translation found for notification_conversation_unbubble (2303087159802926401) -->
- <skip />
- <!-- no translation found for notification_conversation_home_screen (8347136037958438935) -->
- <skip />
+ <string name="demote" msgid="6225813324237153980">"将此通知标记为非对话"</string>
+ <string name="notification_conversation_favorite" msgid="8252976467488182853">"收藏"</string>
+ <string name="notification_conversation_unfavorite" msgid="633301300443356176">"取消收藏"</string>
+ <string name="notification_conversation_mute" msgid="477431709687199671">"静音"</string>
+ <string name="notification_conversation_unmute" msgid="410885000669775294">"取消静音"</string>
+ <string name="notification_conversation_bubble" msgid="4598142032706190028">"显示为气泡"</string>
+ <string name="notification_conversation_unbubble" msgid="2303087159802926401">"关闭气泡"</string>
+ <string name="notification_conversation_home_screen" msgid="8347136037958438935">"添加到主屏幕"</string>
<string name="notification_menu_accessibility" msgid="8984166825879886773">"<xliff:g id="APP_NAME">%1$s</xliff:g><xliff:g id="MENU_DESCRIPTION">%2$s</xliff:g>"</string>
<string name="notification_menu_gear_description" msgid="6429668976593634862">"通知设置"</string>
<string name="notification_menu_snooze_description" msgid="4740133348901973244">"通知延后选项"</string>
diff --git a/packages/SystemUI/res/values-zh-rHK/strings.xml b/packages/SystemUI/res/values-zh-rHK/strings.xml
index 0f1696e7b34e..251e23d1526b 100644
--- a/packages/SystemUI/res/values-zh-rHK/strings.xml
+++ b/packages/SystemUI/res/values-zh-rHK/strings.xml
@@ -71,6 +71,7 @@
<string name="compat_mode_on" msgid="4963711187149440884">"放大為全螢幕"</string>
<string name="compat_mode_off" msgid="7682459748279487945">"放大為全螢幕"</string>
<string name="global_action_screenshot" msgid="2760267567509131654">"螢幕截圖"</string>
+ <string name="remote_input_image_insertion_text" msgid="4613177882724332877">"已插入圖片"</string>
<string name="screenshot_saving_ticker" msgid="6519186952674544916">"正在儲存螢幕擷取畫面..."</string>
<string name="screenshot_saving_title" msgid="2298349784913287333">"正在儲存螢幕擷取畫面..."</string>
<string name="screenshot_saved_title" msgid="8893267638659083153">"螢幕擷取畫面已儲存"</string>
@@ -84,6 +85,7 @@
<string name="screenrecord_start_label" msgid="1539048263178882562">"開始錄影"</string>
<string name="screenrecord_mic_label" msgid="6134198080740031632">"錄製畫面外的音效"</string>
<string name="screenrecord_taps_label" msgid="2518244240225925076">"顯示輕按選項"</string>
+ <string name="screenrecord_stop_text" msgid="6549288689506057686">"輕按即可停止"</string>
<string name="screenrecord_stop_label" msgid="72699670052087989">"停止"</string>
<string name="screenrecord_pause_label" msgid="6004054907104549857">"暫停"</string>
<string name="screenrecord_resume_label" msgid="4972223043729555575">"繼續"</string>
@@ -388,15 +390,14 @@
<string name="quick_settings_dark_mode_secondary_label_battery_saver" msgid="4990712734503013251">"省電模式"</string>
<string name="quick_settings_dark_mode_secondary_label_on_at_sunset" msgid="6017379738102015710">"在日落時開啟"</string>
<string name="quick_settings_dark_mode_secondary_label_until_sunrise" msgid="4404885070316716472">"在日出時關閉"</string>
+ <string name="quick_settings_dark_mode_secondary_label_on_at" msgid="5128758823486361279">"於<xliff:g id="TIME">%s</xliff:g>開啟"</string>
+ <string name="quick_settings_dark_mode_secondary_label_until" msgid="2289774641256492437">"直至<xliff:g id="TIME">%s</xliff:g>"</string>
<string name="quick_settings_nfc_label" msgid="1054317416221168085">"NFC"</string>
<string name="quick_settings_nfc_off" msgid="3465000058515424663">"NFC 已停用"</string>
<string name="quick_settings_nfc_on" msgid="1004976611203202230">"NFC 已啟用"</string>
- <!-- no translation found for quick_settings_screen_record_label (1594046461509776676) -->
- <skip />
- <!-- no translation found for quick_settings_screen_record_start (1574725369331638985) -->
- <skip />
- <!-- no translation found for quick_settings_screen_record_stop (8087348522976412119) -->
- <skip />
+ <string name="quick_settings_screen_record_label" msgid="1594046461509776676">"畫面錄影"</string>
+ <string name="quick_settings_screen_record_start" msgid="1574725369331638985">"開始"</string>
+ <string name="quick_settings_screen_record_stop" msgid="8087348522976412119">"停"</string>
<string name="recents_swipe_up_onboarding" msgid="2820265886420993995">"向上滑動即可切換應用程式"</string>
<string name="recents_quick_scrub_onboarding" msgid="765934300283514912">"向右拖曳即可快速切換應用程式"</string>
<string name="quick_step_accessibility_toggle_overview" msgid="7908949976727578403">"切換概覽"</string>
@@ -696,22 +697,14 @@
<string name="notification_app_settings" msgid="8963648463858039377">"自訂"</string>
<string name="notification_done" msgid="6215117625922713976">"完成"</string>
<string name="inline_undo" msgid="9026953267645116526">"復原"</string>
- <!-- no translation found for demote (6225813324237153980) -->
- <skip />
- <!-- no translation found for notification_conversation_favorite (8252976467488182853) -->
- <skip />
- <!-- no translation found for notification_conversation_unfavorite (633301300443356176) -->
- <skip />
- <!-- no translation found for notification_conversation_mute (477431709687199671) -->
- <skip />
- <!-- no translation found for notification_conversation_unmute (410885000669775294) -->
- <skip />
- <!-- no translation found for notification_conversation_bubble (4598142032706190028) -->
- <skip />
- <!-- no translation found for notification_conversation_unbubble (2303087159802926401) -->
- <skip />
- <!-- no translation found for notification_conversation_home_screen (8347136037958438935) -->
- <skip />
+ <string name="demote" msgid="6225813324237153980">"將此通知標示為非對話"</string>
+ <string name="notification_conversation_favorite" msgid="8252976467488182853">"我的最愛"</string>
+ <string name="notification_conversation_unfavorite" msgid="633301300443356176">"取消收藏"</string>
+ <string name="notification_conversation_mute" msgid="477431709687199671">"靜音"</string>
+ <string name="notification_conversation_unmute" msgid="410885000669775294">"取消靜音"</string>
+ <string name="notification_conversation_bubble" msgid="4598142032706190028">"以小視窗顯示"</string>
+ <string name="notification_conversation_unbubble" msgid="2303087159802926401">"關閉小視窗"</string>
+ <string name="notification_conversation_home_screen" msgid="8347136037958438935">"加入主畫面"</string>
<string name="notification_menu_accessibility" msgid="8984166825879886773">"<xliff:g id="APP_NAME">%1$s</xliff:g> <xliff:g id="MENU_DESCRIPTION">%2$s</xliff:g>"</string>
<string name="notification_menu_gear_description" msgid="6429668976593634862">"通知控制項"</string>
<string name="notification_menu_snooze_description" msgid="4740133348901973244">"通知延後選項"</string>
diff --git a/packages/SystemUI/res/values-zh-rTW/strings.xml b/packages/SystemUI/res/values-zh-rTW/strings.xml
index e8e76ecb6094..a7ec5985a745 100644
--- a/packages/SystemUI/res/values-zh-rTW/strings.xml
+++ b/packages/SystemUI/res/values-zh-rTW/strings.xml
@@ -71,6 +71,7 @@
<string name="compat_mode_on" msgid="4963711187149440884">"放大為全螢幕"</string>
<string name="compat_mode_off" msgid="7682459748279487945">"放大為全螢幕"</string>
<string name="global_action_screenshot" msgid="2760267567509131654">"擷取螢幕畫面"</string>
+ <string name="remote_input_image_insertion_text" msgid="4613177882724332877">"已插入圖片"</string>
<string name="screenshot_saving_ticker" msgid="6519186952674544916">"正在儲存螢幕截圖…"</string>
<string name="screenshot_saving_title" msgid="2298349784913287333">"正在儲存螢幕截圖…"</string>
<string name="screenshot_saved_title" msgid="8893267638659083153">"螢幕截圖已儲存"</string>
@@ -84,6 +85,7 @@
<string name="screenrecord_start_label" msgid="1539048263178882562">"開始錄製"</string>
<string name="screenrecord_mic_label" msgid="6134198080740031632">"錄製畫面外的音效"</string>
<string name="screenrecord_taps_label" msgid="2518244240225925076">"顯示觸控回應"</string>
+ <string name="screenrecord_stop_text" msgid="6549288689506057686">"輕觸即可停止"</string>
<string name="screenrecord_stop_label" msgid="72699670052087989">"停止"</string>
<string name="screenrecord_pause_label" msgid="6004054907104549857">"暫停"</string>
<string name="screenrecord_resume_label" msgid="4972223043729555575">"繼續"</string>
@@ -388,15 +390,14 @@
<string name="quick_settings_dark_mode_secondary_label_battery_saver" msgid="4990712734503013251">"節約耗電量"</string>
<string name="quick_settings_dark_mode_secondary_label_on_at_sunset" msgid="6017379738102015710">"於日落時開啟"</string>
<string name="quick_settings_dark_mode_secondary_label_until_sunrise" msgid="4404885070316716472">"於日出時關閉"</string>
+ <string name="quick_settings_dark_mode_secondary_label_on_at" msgid="5128758823486361279">"開啟時間:<xliff:g id="TIME">%s</xliff:g>"</string>
+ <string name="quick_settings_dark_mode_secondary_label_until" msgid="2289774641256492437">"關閉時間:<xliff:g id="TIME">%s</xliff:g>"</string>
<string name="quick_settings_nfc_label" msgid="1054317416221168085">"NFC"</string>
<string name="quick_settings_nfc_off" msgid="3465000058515424663">"NFC 已停用"</string>
<string name="quick_settings_nfc_on" msgid="1004976611203202230">"NFC 已啟用"</string>
- <!-- no translation found for quick_settings_screen_record_label (1594046461509776676) -->
- <skip />
- <!-- no translation found for quick_settings_screen_record_start (1574725369331638985) -->
- <skip />
- <!-- no translation found for quick_settings_screen_record_stop (8087348522976412119) -->
- <skip />
+ <string name="quick_settings_screen_record_label" msgid="1594046461509776676">"螢幕畫面錄製"</string>
+ <string name="quick_settings_screen_record_start" msgid="1574725369331638985">"開始"</string>
+ <string name="quick_settings_screen_record_stop" msgid="8087348522976412119">"停止"</string>
<string name="recents_swipe_up_onboarding" msgid="2820265886420993995">"向上滑動即可切換應用程式"</string>
<string name="recents_quick_scrub_onboarding" msgid="765934300283514912">"向右拖曳即可快速切換應用程式"</string>
<string name="quick_step_accessibility_toggle_overview" msgid="7908949976727578403">"切換總覽"</string>
@@ -696,22 +697,14 @@
<string name="notification_app_settings" msgid="8963648463858039377">"自訂"</string>
<string name="notification_done" msgid="6215117625922713976">"完成"</string>
<string name="inline_undo" msgid="9026953267645116526">"復原"</string>
- <!-- no translation found for demote (6225813324237153980) -->
- <skip />
- <!-- no translation found for notification_conversation_favorite (8252976467488182853) -->
- <skip />
- <!-- no translation found for notification_conversation_unfavorite (633301300443356176) -->
- <skip />
- <!-- no translation found for notification_conversation_mute (477431709687199671) -->
- <skip />
- <!-- no translation found for notification_conversation_unmute (410885000669775294) -->
- <skip />
- <!-- no translation found for notification_conversation_bubble (4598142032706190028) -->
- <skip />
- <!-- no translation found for notification_conversation_unbubble (2303087159802926401) -->
- <skip />
- <!-- no translation found for notification_conversation_home_screen (8347136037958438935) -->
- <skip />
+ <string name="demote" msgid="6225813324237153980">"將這則通知標示為非對話"</string>
+ <string name="notification_conversation_favorite" msgid="8252976467488182853">"加入收藏"</string>
+ <string name="notification_conversation_unfavorite" msgid="633301300443356176">"從收藏中移除"</string>
+ <string name="notification_conversation_mute" msgid="477431709687199671">"忽略"</string>
+ <string name="notification_conversation_unmute" msgid="410885000669775294">"取消忽略"</string>
+ <string name="notification_conversation_bubble" msgid="4598142032706190028">"以泡泡形式顯示"</string>
+ <string name="notification_conversation_unbubble" msgid="2303087159802926401">"關閉泡泡"</string>
+ <string name="notification_conversation_home_screen" msgid="8347136037958438935">"新增至主螢幕"</string>
<string name="notification_menu_accessibility" msgid="8984166825879886773">"<xliff:g id="APP_NAME">%1$s</xliff:g> <xliff:g id="MENU_DESCRIPTION">%2$s</xliff:g>"</string>
<string name="notification_menu_gear_description" msgid="6429668976593634862">"通知控制項"</string>
<string name="notification_menu_snooze_description" msgid="4740133348901973244">"通知延後選項"</string>
diff --git a/packages/SystemUI/res/values-zu/strings.xml b/packages/SystemUI/res/values-zu/strings.xml
index fa764b575df0..1203b579c52f 100644
--- a/packages/SystemUI/res/values-zu/strings.xml
+++ b/packages/SystemUI/res/values-zu/strings.xml
@@ -71,6 +71,7 @@
<string name="compat_mode_on" msgid="4963711187149440884">"Sondeza ukugcwalisa isikrini"</string>
<string name="compat_mode_off" msgid="7682459748279487945">"Nweba ukugcwalisa isikrini"</string>
<string name="global_action_screenshot" msgid="2760267567509131654">"Isithombe-skrini"</string>
+ <string name="remote_input_image_insertion_text" msgid="4613177882724332877">"Isithombe sifakiwe"</string>
<string name="screenshot_saving_ticker" msgid="6519186952674544916">"Ilondoloz umfanekiso weskrini..."</string>
<string name="screenshot_saving_title" msgid="2298349784913287333">"Ilondoloz umfanekiso weskrini..."</string>
<string name="screenshot_saved_title" msgid="8893267638659083153">"Isithombe-skrini silondoloziwe"</string>
@@ -84,6 +85,7 @@
<string name="screenrecord_start_label" msgid="1539048263178882562">"Qala ukurekhoda"</string>
<string name="screenrecord_mic_label" msgid="6134198080740031632">"Rekhoda izwi elingaphezulu"</string>
<string name="screenrecord_taps_label" msgid="2518244240225925076">"Bonisa amathebhu"</string>
+ <string name="screenrecord_stop_text" msgid="6549288689506057686">"Thepha ukuze umise"</string>
<string name="screenrecord_stop_label" msgid="72699670052087989">"Misa"</string>
<string name="screenrecord_pause_label" msgid="6004054907104549857">"Phumula"</string>
<string name="screenrecord_resume_label" msgid="4972223043729555575">"Qalisa kabusha"</string>
@@ -388,6 +390,8 @@
<string name="quick_settings_dark_mode_secondary_label_battery_saver" msgid="4990712734503013251">"Isilondolozi sebhethri"</string>
<string name="quick_settings_dark_mode_secondary_label_on_at_sunset" msgid="6017379738102015710">"Kuvulwe ekushoneni kwelanga"</string>
<string name="quick_settings_dark_mode_secondary_label_until_sunrise" msgid="4404885070316716472">"Kuze kube sekuphumeni kwelanga"</string>
+ <string name="quick_settings_dark_mode_secondary_label_on_at" msgid="5128758823486361279">"Kuvulwe ngo-<xliff:g id="TIME">%s</xliff:g>"</string>
+ <string name="quick_settings_dark_mode_secondary_label_until" msgid="2289774641256492437">"Kuze kube ngu-<xliff:g id="TIME">%s</xliff:g>"</string>
<string name="quick_settings_nfc_label" msgid="1054317416221168085">"I-NFC"</string>
<string name="quick_settings_nfc_off" msgid="3465000058515424663">"I-NFC ikhutshaziwe"</string>
<string name="quick_settings_nfc_on" msgid="1004976611203202230">"I-NFC inikwe amandla"</string>
diff --git a/packages/SystemUI/res/values/colors.xml b/packages/SystemUI/res/values/colors.xml
index 8dd2a8b9d07a..09058f2460cb 100644
--- a/packages/SystemUI/res/values/colors.xml
+++ b/packages/SystemUI/res/values/colors.xml
@@ -200,18 +200,28 @@
<color name="GM2_grey_800">#3C4043</color>
<color name="GM2_grey_900">#202124</color>
+ <color name="GM2_red_50">#FCE8E6</color>
<color name="GM2_red_300">#F28B82</color>
<color name="GM2_red_500">#B71C1C</color>
<color name="GM2_red_700">#C5221F</color>
+ <color name="GM2_blue_50">#E8F0FE</color>
<color name="GM2_blue_200">#AECBFA</color>
<color name="GM2_blue_300">#8AB4F8</color>
+ <color name="GM2_blue_500">#FF4285F4</color>
<color name="GM2_blue_600">#1A73E8</color>
<color name="GM2_blue_700">#1967D2</color>
+ <color name="GM2_yellow_50">#FEF7E0</color>
<color name="GM2_yellow_500">#FFFBBC04</color>
+
<color name="GM2_green_500">#FF34A853</color>
- <color name="GM2_blue_500">#FF4285F4</color>
+
+ <color name="GM2_orange_900">#B06000</color>
<color name="magnification_border_color">#FF9900</color>
+
+ <!-- controls -->
+ <color name="control_default_foreground">?android:attr/textColorPrimary</color>
+ <color name="control_default_background">?android:attr/colorBackgroundFloating</color>
</resources>
diff --git a/packages/SystemUI/res/values/config.xml b/packages/SystemUI/res/values/config.xml
index edcd8012c82c..2aa6d9512898 100644
--- a/packages/SystemUI/res/values/config.xml
+++ b/packages/SystemUI/res/values/config.xml
@@ -297,6 +297,7 @@
<item>com.android.systemui.theme.ThemeOverlayController</item>
<item>com.android.systemui.accessibility.WindowMagnification</item>
<item>com.android.systemui.accessibility.SystemActions</item>
+ <item>com.android.systemui.toast.ToastUI</item>
</string-array>
<!-- SystemUI vender service, used in config_systemUIServiceComponents. -->
diff --git a/packages/SystemUI/res/values/dimens.xml b/packages/SystemUI/res/values/dimens.xml
index b40c5c0b0264..cc58b2014496 100644
--- a/packages/SystemUI/res/values/dimens.xml
+++ b/packages/SystemUI/res/values/dimens.xml
@@ -1181,5 +1181,12 @@
<dimen name="magnifier_up_down_controls_width">45dp</dimen>
<dimen name="magnifier_up_down_controls_height">40dp</dimen>
+ <!-- Home Controls -->
+ <dimen name="control_spacing">5dp</dimen>
+ <dimen name="control_corner_radius">15dp</dimen>
+ <dimen name="control_height">100dp</dimen>
+ <dimen name="control_padding">15dp</dimen>
+ <dimen name="control_status_normal">12dp</dimen>
+ <dimen name="control_status_expanded">18dp</dimen>
<dimen name="app_icon_size">32dp</dimen>
</resources>
diff --git a/packages/SystemUI/res/values/strings.xml b/packages/SystemUI/res/values/strings.xml
index 44a7fda6bce3..8d935ecc691d 100644
--- a/packages/SystemUI/res/values/strings.xml
+++ b/packages/SystemUI/res/values/strings.xml
@@ -2546,4 +2546,10 @@
<string name="magnification_window_title">Magnification Window</string>
<!-- Title for Magnification Controls Window [CHAR LIMIT=NONE] -->
<string name="magnification_controls_title">Magnification Window Controls</string>
+
+ <!-- Quick Controls strings [CHAR LIMIT=30] -->
+ <string name="quick_controls_title">Quick Controls</string>
+
+ <!-- The tile in quick settings is unavailable. [CHAR LIMIT=32] -->
+ <string name="tile_unavailable">Unvailable</string>
</resources>
diff --git a/packages/SystemUI/src/com/android/systemui/bubbles/Bubble.java b/packages/SystemUI/src/com/android/systemui/bubbles/Bubble.java
index ccce85ca74fb..45705b76f09c 100644
--- a/packages/SystemUI/src/com/android/systemui/bubbles/Bubble.java
+++ b/packages/SystemUI/src/com/android/systemui/bubbles/Bubble.java
@@ -60,6 +60,14 @@ class Bubble {
private long mLastUpdated;
private long mLastAccessed;
+ private BubbleController.NotificationSuppressionChangedListener mSuppressionListener;
+
+ /** Whether the bubble should show a dot for the notification indicating updated content. */
+ private boolean mShowBubbleUpdateDot = true;
+
+ /** Whether flyout text should be suppressed, regardless of any other flags or state. */
+ private boolean mSuppressFlyout;
+
// Items that are typically loaded later
private String mAppName;
private ShortcutInfo mShortcutInfo;
@@ -71,20 +79,6 @@ class Bubble {
private boolean mInflateSynchronously;
/**
- * Whether this notification should be shown in the shade when it is also displayed as a bubble.
- *
- * <p>When a notification is a bubble we don't show it in the shade once the bubble has been
- * expanded</p>
- */
- private boolean mShowInShadeWhenBubble = true;
-
- /** Whether the bubble should show a dot for the notification indicating updated content. */
- private boolean mShowBubbleUpdateDot = true;
-
- /** Whether flyout text should be suppressed, regardless of any other flags or state. */
- private boolean mSuppressFlyout;
-
- /**
* Presentational info about the flyout.
*/
public static class FlyoutMessage {
@@ -106,11 +100,13 @@ class Bubble {
/** Used in tests when no UI is required. */
@VisibleForTesting(visibility = PRIVATE)
- Bubble(NotificationEntry e) {
+ Bubble(NotificationEntry e,
+ BubbleController.NotificationSuppressionChangedListener listener) {
mEntry = e;
mKey = e.getKey();
mLastUpdated = e.getSbn().getPostTime();
mGroupId = groupId(e);
+ mSuppressionListener = listener;
}
public String getKey() {
@@ -278,7 +274,7 @@ class Bubble {
*/
void markAsAccessedAt(long lastAccessedMillis) {
mLastAccessed = lastAccessedMillis;
- setShowInShade(false);
+ setSuppressNotification(true);
setShowDot(false /* show */, true /* animate */);
}
@@ -290,20 +286,30 @@ class Bubble {
}
/**
- * Whether this notification should be shown in the shade when it is also displayed as a
- * bubble.
+ * Whether this notification should be shown in the shade.
*/
boolean showInShade() {
- return !mEntry.isRowDismissed() && !shouldSuppressNotification()
- && (!mEntry.isClearable() || mShowInShadeWhenBubble);
+ return !shouldSuppressNotification() || !mEntry.isClearable();
}
/**
- * Sets whether this notification should be shown in the shade when it is also displayed as a
- * bubble.
+ * Sets whether this notification should be suppressed in the shade.
*/
- void setShowInShade(boolean showInShade) {
- mShowInShadeWhenBubble = showInShade;
+ void setSuppressNotification(boolean suppressNotification) {
+ boolean prevShowInShade = showInShade();
+
+ Notification.BubbleMetadata data = mEntry.getBubbleMetadata();
+ int flags = data.getFlags();
+ if (suppressNotification) {
+ flags |= Notification.BubbleMetadata.FLAG_SUPPRESS_NOTIFICATION;
+ } else {
+ flags &= ~Notification.BubbleMetadata.FLAG_SUPPRESS_NOTIFICATION;
+ }
+ data.setFlags(flags);
+
+ if (showInShade() != prevShowInShade && mSuppressionListener != null) {
+ mSuppressionListener.onBubbleNotificationSuppressionChange(this);
+ }
}
/**
diff --git a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleController.java b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleController.java
index 8c9946fcfc7b..894ecf67bf34 100644
--- a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleController.java
+++ b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleController.java
@@ -224,6 +224,17 @@ public class BubbleController implements ConfigurationController.ConfigurationLi
}
/**
+ * Listener to be notified when a bubbles' notification suppression state changes.
+ */
+ public interface NotificationSuppressionChangedListener {
+ /**
+ * Called when the notification suppression state of a bubble changes.
+ */
+ void onBubbleNotificationSuppressionChange(Bubble bubble);
+
+ }
+
+ /**
* Listens for the current state of the status bar and updates the visibility state
* of bubbles as needed.
*/
@@ -303,9 +314,22 @@ public class BubbleController implements ConfigurationController.ConfigurationLi
configurationController.addCallback(this /* configurationListener */);
+ mMaxBubbles = mContext.getResources().getInteger(R.integer.bubbles_max_rendered);
mBubbleData = data;
mBubbleData.setListener(mBubbleDataListener);
- mMaxBubbles = mContext.getResources().getInteger(R.integer.bubbles_max_rendered);
+ mBubbleData.setSuppressionChangedListener(new NotificationSuppressionChangedListener() {
+ @Override
+ public void onBubbleNotificationSuppressionChange(Bubble bubble) {
+ // Make sure NoMan knows it's not showing in the shade anymore so anyone querying it
+ // can tell.
+ try {
+ mBarService.onBubbleNotificationSuppressionChanged(bubble.getKey(),
+ !bubble.showInShade());
+ } catch (RemoteException e) {
+ // Bad things have happened
+ }
+ }
+ });
mNotificationEntryManager = entryManager;
mNotificationEntryManager.addNotificationEntryListener(mEntryListener);
@@ -720,7 +744,7 @@ public class BubbleController implements ConfigurationController.ConfigurationLi
Bubble bubble = mBubbleData.getBubbleWithKey(key);
boolean bubbleExtended = entry != null && entry.isBubble() && userRemovedNotif;
if (bubbleExtended) {
- bubble.setShowInShade(false);
+ bubble.setSuppressNotification(true);
bubble.setShowDot(false /* show */, true /* animate */);
mNotificationEntryManager.updateNotifications(
"BubbleController.onNotificationRemoveRequested");
@@ -747,7 +771,7 @@ public class BubbleController implements ConfigurationController.ConfigurationLi
// As far as group manager is concerned, once a child is no longer shown
// in the shade, it is essentially removed.
mNotificationGroupManager.onEntryRemoved(bubbleChild.getEntry());
- bubbleChild.setShowInShade(false);
+ bubbleChild.setSuppressNotification(true);
bubbleChild.setShowDot(false /* show */, true /* animate */);
}
// And since all children are removed, remove the summary.
@@ -1074,12 +1098,10 @@ public class BubbleController implements ConfigurationController.ConfigurationLi
@Override
public void onSingleTaskDisplayDrawn(int displayId) {
- final Bubble expandedBubble = mStackView != null
- ? mStackView.getExpandedBubble()
- : null;
- if (expandedBubble != null && expandedBubble.getDisplayId() == displayId) {
- expandedBubble.setContentVisibility(true);
+ if (mStackView == null) {
+ return;
}
+ mStackView.showExpandedViewContents(displayId);
}
@Override
diff --git a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleData.java b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleData.java
index 8b687e7114db..673121f92716 100644
--- a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleData.java
+++ b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleData.java
@@ -134,6 +134,9 @@ public class BubbleData {
@Nullable
private Listener mListener;
+ @Nullable
+ private BubbleController.NotificationSuppressionChangedListener mSuppressionListener;
+
/**
* We track groups with summaries that aren't visibly displayed but still kept around because
* the bubble(s) associated with the summary still exist.
@@ -158,6 +161,11 @@ public class BubbleData {
mMaxOverflowBubbles = mContext.getResources().getInteger(R.integer.bubbles_max_overflow);
}
+ public void setSuppressionChangedListener(
+ BubbleController.NotificationSuppressionChangedListener listener) {
+ mSuppressionListener = listener;
+ }
+
public boolean hasBubbles() {
return !mBubbles.isEmpty();
}
@@ -219,7 +227,7 @@ public class BubbleData {
return b;
}
}
- bubble = new Bubble(entry);
+ bubble = new Bubble(entry, mSuppressionListener);
mPendingBubbles.add(bubble);
} else {
bubble.setEntry(entry);
@@ -258,11 +266,13 @@ public class BubbleData {
} else if (mSelectedBubble == null) {
setSelectedBubbleInternal(bubble);
}
+
boolean isBubbleExpandedAndSelected = mExpanded && mSelectedBubble == bubble;
- bubble.setShowInShade(!isBubbleExpandedAndSelected && showInShade);
+ boolean suppress = isBubbleExpandedAndSelected || !showInShade || !bubble.showInShade();
+ bubble.setSuppressNotification(suppress);
bubble.setShowDot(!isBubbleExpandedAndSelected /* show */, true /* animate */);
- dispatchPendingChanges();
+ dispatchPendingChanges();
}
public void notificationEntryRemoved(NotificationEntry entry, @DismissReason int reason) {
diff --git a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleStackView.java b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleStackView.java
index fe4c91509057..685bb9490a9a 100644
--- a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleStackView.java
+++ b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleStackView.java
@@ -409,8 +409,12 @@ public class BubbleStackView extends FrameLayout {
.setStiffness(SpringForce.STIFFNESS_LOW)
.setDampingRatio(SpringForce.DAMPING_RATIO_LOW_BOUNCY));
mExpandedViewYAnim.addEndListener((anim, cancelled, value, velocity) -> {
- if (mIsExpanded && mExpandedBubble != null) {
- mExpandedBubble.getExpandedView().updateView();
+ if (mIsExpanded) {
+ if (mExpandedBubble == null) {
+ mOverflowExpandedView.updateView();
+ } else {
+ mExpandedBubble.getExpandedView().updateView();
+ }
}
});
@@ -525,7 +529,7 @@ public class BubbleStackView extends FrameLayout {
mInflater.inflate(R.layout.bubble_overflow_button, this);
mOverflowBtn = findViewById(R.id.bubble_overflow_button);
mOverflowBtn.setOnClickListener(v -> {
- showOverflow();
+ setSelectedBubble(null);
});
TypedArray ta = mContext.obtainStyledAttributes(
@@ -540,26 +544,13 @@ public class BubbleStackView extends FrameLayout {
mOverflowBtn.setVisibility(GONE);
}
- void showOverflow() {
- if (DEBUG_BUBBLE_STACK_VIEW) {
- Log.d(TAG, "Show overflow.");
+ void showExpandedViewContents(int displayId) {
+ if (mOverflowExpandedView.getVirtualDisplayId() == displayId) {
+ mOverflowExpandedView.setContentVisibility(true);
+ } else if (mExpandedBubble != null
+ && mExpandedBubble.getExpandedView().getVirtualDisplayId() == displayId) {
+ mExpandedBubble.setContentVisibility(true);
}
- mExpandedViewContainer.setAlpha(0.0f);
- mSurfaceSynchronizer.syncSurfaceAndRun(() -> {
- if (mExpandedBubble != null) {
- mExpandedBubble.setContentVisibility(false);
- mExpandedBubble = null;
- }
- mExpandedViewContainer.removeAllViews();
- if (mIsExpanded) {
- mExpandedViewContainer.addView(mOverflowExpandedView);
- mOverflowExpandedView.populateExpandedView();
- mExpandedViewContainer.setVisibility(VISIBLE);
- mExpandedViewContainer.setAlpha(1.0f);
- mOverflowExpandedView.setContentVisibility(true);
- }
- requestUpdate();
- });
}
private void setUpFlyout() {
@@ -883,7 +874,9 @@ public class BubbleStackView extends FrameLayout {
// expanded view becomes visible on the screen. See b/126856255
mExpandedViewContainer.setAlpha(0.0f);
mSurfaceSynchronizer.syncSurfaceAndRun(() -> {
- if (previouslySelected != null) {
+ if (previouslySelected == null) {
+ mOverflowExpandedView.setContentVisibility(false);
+ } else {
previouslySelected.setContentVisibility(false);
}
updateExpandedBubble();
@@ -1055,7 +1048,9 @@ public class BubbleStackView extends FrameLayout {
() -> {
mBubbleContainer.setActiveController(mStackAnimationController);
afterExpandedViewAnimation();
- if (previouslySelected != null) {
+ if (previouslySelected == null) {
+ mOverflowExpandedView.setContentVisibility(false);
+ } else {
previouslySelected.setContentVisibility(false);
}
});
@@ -1625,10 +1620,14 @@ public class BubbleStackView extends FrameLayout {
Log.d(TAG, "updateExpandedBubble()");
}
mExpandedViewContainer.removeAllViews();
- if (mExpandedBubble != null && mIsExpanded) {
- mExpandedViewContainer.addView(mExpandedBubble.getExpandedView());
- mExpandedBubble.getExpandedView().populateExpandedView();
- mExpandedViewContainer.setVisibility(mIsExpanded ? VISIBLE : GONE);
+ if (mIsExpanded) {
+ BubbleExpandedView bev = mOverflowExpandedView;
+ if (mExpandedBubble != null) {
+ bev = mExpandedBubble.getExpandedView();
+ }
+ mExpandedViewContainer.addView(bev);
+ bev.populateExpandedView();
+ mExpandedViewContainer.setVisibility(VISIBLE);
mExpandedViewContainer.setAlpha(1.0f);
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleViewInfoTask.java b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleViewInfoTask.java
index 6e23777babd9..55a7a11987fc 100644
--- a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleViewInfoTask.java
+++ b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleViewInfoTask.java
@@ -246,7 +246,7 @@ public class BubbleViewInfoTask extends AsyncTask<Void, Void, BubbleViewInfoTask
bubbleMessage.senderName = sender != null
? sender.getName()
: null;
- bubbleMessage.senderAvatar = sender != null
+ bubbleMessage.senderAvatar = sender != null && sender.getIcon() != null
? sender.getIcon().loadDrawable(context)
: null;
return bubbleMessage;
diff --git a/packages/SystemUI/src/com/android/systemui/controls/ui/ControlViewHolder.kt b/packages/SystemUI/src/com/android/systemui/controls/ui/ControlViewHolder.kt
new file mode 100644
index 000000000000..81b5f3698567
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/controls/ui/ControlViewHolder.kt
@@ -0,0 +1,400 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.controls.ui
+
+import android.content.Context
+import android.graphics.drawable.ClipDrawable
+import android.graphics.drawable.Drawable
+import android.graphics.drawable.GradientDrawable
+import android.graphics.drawable.Icon
+import android.graphics.drawable.LayerDrawable
+import android.service.controls.Control
+import android.service.controls.DeviceTypes
+import android.service.controls.actions.BooleanAction
+import android.service.controls.actions.ControlAction
+import android.service.controls.actions.FloatAction
+import android.service.controls.templates.ControlTemplate
+import android.service.controls.templates.RangeTemplate
+import android.service.controls.templates.ToggleRangeTemplate
+import android.service.controls.templates.ToggleTemplate
+import android.util.TypedValue
+import android.view.MotionEvent
+import android.view.View
+import android.view.ViewGroup
+import android.widget.ImageView
+import android.widget.TextView
+
+import com.android.systemui.controls.controller.ControlsController
+import com.android.systemui.R
+
+private const val MIN_LEVEL = 0
+private const val MAX_LEVEL = 10000
+
+class ControlViewHolder(
+ val layout: ViewGroup,
+ val controlsController: ControlsController
+) {
+ val icon: ImageView = layout.requireViewById(R.id.icon)
+ val status: TextView = layout.requireViewById(R.id.status)
+ val statusExtra: TextView = layout.requireViewById(R.id.status_extra)
+ val title: TextView = layout.requireViewById(R.id.title)
+ val subtitle: TextView = layout.requireViewById(R.id.subtitle)
+ val context: Context = layout.getContext()
+ val clipLayer: ClipDrawable
+ val gd: GradientDrawable
+ lateinit var cws: ControlWithState
+
+ init {
+ val ld = layout.getBackground() as LayerDrawable
+ ld.mutate()
+ clipLayer = ld.findDrawableByLayerId(R.id.clip_layer) as ClipDrawable
+ gd = clipLayer.getDrawable() as GradientDrawable
+ }
+
+ fun bindData(cws: ControlWithState) {
+ this.cws = cws
+
+ val (status, template) = cws.control?.let {
+ title.setText(it.getTitle())
+ subtitle.setText(it.getSubtitle())
+ Pair(it.getStatus(), it.getControlTemplate())
+ } ?: run {
+ title.setText(cws.ci.controlTitle)
+ subtitle.setText("")
+ Pair(Control.STATUS_UNKNOWN, ControlTemplate.NO_TEMPLATE)
+ }
+
+ findBehavior(status, template).apply(this, cws)
+ }
+
+ fun action(action: ControlAction) {
+ controlsController.action(cws.ci, action)
+ }
+
+ private fun findBehavior(status: Int, template: ControlTemplate): Behavior {
+ return when {
+ status == Control.STATUS_UNKNOWN -> UnknownBehavior()
+ template is ToggleTemplate -> ToggleTemplateBehavior()
+ template is ToggleRangeTemplate -> ToggleRangeTemplateBehavior()
+ else -> {
+ object : Behavior {
+ override fun apply(cvh: ControlViewHolder, cws: ControlWithState) {
+ cvh.status.setText(cws.control?.getStatusText())
+ cvh.applyRenderInfo(findRenderInfo(cws.ci.deviceType, false))
+ }
+ }
+ }
+ }
+ }
+
+ internal fun applyRenderInfo(ri: RenderInfo) {
+ val fg = context.getResources().getColorStateList(ri.foreground, context.getTheme())
+ val bg = context.getResources().getColorStateList(ri.background, context.getTheme())
+ status.setTextColor(fg)
+ statusExtra.setTextColor(fg)
+
+ icon.setImageIcon(Icon.createWithResource(context, ri.iconResourceId))
+ icon.setImageTintList(fg)
+
+ gd.setColor(bg)
+ }
+
+ fun setEnabled(enabled: Boolean) {
+ status.setEnabled(enabled)
+ icon.setEnabled(enabled)
+ }
+}
+
+private interface Behavior {
+ fun apply(cvh: ControlViewHolder, cws: ControlWithState)
+
+ fun findRenderInfo(deviceType: Int, isActive: Boolean): RenderInfo =
+ deviceRenderMap.getOrDefault(deviceType, unknownDeviceMap).getValue(isActive)
+}
+
+private class UnknownBehavior : Behavior {
+ override fun apply(cvh: ControlViewHolder, cws: ControlWithState) {
+ cvh.status.setText("Loading...")
+ cvh.applyRenderInfo(findRenderInfo(cws.ci.deviceType, false))
+ }
+}
+
+private class ToggleRangeTemplateBehavior : Behavior {
+ lateinit var clipLayer: Drawable
+ lateinit var template: ToggleRangeTemplate
+ lateinit var control: Control
+ lateinit var cvh: ControlViewHolder
+ lateinit var rangeTemplate: RangeTemplate
+ lateinit var statusExtra: TextView
+ lateinit var status: TextView
+ lateinit var context: Context
+
+ override fun apply(cvh: ControlViewHolder, cws: ControlWithState) {
+ this.control = cws.control!!
+ this.cvh = cvh
+
+ statusExtra = cvh.statusExtra
+ status = cvh.status
+
+ status.setText(control.getStatusText())
+
+ context = status.getContext()
+
+ cvh.layout.setOnTouchListener(ToggleRangeTouchListener())
+
+ val ld = cvh.layout.getBackground() as LayerDrawable
+ clipLayer = ld.findDrawableByLayerId(R.id.clip_layer)
+
+ template = control.getControlTemplate() as ToggleRangeTemplate
+ rangeTemplate = template.getRange()
+
+ val checked = template.isChecked()
+ val deviceType = control.getDeviceType()
+
+ updateRange((rangeTemplate.getCurrentValue() / 100.0f), checked)
+
+ cvh.setEnabled(checked)
+ cvh.applyRenderInfo(findRenderInfo(deviceType, checked))
+ }
+
+ fun toggle() {
+ cvh.action(BooleanAction(template.getTemplateId(), !template.isChecked()))
+
+ val nextLevel = if (template.isChecked()) MIN_LEVEL else MAX_LEVEL
+ clipLayer.setLevel(nextLevel)
+ }
+
+ fun beginUpdateRange() {
+ status.setVisibility(View.GONE)
+ statusExtra.setTextSize(TypedValue.COMPLEX_UNIT_PX, context.getResources()
+ .getDimensionPixelSize(R.dimen.control_status_expanded).toFloat())
+ }
+
+ fun updateRange(f: Float, checked: Boolean) {
+ clipLayer.setLevel(if (checked) (MAX_LEVEL * f).toInt() else MIN_LEVEL)
+
+ if (checked && f < 100.0f && f > 0.0f) {
+ statusExtra.setText("" + (f * 100.0).toInt() + "%")
+ statusExtra.setVisibility(View.VISIBLE)
+ } else {
+ statusExtra.setText("")
+ statusExtra.setVisibility(View.GONE)
+ }
+ }
+
+ fun endUpdateRange(f: Float) {
+ statusExtra.setText(" - " + (f * 100.0).toInt() + "%")
+
+ val newValue = rangeTemplate.getMinValue() +
+ (f * (rangeTemplate.getMaxValue() - rangeTemplate.getMinValue()))
+
+ statusExtra.setTextSize(TypedValue.COMPLEX_UNIT_PX, context.getResources()
+ .getDimensionPixelSize(R.dimen.control_status_normal).toFloat())
+ status.setVisibility(View.VISIBLE)
+
+ cvh.action(FloatAction(rangeTemplate.getTemplateId(), findNearestStep(newValue)))
+ }
+
+ fun findNearestStep(value: Float): Float {
+ var minDiff = 1000f
+
+ var f = rangeTemplate.getMinValue()
+ while (f <= rangeTemplate.getMaxValue()) {
+ val currentDiff = Math.abs(value - f)
+ if (currentDiff < minDiff) {
+ minDiff = currentDiff
+ } else {
+ return f - rangeTemplate.getStepValue()
+ }
+
+ f += rangeTemplate.getStepValue()
+ }
+
+ return rangeTemplate.getMaxValue()
+ }
+
+ inner class ToggleRangeTouchListener() : View.OnTouchListener {
+ private var initialTouchX: Float = 0.0f
+ private var initialTouchY: Float = 0.0f
+ private var isDragging: Boolean = false
+ private val minDragDiff = 20
+
+ override fun onTouch(v: View, e: MotionEvent): Boolean {
+ when (e.getActionMasked()) {
+ MotionEvent.ACTION_DOWN -> setupTouch(e)
+ MotionEvent.ACTION_MOVE -> detectDrag(v, e)
+ MotionEvent.ACTION_UP -> endTouch(v, e)
+ }
+
+ return true
+ }
+
+ private fun setupTouch(e: MotionEvent) {
+ initialTouchX = e.getX()
+ initialTouchY = e.getY()
+ }
+
+ private fun detectDrag(v: View, e: MotionEvent) {
+ val xDiff = Math.abs(e.getX() - initialTouchX)
+ val yDiff = Math.abs(e.getY() - initialTouchY)
+
+ if (xDiff < minDragDiff) {
+ isDragging = false
+ } else {
+ if (!isDragging) {
+ this@ToggleRangeTemplateBehavior.beginUpdateRange()
+ }
+ v.getParent().requestDisallowInterceptTouchEvent(true)
+ isDragging = true
+ if (yDiff > xDiff) {
+ endTouch(v, e)
+ } else {
+ val percent = Math.max(0.0f, Math.min(1.0f, e.getX() / v.getWidth()))
+ this@ToggleRangeTemplateBehavior.updateRange(percent, true)
+ }
+ }
+ }
+
+ private fun endTouch(v: View, e: MotionEvent) {
+ if (!isDragging) {
+ this@ToggleRangeTemplateBehavior.toggle()
+ } else {
+ val percent = Math.max(0.0f, Math.min(1.0f, e.getX() / v.getWidth()))
+ this@ToggleRangeTemplateBehavior.endUpdateRange(percent)
+ }
+
+ initialTouchX = 0.0f
+ initialTouchY = 0.0f
+ isDragging = false
+ }
+ }
+}
+
+private class ToggleTemplateBehavior : Behavior {
+ lateinit var clipLayer: Drawable
+ lateinit var template: ToggleTemplate
+ lateinit var control: Control
+ lateinit var cvh: ControlViewHolder
+ lateinit var context: Context
+ lateinit var status: TextView
+
+ override fun apply(cvh: ControlViewHolder, cws: ControlWithState) {
+ this.control = cws.control!!
+ this.cvh = cvh
+ status = cvh.status
+
+ status.setText(control.getStatusText())
+
+ cvh.layout.setOnClickListener(View.OnClickListener() { toggle() })
+
+ val ld = cvh.layout.getBackground() as LayerDrawable
+ clipLayer = ld.findDrawableByLayerId(R.id.clip_layer)
+
+ template = control.getControlTemplate() as ToggleTemplate
+
+ val checked = template.isChecked()
+ val deviceType = control.getDeviceType()
+
+ clipLayer.setLevel(if (checked) MAX_LEVEL else MIN_LEVEL)
+ cvh.setEnabled(checked)
+ cvh.applyRenderInfo(findRenderInfo(deviceType, checked))
+ }
+
+ fun toggle() {
+ cvh.action(BooleanAction(template.getTemplateId(), !template.isChecked()))
+
+ val nextLevel = if (template.isChecked()) MIN_LEVEL else MAX_LEVEL
+ clipLayer.setLevel(nextLevel)
+ }
+}
+
+internal data class RenderInfo(val iconResourceId: Int, val foreground: Int, val background: Int)
+
+private val unknownDeviceMap = mapOf(
+ false to RenderInfo(
+ R.drawable.ic_light_off_gm2_24px,
+ R.color.unknown_foreground,
+ R.color.unknown_foreground),
+ true to RenderInfo(
+ R.drawable.ic_lightbulb_outline_gm2_24px,
+ R.color.unknown_foreground,
+ R.color.unknown_foreground)
+)
+
+private val deviceRenderMap = mapOf<Int, Map<Boolean, RenderInfo>>(
+ DeviceTypes.TYPE_UNKNOWN to unknownDeviceMap,
+ DeviceTypes.TYPE_LIGHT to mapOf(
+ false to RenderInfo(
+ R.drawable.ic_light_off_gm2_24px,
+ R.color.light_foreground,
+ R.color.light_background),
+ true to RenderInfo(
+ R.drawable.ic_lightbulb_outline_gm2_24px,
+ R.color.light_foreground,
+ R.color.light_background)
+ ),
+ DeviceTypes.TYPE_THERMOSTAT to mapOf(
+ false to RenderInfo(
+ R.drawable.ic_device_thermostat_gm2_24px,
+ R.color.light_foreground,
+ R.color.light_background),
+ true to RenderInfo(
+ R.drawable.ic_device_thermostat_gm2_24px,
+ R.color.light_foreground,
+ R.color.light_background)
+ ),
+ DeviceTypes.TYPE_CAMERA to mapOf(
+ false to RenderInfo(
+ R.drawable.ic_videocam_gm2_24px,
+ R.color.light_foreground,
+ R.color.light_background),
+ true to RenderInfo(
+ R.drawable.ic_videocam_gm2_24px,
+ R.color.light_foreground,
+ R.color.light_background)
+ ),
+ DeviceTypes.TYPE_LOCK to mapOf(
+ false to RenderInfo(
+ R.drawable.ic_lock_open_gm2_24px,
+ R.color.lock_foreground,
+ R.color.lock_background),
+ true to RenderInfo(
+ R.drawable.ic_lock_gm2_24px,
+ R.color.lock_foreground,
+ R.color.lock_background)
+ ),
+ DeviceTypes.TYPE_SWITCH to mapOf(
+ false to RenderInfo(
+ R.drawable.ic_switches_gm2_24px,
+ R.color.lock_foreground,
+ R.color.lock_background),
+ true to RenderInfo(
+ R.drawable.ic_switches_gm2_24px,
+ R.color.lock_foreground,
+ R.color.lock_background)
+ ),
+ DeviceTypes.TYPE_OUTLET to mapOf(
+ false to RenderInfo(
+ R.drawable.ic_power_off_gm2_24px,
+ R.color.lock_foreground,
+ R.color.lock_background),
+ true to RenderInfo(
+ R.drawable.ic_power_gm2_24px,
+ R.color.lock_foreground,
+ R.color.lock_background)
+ )
+)
diff --git a/packages/SystemUI/src/com/android/systemui/controls/ui/ControlWithState.kt b/packages/SystemUI/src/com/android/systemui/controls/ui/ControlWithState.kt
new file mode 100644
index 000000000000..816f0b2cb1d1
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/controls/ui/ControlWithState.kt
@@ -0,0 +1,31 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.controls.ui
+
+import android.service.controls.Control
+
+import com.android.systemui.controls.controller.ControlInfo
+
+/**
+ * A container for:
+ * <ul>
+ * <li>ControlInfo - Basic cached info about a Control
+ * <li>Control - Actual Control parcelable received directly from
+ * the participating application
+ * </ul>
+ */
+data class ControlWithState(val ci: ControlInfo, val control: Control?)
diff --git a/packages/SystemUI/src/com/android/systemui/controls/ui/ControlsUiController.kt b/packages/SystemUI/src/com/android/systemui/controls/ui/ControlsUiController.kt
index 0270c2b6b1b3..b07a75d5e757 100644
--- a/packages/SystemUI/src/com/android/systemui/controls/ui/ControlsUiController.kt
+++ b/packages/SystemUI/src/com/android/systemui/controls/ui/ControlsUiController.kt
@@ -19,12 +19,15 @@ package com.android.systemui.controls.ui
import android.content.ComponentName
import android.service.controls.Control
import android.service.controls.actions.ControlAction
+import android.view.ViewGroup
interface ControlsUiController {
+ fun show(parent: ViewGroup)
+ fun hide()
fun onRefreshState(componentName: ComponentName, controls: List<Control>)
fun onActionResponse(
componentName: ComponentName,
controlId: String,
@ControlAction.ResponseResult response: Int
)
-} \ No newline at end of file
+}
diff --git a/packages/SystemUI/src/com/android/systemui/controls/ui/ControlsUiControllerImpl.kt b/packages/SystemUI/src/com/android/systemui/controls/ui/ControlsUiControllerImpl.kt
index 0ace1263b49b..926fb6e75594 100644
--- a/packages/SystemUI/src/com/android/systemui/controls/ui/ControlsUiControllerImpl.kt
+++ b/packages/SystemUI/src/com/android/systemui/controls/ui/ControlsUiControllerImpl.kt
@@ -16,19 +16,204 @@
package com.android.systemui.controls.ui
+import android.accounts.Account
+import android.accounts.AccountManager
import android.content.ComponentName
+import android.content.Context
+import android.content.Intent
+import android.content.ServiceConnection
+import android.os.IBinder
import android.service.controls.Control
+import android.service.controls.TokenProvider
+import android.util.Log
+import android.view.LayoutInflater
+import android.view.View
+import android.view.ViewGroup
+import android.widget.TextView
+
+import com.android.systemui.controls.controller.ControlsController
+import com.android.systemui.controls.controller.ControlInfo
+import com.android.systemui.controls.management.ControlsProviderSelectorActivity
+import com.android.systemui.dagger.qualifiers.Main
+import com.android.systemui.R
+
+import dagger.Lazy
+
+import java.util.concurrent.Executor
import javax.inject.Inject
import javax.inject.Singleton
+private const val TAG = "ControlsUi"
+
+// TEMP CODE for MOCK
+private const val TOKEN = "https://www.googleapis.com/auth/assistant"
+private const val SCOPE = "oauth2:" + TOKEN
+private var tokenProviderConnection: TokenProviderConnection? = null
+class TokenProviderConnection(val cc: ControlsController, val context: Context)
+ : ServiceConnection {
+ private var mTokenProvider: TokenProvider? = null
+
+ override fun onServiceConnected(cName: ComponentName, binder: IBinder) {
+ Thread({
+ Log.i(TAG, "TokenProviderConnection connected")
+ mTokenProvider = TokenProvider.Stub.asInterface(binder)
+
+ val mLastAccountName = mTokenProvider?.getAccountName()
+
+ if (mLastAccountName == null || mLastAccountName.isEmpty()) {
+ Log.e(TAG, "NO ACCOUNT IS SET. Open HomeMock app")
+ } else {
+ mTokenProvider?.setAuthToken(getAuthToken(mLastAccountName))
+ cc.subscribeToFavorites()
+ }
+ }, "TokenProviderThread").start()
+ }
+
+ override fun onServiceDisconnected(cName: ComponentName) {
+ mTokenProvider = null
+ }
+
+ fun getAuthToken(accountName: String): String? {
+ val am = AccountManager.get(context)
+ val accounts = am.getAccountsByType("com.google")
+ if (accounts == null || accounts.size == 0) {
+ Log.w(TAG, "No com.google accounts found")
+ return null
+ }
+
+ var account: Account? = null
+ for (a in accounts) {
+ if (a.name.equals(accountName)) {
+ account = a
+ break
+ }
+ }
+
+ if (account == null) {
+ account = accounts[0]
+ }
+
+ try {
+ return am.blockingGetAuthToken(account!!, SCOPE, true)
+ } catch (e: Throwable) {
+ Log.e(TAG, "Error getting auth token", e)
+ return null
+ }
+ }
+}
+
@Singleton
-class ControlsUiControllerImpl @Inject constructor() : ControlsUiController {
+class ControlsUiControllerImpl @Inject constructor (
+ val controlsController: Lazy<ControlsController>,
+ val context: Context,
+ @Main val uiExecutor: Executor
+) : ControlsUiController {
+
+ private lateinit var controlInfos: List<ControlInfo>
+ private val controlsById = mutableMapOf<Pair<ComponentName, String>, ControlWithState>()
+ private val controlViewsById = mutableMapOf<String, ControlViewHolder>()
+ private lateinit var parent: ViewGroup
+
+ override fun show(parent: ViewGroup) {
+ Log.d(TAG, "show()")
+
+ this.parent = parent
+
+ controlInfos = controlsController.get().getFavoriteControls()
+
+ controlInfos.map {
+ ControlWithState(it, null)
+ }.associateByTo(controlsById) { Pair(it.ci.component, it.ci.controlId) }
+
+ if (controlInfos.isEmpty()) {
+ showInitialSetupView()
+ } else {
+ showControlsView()
+ }
+
+ // Temp code to pass auth
+ tokenProviderConnection = TokenProviderConnection(controlsController.get(), context)
+ val serviceIntent = Intent()
+ serviceIntent.setComponent(ComponentName("com.android.systemui.home.mock",
+ "com.android.systemui.home.mock.AuthService"))
+ context.bindService(serviceIntent, tokenProviderConnection!!, Context.BIND_AUTO_CREATE)
+ }
+
+ private fun showInitialSetupView() {
+ val inflater = LayoutInflater.from(context)
+ inflater.inflate(R.layout.controls_no_favorites, parent, true)
+
+ val textView = parent.requireViewById(R.id.controls_title) as TextView
+ textView.setOnClickListener {
+ val i = Intent()
+ i.setComponent(ComponentName(context, ControlsProviderSelectorActivity::class.java))
+ i.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP)
+ context.startActivity(i)
+ }
+ }
+
+ private fun showControlsView() {
+ val inflater = LayoutInflater.from(context)
+ inflater.inflate(R.layout.controls_with_favorites, parent, true)
+
+ val listView = parent.requireViewById(R.id.global_actions_controls_list) as ViewGroup
+ var lastRow: ViewGroup = createRow(inflater, listView)
+ controlInfos.forEach {
+ Log.d(TAG, "favorited control id: " + it.controlId)
+ if (lastRow.getChildCount() == 2) {
+ lastRow = createRow(inflater, listView)
+ }
+ val item = inflater.inflate(
+ R.layout.controls_base_item, lastRow, false) as ViewGroup
+ lastRow.addView(item)
+ val cvh = ControlViewHolder(item, controlsController.get())
+ cvh.bindData(controlsById.get(Pair(it.component, it.controlId))!!)
+ controlViewsById.put(it.controlId, cvh)
+ }
+
+ val moreImageView = parent.requireViewById(R.id.controls_more) as View
+ moreImageView.setOnClickListener {
+ val i = Intent()
+ i.setComponent(ComponentName(context, ControlsProviderSelectorActivity::class.java))
+ i.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP)
+ context.startActivity(i)
+ }
+ }
+
+ override fun hide() {
+ Log.d(TAG, "hide()")
+ controlsController.get().unsubscribe()
+ context.unbindService(tokenProviderConnection)
+ tokenProviderConnection = null
+
+ parent.removeAllViews()
+ controlsById.clear()
+ controlViewsById.clear()
+ }
override fun onRefreshState(componentName: ComponentName, controls: List<Control>) {
- TODO("not implemented")
+ Log.d(TAG, "onRefreshState()")
+ controls.forEach { c ->
+ controlsById.get(Pair(componentName, c.getControlId()))?.let {
+ Log.d(TAG, "onRefreshState() for id: " + c.getControlId())
+ val cws = ControlWithState(it.ci, c)
+ controlsById.put(Pair(componentName, c.getControlId()), cws)
+
+ uiExecutor.execute {
+ controlViewsById.get(c.getControlId())?.bindData(cws)
+ }
+ }
+ }
}
override fun onActionResponse(componentName: ComponentName, controlId: String, response: Int) {
+ Log.d(TAG, "onActionResponse()")
TODO("not implemented")
}
-} \ No newline at end of file
+
+ private fun createRow(inflater: LayoutInflater, parent: ViewGroup): ViewGroup {
+ val row = inflater.inflate(R.layout.controls_row, parent, false) as ViewGroup
+ parent.addView(row)
+ return row
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/dagger/SystemUIBinder.java b/packages/SystemUI/src/com/android/systemui/dagger/SystemUIBinder.java
index d4e47f699345..5de88e17d320 100644
--- a/packages/SystemUI/src/com/android/systemui/dagger/SystemUIBinder.java
+++ b/packages/SystemUI/src/com/android/systemui/dagger/SystemUIBinder.java
@@ -37,6 +37,7 @@ import com.android.systemui.statusbar.phone.StatusBar;
import com.android.systemui.statusbar.phone.StatusBarModule;
import com.android.systemui.statusbar.tv.TvStatusBar;
import com.android.systemui.theme.ThemeOverlayController;
+import com.android.systemui.toast.ToastUI;
import com.android.systemui.util.leak.GarbageMonitor;
import com.android.systemui.volume.VolumeUI;
@@ -153,6 +154,12 @@ public abstract class SystemUIBinder {
@ClassKey(ThemeOverlayController.class)
public abstract SystemUI bindThemeOverlayController(ThemeOverlayController sysui);
+ /** Inject into ToastUI. */
+ @Binds
+ @IntoMap
+ @ClassKey(ToastUI.class)
+ public abstract SystemUI bindToastUI(ToastUI service);
+
/** Inject into TvStatusBar. */
@Binds
@IntoMap
diff --git a/packages/SystemUI/src/com/android/systemui/dagger/SystemUIModule.java b/packages/SystemUI/src/com/android/systemui/dagger/SystemUIModule.java
index 36a845002deb..f793b3df92a4 100644
--- a/packages/SystemUI/src/com/android/systemui/dagger/SystemUIModule.java
+++ b/packages/SystemUI/src/com/android/systemui/dagger/SystemUIModule.java
@@ -27,6 +27,7 @@ import com.android.systemui.BootCompleteCacheImpl;
import com.android.systemui.DumpController;
import com.android.systemui.assist.AssistModule;
import com.android.systemui.dagger.qualifiers.Main;
+import com.android.systemui.log.dagger.LogModule;
import com.android.systemui.model.SysUiState;
import com.android.systemui.plugins.statusbar.StatusBarStateController;
import com.android.systemui.recents.Recents;
@@ -59,9 +60,12 @@ import dagger.Provides;
* A dagger module for injecting components of System UI that are not overridden by the System UI
* implementation.
*/
-@Module(includes = {AssistModule.class,
- ConcurrencyModule.class,
- PeopleHubModule.class},
+@Module(includes = {
+ AssistModule.class,
+ ConcurrencyModule.class,
+ LogModule.class,
+ PeopleHubModule.class,
+ },
subcomponents = {StatusBarComponent.class, NotificationRowComponent.class})
public abstract class SystemUIModule {
diff --git a/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsDialog.java b/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsDialog.java
index 83f6d45465b3..80d776a59235 100644
--- a/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsDialog.java
+++ b/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsDialog.java
@@ -93,12 +93,13 @@ import com.android.systemui.MultiListLayout;
import com.android.systemui.MultiListLayout.MultiListAdapter;
import com.android.systemui.broadcast.BroadcastDispatcher;
import com.android.systemui.colorextraction.SysuiColorExtractor;
+import com.android.systemui.controls.ui.ControlsUiController;
import com.android.systemui.dagger.qualifiers.Main;
import com.android.systemui.plugins.ActivityStarter;
import com.android.systemui.plugins.GlobalActions.GlobalActionsManager;
import com.android.systemui.plugins.GlobalActionsPanelPlugin;
-import com.android.systemui.statusbar.phone.NotificationShadeWindowController;
import com.android.systemui.statusbar.BlurUtils;
+import com.android.systemui.statusbar.phone.NotificationShadeWindowController;
import com.android.systemui.statusbar.phone.ScrimController;
import com.android.systemui.statusbar.policy.ConfigurationController;
import com.android.systemui.statusbar.policy.KeyguardStateController;
@@ -183,6 +184,7 @@ public class GlobalActionsDialog implements DialogInterface.OnDismissListener,
private final IStatusBarService mStatusBarService;
private final NotificationShadeWindowController mNotificationShadeWindowController;
private GlobalActionsPanelPlugin mPanelPlugin;
+ private ControlsUiController mControlsUiController;
/**
* @param context everything needs a context :(
@@ -200,7 +202,8 @@ public class GlobalActionsDialog implements DialogInterface.OnDismissListener,
TelecomManager telecomManager, MetricsLogger metricsLogger,
BlurUtils blurUtils, SysuiColorExtractor colorExtractor,
IStatusBarService statusBarService,
- NotificationShadeWindowController notificationShadeWindowController) {
+ NotificationShadeWindowController notificationShadeWindowController,
+ ControlsUiController controlsUiController) {
mContext = new ContextThemeWrapper(context, com.android.systemui.R.style.qs_theme);
mWindowManagerFuncs = windowManagerFuncs;
mAudioManager = audioManager;
@@ -220,6 +223,7 @@ public class GlobalActionsDialog implements DialogInterface.OnDismissListener,
mSysuiColorExtractor = colorExtractor;
mStatusBarService = statusBarService;
mNotificationShadeWindowController = notificationShadeWindowController;
+ mControlsUiController = controlsUiController;
// receive broadcasts
IntentFilter filter = new IntentFilter();
@@ -455,9 +459,12 @@ public class GlobalActionsDialog implements DialogInterface.OnDismissListener,
mKeyguardManager.isDeviceLocked())
: null;
+ boolean showControls = !mKeyguardManager.isDeviceLocked() && isControlsEnabled(mContext);
+
ActionsDialog dialog = new ActionsDialog(mContext, mAdapter, panelViewController,
mBlurUtils, mSysuiColorExtractor, mStatusBarService,
- mNotificationShadeWindowController, isControlsEnabled(mContext));
+ mNotificationShadeWindowController,
+ showControls ? mControlsUiController : null);
dialog.setCanceledOnTouchOutside(false); // Handled by the custom class.
dialog.setKeyguardShowing(mKeyguardShowing);
@@ -1543,13 +1550,15 @@ public class GlobalActionsDialog implements DialogInterface.OnDismissListener,
private boolean mHadTopUi;
private final NotificationShadeWindowController mNotificationShadeWindowController;
private final BlurUtils mBlurUtils;
- private final boolean mControlsEnabled;
+
+ private ControlsUiController mControlsUiController;
+ private ViewGroup mControlsView;
ActionsDialog(Context context, MyAdapter adapter,
GlobalActionsPanelPlugin.PanelViewController plugin, BlurUtils blurUtils,
SysuiColorExtractor sysuiColorExtractor, IStatusBarService statusBarService,
NotificationShadeWindowController notificationShadeWindowController,
- boolean controlsEnabled) {
+ ControlsUiController controlsUiController) {
super(context, com.android.systemui.R.style.Theme_SystemUI_Dialog_GlobalActions);
mContext = context;
mAdapter = adapter;
@@ -1557,7 +1566,7 @@ public class GlobalActionsDialog implements DialogInterface.OnDismissListener,
mColorExtractor = sysuiColorExtractor;
mStatusBarService = statusBarService;
mNotificationShadeWindowController = notificationShadeWindowController;
- mControlsEnabled = controlsEnabled;
+ mControlsUiController = controlsUiController;
// Window initialization
Window window = getWindow();
@@ -1639,6 +1648,7 @@ public class GlobalActionsDialog implements DialogInterface.OnDismissListener,
private void initializeLayout() {
setContentView(getGlobalActionsLayoutId(mContext));
fixNavBarClipping();
+ mControlsView = findViewById(com.android.systemui.R.id.global_actions_controls);
mGlobalActionsLayout = findViewById(com.android.systemui.R.id.global_actions_view);
mGlobalActionsLayout.setOutsideTouchListener(view -> dismiss());
((View) mGlobalActionsLayout.getParent()).setOnClickListener(view -> dismiss());
@@ -1674,7 +1684,7 @@ public class GlobalActionsDialog implements DialogInterface.OnDismissListener,
}
private int getGlobalActionsLayoutId(Context context) {
- if (mControlsEnabled) {
+ if (mControlsUiController != null) {
return com.android.systemui.R.layout.global_actions_grid_v2;
}
@@ -1758,6 +1768,9 @@ public class GlobalActionsDialog implements DialogInterface.OnDismissListener,
mBlurUtils.radiusForRatio(animatedValue));
})
.start();
+ if (mControlsUiController != null) {
+ mControlsUiController.show(mControlsView);
+ }
}
@Override
@@ -1766,6 +1779,7 @@ public class GlobalActionsDialog implements DialogInterface.OnDismissListener,
return;
}
mShowing = false;
+ if (mControlsUiController != null) mControlsUiController.hide();
mGlobalActionsLayout.setTranslationX(0);
mGlobalActionsLayout.setTranslationY(0);
mGlobalActionsLayout.setAlpha(1);
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java
index 2fc7a9cf2434..14eec59211bd 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java
@@ -18,6 +18,7 @@ package com.android.systemui.keyguard;
import static android.provider.Settings.System.SCREEN_OFF_TIMEOUT;
+import static com.android.internal.config.sysui.SystemUiDeviceConfigFlags.NAV_BAR_HANDLE_SHOW_OVER_LOCKSCREEN;
import static com.android.internal.widget.LockPatternUtils.StrongAuthTracker.SOME_AUTH_REQUIRED_AFTER_USER_REQUEST;
import static com.android.internal.widget.LockPatternUtils.StrongAuthTracker.STRONG_AUTH_REQUIRED_AFTER_DPM_LOCK_NOW;
import static com.android.internal.widget.LockPatternUtils.StrongAuthTracker.STRONG_AUTH_REQUIRED_AFTER_LOCKOUT;
@@ -53,6 +54,7 @@ import android.os.SystemProperties;
import android.os.Trace;
import android.os.UserHandle;
import android.os.UserManager;
+import android.provider.DeviceConfig;
import android.provider.Settings;
import android.telephony.SubscriptionManager;
import android.telephony.TelephonyManager;
@@ -371,6 +373,7 @@ public class KeyguardViewMediator extends SystemUI {
private boolean mPulsing;
private boolean mLockLater;
+ private boolean mShowHomeOverLockscreen;
private boolean mWakeAndUnlocking;
private IKeyguardDrawnCallback mDrawnCallback;
@@ -703,6 +706,20 @@ public class KeyguardViewMediator extends SystemUI {
mStatusBarKeyguardViewManagerLazy = statusBarKeyguardViewManagerLazy;
mDismissCallbackRegistry = dismissCallbackRegistry;
mUiBgExecutor = uiBgExecutor;
+ mShowHomeOverLockscreen = DeviceConfig.getBoolean(
+ DeviceConfig.NAMESPACE_SYSTEMUI,
+ NAV_BAR_HANDLE_SHOW_OVER_LOCKSCREEN,
+ /* defaultValue = */ true);
+ DeviceConfig.addOnPropertiesChangedListener(DeviceConfig.NAMESPACE_SYSTEMUI, mHandler::post,
+ new DeviceConfig.OnPropertiesChangedListener() {
+ @Override
+ public void onPropertiesChanged(DeviceConfig.Properties properties) {
+ if (properties.getKeyset().contains(NAV_BAR_HANDLE_SHOW_OVER_LOCKSCREEN)) {
+ mShowHomeOverLockscreen = properties.getBoolean(
+ NAV_BAR_HANDLE_SHOW_OVER_LOCKSCREEN, true /* defaultValue */);
+ }
+ }
+ });
}
public void userActivity() {
@@ -1972,7 +1989,10 @@ public class KeyguardViewMediator extends SystemUI {
// windows that appear on top, ever
int flags = StatusBarManager.DISABLE_NONE;
if (forceHideHomeRecentsButtons || isShowingAndNotOccluded()) {
- flags |= StatusBarManager.DISABLE_HOME | StatusBarManager.DISABLE_RECENT;
+ if (!mShowHomeOverLockscreen) {
+ flags |= StatusBarManager.DISABLE_HOME;
+ }
+ flags |= StatusBarManager.DISABLE_RECENT;
}
if (DEBUG) {
diff --git a/packages/SystemUI/src/com/android/systemui/log/LogBuffer.kt b/packages/SystemUI/src/com/android/systemui/log/LogBuffer.kt
new file mode 100644
index 000000000000..18c7baec1f74
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/log/LogBuffer.kt
@@ -0,0 +1,213 @@
+/*
+ * 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.log
+
+import android.util.Log
+import com.android.systemui.DumpController
+import com.android.systemui.Dumpable
+import com.android.systemui.log.dagger.LogModule
+import java.text.SimpleDateFormat
+import java.util.ArrayDeque
+import java.util.Locale
+
+/**
+ * A simple ring buffer of recyclable log messages
+ *
+ * The goal of this class is to enable logging that is both extremely chatty and extremely
+ * lightweight. If done properly, logging a message will not result in any heap allocations or
+ * string generation. Messages are only converted to strings if the log is actually dumped (usually
+ * as the result of taking a bug report).
+ *
+ * You can dump the entire buffer at any time by running:
+ *
+ * ```
+ * $ adb shell dumpsys activity service com.android.systemui/.SystemUIService \
+ * dependency DumpController <bufferName>
+ * ```
+ *
+ * where `bufferName` is the (case-sensitive) [name] passed to the constructor.
+ *
+ * By default, only messages of WARN level or higher are echoed to logcat, but this can be adjusted
+ * locally (usually for debugging purposes).
+ *
+ * To enable logcat echoing for an entire buffer:
+ *
+ * ```
+ * $ adb shell settings put global systemui/buffer/<bufferName> <level>
+ * ```
+ *
+ * To enable logcat echoing for a specific tag:
+ *
+ * ```
+ * $ adb shell settings put global systemui/tag/<tag> <level>
+ * ```
+ *
+ * In either case, `level` can be any of `verbose`, `debug`, `info`, `warn`, `error`, `assert`, or
+ * the first letter of any of the previous.
+ *
+ * Buffers are provided by [LogModule].
+ *
+ * @param name The name of this buffer
+ * @param maxLogs The maximum number of messages to keep in memory at any one time, including the
+ * unused pool.
+ * @param poolSize The maximum amount that the size of the buffer is allowed to flex in response to
+ * sequential calls to [document] that aren't immediately followed by a matching call to [push].
+ */
+class LogBuffer(
+ private val name: String,
+ private val maxLogs: Int,
+ private val poolSize: Int,
+ private val logcatEchoTracker: LogcatEchoTracker
+) {
+ private val buffer: ArrayDeque<LogMessageImpl> = ArrayDeque()
+
+ fun attach(dumpController: DumpController) {
+ dumpController.registerDumpable(name, onDump)
+ }
+
+ /**
+ * Logs a message to the log buffer
+ *
+ * May also log the message to logcat if echoing is enabled for this buffer or tag.
+ *
+ * The actual string of the log message is not constructed until it is needed. To accomplish
+ * this, logging a message is a two-step process. First, a fresh instance of [LogMessage] is
+ * obtained and is passed to the [initializer]. The initializer stores any relevant data on the
+ * message's fields. The message is then inserted into the buffer where it waits until it is
+ * either pushed out by newer messages or it needs to printed. If and when this latter moment
+ * occurs, the [printer] function is called on the message. It reads whatever data the
+ * initializer stored and converts it to a human-readable log message.
+ *
+ * @param tag A string of at most 23 characters, used for grouping logs into categories or
+ * subjects. If this message is echoed to logcat, this will be the tag that is used.
+ * @param level Which level to log the message at, both to the buffer and to logcat if it's
+ * echoed. In general, a module should split most of its logs into either INFO or DEBUG level.
+ * INFO level should be reserved for information that other parts of the system might care
+ * about, leaving the specifics of code's day-to-day operations to DEBUG.
+ * @param initializer A function that will be called immediately to store relevant data on the
+ * log message. The value of `this` will be the LogMessage to be initialized.
+ * @param printer A function that will be called if and when the message needs to be dumped to
+ * logcat or a bug report. It should read the data stored by the initializer and convert it to
+ * a human-readable string. The value of `this` will be the LogMessage to be printed.
+ * **IMPORTANT:** The printer should ONLY ever reference fields on the LogMessage and NEVER any
+ * variables in its enclosing scope. Otherwise, the runtime will need to allocate a new instance
+ * of the printer for each call, thwarting our attempts at avoiding any sort of allocation.
+ */
+ inline fun log(
+ tag: String,
+ level: LogLevel,
+ initializer: LogMessage.() -> Unit,
+ noinline printer: LogMessage.() -> String
+ ) {
+ val message = obtain(tag, level, printer)
+ initializer(message)
+ push(message)
+ }
+
+ /**
+ * Same as [log], but doesn't push the message to the buffer. Useful if you need to supply a
+ * "reason" for doing something (the thing you supply the reason to will presumably call [push]
+ * on that message at some point).
+ */
+ inline fun document(
+ tag: String,
+ level: LogLevel,
+ initializer: LogMessage.() -> Unit,
+ noinline printer: LogMessage.() -> String
+ ): LogMessage {
+ val message = obtain(tag, level, printer)
+ initializer(message)
+ return message
+ }
+
+ /**
+ * Obtains an instance of [LogMessageImpl], usually from the object pool. If the pool has been
+ * exhausted, creates a new instance.
+ *
+ * In general, you should call [log] or [document] instead of this method.
+ */
+ fun obtain(
+ tag: String,
+ level: LogLevel,
+ printer: (LogMessage) -> String
+ ): LogMessageImpl {
+ val message = synchronized(buffer) {
+ if (buffer.size > maxLogs - poolSize) {
+ buffer.removeFirst()
+ } else {
+ LogMessageImpl.create()
+ }
+ }
+ message.reset(tag, level, System.currentTimeMillis(), printer)
+ return message
+ }
+
+ /**
+ * Pushes a message into buffer, possibly evicting an older message if the buffer is full.
+ */
+ fun push(message: LogMessage) {
+ synchronized(buffer) {
+ if (buffer.size == maxLogs) {
+ Log.e(TAG, "LogBuffer $name has exceeded its pool size")
+ buffer.removeFirst()
+ }
+ buffer.add(message as LogMessageImpl)
+ if (logcatEchoTracker.isBufferLoggable(name, message.level) ||
+ logcatEchoTracker.isTagLoggable(message.tag, message.level)) {
+ echoToLogcat(message)
+ }
+ }
+ }
+
+ /** Converts the entire buffer to a newline-delimited string */
+ fun dump(): String {
+ synchronized(buffer) {
+ val sb = StringBuilder()
+ for (message in buffer) {
+ dumpMessage(message, sb)
+ }
+ return sb.toString()
+ }
+ }
+
+ private fun dumpMessage(message: LogMessage, sb: StringBuilder) {
+ sb.append(DATE_FORMAT.format(message.timestamp))
+ .append(" ").append(message.level)
+ .append(" ").append(message.tag)
+ .append(" ").append(message.printer(message))
+ .append("\n")
+ }
+
+ private fun echoToLogcat(message: LogMessage) {
+ val strMessage = message.printer(message)
+ when (message.level) {
+ LogLevel.VERBOSE -> Log.v(message.tag, strMessage)
+ LogLevel.DEBUG -> Log.d(message.tag, strMessage)
+ LogLevel.INFO -> Log.i(message.tag, strMessage)
+ LogLevel.WARNING -> Log.w(message.tag, strMessage)
+ LogLevel.ERROR -> Log.e(message.tag, strMessage)
+ LogLevel.WTF -> Log.wtf(message.tag, strMessage)
+ }
+ }
+
+ private val onDump = Dumpable { _, pw, _ ->
+ pw.println(dump())
+ }
+}
+
+private const val TAG = "LogBuffer"
+private val DATE_FORMAT = SimpleDateFormat("MM-dd HH:mm:ss.S", Locale.US)
diff --git a/packages/SystemUI/src/com/android/systemui/log/LogLevel.kt b/packages/SystemUI/src/com/android/systemui/log/LogLevel.kt
new file mode 100644
index 000000000000..7b9af0f91200
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/log/LogLevel.kt
@@ -0,0 +1,31 @@
+/*
+ * 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.log
+
+import android.util.Log
+
+/**
+ * Enum version of @Log.Level
+ */
+enum class LogLevel(@Log.Level val nativeLevel: Int) {
+ VERBOSE(Log.VERBOSE),
+ DEBUG(Log.DEBUG),
+ INFO(Log.INFO),
+ WARNING(Log.WARN),
+ ERROR(Log.ERROR),
+ WTF(Log.ASSERT)
+}
diff --git a/packages/SystemUI/src/com/android/systemui/log/LogMessage.kt b/packages/SystemUI/src/com/android/systemui/log/LogMessage.kt
new file mode 100644
index 000000000000..d971ac58fb0b
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/log/LogMessage.kt
@@ -0,0 +1,47 @@
+/*
+ * 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.log
+
+/**
+ * Generic data class for storing messages logged to a [LogBuffer]
+ *
+ * Each LogMessage has a few standard fields ([level], [tag], and [timestamp]). The rest are generic
+ * data slots that may or may not be used, depending on the nature of the specific message being
+ * logged.
+ *
+ * When a message is logged, the code doing the logging stores data in one or more of the generic
+ * fields ([str1], [int1], etc). When it comes time to dump the message to logcat/bugreport/etc, the
+ * [printer] function reads the data stored in the generic fields and converts that to a human-
+ * readable string. Thus, for every log type there must be a specialized initializer function that
+ * stores data specific to that log type and a specialized printer function that prints that data.
+ *
+ * See [LogBuffer.log] for more information.
+ */
+interface LogMessage {
+ val level: LogLevel
+ val tag: String
+ val timestamp: Long
+ val printer: LogMessage.() -> String
+
+ var str1: String?
+ var str2: String?
+ var str3: String?
+ var int1: Int
+ var int2: Int
+ var long1: Long
+ var double1: Double
+}
diff --git a/packages/SystemUI/src/com/android/systemui/log/LogMessageImpl.kt b/packages/SystemUI/src/com/android/systemui/log/LogMessageImpl.kt
new file mode 100644
index 000000000000..32334bc382e1
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/log/LogMessageImpl.kt
@@ -0,0 +1,74 @@
+/*
+ * 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.log
+
+/**
+ * Recyclable implementation of [LogMessage].
+ */
+data class LogMessageImpl(
+ override var level: LogLevel,
+ override var tag: String,
+ override var timestamp: Long,
+ override var printer: LogMessage.() -> String,
+ override var str1: String?,
+ override var str2: String?,
+ override var str3: String?,
+ override var int1: Int,
+ override var int2: Int,
+ override var long1: Long,
+ override var double1: Double
+) : LogMessage {
+
+ fun reset(
+ tag: String,
+ level: LogLevel,
+ timestamp: Long,
+ renderer: LogMessage.() -> String
+ ) {
+ this.level = level
+ this.tag = tag
+ this.timestamp = timestamp
+ this.printer = renderer
+ str1 = null
+ str2 = null
+ str3 = null
+ int1 = 0
+ int2 = 0
+ long1 = 0
+ double1 = 0.0
+ }
+
+ companion object Factory {
+ fun create(): LogMessageImpl {
+ return LogMessageImpl(
+ LogLevel.DEBUG,
+ DEFAULT_TAG,
+ 0,
+ DEFAULT_RENDERER,
+ null,
+ null,
+ null,
+ 0,
+ 0,
+ 0,
+ 0.0)
+ }
+ }
+}
+
+private const val DEFAULT_TAG = "UnknownTag"
+private val DEFAULT_RENDERER: LogMessage.() -> String = { "Unknown message: $this" }
diff --git a/packages/SystemUI/src/com/android/systemui/log/LogcatEchoTracker.kt b/packages/SystemUI/src/com/android/systemui/log/LogcatEchoTracker.kt
new file mode 100644
index 000000000000..3022f4b42a42
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/log/LogcatEchoTracker.kt
@@ -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 com.android.systemui.log
+
+/**
+ * Keeps track of which [LogBuffer] messages should also appear in logcat.
+ */
+interface LogcatEchoTracker {
+ /**
+ * Whether [bufferName] should echo messages of [level] or higher to logcat.
+ */
+ fun isBufferLoggable(bufferName: String, level: LogLevel): Boolean
+
+ /**
+ * Whether [tagName] should echo messages of [level] or higher to logcat.
+ */
+ fun isTagLoggable(tagName: String, level: LogLevel): Boolean
+}
diff --git a/packages/SystemUI/src/com/android/systemui/log/LogcatEchoTrackerDebug.kt b/packages/SystemUI/src/com/android/systemui/log/LogcatEchoTrackerDebug.kt
new file mode 100644
index 000000000000..23942e1d6e3c
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/log/LogcatEchoTrackerDebug.kt
@@ -0,0 +1,133 @@
+/*
+ * 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.log
+
+import android.content.ContentResolver
+import android.database.ContentObserver
+import android.net.Uri
+import android.os.Handler
+import android.os.Looper
+import android.provider.Settings
+
+/**
+ * Version of [LogcatEchoTracker] for debuggable builds
+ *
+ * The log level of individual buffers or tags can be controlled via global settings:
+ *
+ * ```
+ * # Echo any message to <bufferName> of <level> or higher
+ * $ adb shell settings put global systemui/buffer/<bufferName> <level>
+ *
+ * # Echo any message of <tag> and of <level> or higher
+ * $ adb shell settings put global systemui/tag/<tag> <level>
+ * ```
+ */
+class LogcatEchoTrackerDebug private constructor(
+ private val contentResolver: ContentResolver
+) : LogcatEchoTracker {
+ private val cachedBufferLevels: MutableMap<String, LogLevel> = mutableMapOf()
+ private val cachedTagLevels: MutableMap<String, LogLevel> = mutableMapOf()
+
+ companion object Factory {
+ @JvmStatic
+ fun create(
+ contentResolver: ContentResolver,
+ mainLooper: Looper
+ ): LogcatEchoTrackerDebug {
+ val tracker = LogcatEchoTrackerDebug(contentResolver)
+ tracker.attach(mainLooper)
+ return tracker
+ }
+ }
+
+ private fun attach(mainLooper: Looper) {
+ contentResolver.registerContentObserver(
+ Settings.Global.getUriFor(BUFFER_PATH),
+ true,
+ object : ContentObserver(Handler(mainLooper)) {
+ override fun onChange(selfChange: Boolean, uri: Uri) {
+ super.onChange(selfChange, uri)
+ cachedBufferLevels.clear()
+ }
+ })
+
+ contentResolver.registerContentObserver(
+ Settings.Global.getUriFor(TAG_PATH),
+ true,
+ object : ContentObserver(Handler(mainLooper)) {
+ override fun onChange(selfChange: Boolean, uri: Uri) {
+ super.onChange(selfChange, uri)
+ cachedTagLevels.clear()
+ }
+ })
+ }
+
+ /**
+ * Whether [bufferName] should echo messages of [level] or higher to logcat.
+ */
+ @Synchronized
+ override fun isBufferLoggable(bufferName: String, level: LogLevel): Boolean {
+ return level.ordinal >= getLogLevel(bufferName, BUFFER_PATH, cachedBufferLevels).ordinal
+ }
+
+ /**
+ * Whether [tagName] should echo messages of [level] or higher to logcat.
+ */
+ @Synchronized
+ override fun isTagLoggable(tagName: String, level: LogLevel): Boolean {
+ return level >= getLogLevel(tagName, TAG_PATH, cachedTagLevels)
+ }
+
+ private fun getLogLevel(
+ name: String,
+ path: String,
+ cache: MutableMap<String, LogLevel>
+ ): LogLevel {
+ return cache[name] ?: readSetting("$path/$name").also { cache[name] = it }
+ }
+
+ private fun readSetting(path: String): LogLevel {
+ return try {
+ parseProp(Settings.Global.getString(contentResolver, path))
+ } catch (_: Settings.SettingNotFoundException) {
+ DEFAULT_LEVEL
+ }
+ }
+
+ private fun parseProp(propValue: String?): LogLevel {
+ return when (propValue?.toLowerCase()) {
+ "verbose" -> LogLevel.VERBOSE
+ "v" -> LogLevel.VERBOSE
+ "debug" -> LogLevel.DEBUG
+ "d" -> LogLevel.DEBUG
+ "info" -> LogLevel.INFO
+ "i" -> LogLevel.INFO
+ "warning" -> LogLevel.WARNING
+ "warn" -> LogLevel.WARNING
+ "w" -> LogLevel.WARNING
+ "error" -> LogLevel.ERROR
+ "e" -> LogLevel.ERROR
+ "assert" -> LogLevel.WTF
+ "wtf" -> LogLevel.WTF
+ else -> DEFAULT_LEVEL
+ }
+ }
+}
+
+private val DEFAULT_LEVEL = LogLevel.WARNING
+private const val BUFFER_PATH = "systemui/buffer"
+private const val TAG_PATH = "systemui/tag"
diff --git a/packages/SystemUI/src/com/android/systemui/log/LogcatEchoTrackerProd.kt b/packages/SystemUI/src/com/android/systemui/log/LogcatEchoTrackerProd.kt
new file mode 100644
index 000000000000..394f624a3e58
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/log/LogcatEchoTrackerProd.kt
@@ -0,0 +1,30 @@
+/*
+ * 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.log
+
+/**
+ * Production version of [LogcatEchoTracker] that isn't configurable.
+ */
+class LogcatEchoTrackerProd : LogcatEchoTracker {
+ override fun isBufferLoggable(bufferName: String, level: LogLevel): Boolean {
+ return level >= LogLevel.WARNING
+ }
+
+ override fun isTagLoggable(tagName: String, level: LogLevel): Boolean {
+ return level >= LogLevel.WARNING
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/log/dagger/DozeLog.java b/packages/SystemUI/src/com/android/systemui/log/dagger/DozeLog.java
new file mode 100644
index 000000000000..7c5f4025117f
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/log/dagger/DozeLog.java
@@ -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.
+ */
+
+package com.android.systemui.log.dagger;
+
+import static java.lang.annotation.RetentionPolicy.RUNTIME;
+
+import com.android.systemui.log.LogBuffer;
+
+import java.lang.annotation.Documented;
+import java.lang.annotation.Retention;
+
+import javax.inject.Qualifier;
+
+/** A {@link LogBuffer} for dozing-related messages. */
+@Qualifier
+@Documented
+@Retention(RUNTIME)
+public @interface DozeLog {
+}
diff --git a/packages/SystemUI/src/com/android/systemui/log/dagger/LogModule.java b/packages/SystemUI/src/com/android/systemui/log/dagger/LogModule.java
new file mode 100644
index 000000000000..b1990beb9f57
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/log/dagger/LogModule.java
@@ -0,0 +1,76 @@
+/*
+ * 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.log.dagger;
+
+import android.content.ContentResolver;
+import android.os.Build;
+import android.os.Looper;
+
+import com.android.systemui.DumpController;
+import com.android.systemui.dagger.qualifiers.Main;
+import com.android.systemui.log.LogBuffer;
+import com.android.systemui.log.LogcatEchoTracker;
+import com.android.systemui.log.LogcatEchoTrackerDebug;
+import com.android.systemui.log.LogcatEchoTrackerProd;
+
+import javax.inject.Singleton;
+
+import dagger.Module;
+import dagger.Provides;
+
+/**
+ * Dagger module for providing instances of {@link LogBuffer}.
+ */
+@Module
+public class LogModule {
+ /** Provides a logging buffer for doze-related logs. */
+ @Provides
+ @Singleton
+ @DozeLog
+ public static LogBuffer provideDozeLogBuffer(
+ LogcatEchoTrackerDebug bufferFilter,
+ DumpController dumpController) {
+ LogBuffer buffer = new LogBuffer("DozeLog", 100, 10, bufferFilter);
+ buffer.attach(dumpController);
+ return buffer;
+ }
+
+ /** Provides a logging buffer for all logs related to the data layer of notifications. */
+ @Provides
+ @Singleton
+ @NotificationLog
+ public static LogBuffer provideNotificationsLogBuffer(
+ LogcatEchoTracker bufferFilter,
+ DumpController dumpController) {
+ LogBuffer buffer = new LogBuffer("NotifLog2", 1000, 10, bufferFilter);
+ buffer.attach(dumpController);
+ return buffer;
+ }
+
+ /** Allows logging buffers to be tweaked via adb on debug builds but not on prod builds. */
+ @Provides
+ @Singleton
+ public static LogcatEchoTracker provideLogcatEchoTracker(
+ ContentResolver contentResolver,
+ @Main Looper looper) {
+ if (Build.IS_DEBUGGABLE) {
+ return LogcatEchoTrackerDebug.create(contentResolver, looper);
+ } else {
+ return new LogcatEchoTrackerProd();
+ }
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/log/dagger/NotificationLog.java b/packages/SystemUI/src/com/android/systemui/log/dagger/NotificationLog.java
new file mode 100644
index 000000000000..a0b686487bec
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/log/dagger/NotificationLog.java
@@ -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.
+ */
+
+package com.android.systemui.log.dagger;
+
+import static java.lang.annotation.RetentionPolicy.RUNTIME;
+
+import com.android.systemui.log.LogBuffer;
+
+import java.lang.annotation.Documented;
+import java.lang.annotation.Retention;
+
+import javax.inject.Qualifier;
+
+/** A {@link LogBuffer} for notification-related messages. */
+@Qualifier
+@Documented
+@Retention(RUNTIME)
+public @interface NotificationLog {
+}
diff --git a/packages/SystemUI/src/com/android/systemui/pip/PipBoundsHandler.java b/packages/SystemUI/src/com/android/systemui/pip/PipBoundsHandler.java
index 8e34a90e7b51..6f03f18ef64b 100644
--- a/packages/SystemUI/src/com/android/systemui/pip/PipBoundsHandler.java
+++ b/packages/SystemUI/src/com/android/systemui/pip/PipBoundsHandler.java
@@ -261,8 +261,6 @@ public class PipBoundsHandler {
mPinnedStackController.startAnimation(destinationBounds, sourceRectHint,
-1 /* animationDuration */);
mLastDestinationBounds.set(destinationBounds);
- mPinnedStackController.reportBounds(defaultBounds,
- getMovementBounds(defaultBounds));
} catch (RemoteException e) {
Log.e(TAG, "Failed to start PiP animation from SysUI", e);
}
@@ -317,7 +315,6 @@ public class PipBoundsHandler {
outBounds.set(postChangeStackBounds);
mLastDestinationBounds.set(outBounds);
mPinnedStackController.resetBoundsAnimation(outBounds);
- mPinnedStackController.reportBounds(outBounds, getMovementBounds(outBounds));
t.setBounds(pinnedStackInfo.stackToken, outBounds);
} catch (RemoteException e) {
Log.e(TAG, "Failed to resize PiP on display rotation", e);
diff --git a/packages/SystemUI/src/com/android/systemui/pip/phone/PipManager.java b/packages/SystemUI/src/com/android/systemui/pip/phone/PipManager.java
index f39d1ecb0585..e48a23f4cd2c 100644
--- a/packages/SystemUI/src/com/android/systemui/pip/phone/PipManager.java
+++ b/packages/SystemUI/src/com/android/systemui/pip/phone/PipManager.java
@@ -297,6 +297,13 @@ public class PipManager implements BasePipManager {
}
/**
+ * Sets a customized touch gesture that replaces the default one.
+ */
+ public void setTouchGesture(PipTouchGesture gesture) {
+ mTouchHandler.setTouchGesture(gesture);
+ }
+
+ /**
* Sets both shelf visibility and its height.
*/
@Override
diff --git a/packages/SystemUI/src/com/android/systemui/pip/phone/PipTouchGesture.java b/packages/SystemUI/src/com/android/systemui/pip/phone/PipTouchGesture.java
index e8e8a4d3215a..72335dbed115 100644
--- a/packages/SystemUI/src/com/android/systemui/pip/phone/PipTouchGesture.java
+++ b/packages/SystemUI/src/com/android/systemui/pip/phone/PipTouchGesture.java
@@ -24,19 +24,19 @@ public abstract class PipTouchGesture {
/**
* Handle the touch down.
*/
- void onDown(PipTouchState touchState) {}
+ public void onDown(PipTouchState touchState) {}
/**
* Handle the touch move, and return whether the event was consumed.
*/
- boolean onMove(PipTouchState touchState) {
+ public boolean onMove(PipTouchState touchState) {
return false;
}
/**
* Handle the touch up, and return whether the gesture was consumed.
*/
- boolean onUp(PipTouchState touchState) {
+ public boolean onUp(PipTouchState touchState) {
return false;
}
}
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 09f163810d27..65cc666d5164 100644
--- a/packages/SystemUI/src/com/android/systemui/pip/phone/PipTouchHandler.java
+++ b/packages/SystemUI/src/com/android/systemui/pip/phone/PipTouchHandler.java
@@ -126,8 +126,8 @@ public class PipTouchHandler {
// Touch state
private final PipTouchState mTouchState;
private final FlingAnimationUtils mFlingAnimationUtils;
- private final PipTouchGesture[] mGestures;
private final PipMotionHelper mMotionHelper;
+ private PipTouchGesture mGesture;
// Temp vars
private final Rect mTmpBounds = new Rect();
@@ -185,9 +185,7 @@ public class PipTouchHandler {
mSnapAlgorithm = new PipSnapAlgorithm(mContext);
mFlingAnimationUtils = new FlingAnimationUtils(context.getResources().getDisplayMetrics(),
2.5f);
- mGestures = new PipTouchGesture[] {
- mDefaultMovementGesture
- };
+ mGesture = new DefaultPipTouchGesture();
mMotionHelper = new PipMotionHelper(mContext, mActivityManager, mActivityTaskManager,
mMenuController, mSnapAlgorithm, mFlingAnimationUtils);
mTouchState = new PipTouchState(mViewConfig, mHandler,
@@ -210,6 +208,10 @@ public class PipTouchHandler {
this::onAccessibilityShowMenu, mHandler);
}
+ public void setTouchGesture(PipTouchGesture gesture) {
+ mGesture = gesture;
+ }
+
public void setTouchEnabled(boolean enabled) {
mTouchState.setAllowTouches(enabled);
}
@@ -363,17 +365,12 @@ public class PipTouchHandler {
switch (ev.getAction()) {
case MotionEvent.ACTION_DOWN: {
mMotionHelper.synchronizePinnedStackBounds();
-
- for (PipTouchGesture gesture : mGestures) {
- gesture.onDown(mTouchState);
- }
+ mGesture.onDown(mTouchState);
break;
}
case MotionEvent.ACTION_MOVE: {
- for (PipTouchGesture gesture : mGestures) {
- if (gesture.onMove(mTouchState)) {
- break;
- }
+ if (mGesture.onMove(mTouchState)) {
+ break;
}
shouldDeliverToMenu = !mTouchState.isDragging();
@@ -384,10 +381,8 @@ public class PipTouchHandler {
// dragging (ie. when the IME shows)
updateMovementBounds(mMenuState);
- for (PipTouchGesture gesture : mGestures) {
- if (gesture.onUp(mTouchState)) {
- break;
- }
+ if (mGesture.onUp(mTouchState)) {
+ break;
}
// Fall through to clean up
@@ -591,7 +586,7 @@ public class PipTouchHandler {
/**
* Gesture controlling normal movement of the PIP.
*/
- private PipTouchGesture mDefaultMovementGesture = new PipTouchGesture() {
+ private class DefaultPipTouchGesture extends PipTouchGesture {
// Whether the PiP was on the left side of the screen at the start of the gesture
private boolean mStartedOnLeft;
private final Point mStartPosition = new Point();
@@ -623,7 +618,7 @@ public class PipTouchHandler {
}
@Override
- boolean onMove(PipTouchState touchState) {
+ public boolean onMove(PipTouchState touchState) {
if (!touchState.isUserInteracting()) {
return false;
}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/external/CustomTile.java b/packages/SystemUI/src/com/android/systemui/qs/external/CustomTile.java
index 411980b399bd..557c64b7dfb9 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/external/CustomTile.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/external/CustomTile.java
@@ -191,6 +191,7 @@ public class CustomTile extends QSTileImpl<State> implements TileChangeListener
mTile.setLabel(tile.getLabel());
mTile.setSubtitle(tile.getSubtitle());
mTile.setContentDescription(tile.getContentDescription());
+ mTile.setStateDescription(tile.getStateDescription());
mTile.setState(tile.getState());
}
@@ -345,6 +346,12 @@ public class CustomTile extends QSTileImpl<State> implements TileChangeListener
state.contentDescription = state.label;
}
+ if (mTile.getStateDescription() != null) {
+ state.stateDescription = mTile.getStateDescription();
+ } else {
+ state.stateDescription = null;
+ }
+
if (state instanceof BooleanState) {
state.expandedAccessibilityClassName = Switch.class.getName();
((BooleanState) state).value = (state.state == Tile.STATE_ACTIVE);
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSTileBaseView.java b/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSTileBaseView.java
index 8b7f280608a5..fda9e5b1f1ef 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSTileBaseView.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSTileBaseView.java
@@ -65,7 +65,6 @@ public class QSTileBaseView extends com.android.systemui.plugins.qs.QSTileView {
private String mAccessibilityClass;
private boolean mTileState;
private boolean mCollapsedView;
- private boolean mClicked;
private boolean mShowRippleEffect = true;
private final ImageView mBg;
@@ -234,13 +233,35 @@ public class QSTileBaseView extends com.android.systemui.plugins.qs.QSTileView {
setLongClickable(state.handlesLongClick);
mIcon.setIcon(state, allowAnimations);
setContentDescription(state.contentDescription);
+ final StringBuilder stateDescription = new StringBuilder();
+ switch (state.state) {
+ case Tile.STATE_UNAVAILABLE:
+ stateDescription.append(mContext.getString(R.string.tile_unavailable));
+ break;
+ case Tile.STATE_INACTIVE:
+ if (state instanceof QSTile.BooleanState) {
+ stateDescription.append(mContext.getString(R.string.switch_bar_off));
+ }
+ break;
+ case Tile.STATE_ACTIVE:
+ if (state instanceof QSTile.BooleanState) {
+ stateDescription.append(mContext.getString(R.string.switch_bar_on));
+ }
+ break;
+ default:
+ break;
+ }
+ if (!TextUtils.isEmpty(state.stateDescription)) {
+ stateDescription.append(", ");
+ stateDescription.append(state.stateDescription);
+ }
+ setStateDescription(stateDescription.toString());
mAccessibilityClass =
state.state == Tile.STATE_UNAVAILABLE ? null : state.expandedAccessibilityClassName;
if (state instanceof QSTile.BooleanState) {
boolean newState = ((BooleanState) state).value;
if (mTileState != newState) {
- mClicked = false;
mTileState = newState;
}
}
@@ -297,23 +318,10 @@ public class QSTileBaseView extends com.android.systemui.plugins.qs.QSTileView {
}
@Override
- public boolean performClick() {
- mClicked = true;
- return super.performClick();
- }
-
- @Override
public void onInitializeAccessibilityEvent(AccessibilityEvent event) {
super.onInitializeAccessibilityEvent(event);
if (!TextUtils.isEmpty(mAccessibilityClass)) {
event.setClassName(mAccessibilityClass);
- if (Switch.class.getName().equals(mAccessibilityClass)) {
- boolean b = mClicked ? !mTileState : mTileState;
- String label = getResources()
- .getString(b ? R.string.switch_bar_on : R.string.switch_bar_off);
- event.setContentDescription(label);
- event.setChecked(b);
- }
}
}
@@ -325,11 +333,6 @@ public class QSTileBaseView extends com.android.systemui.plugins.qs.QSTileView {
if (!TextUtils.isEmpty(mAccessibilityClass)) {
info.setClassName(mAccessibilityClass);
if (Switch.class.getName().equals(mAccessibilityClass)) {
- boolean b = mClicked ? !mTileState : mTileState;
- String label = getResources()
- .getString(b ? R.string.switch_bar_on : R.string.switch_bar_off);
- info.setText(label);
- info.setChecked(b);
info.setCheckable(true);
if (isLongClickable()) {
info.addAction(
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/BluetoothTile.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/BluetoothTile.java
index 9282a2e3b312..361b6c1b1260 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/BluetoothTile.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/BluetoothTile.java
@@ -134,25 +134,27 @@ public class BluetoothTile extends QSTileImpl<BooleanState> {
state.label = mContext.getString(R.string.quick_settings_bluetooth_label);
state.secondaryLabel = TextUtils.emptyIfNull(
getSecondaryLabel(enabled, connecting, connected, state.isTransient));
+ state.contentDescription = state.label;
+ state.stateDescription = "";
if (enabled) {
if (connected) {
state.icon = new BluetoothConnectedTileIcon();
if (!TextUtils.isEmpty(mController.getConnectedDeviceName())) {
state.label = mController.getConnectedDeviceName();
}
- state.contentDescription =
+ state.stateDescription =
mContext.getString(R.string.accessibility_bluetooth_name, state.label)
+ ", " + state.secondaryLabel;
} else if (state.isTransient) {
state.icon = ResourceIcon.get(
com.android.internal.R.drawable.ic_bluetooth_transient_animation);
- state.contentDescription = state.secondaryLabel;
+ state.stateDescription = state.secondaryLabel;
} else {
state.icon =
ResourceIcon.get(com.android.internal.R.drawable.ic_qs_bluetooth);
state.contentDescription = mContext.getString(
- R.string.accessibility_quick_settings_bluetooth) + ","
- + mContext.getString(R.string.accessibility_not_connected);
+ R.string.accessibility_quick_settings_bluetooth);
+ state.stateDescription = mContext.getString(R.string.accessibility_not_connected);
}
state.state = Tile.STATE_ACTIVE;
} else {
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/CastTile.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/CastTile.java
index 0e813d1ab4e4..58de0575fa75 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/CastTile.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/CastTile.java
@@ -183,6 +183,7 @@ public class CastTile extends QSTileImpl<BooleanState> {
protected void handleUpdateState(BooleanState state, Object arg) {
state.label = mContext.getString(R.string.quick_settings_cast_title);
state.contentDescription = state.label;
+ state.stateDescription = "";
state.value = false;
final List<CastDevice> devices = mController.getCastDevices();
boolean connecting = false;
@@ -192,8 +193,9 @@ public class CastTile extends QSTileImpl<BooleanState> {
if (device.state == CastDevice.STATE_CONNECTED) {
state.value = true;
state.secondaryLabel = getDeviceName(device);
- state.contentDescription = state.contentDescription + "," +
- mContext.getString(R.string.accessibility_cast_name, state.label);
+ state.stateDescription = state.stateDescription + ","
+ + mContext.getString(
+ R.string.accessibility_cast_name, state.label);
connecting = false;
break;
} else if (device.state == CastDevice.STATE_CONNECTING) {
@@ -217,9 +219,8 @@ public class CastTile extends QSTileImpl<BooleanState> {
state.state = Tile.STATE_UNAVAILABLE;
String noWifi = mContext.getString(R.string.quick_settings_cast_no_wifi);
state.secondaryLabel = noWifi;
- state.contentDescription = state.contentDescription + ", " + mContext.getString(
- R.string.accessibility_quick_settings_not_available, noWifi);
}
+ state.stateDescription = state.stateDescription + ", " + state.secondaryLabel;
mDetailAdapter.updateItems(devices);
}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/CellularTile.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/CellularTile.java
index 22470c7f5af5..d5f86c951407 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/CellularTile.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/CellularTile.java
@@ -194,17 +194,13 @@ public class CellularTile extends QSTileImpl<SignalState> {
state.secondaryLabel = r.getString(R.string.cell_data_off);
}
-
- // TODO(b/77881974): Instead of switching out the description via a string check for
- // we need to have two strings provided by the MobileIconGroup.
- final CharSequence contentDescriptionSuffix;
+ state.contentDescription = state.label;
if (state.state == Tile.STATE_INACTIVE) {
- contentDescriptionSuffix = r.getString(R.string.cell_data_off_content_description);
+ // This information is appended later by converting the Tile.STATE_INACTIVE state.
+ state.stateDescription = "";
} else {
- contentDescriptionSuffix = state.secondaryLabel;
+ state.stateDescription = state.secondaryLabel;
}
-
- state.contentDescription = state.label + ", " + contentDescriptionSuffix;
}
private CharSequence appendMobileDataType(CharSequence current, CharSequence dataType) {
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/DndTile.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/DndTile.java
index 52d1a5b3b991..9215da4cda9a 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/DndTile.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/DndTile.java
@@ -240,6 +240,8 @@ public class DndTile extends QSTileImpl<BooleanState> {
zen != Global.ZEN_MODE_OFF, mController.getConfig(), false));
state.icon = ResourceIcon.get(com.android.internal.R.drawable.ic_qs_dnd);
checkIfRestrictionEnforcedByAdminOnly(state, UserManager.DISALLOW_ADJUST_VOLUME);
+ // Keeping the secondaryLabel in contentDescription instead of stateDescription is easier
+ // to understand.
switch (zen) {
case Global.ZEN_MODE_IMPORTANT_INTERRUPTIONS:
state.contentDescription =
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/FlashlightTile.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/FlashlightTile.java
index dafdd89ee62c..792c36477962 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/FlashlightTile.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/FlashlightTile.java
@@ -102,14 +102,13 @@ public class FlashlightTile extends QSTileImpl<BooleanState> implements
}
state.label = mHost.getContext().getString(R.string.quick_settings_flashlight_label);
state.secondaryLabel = "";
+ state.stateDescription = "";
if (!mFlashlightController.isAvailable()) {
state.icon = mIcon;
state.slash.isSlashed = true;
state.secondaryLabel = mContext.getString(
R.string.quick_settings_flashlight_camera_in_use);
- state.contentDescription = mContext.getString(
- R.string.accessibility_quick_settings_flashlight_unavailable)
- + ", " + state.secondaryLabel;
+ state.stateDescription = state.secondaryLabel;
state.state = Tile.STATE_UNAVAILABLE;
return;
}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/HotspotTile.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/HotspotTile.java
index 001e09406e3a..fd6b936d71c0 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/HotspotTile.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/HotspotTile.java
@@ -147,6 +147,7 @@ public class HotspotTile extends QSTileImpl<BooleanState> {
state.secondaryLabel = getSecondaryLabel(
isTileActive, isTransient, isDataSaverEnabled, numConnectedDevices);
+ state.stateDescription = state.secondaryLabel;
}
@Nullable
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/LocationTile.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/LocationTile.java
index fbdca3ba1c7b..e617867eb10e 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/LocationTile.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/LocationTile.java
@@ -105,15 +105,8 @@ public class LocationTile extends QSTileImpl<BooleanState> {
}
state.icon = mIcon;
state.slash.isSlashed = !state.value;
- if (locationEnabled) {
- state.label = mContext.getString(R.string.quick_settings_location_label);
- state.contentDescription = mContext.getString(
- R.string.accessibility_quick_settings_location_on);
- } else {
- state.label = mContext.getString(R.string.quick_settings_location_label);
- state.contentDescription = mContext.getString(
- R.string.accessibility_quick_settings_location_off);
- }
+ state.label = mContext.getString(R.string.quick_settings_location_label);
+ state.contentDescription = state.label;
state.state = state.value ? Tile.STATE_ACTIVE : Tile.STATE_INACTIVE;
state.expandedAccessibilityClassName = Switch.class.getName();
}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/WifiTile.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/WifiTile.java
index b7ce101cacab..6e8dcf36bacc 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/WifiTile.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/WifiTile.java
@@ -195,6 +195,7 @@ public class WifiTile extends QSTileImpl<SignalState> {
state.activityIn = cb.enabled && cb.activityIn;
state.activityOut = cb.enabled && cb.activityOut;
final StringBuffer minimalContentDescription = new StringBuffer();
+ final StringBuffer minimalStateDescription = new StringBuffer();
final Resources r = mContext.getResources();
if (isTransient) {
state.icon = ResourceIcon.get(
@@ -219,13 +220,14 @@ public class WifiTile extends QSTileImpl<SignalState> {
mContext.getString(R.string.quick_settings_wifi_label)).append(",");
if (state.value) {
if (wifiConnected) {
- minimalContentDescription.append(cb.wifiSignalContentDescription).append(",");
+ minimalStateDescription.append(cb.wifiSignalContentDescription);
minimalContentDescription.append(removeDoubleQuotes(cb.ssid));
if (!TextUtils.isEmpty(state.secondaryLabel)) {
minimalContentDescription.append(",").append(state.secondaryLabel);
}
}
}
+ state.stateDescription = minimalStateDescription.toString();
state.contentDescription = minimalContentDescription.toString();
state.dualLabelContentDescription = r.getString(
R.string.accessibility_quick_settings_open_settings, getTileLabel());
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/WorkModeTile.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/WorkModeTile.java
index 7853dc388bcb..e54ee51fb9d4 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/WorkModeTile.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/WorkModeTile.java
@@ -103,14 +103,11 @@ public class WorkModeTile extends QSTileImpl<BooleanState> implements
state.icon = mIcon;
if (state.value) {
state.slash.isSlashed = false;
- state.contentDescription = mContext.getString(
- R.string.accessibility_quick_settings_work_mode_on);
} else {
state.slash.isSlashed = true;
- state.contentDescription = mContext.getString(
- R.string.accessibility_quick_settings_work_mode_off);
}
state.label = mContext.getString(R.string.quick_settings_work_mode_label);
+ state.contentDescription = state.label;
state.expandedAccessibilityClassName = Switch.class.getName();
state.state = state.value ? Tile.STATE_ACTIVE : Tile.STATE_INACTIVE;
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/CommandQueue.java b/packages/SystemUI/src/com/android/systemui/statusbar/CommandQueue.java
index 7c0f4f942bce..3af3701dff97 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/CommandQueue.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/CommandQueue.java
@@ -25,6 +25,8 @@ import static android.view.Display.INVALID_DISPLAY;
import static com.android.systemui.statusbar.phone.StatusBar.ONLY_CORE_APPS;
+import android.annotation.Nullable;
+import android.app.ITransientNotificationCallback;
import android.app.StatusBarManager;
import android.app.StatusBarManager.Disable2Flags;
import android.app.StatusBarManager.DisableFlags;
@@ -119,6 +121,8 @@ public class CommandQueue extends IStatusBar.Stub implements CallbackController<
private static final int MSG_TOP_APP_WINDOW_CHANGED = 50 << MSG_SHIFT;
private static final int MSG_SHOW_INATTENTIVE_SLEEP_WARNING = 51 << MSG_SHIFT;
private static final int MSG_DISMISS_INATTENTIVE_SLEEP_WARNING = 52 << MSG_SHIFT;
+ private static final int MSG_SHOW_TOAST = 53 << MSG_SHIFT;
+ private static final int MSG_HIDE_TOAST = 54 << MSG_SHIFT;
public static final int FLAG_EXCLUDE_NONE = 0;
public static final int FLAG_EXCLUDE_SEARCH_PANEL = 1 << 0;
@@ -308,6 +312,19 @@ public class CommandQueue extends IStatusBar.Stub implements CallbackController<
* due to prolonged user inactivity should be dismissed.
*/
default void dismissInattentiveSleepWarning(boolean animated) { }
+
+ /**
+ * @see IStatusBar#showToast(String, IBinder, CharSequence, IBinder, int,
+ * ITransientNotificationCallback)
+ */
+ default void showToast(String packageName, IBinder token, CharSequence text,
+ IBinder windowToken, int duration,
+ @Nullable ITransientNotificationCallback callback) { }
+
+ /**
+ * @see IStatusBar#hideToast(String, IBinder) (String, IBinder)
+ */
+ default void hideToast(String packageName, IBinder token) { }
}
public CommandQueue(Context context) {
@@ -761,6 +778,31 @@ public class CommandQueue extends IStatusBar.Stub implements CallbackController<
}
@Override
+ public void showToast(String packageName, IBinder token, CharSequence text,
+ IBinder windowToken, int duration, @Nullable ITransientNotificationCallback callback) {
+ synchronized (mLock) {
+ SomeArgs args = SomeArgs.obtain();
+ args.arg1 = packageName;
+ args.arg2 = token;
+ args.arg3 = text;
+ args.arg4 = windowToken;
+ args.arg5 = callback;
+ args.argi1 = duration;
+ mHandler.obtainMessage(MSG_SHOW_TOAST, args).sendToTarget();
+ }
+ }
+
+ @Override
+ public void hideToast(String packageName, IBinder token) {
+ synchronized (mLock) {
+ SomeArgs args = SomeArgs.obtain();
+ args.arg1 = packageName;
+ args.arg2 = token;
+ mHandler.obtainMessage(MSG_HIDE_TOAST, args).sendToTarget();
+ }
+ }
+
+ @Override
public void onBiometricAuthenticated() {
synchronized (mLock) {
mHandler.obtainMessage(MSG_BIOMETRIC_AUTHENTICATED).sendToTarget();
@@ -1178,6 +1220,30 @@ public class CommandQueue extends IStatusBar.Stub implements CallbackController<
mCallbacks.get(i).dismissInattentiveSleepWarning((Boolean) msg.obj);
}
break;
+ case MSG_SHOW_TOAST: {
+ args = (SomeArgs) msg.obj;
+ String packageName = (String) args.arg1;
+ IBinder token = (IBinder) args.arg2;
+ CharSequence text = (CharSequence) args.arg3;
+ IBinder windowToken = (IBinder) args.arg4;
+ ITransientNotificationCallback callback =
+ (ITransientNotificationCallback) args.arg5;
+ int duration = args.argi1;
+ for (Callbacks callbacks : mCallbacks) {
+ callbacks.showToast(packageName, token, text, windowToken, duration,
+ callback);
+ }
+ break;
+ }
+ case MSG_HIDE_TOAST: {
+ args = (SomeArgs) msg.obj;
+ String packageName = (String) args.arg1;
+ IBinder token = (IBinder) args.arg2;
+ for (Callbacks callbacks : mCallbacks) {
+ callbacks.hideToast(packageName, token);
+ }
+ break;
+ }
}
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/ListEntry.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/ListEntry.java
index 56ad0e1df36f..b048d032feaf 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/ListEntry.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/ListEntry.java
@@ -44,10 +44,11 @@ public abstract class ListEntry {
/**
* Should return the "representative entry" for this ListEntry. For NotificationEntries, its
- * the entry itself. For groups, it should be the summary. This method exists to interface with
+ * the entry itself. For groups, it should be the summary (but if a summary doesn't exist,
+ * this can return null). This method exists to interface with
* legacy code that expects groups to also be NotificationEntries.
*/
- public abstract NotificationEntry getRepresentativeEntry();
+ public abstract @Nullable NotificationEntry getRepresentativeEntry();
@Nullable public GroupEntry getParent() {
return mParent;
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotifCollection.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotifCollection.java
index c488c6bb8721..92927cf104a0 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotifCollection.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotifCollection.java
@@ -44,17 +44,18 @@ import android.service.notification.NotificationListenerService.Ranking;
import android.service.notification.NotificationListenerService.RankingMap;
import android.service.notification.StatusBarNotification;
import android.util.ArrayMap;
-import android.util.Log;
import com.android.internal.statusbar.IStatusBarService;
import com.android.systemui.DumpController;
import com.android.systemui.Dumpable;
+import com.android.systemui.statusbar.FeatureFlags;
import com.android.systemui.statusbar.notification.collection.coalescer.CoalescedEvent;
import com.android.systemui.statusbar.notification.collection.coalescer.GroupCoalescer;
import com.android.systemui.statusbar.notification.collection.coalescer.GroupCoalescer.BatchableNotificationHandler;
import com.android.systemui.statusbar.notification.collection.notifcollection.CollectionReadyForBuildListener;
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.notifcollection.NotifCollectionLogger;
import com.android.systemui.statusbar.notification.collection.notifcollection.NotifLifetimeExtender;
import com.android.systemui.util.Assert;
@@ -98,6 +99,8 @@ import javax.inject.Singleton;
@Singleton
public class NotifCollection implements Dumpable {
private final IStatusBarService mStatusBarService;
+ private final FeatureFlags mFeatureFlags;
+ private final NotifCollectionLogger mLogger;
private final Map<String, NotificationEntry> mNotificationSet = new ArrayMap<>();
private final Collection<NotificationEntry> mReadOnlyNotificationSet =
@@ -111,10 +114,16 @@ public class NotifCollection implements Dumpable {
private boolean mAmDispatchingToOtherCode;
@Inject
- public NotifCollection(IStatusBarService statusBarService, DumpController dumpController) {
+ public NotifCollection(
+ IStatusBarService statusBarService,
+ DumpController dumpController,
+ FeatureFlags featureFlags,
+ NotifCollectionLogger logger) {
Assert.isMainThread();
mStatusBarService = statusBarService;
+ mLogger = logger;
dumpController.registerDumpable(TAG, this);
+ mFeatureFlags = featureFlags;
}
/** Initializes the NotifCollection and registers it to receive notification events. */
@@ -184,8 +193,8 @@ public class NotifCollection implements Dumpable {
private void onNotificationGroupPosted(List<CoalescedEvent> batch) {
Assert.isMainThread();
- Log.d(TAG, "POSTED GROUP " + batch.get(0).getSbn().getGroupKey()
- + " (" + batch.size() + " events)");
+ mLogger.logNotifGroupPosted(batch.get(0).getSbn().getGroupKey(), batch.size());
+
for (CoalescedEvent event : batch) {
postNotification(event.getSbn(), event.getRanking(), null);
}
@@ -198,7 +207,7 @@ public class NotifCollection implements Dumpable {
int reason) {
Assert.isMainThread();
- Log.d(TAG, "REMOVED " + sbn.getKey() + " reason=" + reason);
+ mLogger.logNotifRemoved(sbn.getKey(), reason);
removeNotification(sbn.getKey(), rankingMap, reason, null);
}
@@ -216,7 +225,7 @@ public class NotifCollection implements Dumpable {
if (entry == null) {
// A new notification!
- Log.d(TAG, "POSTED " + sbn.getKey());
+ mLogger.logNotifPosted(sbn.getKey());
entry = new NotificationEntry(sbn, ranking);
mNotificationSet.put(sbn.getKey(), entry);
@@ -228,7 +237,7 @@ public class NotifCollection implements Dumpable {
} else {
// Update to an existing entry
- Log.d(TAG, "UPDATED " + sbn.getKey());
+ mLogger.logNotifUpdated(sbn.getKey());
// Notification is updated so it is essentially re-added and thus alive again. Don't
// need to keep its lifetime extended.
@@ -301,9 +310,12 @@ public class NotifCollection implements Dumpable {
// TODO: (b/145659174) update the sbn's overrideGroupKey in
// NotificationEntry.setRanking instead of here once we fully migrate to the
// NewNotifPipeline
- final String newOverrideGroupKey = ranking.getOverrideGroupKey();
- if (!Objects.equals(entry.getSbn().getOverrideGroupKey(), newOverrideGroupKey)) {
- entry.getSbn().setOverrideGroupKey(newOverrideGroupKey);
+ if (mFeatureFlags.isNewNotifPipelineRenderingEnabled()) {
+ final String newOverrideGroupKey = ranking.getOverrideGroupKey();
+ if (!Objects.equals(entry.getSbn().getOverrideGroupKey(),
+ newOverrideGroupKey)) {
+ entry.getSbn().setOverrideGroupKey(newOverrideGroupKey);
+ }
}
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/ShadeListBuilder.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/ShadeListBuilder.java
index 97f8ec5f5bb7..9f8f42ee116c 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/ShadeListBuilder.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/ShadeListBuilder.java
@@ -32,19 +32,20 @@ import android.annotation.Nullable;
import android.util.ArrayMap;
import android.util.Pair;
+import androidx.annotation.NonNull;
+
import com.android.systemui.DumpController;
import com.android.systemui.Dumpable;
import com.android.systemui.statusbar.notification.collection.listbuilder.OnBeforeRenderListListener;
import com.android.systemui.statusbar.notification.collection.listbuilder.OnBeforeSortListener;
import com.android.systemui.statusbar.notification.collection.listbuilder.OnBeforeTransformGroupsListener;
import com.android.systemui.statusbar.notification.collection.listbuilder.PipelineState;
+import com.android.systemui.statusbar.notification.collection.listbuilder.ShadeListBuilderLogger;
import com.android.systemui.statusbar.notification.collection.listbuilder.pluggable.NotifComparator;
import com.android.systemui.statusbar.notification.collection.listbuilder.pluggable.NotifFilter;
import com.android.systemui.statusbar.notification.collection.listbuilder.pluggable.NotifPromoter;
import com.android.systemui.statusbar.notification.collection.listbuilder.pluggable.NotifSection;
import com.android.systemui.statusbar.notification.collection.notifcollection.CollectionReadyForBuildListener;
-import com.android.systemui.statusbar.notification.logging.NotifEvent;
-import com.android.systemui.statusbar.notification.logging.NotifLog;
import com.android.systemui.util.Assert;
import com.android.systemui.util.time.SystemClock;
@@ -69,7 +70,7 @@ import javax.inject.Singleton;
@Singleton
public class ShadeListBuilder implements Dumpable {
private final SystemClock mSystemClock;
- private final NotifLog mNotifLog;
+ private final ShadeListBuilderLogger mLogger;
private List<ListEntry> mNotifList = new ArrayList<>();
private List<ListEntry> mNewNotifList = new ArrayList<>();
@@ -98,11 +99,11 @@ public class ShadeListBuilder implements Dumpable {
@Inject
public ShadeListBuilder(
SystemClock systemClock,
- NotifLog notifLog,
+ ShadeListBuilderLogger logger,
DumpController dumpController) {
Assert.isMainThread();
mSystemClock = systemClock;
- mNotifLog = notifLog;
+ mLogger = logger;
dumpController.registerDumpable(TAG, this);
}
@@ -205,8 +206,7 @@ public class ShadeListBuilder implements Dumpable {
Assert.isMainThread();
mPipelineState.requireIsBefore(STATE_BUILD_STARTED);
- mNotifLog.log(NotifEvent.ON_BUILD_LIST, "Request received from "
- + "NotifCollection");
+ mLogger.logOnBuildList();
mAllEntries = entries;
buildList();
}
@@ -215,21 +215,15 @@ public class ShadeListBuilder implements Dumpable {
private void onPreGroupFilterInvalidated(NotifFilter filter) {
Assert.isMainThread();
- mNotifLog.log(NotifEvent.PRE_GROUP_FILTER_INVALIDATED, String.format(
- "Filter \"%s\" invalidated; pipeline state is %d",
- filter.getName(),
- mPipelineState.getState()));
+ mLogger.logPreGroupFilterInvalidated(filter.getName(), mPipelineState.getState());
rebuildListIfBefore(STATE_PRE_GROUP_FILTERING);
}
- private void onPromoterInvalidated(NotifPromoter filter) {
+ private void onPromoterInvalidated(NotifPromoter promoter) {
Assert.isMainThread();
- mNotifLog.log(NotifEvent.PROMOTER_INVALIDATED, String.format(
- "NotifPromoter \"%s\" invalidated; pipeline state is %d",
- filter.getName(),
- mPipelineState.getState()));
+ mLogger.logPromoterInvalidated(promoter.getName(), mPipelineState.getState());
rebuildListIfBefore(STATE_TRANSFORMING);
}
@@ -237,10 +231,7 @@ public class ShadeListBuilder implements Dumpable {
private void onNotifSectionInvalidated(NotifSection section) {
Assert.isMainThread();
- mNotifLog.log(NotifEvent.SECTION_INVALIDATED, String.format(
- "Section \"%s\" invalidated; pipeline state is %d",
- section.getName(),
- mPipelineState.getState()));
+ mLogger.logNotifSectionInvalidated(section.getName(), mPipelineState.getState());
rebuildListIfBefore(STATE_SORTING);
}
@@ -248,10 +239,7 @@ public class ShadeListBuilder implements Dumpable {
private void onPreRenderFilterInvalidated(NotifFilter filter) {
Assert.isMainThread();
- mNotifLog.log(NotifEvent.PRE_RENDER_FILTER_INVALIDATED, String.format(
- "Filter \"%s\" invalidated; pipeline state is %d",
- filter.getName(),
- mPipelineState.getState()));
+ mLogger.logPreRenderFilterInvalidated(filter.getName(), mPipelineState.getState());
rebuildListIfBefore(STATE_PRE_RENDER_FILTERING);
}
@@ -259,10 +247,7 @@ public class ShadeListBuilder implements Dumpable {
private void onNotifComparatorInvalidated(NotifComparator comparator) {
Assert.isMainThread();
- mNotifLog.log(NotifEvent.COMPARATOR_INVALIDATED, String.format(
- "Comparator \"%s\" invalidated; pipeline state is %d",
- comparator.getName(),
- mPipelineState.getState()));
+ mLogger.logNotifComparatorInvalidated(comparator.getName(), mPipelineState.getState());
rebuildListIfBefore(STATE_SORTING);
}
@@ -288,7 +273,7 @@ public class ShadeListBuilder implements Dumpable {
* if we detect that behavior, we should crash instantly.
*/
private void buildList() {
- mNotifLog.log(NotifEvent.START_BUILD_LIST, "Run #" + mIterationCount + "...");
+ mLogger.logStartBuildList(mIterationCount);
mPipelineState.requireIsBefore(STATE_BUILD_STARTED);
mPipelineState.setState(STATE_BUILD_STARTED);
@@ -334,16 +319,16 @@ public class ShadeListBuilder implements Dumpable {
freeEmptyGroups();
// Step 6: Dispatch the new list, first to any listeners and then to the view layer
- mNotifLog.log(NotifEvent.DISPATCH_FINAL_LIST, "List finalized, is:\n"
- + ListDumper.dumpTree(mNotifList, false, "\t\t"));
+ if (mIterationCount % 10 == 0) {
+ mLogger.logFinalList(mNotifList);
+ }
dispatchOnBeforeRenderList(mReadOnlyNotifList);
if (mOnRenderListListener != null) {
mOnRenderListListener.onRenderList(mReadOnlyNotifList);
}
// Step 7: We're done!
- mNotifLog.log(NotifEvent.LIST_BUILD_COMPLETE,
- "Notif list build #" + mIterationCount + " completed");
+ mLogger.logEndBuildList(mIterationCount);
mPipelineState.setState(STATE_IDLE);
mIterationCount++;
}
@@ -429,11 +414,10 @@ public class ShadeListBuilder implements Dumpable {
if (existingSummary == null) {
group.setSummary(entry);
} else {
- mNotifLog.log(NotifEvent.WARN, String.format(
- "Duplicate summary for group '%s': '%s' vs. '%s'",
+ mLogger.logDuplicateSummary(
group.getKey(),
existingSummary.getKey(),
- entry.getKey()));
+ entry.getKey());
// Use whichever one was posted most recently
if (entry.getSbn().getPostTime()
@@ -452,8 +436,7 @@ public class ShadeListBuilder implements Dumpable {
final String topLevelKey = entry.getKey();
if (mGroups.containsKey(topLevelKey)) {
- mNotifLog.log(NotifEvent.WARN,
- "Duplicate non-group top-level key: " + topLevelKey);
+ mLogger.logDuplicateTopLevelKey(topLevelKey);
} else {
entry.setParent(ROOT_ENTRY);
out.add(entry);
@@ -617,24 +600,22 @@ public class ShadeListBuilder implements Dumpable {
private void logParentingChanges() {
for (NotificationEntry entry : mAllEntries) {
if (entry.getParent() != entry.getPreviousParent()) {
- mNotifLog.log(NotifEvent.PARENT_CHANGED, String.format(
- "%s: parent changed from %s to %s",
+ mLogger.logParentChanged(
entry.getKey(),
entry.getPreviousParent() == null
- ? "null" : entry.getPreviousParent().getKey(),
+ ? null : entry.getPreviousParent().getKey(),
entry.getParent() == null
- ? "null" : entry.getParent().getKey()));
+ ? null : entry.getParent().getKey());
}
}
for (GroupEntry group : mGroups.values()) {
if (group.getParent() != group.getPreviousParent()) {
- mNotifLog.log(NotifEvent.PARENT_CHANGED, String.format(
- "%s: parent changed from %s to %s",
+ mLogger.logParentChanged(
group.getKey(),
group.getPreviousParent() == null
- ? "null" : group.getPreviousParent().getKey(),
+ ? null : group.getPreviousParent().getKey(),
group.getParent() == null
- ? "null" : group.getParent().getKey()));
+ ? null : group.getParent().getKey());
}
}
}
@@ -684,23 +665,10 @@ public class ShadeListBuilder implements Dumpable {
NotifFilter filter = findRejectingFilter(entry, now, filters);
if (filter != entry.mExcludingFilter) {
- if (entry.mExcludingFilter == null) {
- mNotifLog.log(NotifEvent.FILTER_CHANGED, String.format(
- "%s: filtered out by '%s'",
- entry.getKey(),
- filter.getName()));
- } else if (filter == null) {
- mNotifLog.log(NotifEvent.FILTER_CHANGED, String.format(
- "%s: no longer filtered out (previous filter was '%s')",
- entry.getKey(),
- entry.mExcludingFilter.getName()));
- } else {
- mNotifLog.log(NotifEvent.FILTER_CHANGED, String.format(
- "%s: filter changed: '%s' -> '%s'",
- entry.getKey(),
- entry.mExcludingFilter,
- filter));
- }
+ mLogger.logFilterChanged(
+ entry.getKey(),
+ entry.mExcludingFilter != null ? entry.mExcludingFilter.getName() : null,
+ filter != null ? filter.getName() : null);
// Note that groups and summaries can also be filtered out later if they're part of a
// malformed group. We currently don't have a great way to track that beyond parenting
@@ -728,23 +696,10 @@ public class ShadeListBuilder implements Dumpable {
NotifPromoter promoter = findPromoter(entry);
if (promoter != entry.mNotifPromoter) {
- if (entry.mNotifPromoter == null) {
- mNotifLog.log(NotifEvent.PROMOTER_CHANGED, String.format(
- "%s: Entry promoted to top level by '%s'",
- entry.getKey(),
- promoter.getName()));
- } else if (promoter == null) {
- mNotifLog.log(NotifEvent.PROMOTER_CHANGED, String.format(
- "%s: Entry is no longer promoted to top level (previous promoter was '%s')",
- entry.getKey(),
- entry.mNotifPromoter.getName()));
- } else {
- mNotifLog.log(NotifEvent.PROMOTER_CHANGED, String.format(
- "%s: Top-level promoter changed: '%s' -> '%s'",
- entry.getKey(),
- entry.mNotifPromoter,
- promoter));
- }
+ mLogger.logPromoterChanged(
+ entry.getKey(),
+ entry.mNotifPromoter != null ? entry.mNotifPromoter.getName() : null,
+ promoter != null ? promoter.getName() : null);
entry.mNotifPromoter = promoter;
}
@@ -767,21 +722,12 @@ public class ShadeListBuilder implements Dumpable {
final Integer sectionIndex = sectionWithIndex.second;
if (section != entry.mNotifSection) {
- if (entry.mNotifSection == null) {
- mNotifLog.log(NotifEvent.SECTION_CHANGED, String.format(
- "%s: sectioned by '%s' [index=%d].",
- entry.getKey(),
- section.getName(),
- sectionIndex));
- } else {
- mNotifLog.log(NotifEvent.SECTION_CHANGED, String.format(
- "%s: section changed: '%s' [index=%d] -> '%s [index=%d]'.",
- entry.getKey(),
- entry.mNotifSection,
- entry.getSection(),
- section,
- sectionIndex));
- }
+ mLogger.logSectionChanged(
+ entry.getKey(),
+ entry.mNotifSection != null ? entry.mNotifSection.getName() : null,
+ entry.getSection(),
+ section.getName(),
+ sectionIndex);
entry.mNotifSection = section;
entry.setSection(sectionIndex);
@@ -826,7 +772,7 @@ public class ShadeListBuilder implements Dumpable {
}
@Override
- public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
+ public void dump(@NonNull FileDescriptor fd, PrintWriter pw, @NonNull String[] args) {
pw.println("\t" + TAG + " shade notifications:");
if (getShadeList().size() == 0) {
pw.println("\t\t None");
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coalescer/GroupCoalescer.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coalescer/GroupCoalescer.java
index f5890386a14f..98c45ffd6afb 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coalescer/GroupCoalescer.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coalescer/GroupCoalescer.java
@@ -16,11 +16,6 @@
package com.android.systemui.statusbar.notification.collection.coalescer;
-import static com.android.systemui.statusbar.notification.logging.NotifEvent.BATCH_MAX_TIMEOUT;
-import static com.android.systemui.statusbar.notification.logging.NotifEvent.COALESCED_EVENT;
-import static com.android.systemui.statusbar.notification.logging.NotifEvent.EARLY_BATCH_EMIT;
-import static com.android.systemui.statusbar.notification.logging.NotifEvent.EMIT_EVENT_BATCH;
-
import static java.util.Objects.requireNonNull;
import android.annotation.MainThread;
@@ -35,7 +30,6 @@ import com.android.systemui.Dumpable;
import com.android.systemui.dagger.qualifiers.Main;
import com.android.systemui.statusbar.NotificationListener;
import com.android.systemui.statusbar.NotificationListener.NotificationHandler;
-import com.android.systemui.statusbar.notification.logging.NotifLog;
import com.android.systemui.util.concurrency.DelayableExecutor;
import com.android.systemui.util.time.SystemClock;
@@ -71,7 +65,7 @@ import javax.inject.Inject;
public class GroupCoalescer implements Dumpable {
private final DelayableExecutor mMainExecutor;
private final SystemClock mClock;
- private final NotifLog mLog;
+ private final GroupCoalescerLogger mLogger;
private final long mMinGroupLingerDuration;
private final long mMaxGroupLingerDuration;
@@ -83,8 +77,9 @@ public class GroupCoalescer implements Dumpable {
@Inject
public GroupCoalescer(
@Main DelayableExecutor mainExecutor,
- SystemClock clock, NotifLog log) {
- this(mainExecutor, clock, log, MIN_GROUP_LINGER_DURATION, MAX_GROUP_LINGER_DURATION);
+ SystemClock clock,
+ GroupCoalescerLogger logger) {
+ this(mainExecutor, clock, logger, MIN_GROUP_LINGER_DURATION, MAX_GROUP_LINGER_DURATION);
}
/**
@@ -98,12 +93,12 @@ public class GroupCoalescer implements Dumpable {
GroupCoalescer(
@Main DelayableExecutor mainExecutor,
SystemClock clock,
- NotifLog log,
+ GroupCoalescerLogger logger,
long minGroupLingerDuration,
long maxGroupLingerDuration) {
mMainExecutor = mainExecutor;
mClock = clock;
- mLog = log;
+ mLogger = logger;
mMinGroupLingerDuration = minGroupLingerDuration;
mMaxGroupLingerDuration = maxGroupLingerDuration;
}
@@ -129,7 +124,7 @@ public class GroupCoalescer implements Dumpable {
final boolean shouldCoalesce = handleNotificationPosted(sbn, rankingMap);
if (shouldCoalesce) {
- mLog.log(COALESCED_EVENT, String.format("Coalesced notification %s", sbn.getKey()));
+ mLogger.logEventCoalesced(sbn.getKey());
mHandler.onNotificationRankingUpdate(rankingMap);
} else {
mHandler.onNotificationPosted(sbn, rankingMap);
@@ -164,15 +159,11 @@ public class GroupCoalescer implements Dumpable {
final CoalescedEvent event = mCoalescedEvents.get(sbn.getKey());
final EventBatch batch = mBatches.get(sbn.getGroupKey());
if (event != null) {
- mLog.log(EARLY_BATCH_EMIT,
- String.format("Modification of %s triggered early emit of batched group %s",
- sbn.getKey(), requireNonNull(event.getBatch()).mGroupKey));
+ mLogger.logEarlyEmit(sbn.getKey(), requireNonNull(event.getBatch()).mGroupKey);
emitBatch(requireNonNull(event.getBatch()));
} else if (batch != null
&& mClock.uptimeMillis() - batch.mCreatedTimestamp >= mMaxGroupLingerDuration) {
- mLog.log(BATCH_MAX_TIMEOUT,
- String.format("Modification of %s triggered timeout emit of batched group %s",
- sbn.getKey(), batch.mGroupKey));
+ mLogger.logMaxBatchTimeout(sbn.getKey(), batch.mGroupKey);
emitBatch(batch);
}
}
@@ -253,7 +244,7 @@ public class GroupCoalescer implements Dumpable {
}
events.sort(mEventComparator);
- mLog.log(EMIT_EVENT_BATCH, "Emitting event batch for group " + batch.mGroupKey);
+ mLogger.logEmitBatch(batch.mGroupKey);
mHandler.onNotificationBatchPosted(events);
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coalescer/GroupCoalescerLogger.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coalescer/GroupCoalescerLogger.kt
new file mode 100644
index 000000000000..6e8788db59d7
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coalescer/GroupCoalescerLogger.kt
@@ -0,0 +1,62 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.statusbar.notification.collection.coalescer
+
+import com.android.systemui.log.LogBuffer
+import com.android.systemui.log.LogLevel
+import com.android.systemui.log.dagger.NotificationLog
+import javax.inject.Inject
+
+class GroupCoalescerLogger @Inject constructor(
+ @NotificationLog private val buffer: LogBuffer
+) {
+ fun logEventCoalesced(key: String) {
+ buffer.log(TAG, LogLevel.INFO, {
+ str1 = key
+ }, {
+ "COALESCED: $str1"
+ })
+ }
+
+ fun logEmitBatch(groupKey: String) {
+ buffer.log(TAG, LogLevel.DEBUG, {
+ str1 = groupKey
+ }, {
+ "Emitting event batch for group $str1"
+ })
+ }
+
+ fun logEarlyEmit(modifiedKey: String, groupKey: String) {
+ buffer.log(TAG, LogLevel.DEBUG, {
+ str1 = modifiedKey
+ str2 = groupKey
+ }, {
+ "Modification of notif $str1 triggered early emit of batched group $str2"
+ })
+ }
+
+ fun logMaxBatchTimeout(modifiedKey: String, groupKey: String) {
+ buffer.log(TAG, LogLevel.INFO, {
+ str1 = modifiedKey
+ str2 = groupKey
+ }, {
+ "Modification of notif $str1 triggered TIMEOUT emit of batched group $str2"
+ })
+ }
+}
+
+private const val TAG = "GroupCoalescer" \ No newline at end of file
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/listbuilder/ShadeListBuilderLogger.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/listbuilder/ShadeListBuilderLogger.kt
new file mode 100644
index 000000000000..6e15043973f7
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/listbuilder/ShadeListBuilderLogger.kt
@@ -0,0 +1,217 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.statusbar.notification.collection.listbuilder
+
+import com.android.systemui.log.LogBuffer
+import com.android.systemui.log.LogLevel.DEBUG
+import com.android.systemui.log.LogLevel.INFO
+import com.android.systemui.log.LogLevel.WARNING
+import com.android.systemui.log.dagger.NotificationLog
+import com.android.systemui.statusbar.notification.collection.GroupEntry
+import com.android.systemui.statusbar.notification.collection.ListEntry
+import javax.inject.Inject
+
+class ShadeListBuilderLogger @Inject constructor(
+ @NotificationLog private val buffer: LogBuffer
+) {
+ fun logOnBuildList() {
+ buffer.log(TAG, INFO, {
+ }, {
+ "Request received from NotifCollection"
+ })
+ }
+
+ fun logStartBuildList(iterationCount: Int) {
+ buffer.log(TAG, INFO, {
+ int1 = iterationCount
+ }, {
+ "Starting to build shade list (run #$int1)"
+ })
+ }
+
+ fun logEndBuildList(iterationCount: Int) {
+ buffer.log(TAG, INFO, {
+ int1 = iterationCount
+ }, {
+ "Finished building shade list (run #$int1)"
+ })
+ }
+
+ fun logPreGroupFilterInvalidated(filterName: String, pipelineState: Int) {
+ buffer.log(TAG, DEBUG, {
+ str1 = filterName
+ int1 = pipelineState
+ }, {
+ """Pre-group NotifFilter "$str1" invalidated; pipeline state is $int1"""
+ })
+ }
+
+ fun logPromoterInvalidated(name: String, pipelineState: Int) {
+ buffer.log(TAG, DEBUG, {
+ str1 = name
+ int1 = pipelineState
+ }, {
+ """NotifPromoter "$str1" invalidated; pipeline state is $int1"""
+ })
+ }
+
+ fun logNotifSectionInvalidated(name: String, pipelineState: Int) {
+ buffer.log(TAG, DEBUG, {
+ str1 = name
+ int1 = pipelineState
+ }, {
+ """NotifSection "$str1" invalidated; pipeline state is $int1"""
+ })
+ }
+
+ fun logNotifComparatorInvalidated(name: String, pipelineState: Int) {
+ buffer.log(TAG, DEBUG, {
+ str1 = name
+ int1 = pipelineState
+ }, {
+ """NotifComparator "$str1" invalidated; pipeline state is $int1"""
+ })
+ }
+
+ fun logPreRenderFilterInvalidated(name: String, pipelineState: Int) {
+ buffer.log(TAG, DEBUG, {
+ str1 = name
+ int1 = pipelineState
+ }, {
+ """Pre-render NotifFilter "$str1" invalidated; pipeline state is $int1"""
+ })
+ }
+
+ fun logDuplicateSummary(groupKey: String, existingKey: String, newKey: String) {
+ buffer.log(TAG, WARNING, {
+ str1 = groupKey
+ str2 = existingKey
+ str3 = newKey
+ }, {
+ """Duplicate summary for group "$str1": "$str2" vs. "$str3""""
+ })
+ }
+
+ fun logDuplicateTopLevelKey(topLevelKey: String) {
+ buffer.log(TAG, WARNING, {
+ str1 = topLevelKey
+ }, {
+ "Duplicate top-level key: $str1"
+ })
+ }
+
+ fun logParentChanged(key: String, prevParent: String?, newParent: String?) {
+ buffer.log(TAG, INFO, {
+ str1 = key
+ str2 = prevParent
+ str3 = newParent
+ }, {
+ "Parent change for $str1: $str2 -> $str3"
+ })
+ }
+
+ fun logFilterChanged(
+ key: String,
+ prevFilter: String?,
+ newFilter: String?
+ ) {
+ buffer.log(TAG, INFO, {
+ str1 = key
+ str2 = prevFilter
+ str3 = newFilter
+ }, {
+ "Filter changed for $str1: $str2 -> $str3"
+ })
+ }
+
+ fun logPromoterChanged(
+ key: String,
+ prevPromoter: String?,
+ newPromoter: String?
+ ) {
+ buffer.log(TAG, INFO, {
+ str1 = key
+ str2 = prevPromoter
+ str3 = newPromoter
+ }, {
+ "Promoter changed for $str1: $str2 -> $str3"
+ })
+ }
+
+ fun logSectionChanged(
+ key: String,
+ prevSection: String?,
+ prevIndex: Int,
+ section: String,
+ index: Int
+ ) {
+ buffer.log(TAG, INFO, {
+ str1 = key
+ str2 = section
+ int1 = index
+ str3 = prevSection
+ int2 = prevIndex
+ }, {
+ if (str3 == null) {
+ "Section assigned for $str1: '$str2' (#$int1)"
+ } else {
+ "Section changed for $str1: '$str3' (#$int2) -> '$str2' (#$int1)"
+ }
+ })
+ }
+
+ fun logFinalList(entries: List<ListEntry>) {
+ buffer.log(TAG, DEBUG, {
+ int1 = entries.size
+ }, {
+ "List is finalized ($int1 top-level entries):"
+ })
+ if (entries.isEmpty()) {
+ buffer.log(TAG, DEBUG, {}, { "(empty list)" })
+ }
+ for (i in entries.indices) {
+ val entry = entries[i]
+ buffer.log(TAG, DEBUG, {
+ int1 = i
+ str1 = entry.key
+ }, {
+ "[$int1] $str1"
+ })
+
+ if (entry is GroupEntry) {
+ entry.summary?.let {
+ buffer.log(TAG, DEBUG, {
+ str1 = it.key
+ }, {
+ " [*] $str1 (summary)"
+ })
+ }
+ for (j in entry.children.indices) {
+ val child = entry.children[j]
+ buffer.log(TAG, DEBUG, {
+ int1 = j
+ str1 = child.key
+ }, {
+ " [$int1] $str1"
+ })
+ }
+ }
+ }
+ }
+}
+
+private const val TAG = "ShadeListBuilder" \ No newline at end of file
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/notifcollection/NotifCollectionLogger.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/notifcollection/NotifCollectionLogger.kt
new file mode 100644
index 000000000000..bd1bd860f80c
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/notifcollection/NotifCollectionLogger.kt
@@ -0,0 +1,62 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.statusbar.notification.collection.notifcollection
+
+import com.android.systemui.log.LogBuffer
+import com.android.systemui.log.LogLevel
+import com.android.systemui.log.dagger.NotificationLog
+import javax.inject.Inject
+
+class NotifCollectionLogger @Inject constructor(
+ @NotificationLog private val buffer: LogBuffer
+) {
+ fun logNotifPosted(key: String) {
+ buffer.log(TAG, LogLevel.INFO, {
+ str1 = key
+ }, {
+ "POSTED $str1"
+ })
+ }
+
+ fun logNotifGroupPosted(groupKey: String, batchSize: Int) {
+ buffer.log(TAG, LogLevel.INFO, {
+ str1 = groupKey
+ int1 = batchSize
+ }, {
+ "POSTED GROUP $str1 ($int1 events)"
+ })
+ }
+
+ fun logNotifUpdated(key: String) {
+ buffer.log(TAG, LogLevel.INFO, {
+ str1 = key
+ }, {
+ "UPDATED $str1"
+ })
+ }
+
+ fun logNotifRemoved(key: String, reason: Int) {
+ buffer.log(TAG, LogLevel.INFO, {
+ str1 = key
+ int1 = reason
+ }, {
+ "REMOVED $str1 reason=$int1"
+ })
+ }
+}
+
+private const val TAG = "NotifCollection" \ No newline at end of file
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/provider/HighPriorityProvider.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/provider/HighPriorityProvider.java
index b49611688bc7..0d9beaefbf8d 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/provider/HighPriorityProvider.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/provider/HighPriorityProvider.java
@@ -63,6 +63,10 @@ public class HighPriorityProvider {
}
final NotificationEntry notifEntry = entry.getRepresentativeEntry();
+ if (notifEntry == null) {
+ return false;
+ }
+
return notifEntry.getRanking().getImportance() >= NotificationManager.IMPORTANCE_DEFAULT
|| hasHighPriorityCharacteristics(notifEntry)
|| hasHighPriorityChild(entry);
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationConversationInfo.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationConversationInfo.java
index 18c755da53ff..6045524f30b6 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationConversationInfo.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationConversationInfo.java
@@ -29,6 +29,7 @@ import static com.android.systemui.statusbar.notification.row.NotificationConver
import static com.android.systemui.statusbar.notification.row.NotificationConversationInfo.UpdateChannelRunnable.ACTION_HOME;
import static com.android.systemui.statusbar.notification.row.NotificationConversationInfo.UpdateChannelRunnable.ACTION_MUTE;
import static com.android.systemui.statusbar.notification.row.NotificationConversationInfo.UpdateChannelRunnable.ACTION_SNOOZE;
+import static com.android.systemui.statusbar.notification.row.NotificationConversationInfo.UpdateChannelRunnable.ACTION_UNBUBBLE;
import static java.lang.annotation.RetentionPolicy.SOURCE;
@@ -121,7 +122,7 @@ public class NotificationConversationInfo extends LinearLayout implements
boolean mSkipPost = false;
private OnClickListener mOnBubbleClick = v -> {
- mSelectedAction = ACTION_BUBBLE;
+ mSelectedAction = mStartedAsBubble ? ACTION_UNBUBBLE : ACTION_BUBBLE;
if (mStartedAsBubble) {
mBubbleController.onUserDemotedBubbleFromNotification(mEntry);
} else {
@@ -586,6 +587,7 @@ public class NotificationConversationInfo extends LinearLayout implements
static final int ACTION_SNOOZE = 3;
static final int ACTION_MUTE = 4;
static final int ACTION_DEMOTE = 5;
+ static final int ACTION_UNBUBBLE = 6;
private final INotificationManager mINotificationManager;
private final String mAppPkg;
@@ -606,9 +608,17 @@ public class NotificationConversationInfo extends LinearLayout implements
@Override
public void run() {
try {
+ boolean channelSettingChanged = mAction != ACTION_HOME && mAction != ACTION_SNOOZE;
switch (mAction) {
case ACTION_BUBBLE:
- mChannelToUpdate.setAllowBubbles(!mChannelToUpdate.canBubble());
+ case ACTION_UNBUBBLE:
+ boolean canBubble = mAction == ACTION_BUBBLE;
+ if (mChannelToUpdate.canBubble() != canBubble) {
+ channelSettingChanged = true;
+ mChannelToUpdate.setAllowBubbles(canBubble);
+ } else {
+ channelSettingChanged = false;
+ }
break;
case ACTION_FAVORITE:
// TODO: extend beyond DND
@@ -629,7 +639,7 @@ public class NotificationConversationInfo extends LinearLayout implements
}
- if (mAction != ACTION_HOME && mAction != ACTION_SNOOZE) {
+ if (channelSettingChanged) {
mINotificationManager.updateNotificationChannelForPackage(
mAppPkg, mAppUid, mChannelToUpdate);
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarFragment.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarFragment.java
index 3e3ef0ccb8ce..9e64748f2e65 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarFragment.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarFragment.java
@@ -302,14 +302,14 @@ public class NavigationBarFragment extends LifecycleFragment implements Callback
mForceNavBarHandleOpaque = DeviceConfig.getBoolean(
DeviceConfig.NAMESPACE_SYSTEMUI,
NAV_BAR_HANDLE_FORCE_OPAQUE,
- /* defaultValue = */ false);
+ /* defaultValue = */ true);
DeviceConfig.addOnPropertiesChangedListener(DeviceConfig.NAMESPACE_SYSTEMUI, mHandler::post,
new DeviceConfig.OnPropertiesChangedListener() {
@Override
public void onPropertiesChanged(DeviceConfig.Properties properties) {
if (properties.getKeyset().contains(NAV_BAR_HANDLE_FORCE_OPAQUE)) {
mForceNavBarHandleOpaque = properties.getBoolean(
- NAV_BAR_HANDLE_FORCE_OPAQUE, /* defaultValue = */ false);
+ NAV_BAR_HANDLE_FORCE_OPAQUE, /* defaultValue = */ true);
}
}
});
diff --git a/packages/SystemUI/src/com/android/systemui/toast/ToastUI.java b/packages/SystemUI/src/com/android/systemui/toast/ToastUI.java
new file mode 100644
index 000000000000..dea8c5d49dfc
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/toast/ToastUI.java
@@ -0,0 +1,212 @@
+/*
+ * 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.toast;
+
+import android.annotation.MainThread;
+import android.annotation.Nullable;
+import android.app.INotificationManager;
+import android.app.ITransientNotificationCallback;
+import android.content.Context;
+import android.content.res.Configuration;
+import android.graphics.PixelFormat;
+import android.os.IBinder;
+import android.os.RemoteException;
+import android.os.ServiceManager;
+import android.util.Log;
+import android.view.Gravity;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.ViewGroup.LayoutParams;
+import android.view.WindowManager;
+import android.view.accessibility.AccessibilityEvent;
+import android.view.accessibility.AccessibilityManager;
+import android.widget.TextView;
+import android.widget.Toast;
+
+import com.android.internal.R;
+import com.android.systemui.SystemUI;
+import com.android.systemui.statusbar.CommandQueue;
+
+import java.util.Objects;
+
+import javax.inject.Inject;
+import javax.inject.Singleton;
+
+/**
+ * Controls display of text toasts.
+ */
+@Singleton
+public class ToastUI extends SystemUI implements CommandQueue.Callbacks {
+ private static final String TAG = "ToastUI";
+
+ /**
+ * Values taken from {@link Toast}.
+ */
+ private static final long DURATION_SHORT = 4000;
+ private static final long DURATION_LONG = 7000;
+
+ private final CommandQueue mCommandQueue;
+ private final WindowManager mWindowManager;
+ private final INotificationManager mNotificationManager;
+ private final AccessibilityManager mAccessibilityManager;
+ private ToastEntry mCurrentToast;
+
+ @Inject
+ public ToastUI(Context context, CommandQueue commandQueue) {
+ super(context);
+ mCommandQueue = commandQueue;
+ mWindowManager = (WindowManager) context.getSystemService(Context.WINDOW_SERVICE);
+ mNotificationManager = INotificationManager.Stub.asInterface(
+ ServiceManager.getService(Context.NOTIFICATION_SERVICE));
+ mAccessibilityManager = AccessibilityManager.getInstance(context);
+ }
+
+ @Override
+ public void start() {
+ mCommandQueue.addCallback(this);
+ }
+
+ @Override
+ @MainThread
+ public void showToast(String packageName, IBinder token, CharSequence text,
+ IBinder windowToken, int duration, @Nullable ITransientNotificationCallback callback) {
+ if (mCurrentToast != null) {
+ hideCurrentToast();
+ }
+ View view = getView(text);
+ LayoutParams params = getLayoutParams(windowToken, duration);
+ mCurrentToast = new ToastEntry(packageName, token, view, windowToken, callback);
+ try {
+ mWindowManager.addView(view, params);
+ } catch (WindowManager.BadTokenException e) {
+ Log.w(TAG, "Error while attempting to show toast from " + packageName, e);
+ return;
+ }
+ trySendAccessibilityEvent(view, packageName);
+ if (callback != null) {
+ try {
+ callback.onToastShown();
+ } catch (RemoteException e) {
+ Log.w(TAG, "Error calling back " + packageName + " to notify onToastShow()", e);
+ }
+ }
+ }
+
+ @Override
+ @MainThread
+ public void hideToast(String packageName, IBinder token) {
+ if (mCurrentToast == null || !Objects.equals(mCurrentToast.packageName, packageName)
+ || !Objects.equals(mCurrentToast.token, token)) {
+ Log.w(TAG, "Attempt to hide non-current toast from package " + packageName);
+ return;
+ }
+ hideCurrentToast();
+ }
+
+ @MainThread
+ private void hideCurrentToast() {
+ if (mCurrentToast.view.getParent() != null) {
+ mWindowManager.removeViewImmediate(mCurrentToast.view);
+ }
+ String packageName = mCurrentToast.packageName;
+ try {
+ mNotificationManager.finishToken(packageName, mCurrentToast.windowToken);
+ } catch (RemoteException e) {
+ Log.w(TAG, "Error finishing toast window token from package " + packageName, e);
+ }
+ if (mCurrentToast.callback != null) {
+ try {
+ mCurrentToast.callback.onToastHidden();
+ } catch (RemoteException e) {
+ Log.w(TAG, "Error calling back " + packageName + " to notify onToastHide()", e);
+ }
+ }
+ mCurrentToast = null;
+ }
+
+ private void trySendAccessibilityEvent(View view, String packageName) {
+ if (!mAccessibilityManager.isEnabled()) {
+ return;
+ }
+ AccessibilityEvent event = AccessibilityEvent.obtain(
+ AccessibilityEvent.TYPE_NOTIFICATION_STATE_CHANGED);
+ event.setClassName(Toast.class.getName());
+ event.setPackageName(packageName);
+ view.dispatchPopulateAccessibilityEvent(event);
+ mAccessibilityManager.sendAccessibilityEvent(event);
+ }
+
+ private View getView(CharSequence text) {
+ View view = LayoutInflater.from(mContext).inflate(
+ R.layout.transient_notification, null);
+ TextView textView = view.findViewById(com.android.internal.R.id.message);
+ textView.setText(text);
+ return view;
+ }
+
+ private LayoutParams getLayoutParams(IBinder windowToken, int duration) {
+ WindowManager.LayoutParams params = new WindowManager.LayoutParams();
+ params.height = WindowManager.LayoutParams.WRAP_CONTENT;
+ params.width = WindowManager.LayoutParams.WRAP_CONTENT;
+ params.format = PixelFormat.TRANSLUCENT;
+ params.windowAnimations = com.android.internal.R.style.Animation_Toast;
+ params.type = WindowManager.LayoutParams.TYPE_TOAST;
+ params.setTitle("Toast");
+ params.flags = WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON
+ | WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE
+ | WindowManager.LayoutParams.FLAG_NOT_TOUCHABLE;
+ Configuration config = mContext.getResources().getConfiguration();
+ int specificGravity = mContext.getResources().getInteger(
+ com.android.internal.R.integer.config_toastDefaultGravity);
+ int gravity = Gravity.getAbsoluteGravity(specificGravity, config.getLayoutDirection());
+ params.gravity = gravity;
+ if ((gravity & Gravity.HORIZONTAL_GRAVITY_MASK) == Gravity.FILL_HORIZONTAL) {
+ params.horizontalWeight = 1.0f;
+ }
+ if ((gravity & Gravity.VERTICAL_GRAVITY_MASK) == Gravity.FILL_VERTICAL) {
+ params.verticalWeight = 1.0f;
+ }
+ params.x = 0;
+ params.y = mContext.getResources().getDimensionPixelSize(R.dimen.toast_y_offset);
+ params.verticalMargin = 0;
+ params.horizontalMargin = 0;
+ params.packageName = mContext.getPackageName();
+ params.hideTimeoutMilliseconds =
+ (duration == Toast.LENGTH_LONG) ? DURATION_LONG : DURATION_SHORT;
+ params.token = windowToken;
+ return params;
+ }
+
+ private static class ToastEntry {
+ public final String packageName;
+ public final IBinder token;
+ public final View view;
+ public final IBinder windowToken;
+
+ @Nullable
+ public final ITransientNotificationCallback callback;
+
+ private ToastEntry(String packageName, IBinder token, View view, IBinder windowToken,
+ @Nullable ITransientNotificationCallback callback) {
+ this.packageName = packageName;
+ this.token = token;
+ this.view = view;
+ this.windowToken = windowToken;
+ this.callback = callback;
+ }
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/wm/SystemWindows.java b/packages/SystemUI/src/com/android/systemui/wm/SystemWindows.java
index 5aba013a7fb8..adce1248b04b 100644
--- a/packages/SystemUI/src/com/android/systemui/wm/SystemWindows.java
+++ b/packages/SystemUI/src/com/android/systemui/wm/SystemWindows.java
@@ -239,12 +239,12 @@ public class SystemWindows {
Rect outVisibleInsets, Rect outStableInsets,
DisplayCutout.ParcelableWrapper cutout, MergedConfiguration mergedConfiguration,
SurfaceControl outSurfaceControl, InsetsState outInsetsState,
- Point outSurfaceSize) {
+ Point outSurfaceSize, SurfaceControl outBLASTSurfaceControl) {
int res = super.relayout(window, seq, attrs, requestedWidth, requestedHeight,
viewVisibility, flags, frameNumber, outFrame, outOverscanInsets,
outContentInsets, outVisibleInsets, outStableInsets,
cutout, mergedConfiguration, outSurfaceControl, outInsetsState,
- outSurfaceSize);
+ outSurfaceSize, outBLASTSurfaceControl);
if (res != 0) {
return res;
}
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 2c9058afd7ab..28f01da56f06 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/bubbles/BubbleControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/bubbles/BubbleControllerTest.java
@@ -282,7 +282,7 @@ public class BubbleControllerTest extends SysuiTestCase {
mRow.getEntry().getKey()));
// Make it look like dismissed notif
- mBubbleData.getBubbleWithKey(mRow.getEntry().getKey()).setShowInShade(false);
+ mBubbleData.getBubbleWithKey(mRow.getEntry().getKey()).setSuppressNotification(true);
// Now remove the bubble
mBubbleController.removeBubble(
@@ -372,7 +372,8 @@ public class BubbleControllerTest extends SysuiTestCase {
// Switch which bubble is expanded
mBubbleController.selectBubble(mRow.getEntry().getKey());
- mBubbleController.expandStack();
+ mBubbleData.setExpanded(true);
+ assertEquals(mRow.getEntry(), stackView.getExpandedBubble().getEntry());
assertTrue(mBubbleController.isBubbleNotificationSuppressedFromShade(
mRow.getEntry().getKey()));
@@ -571,7 +572,6 @@ public class BubbleControllerTest extends SysuiTestCase {
verify(mBubbleStateChangeListener).onHasBubblesChanged(true /* hasBubbles */);
}
-
@Test
public void testExpandStackAndSelectBubble_removedFirst() {
final String key = mRow.getEntry().getKey();
@@ -724,6 +724,52 @@ public class BubbleControllerTest extends SysuiTestCase {
assertFalse(intercepted);
}
+ @Test
+ public void testNotifyShadeSuppressionChange_notificationDismiss() {
+ BubbleController.NotificationSuppressionChangedListener listener =
+ mock(BubbleController.NotificationSuppressionChangedListener.class);
+ mBubbleData.setSuppressionChangedListener(listener);
+
+ mEntryListener.onNotificationAdded(mRow.getEntry());
+
+ assertTrue(mBubbleController.hasBubbles());
+ assertFalse(mBubbleController.isBubbleNotificationSuppressedFromShade(
+ mRow.getEntry().getKey()));
+
+ mRemoveInterceptor.onNotificationRemoveRequested(mRow.getEntry().getKey(), REASON_CANCEL);
+
+ // Should update show in shade state
+ assertTrue(mBubbleController.isBubbleNotificationSuppressedFromShade(
+ mRow.getEntry().getKey()));
+
+ // Should notify delegate that shade state changed
+ verify(listener).onBubbleNotificationSuppressionChange(
+ mBubbleData.getBubbleWithKey(mRow.getEntry().getKey()));
+ }
+
+ @Test
+ public void testNotifyShadeSuppressionChange_bubbleExpanded() {
+ BubbleController.NotificationSuppressionChangedListener listener =
+ mock(BubbleController.NotificationSuppressionChangedListener.class);
+ mBubbleData.setSuppressionChangedListener(listener);
+
+ mEntryListener.onNotificationAdded(mRow.getEntry());
+
+ assertTrue(mBubbleController.hasBubbles());
+ assertFalse(mBubbleController.isBubbleNotificationSuppressedFromShade(
+ mRow.getEntry().getKey()));
+
+ mBubbleData.setExpanded(true);
+
+ // Once a bubble is expanded the notif is suppressed
+ assertTrue(mBubbleController.isBubbleNotificationSuppressedFromShade(
+ mRow.getEntry().getKey()));
+
+ // Should notify delegate that shade state changed
+ verify(listener).onBubbleNotificationSuppressionChange(
+ mBubbleData.getBubbleWithKey(mRow.getEntry().getKey()));
+ }
+
static class TestableBubbleController extends BubbleController {
// Let's assume surfaces can be synchronized immediately.
TestableBubbleController(Context context,
diff --git a/packages/SystemUI/tests/src/com/android/systemui/bubbles/BubbleDataTest.java b/packages/SystemUI/tests/src/com/android/systemui/bubbles/BubbleDataTest.java
index 1a2e23796c78..c9f5b40e8f9f 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/bubbles/BubbleDataTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/bubbles/BubbleDataTest.java
@@ -104,6 +104,9 @@ public class BubbleDataTest extends SysuiTestCase {
@Captor
private ArgumentCaptor<BubbleData.Update> mUpdateCaptor;
+ @Mock
+ private BubbleController.NotificationSuppressionChangedListener mSuppressionListener;
+
@Before
public void setUp() throws Exception {
mNotificationTestHelper = new NotificationTestHelper(mContext, mDependency);
@@ -121,20 +124,20 @@ public class BubbleDataTest extends SysuiTestCase {
modifyRanking(mEntryInterruptive)
.setVisuallyInterruptive(true)
.build();
- mBubbleInterruptive = new Bubble(mEntryInterruptive);
+ mBubbleInterruptive = new Bubble(mEntryInterruptive, mSuppressionListener);
ExpandableNotificationRow row = mNotificationTestHelper.createBubble();
mEntryDismissed = createBubbleEntry(1, "dismissed", "package.d");
mEntryDismissed.setRow(row);
- mBubbleDismissed = new Bubble(mEntryDismissed);
+ mBubbleDismissed = new Bubble(mEntryDismissed, mSuppressionListener);
- mBubbleA1 = new Bubble(mEntryA1);
- mBubbleA2 = new Bubble(mEntryA2);
- mBubbleA3 = new Bubble(mEntryA3);
- mBubbleB1 = new Bubble(mEntryB1);
- mBubbleB2 = new Bubble(mEntryB2);
- mBubbleB3 = new Bubble(mEntryB3);
- mBubbleC1 = new Bubble(mEntryC1);
+ mBubbleA1 = new Bubble(mEntryA1, mSuppressionListener);
+ mBubbleA2 = new Bubble(mEntryA2, mSuppressionListener);
+ mBubbleA3 = new Bubble(mEntryA3, mSuppressionListener);
+ mBubbleB1 = new Bubble(mEntryB1, mSuppressionListener);
+ mBubbleB2 = new Bubble(mEntryB2, mSuppressionListener);
+ mBubbleB3 = new Bubble(mEntryB3, mSuppressionListener);
+ mBubbleC1 = new Bubble(mEntryC1, mSuppressionListener);
mBubbleData = new BubbleData(getContext());
@@ -237,9 +240,8 @@ public class BubbleDataTest extends SysuiTestCase {
true /* showInShade */);
verifyUpdateReceived();
- // Make it look like user swiped away row
- mEntryDismissed.getRow().dismiss(false /* refocusOnDismiss */);
- assertThat(mBubbleData.getBubbleWithKey(mBubbleDismissed.getKey()).showInShade()).isFalse();
+ // Suppress the notif / make it look dismissed
+ mBubbleDismissed.setSuppressNotification(true);
mBubbleData.notificationEntryUpdated(mBubbleDismissed, false /* suppressFlyout */,
true /* showInShade */);
diff --git a/packages/SystemUI/tests/src/com/android/systemui/bubbles/BubbleTest.java b/packages/SystemUI/tests/src/com/android/systemui/bubbles/BubbleTest.java
index 02f721ce58a7..7f67657e1109 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/bubbles/BubbleTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/bubbles/BubbleTest.java
@@ -16,11 +16,19 @@
package com.android.systemui.bubbles;
+import static com.google.common.truth.Truth.assertThat;
+
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNull;
+import static org.mockito.ArgumentMatchers.any;
import static org.mockito.Mockito.doReturn;
+import static org.mockito.Mockito.never;
+import static org.mockito.Mockito.verify;
import android.app.Notification;
+import android.app.PendingIntent;
+import android.content.Intent;
+import android.graphics.drawable.Icon;
import android.os.Bundle;
import android.testing.AndroidTestingRunner;
import android.testing.TestableLooper;
@@ -30,6 +38,7 @@ import androidx.test.filters.SmallTest;
import com.android.systemui.SysuiTestCase;
import com.android.systemui.statusbar.notification.collection.NotificationEntry;
import com.android.systemui.statusbar.notification.collection.NotificationEntryBuilder;
+import com.android.systemui.tests.R;
import org.junit.Before;
import org.junit.Test;
@@ -46,6 +55,10 @@ public class BubbleTest extends SysuiTestCase {
private NotificationEntry mEntry;
private Bundle mExtras;
+ private Bubble mBubble;
+
+ @Mock
+ private BubbleController.NotificationSuppressionChangedListener mSuppressionListener;
@Before
public void setUp() {
@@ -57,6 +70,15 @@ public class BubbleTest extends SysuiTestCase {
mEntry = new NotificationEntryBuilder()
.setNotification(mNotif)
.build();
+
+ mBubble = new Bubble(mEntry, mSuppressionListener);
+
+ Intent target = new Intent(mContext, BubblesTestActivity.class);
+ Notification.BubbleMetadata metadata = new Notification.BubbleMetadata.Builder()
+ .createIntentBubble(PendingIntent.getActivity(mContext, 0, target, 0),
+ Icon.createWithResource(mContext, R.drawable.android))
+ .build();
+ mEntry.setBubbleMetadata(metadata);
}
@Test
@@ -123,4 +145,24 @@ public class BubbleTest extends SysuiTestCase {
BubbleViewInfoTask.extractFlyoutMessage(mContext,
mEntry).senderName);
}
+
+ @Test
+ public void testSuppressionListener_change_notified() {
+ assertThat(mBubble.showInShade()).isTrue();
+
+ mBubble.setSuppressNotification(true);
+
+ assertThat(mBubble.showInShade()).isFalse();
+
+ verify(mSuppressionListener).onBubbleNotificationSuppressionChange(mBubble);
+ }
+
+ @Test
+ public void testSuppressionListener_noChange_doesntNotify() {
+ assertThat(mBubble.showInShade()).isTrue();
+
+ mBubble.setSuppressNotification(false);
+
+ verify(mSuppressionListener, never()).onBubbleNotificationSuppressionChange(any());
+ }
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/pip/PipBoundsHandlerTest.java b/packages/SystemUI/tests/src/com/android/systemui/pip/PipBoundsHandlerTest.java
new file mode 100644
index 000000000000..bc3ce8baddee
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/pip/PipBoundsHandlerTest.java
@@ -0,0 +1,240 @@
+/*
+ * 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;
+
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
+import static org.mockito.ArgumentMatchers.anyInt;
+import static org.mockito.ArgumentMatchers.eq;
+import static org.mockito.ArgumentMatchers.isNull;
+import static org.mockito.Mockito.reset;
+import static org.mockito.Mockito.verify;
+
+import android.content.ComponentName;
+import android.graphics.Rect;
+import android.testing.AndroidTestingRunner;
+import android.testing.TestableLooper;
+import android.testing.TestableResources;
+import android.view.DisplayInfo;
+import android.view.Gravity;
+import android.view.IPinnedStackController;
+
+import androidx.test.filters.SmallTest;
+
+import com.android.systemui.SysuiTestCase;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.ArgumentCaptor;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+
+/**
+ * Unit tests against {@link PipBoundsHandler}, including but not limited to:
+ * - default/movement bounds
+ * - save/restore PiP position on application lifecycle
+ * - save/restore PiP position on screen rotation
+ */
+@RunWith(AndroidTestingRunner.class)
+@SmallTest
+@TestableLooper.RunWithLooper(setAsMainLooper = true)
+public class PipBoundsHandlerTest extends SysuiTestCase {
+ private static final int ROUNDING_ERROR_MARGIN = 10;
+
+ private PipBoundsHandler mPipBoundsHandler;
+ private DisplayInfo mDefaultDisplayInfo;
+ private Rect mDefaultDisplayRect;
+
+ @Mock
+ private IPinnedStackController mPinnedStackController;
+
+ @Before
+ public void setUp() throws Exception {
+ mPipBoundsHandler = new PipBoundsHandler(mContext);
+ MockitoAnnotations.initMocks(this);
+ initializeMockResources();
+
+ mPipBoundsHandler.onDisplayInfoChanged(mDefaultDisplayInfo);
+ mPipBoundsHandler.setPinnedStackController(mPinnedStackController);
+ }
+
+ private void initializeMockResources() {
+ final TestableResources res = mContext.getOrCreateTestableResources();
+ res.addOverride(
+ com.android.internal.R.dimen.config_pictureInPictureDefaultAspectRatio, 1f);
+ res.addOverride(
+ com.android.internal.R.integer.config_defaultPictureInPictureGravity,
+ Gravity.END | Gravity.BOTTOM);
+ res.addOverride(
+ com.android.internal.R.dimen.default_minimal_size_pip_resizable_task, 100);
+ res.addOverride(
+ com.android.internal.R.string.config_defaultPictureInPictureScreenEdgeInsets,
+ "16x16");
+ res.addOverride(
+ com.android.internal.R.dimen.config_pictureInPictureMinAspectRatio, 0.5f);
+ res.addOverride(
+ com.android.internal.R.dimen.config_pictureInPictureMaxAspectRatio, 2f);
+
+ mDefaultDisplayInfo = new DisplayInfo();
+ mDefaultDisplayInfo.displayId = 1;
+ mDefaultDisplayInfo.logicalWidth = 1000;
+ mDefaultDisplayInfo.logicalHeight = 1500;
+ mDefaultDisplayRect = new Rect(0, 0,
+ mDefaultDisplayInfo.logicalWidth, mDefaultDisplayInfo.logicalHeight);
+ }
+
+ @Test
+ public void setShelfHeight_offsetBounds() throws Exception {
+ final ArgumentCaptor<Rect> destinationBounds = ArgumentCaptor.forClass(Rect.class);
+ final int shelfHeight = 100;
+
+ mPipBoundsHandler.onPrepareAnimation(null, 1f, null);
+
+ verify(mPinnedStackController).startAnimation(
+ destinationBounds.capture(), isNull(), anyInt());
+ final Rect lastPosition = destinationBounds.getValue();
+ // Reset the pinned stack controller since we will do another verify later on
+ reset(mPinnedStackController);
+
+ mPipBoundsHandler.setShelfHeight(true, shelfHeight);
+ mPipBoundsHandler.onPrepareAnimation(null, 1f, null);
+
+ verify(mPinnedStackController).startAnimation(
+ destinationBounds.capture(), isNull(), anyInt());
+ lastPosition.offset(0, -shelfHeight);
+ assertBoundsWithMargin("PiP bounds offset by shelf height",
+ lastPosition, destinationBounds.getValue());
+ }
+
+ @Test
+ public void onImeVisibilityChanged_offsetBounds() throws Exception {
+ final ArgumentCaptor<Rect> destinationBounds = ArgumentCaptor.forClass(Rect.class);
+ final int imeHeight = 100;
+
+ mPipBoundsHandler.onPrepareAnimation(null, 1f, null);
+
+ verify(mPinnedStackController).startAnimation(
+ destinationBounds.capture(), isNull(), anyInt());
+ final Rect lastPosition = destinationBounds.getValue();
+ // Reset the pinned stack controller since we will do another verify later on
+ reset(mPinnedStackController);
+
+ mPipBoundsHandler.onImeVisibilityChanged(true, imeHeight);
+ mPipBoundsHandler.onPrepareAnimation(null, 1f, null);
+
+ verify(mPinnedStackController).startAnimation(
+ destinationBounds.capture(), isNull(), anyInt());
+ lastPosition.offset(0, -imeHeight);
+ assertBoundsWithMargin("PiP bounds offset by IME height",
+ lastPosition, destinationBounds.getValue());
+ }
+
+ @Test
+ public void onPrepareAnimation_startAnimation() throws Exception {
+ final Rect sourceRectHint = new Rect(100, 100, 200, 200);
+ final ArgumentCaptor<Rect> destinationBounds = ArgumentCaptor.forClass(Rect.class);
+
+ mPipBoundsHandler.onPrepareAnimation(sourceRectHint, 1f, null);
+
+ verify(mPinnedStackController).startAnimation(
+ destinationBounds.capture(), eq(sourceRectHint), anyInt());
+ final Rect capturedDestinationBounds = destinationBounds.getValue();
+ assertFalse("Destination bounds is not empty",
+ capturedDestinationBounds.isEmpty());
+ assertBoundsWithMargin("Destination bounds within Display",
+ mDefaultDisplayRect, capturedDestinationBounds);
+ }
+
+ @Test
+ public void onSaveReentryBounds_restoreLastPosition() throws Exception {
+ final ComponentName componentName = new ComponentName(mContext, "component1");
+ final ArgumentCaptor<Rect> destinationBounds = ArgumentCaptor.forClass(Rect.class);
+
+ mPipBoundsHandler.onPrepareAnimation(null, 1f, null);
+
+ verify(mPinnedStackController).startAnimation(
+ destinationBounds.capture(), isNull(), anyInt());
+ final Rect lastPosition = destinationBounds.getValue();
+ lastPosition.offset(0, -100);
+ mPipBoundsHandler.onSaveReentryBounds(componentName, lastPosition);
+ // Reset the pinned stack controller since we will do another verify later on
+ reset(mPinnedStackController);
+
+ mPipBoundsHandler.onPrepareAnimation(null, 1f, null);
+
+ verify(mPinnedStackController).startAnimation(
+ destinationBounds.capture(), isNull(), anyInt());
+ assertBoundsWithMargin("Last position is restored",
+ lastPosition, destinationBounds.getValue());
+ }
+
+ @Test
+ public void onResetReentryBounds_componentMatch_useDefaultBounds() throws Exception {
+ final ComponentName componentName = new ComponentName(mContext, "component1");
+ final ArgumentCaptor<Rect> destinationBounds = ArgumentCaptor.forClass(Rect.class);
+
+ mPipBoundsHandler.onPrepareAnimation(null, 1f, null);
+
+ verify(mPinnedStackController).startAnimation(
+ destinationBounds.capture(), isNull(), anyInt());
+ final Rect defaultBounds = new Rect(destinationBounds.getValue());
+ final Rect newBounds = new Rect(defaultBounds);
+ newBounds.offset(0, -100);
+ mPipBoundsHandler.onSaveReentryBounds(componentName, newBounds);
+ // Reset the pinned stack controller since we will do another verify later on
+ reset(mPinnedStackController);
+
+ mPipBoundsHandler.onResetReentryBounds(componentName);
+ mPipBoundsHandler.onPrepareAnimation(null, 1f, null);
+
+ verify(mPinnedStackController).startAnimation(
+ destinationBounds.capture(), isNull(), anyInt());
+ final Rect actualBounds = destinationBounds.getValue();
+ assertBoundsWithMargin("Use default bounds", defaultBounds, actualBounds);
+ }
+
+ @Test
+ public void onResetReentryBounds_componentMismatch_restoreLastPosition() throws Exception {
+ final ComponentName componentName = new ComponentName(mContext, "component1");
+ final ArgumentCaptor<Rect> destinationBounds = ArgumentCaptor.forClass(Rect.class);
+
+ mPipBoundsHandler.onPrepareAnimation(null, 1f, null);
+
+ verify(mPinnedStackController).startAnimation(
+ destinationBounds.capture(), isNull(), anyInt());
+ final Rect defaultBounds = new Rect(destinationBounds.getValue());
+ final Rect newBounds = new Rect(defaultBounds);
+ newBounds.offset(0, -100);
+ mPipBoundsHandler.onSaveReentryBounds(componentName, newBounds);
+ // Reset the pinned stack controller since we will do another verify later on
+ reset(mPinnedStackController);
+
+ mPipBoundsHandler.onResetReentryBounds(new ComponentName(mContext, "component2"));
+ mPipBoundsHandler.onPrepareAnimation(null, 1f, null);
+
+ verify(mPinnedStackController).startAnimation(
+ destinationBounds.capture(), isNull(), anyInt());
+ final Rect actualBounds = destinationBounds.getValue();
+ assertBoundsWithMargin("Last position is restored", newBounds, actualBounds);
+ }
+
+ private void assertBoundsWithMargin(String msg, Rect expected, Rect actual) {
+ expected.inset(-ROUNDING_ERROR_MARGIN, -ROUNDING_ERROR_MARGIN);
+ assertTrue(msg, expected.contains(actual));
+ }
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/NotifCollectionTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/NotifCollectionTest.java
index fe8d76923141..9a7e97b5d55a 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/NotifCollectionTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/NotifCollectionTest.java
@@ -33,6 +33,7 @@ import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.never;
import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
import static java.util.Objects.requireNonNull;
@@ -51,6 +52,7 @@ import com.android.internal.statusbar.IStatusBarService;
import com.android.internal.statusbar.NotificationVisibility;
import com.android.systemui.DumpController;
import com.android.systemui.SysuiTestCase;
+import com.android.systemui.statusbar.FeatureFlags;
import com.android.systemui.statusbar.RankingBuilder;
import com.android.systemui.statusbar.notification.collection.NoManSimulator.NotifEvent;
import com.android.systemui.statusbar.notification.collection.NotifCollection.CancellationReason;
@@ -60,6 +62,7 @@ import com.android.systemui.statusbar.notification.collection.coalescer.GroupCoa
import com.android.systemui.statusbar.notification.collection.notifcollection.CollectionReadyForBuildListener;
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.notifcollection.NotifCollectionLogger;
import com.android.systemui.statusbar.notification.collection.notifcollection.NotifLifetimeExtender;
import com.android.systemui.util.Assert;
@@ -83,9 +86,11 @@ import java.util.Map;
public class NotifCollectionTest extends SysuiTestCase {
@Mock private IStatusBarService mStatusBarService;
+ @Mock private NotifCollectionLogger mLogger;
@Mock private GroupCoalescer mGroupCoalescer;
@Spy private RecordingCollectionListener mCollectionListener;
@Mock private CollectionReadyForBuildListener mBuildListener;
+ @Mock private FeatureFlags mFeatureFlags;
@Spy private RecordingLifetimeExtender mExtender1 = new RecordingLifetimeExtender("Extender1");
@Spy private RecordingLifetimeExtender mExtender2 = new RecordingLifetimeExtender("Extender2");
@@ -105,7 +110,14 @@ public class NotifCollectionTest extends SysuiTestCase {
MockitoAnnotations.initMocks(this);
Assert.sMainLooper = TestableLooper.get(this).getLooper();
- mCollection = new NotifCollection(mStatusBarService, mock(DumpController.class));
+ when(mFeatureFlags.isNewNotifPipelineRenderingEnabled()).thenReturn(true);
+ when(mFeatureFlags.isNewNotifPipelineEnabled()).thenReturn(true);
+
+ mCollection = new NotifCollection(
+ mStatusBarService,
+ mock(DumpController.class),
+ mFeatureFlags,
+ mLogger);
mCollection.attach(mGroupCoalescer);
mCollection.addCollectionListener(mCollectionListener);
mCollection.setBuildListener(mBuildListener);
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/ShadeListBuilderTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/ShadeListBuilderTest.java
index e915be37705b..18f133f09811 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/ShadeListBuilderTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/ShadeListBuilderTest.java
@@ -45,12 +45,12 @@ import com.android.systemui.statusbar.notification.collection.ShadeListBuilder.O
import com.android.systemui.statusbar.notification.collection.listbuilder.OnBeforeRenderListListener;
import com.android.systemui.statusbar.notification.collection.listbuilder.OnBeforeSortListener;
import com.android.systemui.statusbar.notification.collection.listbuilder.OnBeforeTransformGroupsListener;
+import com.android.systemui.statusbar.notification.collection.listbuilder.ShadeListBuilderLogger;
import com.android.systemui.statusbar.notification.collection.listbuilder.pluggable.NotifComparator;
import com.android.systemui.statusbar.notification.collection.listbuilder.pluggable.NotifFilter;
import com.android.systemui.statusbar.notification.collection.listbuilder.pluggable.NotifPromoter;
import com.android.systemui.statusbar.notification.collection.listbuilder.pluggable.NotifSection;
import com.android.systemui.statusbar.notification.collection.notifcollection.CollectionReadyForBuildListener;
-import com.android.systemui.statusbar.notification.logging.NotifLog;
import com.android.systemui.util.Assert;
import com.android.systemui.util.time.FakeSystemClock;
@@ -81,7 +81,7 @@ public class ShadeListBuilderTest extends SysuiTestCase {
private ShadeListBuilder mListBuilder;
private FakeSystemClock mSystemClock = new FakeSystemClock();
- @Mock private NotifLog mNotifLog;
+ @Mock private ShadeListBuilderLogger mLogger;
@Mock private NotifCollection mNotifCollection;
@Spy private OnBeforeTransformGroupsListener mOnBeforeTransformGroupsListener;
@Spy private OnBeforeSortListener mOnBeforeSortListener;
@@ -103,7 +103,7 @@ public class ShadeListBuilderTest extends SysuiTestCase {
MockitoAnnotations.initMocks(this);
Assert.sMainLooper = TestableLooper.get(this).getLooper();
- mListBuilder = new ShadeListBuilder(mSystemClock, mNotifLog, mock(DumpController.class));
+ mListBuilder = new ShadeListBuilder(mSystemClock, mLogger, mock(DumpController.class));
mListBuilder.setOnRenderListListener(mOnRenderListListener);
mListBuilder.attach(mNotifCollection);
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/coalescer/GroupCoalescerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/coalescer/GroupCoalescerTest.java
index 86c1eb97d186..ac9a57022f53 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/coalescer/GroupCoalescerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/coalescer/GroupCoalescerTest.java
@@ -16,8 +16,6 @@
package com.android.systemui.statusbar.notification.collection.coalescer;
-import static com.android.internal.util.Preconditions.checkNotNull;
-
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.anyList;
import static org.mockito.Mockito.clearInvocations;
@@ -25,6 +23,8 @@ import static org.mockito.Mockito.inOrder;
import static org.mockito.Mockito.never;
import static org.mockito.Mockito.verify;
+import static java.util.Objects.requireNonNull;
+
import android.service.notification.NotificationListenerService.Ranking;
import android.service.notification.NotificationListenerService.RankingMap;
import android.service.notification.StatusBarNotification;
@@ -39,7 +39,6 @@ import com.android.systemui.statusbar.RankingBuilder;
import com.android.systemui.statusbar.notification.collection.NoManSimulator;
import com.android.systemui.statusbar.notification.collection.NoManSimulator.NotifEvent;
import com.android.systemui.statusbar.notification.collection.NotificationEntryBuilder;
-import com.android.systemui.statusbar.notification.logging.NotifLog;
import com.android.systemui.util.concurrency.FakeExecutor;
import com.android.systemui.util.time.FakeSystemClock;
@@ -63,7 +62,7 @@ public class GroupCoalescerTest extends SysuiTestCase {
@Mock private NotificationListener mListenerService;
@Mock private GroupCoalescer.BatchableNotificationHandler mListener;
- @Mock private NotifLog mLog;
+ @Mock private GroupCoalescerLogger mLogger;
@Captor private ArgumentCaptor<NotificationHandler> mListenerCaptor;
@@ -79,14 +78,14 @@ public class GroupCoalescerTest extends SysuiTestCase {
new GroupCoalescer(
mExecutor,
mClock,
- mLog,
+ mLogger,
MIN_LINGER_DURATION,
MAX_LINGER_DURATION);
mCoalescer.setNotificationHandler(mListener);
mCoalescer.attach(mListenerService);
verify(mListenerService).addNotificationHandler(mListenerCaptor.capture());
- NotificationHandler serviceListener = checkNotNull(mListenerCaptor.getValue());
+ NotificationHandler serviceListener = requireNonNull(mListenerCaptor.getValue());
mNoMan.addListener(serviceListener);
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationConversationInfoTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationConversationInfoTest.java
index 5fc40ccb6c8a..20a089f97f51 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationConversationInfoTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationConversationInfoTest.java
@@ -58,7 +58,6 @@ import android.service.notification.StatusBarNotification;
import android.test.suitebuilder.annotation.SmallTest;
import android.testing.AndroidTestingRunner;
import android.testing.TestableLooper;
-import android.util.Slog;
import android.view.LayoutInflater;
import android.view.View;
import android.widget.Button;
@@ -601,6 +600,34 @@ public class NotificationConversationInfoTest extends SysuiTestCase {
}
@Test
+ public void testBubble_noChannelChange() throws Exception {
+ mNotificationInfo.bindNotification(
+ mShortcutManager,
+ mLauncherApps,
+ mMockPackageManager,
+ mMockINotificationManager,
+ mVisualStabilityManager,
+ TEST_PACKAGE_NAME,
+ mNotificationChannel,
+ mBubbleEntry,
+ null,
+ null,
+ null,
+ true);
+
+ assertFalse(mBubbleEntry.isBubble());
+ assertTrue(mNotificationChannel.canBubble());
+
+ // Promote it
+ mNotificationInfo.findViewById(R.id.bubble).performClick();
+ mTestableLooper.processAllMessages();
+
+ verify(mBubbleController, times(1)).onUserCreatedBubbleFromNotification(mBubbleEntry);
+ verify(mMockINotificationManager, never()).updateNotificationChannelForPackage(
+ anyString(), anyInt(), any());
+ }
+
+ @Test
public void testFavorite_favorite() throws Exception {
mNotificationInfo.bindNotification(
mShortcutManager,
diff --git a/packages/Tethering/src/android/net/dhcp/DhcpServerCallbacks.java b/packages/Tethering/src/android/net/dhcp/DhcpServerCallbacks.java
index 7c41377985d3..9fda1257b4c9 100644
--- a/packages/Tethering/src/android/net/dhcp/DhcpServerCallbacks.java
+++ b/packages/Tethering/src/android/net/dhcp/DhcpServerCallbacks.java
@@ -28,4 +28,9 @@ public abstract class DhcpServerCallbacks extends IDhcpServerCallbacks.Stub {
public int getInterfaceVersion() {
return IDhcpServerCallbacks.VERSION;
}
+
+ @Override
+ public String getInterfaceHash() {
+ return IDhcpServerCallbacks.HASH;
+ }
}
diff --git a/packages/Tethering/src/android/net/ip/IpServer.java b/packages/Tethering/src/android/net/ip/IpServer.java
index 190d25098644..f39e7af43ee6 100644
--- a/packages/Tethering/src/android/net/ip/IpServer.java
+++ b/packages/Tethering/src/android/net/ip/IpServer.java
@@ -301,6 +301,11 @@ public class IpServer extends StateMachine {
public int getInterfaceVersion() {
return this.VERSION;
}
+
+ @Override
+ public String getInterfaceHash() {
+ return this.HASH;
+ }
}
private class DhcpServerCallbacksImpl extends DhcpServerCallbacks {
diff --git a/packages/Tethering/src/android/net/util/BaseNetdUnsolicitedEventListener.java b/packages/Tethering/src/android/net/util/BaseNetdUnsolicitedEventListener.java
index 3218c0b387bb..b1ffdb01f5f3 100644
--- a/packages/Tethering/src/android/net/util/BaseNetdUnsolicitedEventListener.java
+++ b/packages/Tethering/src/android/net/util/BaseNetdUnsolicitedEventListener.java
@@ -67,4 +67,9 @@ public class BaseNetdUnsolicitedEventListener extends INetdUnsolicitedEventListe
public int getInterfaceVersion() {
return INetdUnsolicitedEventListener.VERSION;
}
+
+ @Override
+ public String getInterfaceHash() {
+ return INetdUnsolicitedEventListener.HASH;
+ }
}
diff --git a/services/autofill/java/com/android/server/autofill/AutofillManagerServiceImpl.java b/services/autofill/java/com/android/server/autofill/AutofillManagerServiceImpl.java
index bf801fc93809..e28ef0f920e9 100644
--- a/services/autofill/java/com/android/server/autofill/AutofillManagerServiceImpl.java
+++ b/services/autofill/java/com/android/server/autofill/AutofillManagerServiceImpl.java
@@ -57,6 +57,7 @@ import android.service.autofill.FillEventHistory;
import android.service.autofill.FillEventHistory.Event;
import android.service.autofill.FillResponse;
import android.service.autofill.IAutoFillService;
+import android.service.autofill.InlineSuggestionRenderService;
import android.service.autofill.SaveInfo;
import android.service.autofill.UserData;
import android.util.ArrayMap;
@@ -117,6 +118,7 @@ final class AutofillManagerServiceImpl
private final LocalLog mUiLatencyHistory;
private final LocalLog mWtfHistory;
private final FieldClassificationStrategy mFieldClassificationStrategy;
+ private RemoteInlineSuggestionRenderService mRemoteInlineSuggestionRenderService;
/**
* Apps disabled by the service; key is package name, value is when they will be enabled again.
@@ -212,6 +214,17 @@ final class AutofillManagerServiceImpl
sendStateToClients(/* resetClient= */ false);
}
updateRemoteAugmentedAutofillService();
+
+ final ComponentName componentName = RemoteInlineSuggestionRenderService
+ .getServiceComponentName(getContext());
+ if (componentName != null) {
+ mRemoteInlineSuggestionRenderService = new RemoteInlineSuggestionRenderService(
+ getContext(), componentName, InlineSuggestionRenderService.SERVICE_INTERFACE,
+ mUserId, new InlineSuggestionRenderCallbacksImpl(),
+ mMaster.isBindInstantServiceAllowed(), mMaster.verbose);
+ } else {
+ mRemoteInlineSuggestionRenderService = null;
+ }
return enabledChanged;
}
@@ -1542,6 +1555,21 @@ final class AutofillManagerServiceImpl
return mFieldClassificationStrategy.getDefaultAlgorithm();
}
+ RemoteInlineSuggestionRenderService getRemoteInlineSuggestionRenderService() {
+ return mRemoteInlineSuggestionRenderService;
+ }
+
+ private class InlineSuggestionRenderCallbacksImpl implements
+ RemoteInlineSuggestionRenderService.InlineSuggestionRenderCallbacks {
+
+ @Override // from InlineSuggestionRenderCallbacksImpl
+ public void onServiceDied(@NonNull RemoteInlineSuggestionRenderService service) {
+ // Don't do anything; eventually the system will bind to it again...
+ Slog.w(TAG, "remote service died: " + service);
+ mRemoteInlineSuggestionRenderService = null;
+ }
+ }
+
@Override
public String toString() {
return "AutofillManagerServiceImpl: [userId=" + mUserId
diff --git a/services/autofill/java/com/android/server/autofill/RemoteInlineSuggestionRenderService.java b/services/autofill/java/com/android/server/autofill/RemoteInlineSuggestionRenderService.java
new file mode 100644
index 000000000000..f9e08e683b6c
--- /dev/null
+++ b/services/autofill/java/com/android/server/autofill/RemoteInlineSuggestionRenderService.java
@@ -0,0 +1,135 @@
+/*
+ * 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.autofill;
+
+import static com.android.server.autofill.Helper.sVerbose;
+
+import android.Manifest;
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.content.ComponentName;
+import android.content.Context;
+import android.content.Intent;
+import android.content.pm.PackageManager;
+import android.content.pm.ResolveInfo;
+import android.content.pm.ServiceInfo;
+import android.os.IBinder;
+import android.service.autofill.IInlineSuggestionRenderService;
+import android.service.autofill.IInlineSuggestionUiCallback;
+import android.service.autofill.InlinePresentation;
+import android.service.autofill.InlineSuggestionRenderService;
+import android.util.Slog;
+
+import com.android.internal.infra.AbstractMultiplePendingRequestsRemoteService;
+
+final class RemoteInlineSuggestionRenderService extends
+ AbstractMultiplePendingRequestsRemoteService<RemoteInlineSuggestionRenderService,
+ IInlineSuggestionRenderService> {
+
+ private static final String TAG = "RemoteInlineSuggestionRenderService";
+
+ private final int mIdleUnbindTimeoutMs = 5000;
+
+ RemoteInlineSuggestionRenderService(Context context, ComponentName componentName,
+ String serviceInterface, int userId, InlineSuggestionRenderCallbacks callback,
+ boolean bindInstantServiceAllowed, boolean verbose) {
+ super(context, serviceInterface, componentName, userId, callback,
+ context.getMainThreadHandler(),
+ bindInstantServiceAllowed ? Context.BIND_ALLOW_INSTANT : 0, verbose,
+ /* initialCapacity= */ 2);
+
+ ensureBound();
+ }
+
+ @Override // from AbstractRemoteService
+ protected IInlineSuggestionRenderService getServiceInterface(@NonNull IBinder service) {
+ return IInlineSuggestionRenderService.Stub.asInterface(service);
+ }
+
+ @Override // from AbstractRemoteService
+ protected long getTimeoutIdleBindMillis() {
+ return mIdleUnbindTimeoutMs;
+ }
+
+ @Override // from AbstractRemoteService
+ protected void handleOnConnectedStateChanged(boolean connected) {
+ if (connected && getTimeoutIdleBindMillis() != PERMANENT_BOUND_TIMEOUT_MS) {
+ scheduleUnbind();
+ }
+ super.handleOnConnectedStateChanged(connected);
+ }
+
+ public void ensureBound() {
+ scheduleBind();
+ }
+
+ /**
+ * Called by {@link Session} to generate a call to the
+ * {@link RemoteInlineSuggestionRenderService} to request rendering a slice .
+ */
+ void renderSuggestion(@NonNull IInlineSuggestionUiCallback callback,
+ @NonNull InlinePresentation presentation, int width, int height) {
+ scheduleAsyncRequest((s) -> s.renderSuggestion(callback, presentation, width, height));
+ }
+
+ @Nullable
+ private static ServiceInfo getServiceInfo(Context context) {
+ final String packageName =
+ context.getPackageManager().getServicesSystemSharedLibraryPackageName();
+ if (packageName == null) {
+ Slog.w(TAG, "no external services package!");
+ return null;
+ }
+
+ final Intent intent = new Intent(InlineSuggestionRenderService.SERVICE_INTERFACE);
+ intent.setPackage(packageName);
+ final ResolveInfo resolveInfo = context.getPackageManager().resolveService(intent,
+ PackageManager.GET_SERVICES | PackageManager.GET_META_DATA);
+ final ServiceInfo serviceInfo = resolveInfo == null ? null : resolveInfo.serviceInfo;
+ if (resolveInfo == null || serviceInfo == null) {
+ Slog.w(TAG, "No valid components found.");
+ return null;
+ }
+
+ if (!Manifest.permission.BIND_INLINE_SUGGESTION_RENDER_SERVICE
+ .equals(serviceInfo.permission)) {
+ Slog.w(TAG, serviceInfo.name + " does not require permission "
+ + Manifest.permission.BIND_INLINE_SUGGESTION_RENDER_SERVICE);
+ return null;
+ }
+
+ return serviceInfo;
+ }
+
+ @Nullable
+ public static ComponentName getServiceComponentName(Context context) {
+ final ServiceInfo serviceInfo = getServiceInfo(context);
+ if (serviceInfo == null) return null;
+
+ final ComponentName componentName = new ComponentName(serviceInfo.packageName,
+ serviceInfo.name);
+
+ if (sVerbose) Slog.v(TAG, "getServiceComponentName(): " + componentName);
+ return componentName;
+ }
+
+ interface InlineSuggestionRenderCallbacks
+ extends VultureCallback<RemoteInlineSuggestionRenderService> {
+ // NOTE: so far we don't need to notify the callback implementation
+ // (AutofillManagerServiceImpl) of the request results (success, timeouts, etc..), so this
+ // callback interface is empty.
+ }
+}
diff --git a/services/backup/java/com/android/server/backup/UserBackupManagerService.java b/services/backup/java/com/android/server/backup/UserBackupManagerService.java
index f0fa99a4eec7..e3d2dcc8141f 100644
--- a/services/backup/java/com/android/server/backup/UserBackupManagerService.java
+++ b/services/backup/java/com/android/server/backup/UserBackupManagerService.java
@@ -186,6 +186,7 @@ public class UserBackupManagerService {
return;
}
mPowerManagerWakeLock.acquire();
+ Slog.v(TAG, "Acquired wakelock:" + mPowerManagerWakeLock.getTag());
}
/** Releases the {@link PowerManager.WakeLock} if hasn't been quit. */
@@ -195,6 +196,7 @@ public class UserBackupManagerService {
return;
}
mPowerManagerWakeLock.release();
+ Slog.v(TAG, "Released wakelock:" + mPowerManagerWakeLock.getTag());
}
/**
diff --git a/services/backup/java/com/android/server/backup/restore/PerformUnifiedRestoreTask.java b/services/backup/java/com/android/server/backup/restore/PerformUnifiedRestoreTask.java
index 16484df9f328..f90d936a5f4d 100644
--- a/services/backup/java/com/android/server/backup/restore/PerformUnifiedRestoreTask.java
+++ b/services/backup/java/com/android/server/backup/restore/PerformUnifiedRestoreTask.java
@@ -76,6 +76,7 @@ import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.util.ArrayList;
+import java.util.Collections;
import java.util.List;
import java.util.Map;
import java.util.Objects;
@@ -697,11 +698,7 @@ public class PerformUnifiedRestoreTask implements BackupRestoreTask {
mStageName = new File(backupManagerService.getDataDir(), packageName + ".stage");
mNewStateName = new File(mStateDir, packageName + ".new");
- // don't stage the 'android' package where the wallpaper data lives. this is
- // an optimization: we know there's no widget data hosted/published by that
- // package, and this way we avoid doing a spurious copy of MB-sized wallpaper
- // data following the download.
- boolean staging = !packageName.equals(PLATFORM_PACKAGE_NAME);
+ boolean staging = shouldStageBackupData(packageName);
ParcelFileDescriptor stage;
File downloadFile = (staging) ? mStageName : mBackupDataName;
boolean startedAgentRestore = false;
@@ -768,8 +765,7 @@ public class PerformUnifiedRestoreTask implements BackupRestoreTask {
startedAgentRestore = true;
mAgent.doRestoreWithExcludedKeys(mBackupData, appVersionCode, mNewState,
mEphemeralOpToken, backupManagerService.getBackupManagerBinder(),
- mExcludedKeys.containsKey(packageName)
- ? new ArrayList<>(mExcludedKeys.get(packageName)) : null);
+ new ArrayList<>(getExcludedKeysForPackage(packageName)));
} catch (Exception e) {
Slog.e(TAG, "Unable to call app for restore: " + packageName, e);
EventLog.writeEvent(EventLogTags.RESTORE_AGENT_FAILURE,
@@ -785,9 +781,24 @@ public class PerformUnifiedRestoreTask implements BackupRestoreTask {
}
@VisibleForTesting
+ boolean shouldStageBackupData(String packageName) {
+ // Backup data is staged for 2 reasons:
+ // 1. We might need to exclude keys from the data before passing it to the agent
+ // 2. Widget metadata needs to be separated from the rest to be handled separately
+ // But 'android' package doesn't contain widget metadata so we want to skip staging for it
+ // when there are no keys to be excluded either.
+ return !packageName.equals(PLATFORM_PACKAGE_NAME) ||
+ !getExcludedKeysForPackage(PLATFORM_PACKAGE_NAME).isEmpty();
+ }
+
+ private Set<String> getExcludedKeysForPackage(String packageName) {
+ return mExcludedKeys.getOrDefault(packageName, Collections.emptySet());
+ }
+
+ @VisibleForTesting
void filterExcludedKeys(String packageName, BackupDataInput in, BackupDataOutput out)
throws Exception {
- Set<String> excludedKeysForPackage = mExcludedKeys.get(packageName);
+ Set<String> excludedKeysForPackage = getExcludedKeysForPackage(packageName);
byte[] buffer = new byte[8192]; // will grow when needed
while (in.readNextHeader()) {
diff --git a/services/contentcapture/java/com/android/server/contentcapture/ContentCaptureManagerService.java b/services/contentcapture/java/com/android/server/contentcapture/ContentCaptureManagerService.java
index 9245a1da43b2..5602d1a851e8 100644
--- a/services/contentcapture/java/com/android/server/contentcapture/ContentCaptureManagerService.java
+++ b/services/contentcapture/java/com/android/server/contentcapture/ContentCaptureManagerService.java
@@ -92,6 +92,7 @@ import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.io.PrintWriter;
+import java.lang.ref.WeakReference;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
@@ -109,6 +110,7 @@ import java.util.concurrent.Executors;
public final class ContentCaptureManagerService extends
AbstractMasterSystemService<ContentCaptureManagerService, ContentCapturePerUserService> {
+ private static final String TAG = ContentCaptureManagerService.class.getSimpleName();
static final String RECEIVER_BUNDLE_EXTRA_SESSIONS = "sessions";
private static final int MAX_TEMP_SERVICE_DURATION_MS = 1_000 * 60 * 2; // 2 minutes
@@ -167,11 +169,11 @@ public final class ContentCaptureManagerService extends
setDeviceConfigProperties();
if (mDevCfgLogHistorySize > 0) {
- if (debug) Slog.d(mTag, "log history size: " + mDevCfgLogHistorySize);
+ if (debug) Slog.d(TAG, "log history size: " + mDevCfgLogHistorySize);
mRequestsHistory = new LocalLog(mDevCfgLogHistorySize);
} else {
if (debug) {
- Slog.d(mTag, "disabled log history because size is " + mDevCfgLogHistorySize);
+ Slog.d(TAG, "disabled log history because size is " + mDevCfgLogHistorySize);
}
mRequestsHistory = null;
}
@@ -182,7 +184,7 @@ public final class ContentCaptureManagerService extends
final boolean disabled = !isEnabledBySettings(userId);
// Sets which services are disabled by settings
if (disabled) {
- Slog.i(mTag, "user " + userId + " disabled by settings");
+ Slog.i(TAG, "user " + userId + " disabled by settings");
if (mDisabledBySettings == null) {
mDisabledBySettings = new SparseBooleanArray(1);
}
@@ -245,7 +247,7 @@ public final class ContentCaptureManagerService extends
@Override // from AbstractMasterSystemService
protected void enforceCallingPermissionForManagement() {
- getContext().enforceCallingPermission(MANAGE_CONTENT_CAPTURE, mTag);
+ getContext().enforceCallingPermission(MANAGE_CONTENT_CAPTURE, TAG);
}
@Override // from AbstractMasterSystemService
@@ -269,7 +271,7 @@ public final class ContentCaptureManagerService extends
isEnabledBySettings(userId));
return;
default:
- Slog.w(mTag, "Unexpected property (" + property + "); updating cache instead");
+ Slog.w(TAG, "Unexpected property (" + property + "); updating cache instead");
}
}
@@ -306,7 +308,7 @@ public final class ContentCaptureManagerService extends
setFineTuneParamsFromDeviceConfig();
return;
default:
- Slog.i(mTag, "Ignoring change on " + key);
+ Slog.i(TAG, "Ignoring change on " + key);
}
}
}
@@ -333,7 +335,7 @@ public final class ContentCaptureManagerService extends
ContentCaptureManager.DEVICE_CONFIG_PROPERTY_IDLE_UNBIND_TIMEOUT,
(int) AbstractRemoteService.PERMANENT_BOUND_TIMEOUT_MS);
if (verbose) {
- Slog.v(mTag, "setFineTuneParamsFromDeviceConfig(): "
+ Slog.v(TAG, "setFineTuneParamsFromDeviceConfig(): "
+ "bufferSize=" + mDevCfgMaxBufferSize
+ ", idleFlush=" + mDevCfgIdleFlushingFrequencyMs
+ ", textFluxh=" + mDevCfgTextChangeFlushingFrequencyMs
@@ -352,7 +354,7 @@ public final class ContentCaptureManagerService extends
verbose = ContentCaptureHelper.sVerbose;
debug = ContentCaptureHelper.sDebug;
if (verbose) {
- Slog.v(mTag, "setLoggingLevelFromDeviceConfig(): level=" + mDevCfgLoggingLevel
+ Slog.v(TAG, "setLoggingLevelFromDeviceConfig(): level=" + mDevCfgLoggingLevel
+ ", debug=" + debug + ", verbose=" + verbose);
}
}
@@ -367,7 +369,7 @@ public final class ContentCaptureManagerService extends
private void setDisabledByDeviceConfig(@Nullable String explicitlyEnabled) {
if (verbose) {
- Slog.v(mTag, "setDisabledByDeviceConfig(): explicitlyEnabled=" + explicitlyEnabled);
+ Slog.v(TAG, "setDisabledByDeviceConfig(): explicitlyEnabled=" + explicitlyEnabled);
}
final List<UserInfo> users = getSupportedUsers();
@@ -382,17 +384,17 @@ public final class ContentCaptureManagerService extends
synchronized (mLock) {
if (mDisabledByDeviceConfig == newDisabledValue) {
if (verbose) {
- Slog.v(mTag, "setDisabledByDeviceConfig(): already " + newDisabledValue);
+ Slog.v(TAG, "setDisabledByDeviceConfig(): already " + newDisabledValue);
}
return;
}
mDisabledByDeviceConfig = newDisabledValue;
- Slog.i(mTag, "setDisabledByDeviceConfig(): set to " + mDisabledByDeviceConfig);
+ Slog.i(TAG, "setDisabledByDeviceConfig(): set to " + mDisabledByDeviceConfig);
for (int i = 0; i < users.size(); i++) {
final int userId = users.get(i).id;
boolean disabled = mDisabledByDeviceConfig || isDisabledBySettingsLocked(userId);
- Slog.i(mTag, "setDisabledByDeviceConfig(): updating service for user "
+ Slog.i(TAG, "setDisabledByDeviceConfig(): updating service for user "
+ userId + " to " + (disabled ? "'disabled'" : "'enabled'"));
updateCachedServiceLocked(userId, disabled);
}
@@ -408,16 +410,16 @@ public final class ContentCaptureManagerService extends
final boolean alreadyEnabled = !mDisabledBySettings.get(userId);
if (!(enabled ^ alreadyEnabled)) {
if (debug) {
- Slog.d(mTag, "setContentCaptureFeatureEnabledForUser(): already " + enabled);
+ Slog.d(TAG, "setContentCaptureFeatureEnabledForUser(): already " + enabled);
}
return;
}
if (enabled) {
- Slog.i(mTag, "setContentCaptureFeatureEnabled(): enabling service for user "
+ Slog.i(TAG, "setContentCaptureFeatureEnabled(): enabling service for user "
+ userId);
mDisabledBySettings.delete(userId);
} else {
- Slog.i(mTag, "setContentCaptureFeatureEnabled(): disabling service for user "
+ Slog.i(TAG, "setContentCaptureFeatureEnabled(): disabling service for user "
+ userId);
mDisabledBySettings.put(userId, true);
}
@@ -428,7 +430,7 @@ public final class ContentCaptureManagerService extends
// Called by Shell command.
void destroySessions(@UserIdInt int userId, @NonNull IResultReceiver receiver) {
- Slog.i(mTag, "destroySessions() for userId " + userId);
+ Slog.i(TAG, "destroySessions() for userId " + userId);
enforceCallingPermissionForManagement();
synchronized (mLock) {
@@ -451,7 +453,7 @@ public final class ContentCaptureManagerService extends
// Called by Shell command.
void listSessions(int userId, IResultReceiver receiver) {
- Slog.i(mTag, "listSessions() for userId " + userId);
+ Slog.i(TAG, "listSessions() for userId " + userId);
enforceCallingPermissionForManagement();
final Bundle resultData = new Bundle();
@@ -498,14 +500,14 @@ public final class ContentCaptureManagerService extends
final int callingUid = Binder.getCallingUid();
final String serviceName = mServiceNameResolver.getServiceName(userId);
if (serviceName == null) {
- Slog.e(mTag, methodName + ": called by UID " + callingUid
+ Slog.e(TAG, methodName + ": called by UID " + callingUid
+ ", but there's no service set for user " + userId);
return false;
}
final ComponentName serviceComponent = ComponentName.unflattenFromString(serviceName);
if (serviceComponent == null) {
- Slog.w(mTag, methodName + ": invalid service name: " + serviceName);
+ Slog.w(TAG, methodName + ": invalid service name: " + serviceName);
return false;
}
@@ -516,11 +518,11 @@ public final class ContentCaptureManagerService extends
try {
serviceUid = pm.getPackageUidAsUser(servicePackageName, UserHandle.getCallingUserId());
} catch (NameNotFoundException e) {
- Slog.w(mTag, methodName + ": could not verify UID for " + serviceName);
+ Slog.w(TAG, methodName + ": could not verify UID for " + serviceName);
return false;
}
if (callingUid != serviceUid) {
- Slog.e(mTag, methodName + ": called by UID " + callingUid + ", but service UID is "
+ Slog.e(TAG, methodName + ": called by UID " + callingUid + ", but service UID is "
+ serviceUid);
return false;
}
@@ -543,7 +545,7 @@ public final class ContentCaptureManagerService extends
try {
result.send(RESULT_CODE_SECURITY_EXCEPTION, bundleFor(e.getMessage()));
} catch (RemoteException e2) {
- Slog.w(mTag, "Unable to send security exception (" + e + "): ", e2);
+ Slog.w(TAG, "Unable to send security exception (" + e + "): ", e2);
}
}
return true;
@@ -628,7 +630,7 @@ public final class ContentCaptureManagerService extends
try {
result.send(RESULT_CODE_OK, bundleFor(connectedServiceComponentName));
} catch (RemoteException e) {
- Slog.w(mTag, "Unable to send service component name: " + e);
+ Slog.w(TAG, "Unable to send service component name: " + e);
}
}
@@ -646,6 +648,7 @@ public final class ContentCaptureManagerService extends
@Override
public void shareData(@NonNull DataShareRequest request,
+ @NonNull ICancellationSignal clientCancellationSignal,
@NonNull IDataShareWriteAdapter clientAdapter) {
Preconditions.checkNotNull(request);
Preconditions.checkNotNull(clientAdapter);
@@ -661,13 +664,14 @@ public final class ContentCaptureManagerService extends
try {
clientAdapter.error(DataShareWriteAdapter.ERROR_CONCURRENT_REQUEST);
} catch (RemoteException e) {
- Slog.e(mTag, "Failed to send error message to client");
+ Slog.e(TAG, "Failed to send error message to client");
}
return;
}
service.onDataSharedLocked(request,
- new DataShareCallbackDelegate(request, clientAdapter));
+ new DataShareCallbackDelegate(request, clientCancellationSignal,
+ clientAdapter, ContentCaptureManagerService.this));
}
}
@@ -686,7 +690,7 @@ public final class ContentCaptureManagerService extends
try {
result.send(enabled ? RESULT_CODE_TRUE : RESULT_CODE_FALSE, /* resultData= */null);
} catch (RemoteException e) {
- Slog.w(mTag, "Unable to send isContentCaptureFeatureEnabled(): " + e);
+ Slog.w(TAG, "Unable to send isContentCaptureFeatureEnabled(): " + e);
}
}
@@ -706,7 +710,7 @@ public final class ContentCaptureManagerService extends
try {
result.send(RESULT_CODE_OK, bundleFor(componentName));
} catch (RemoteException e) {
- Slog.w(mTag, "Unable to send getServiceSettingsIntent(): " + e);
+ Slog.w(TAG, "Unable to send getServiceSettingsIntent(): " + e);
}
}
@@ -727,13 +731,13 @@ public final class ContentCaptureManagerService extends
try {
result.send(RESULT_CODE_OK, bundleFor(conditions));
} catch (RemoteException e) {
- Slog.w(mTag, "Unable to send getServiceComponentName(): " + e);
+ Slog.w(TAG, "Unable to send getServiceComponentName(): " + e);
}
}
@Override
public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
- if (!DumpUtils.checkDumpPermission(getContext(), mTag, pw)) return;
+ if (!DumpUtils.checkDumpPermission(getContext(), TAG, pw)) return;
boolean showHistory = true;
if (args != null) {
@@ -746,7 +750,7 @@ public final class ContentCaptureManagerService extends
pw.println("Usage: dumpsys content_capture [--no-history]");
return;
default:
- Slog.w(mTag, "Ignoring invalid dump arg: " + arg);
+ Slog.w(TAG, "Ignoring invalid dump arg: " + arg);
}
}
}
@@ -843,7 +847,7 @@ public final class ContentCaptureManagerService extends
final ComponentName componentName =
ComponentName.unflattenFromString(serviceName);
if (componentName == null) {
- Slog.w(mTag, "setServiceInfo(): invalid name: " + serviceName);
+ Slog.w(TAG, "setServiceInfo(): invalid name: " + serviceName);
mServicePackages.remove(userId);
} else {
mServicePackages.put(userId, componentName.getPackageName());
@@ -869,7 +873,7 @@ public final class ContentCaptureManagerService extends
&& packageName.equals(mServicePackages.get(userId))) {
// No components whitelisted either, but let it go because it's the
// service's own package
- if (verbose) Slog.v(mTag, "getOptionsForPackage() lite for " + packageName);
+ if (verbose) Slog.v(TAG, "getOptionsForPackage() lite for " + packageName);
return new ContentCaptureOptions(mDevCfgLoggingLevel);
}
}
@@ -878,7 +882,7 @@ public final class ContentCaptureManagerService extends
// Restrict what temporary services can whitelist
if (Build.IS_USER && mServiceNameResolver.isTemporary(userId)) {
if (!packageName.equals(mServicePackages.get(userId))) {
- Slog.w(mTag, "Ignoring package " + packageName + " while using temporary "
+ Slog.w(TAG, "Ignoring package " + packageName + " while using temporary "
+ "service " + mServicePackages.get(userId));
return null;
}
@@ -887,7 +891,7 @@ public final class ContentCaptureManagerService extends
if (!packageWhitelisted && whitelistedComponents == null) {
// No can do!
if (verbose) {
- Slog.v(mTag, "getOptionsForPackage(" + packageName + "): not whitelisted");
+ Slog.v(TAG, "getOptionsForPackage(" + packageName + "): not whitelisted");
}
return null;
}
@@ -896,7 +900,7 @@ public final class ContentCaptureManagerService extends
mDevCfgMaxBufferSize, mDevCfgIdleFlushingFrequencyMs,
mDevCfgTextChangeFlushingFrequencyMs, mDevCfgLogHistorySize,
whitelistedComponents);
- if (verbose) Slog.v(mTag, "getOptionsForPackage(" + packageName + "): " + options);
+ if (verbose) Slog.v(TAG, "getOptionsForPackage(" + packageName + "): " + options);
return options;
}
@@ -915,77 +919,90 @@ public final class ContentCaptureManagerService extends
}
}
- // TODO(b/148265162): DataShareCallbackDelegate should be a static class keeping week references
- // to the needed info
- private class DataShareCallbackDelegate extends IDataShareCallback.Stub {
+ private static class DataShareCallbackDelegate extends IDataShareCallback.Stub {
@NonNull private final DataShareRequest mDataShareRequest;
- @NonNull private final IDataShareWriteAdapter mClientAdapter;
+ @NonNull
+ private final WeakReference<ICancellationSignal> mClientCancellationSignalReference;
+ @NonNull private final WeakReference<IDataShareWriteAdapter> mClientAdapterReference;
+ @NonNull private final WeakReference<ContentCaptureManagerService> mParentServiceReference;
DataShareCallbackDelegate(@NonNull DataShareRequest dataShareRequest,
- @NonNull IDataShareWriteAdapter clientAdapter) {
+ @NonNull ICancellationSignal clientCancellationSignal,
+ @NonNull IDataShareWriteAdapter clientAdapter,
+ ContentCaptureManagerService parentService) {
mDataShareRequest = dataShareRequest;
- mClientAdapter = clientAdapter;
+ mClientCancellationSignalReference = new WeakReference<>(clientCancellationSignal);
+ mClientAdapterReference = new WeakReference<>(clientAdapter);
+ mParentServiceReference = new WeakReference<>(parentService);
}
@Override
- public void accept(IDataShareReadAdapter serviceAdapter)
- throws RemoteException {
- Slog.i(mTag, "Data share request accepted by Content Capture service");
+ public void accept(ICancellationSignal serviceCancellationSignal,
+ IDataShareReadAdapter serviceAdapter) throws RemoteException {
+ Slog.i(TAG, "Data share request accepted by Content Capture service");
+
+ final ContentCaptureManagerService parentService = mParentServiceReference.get();
+ final ICancellationSignal clientCancellationSignal =
+ mClientCancellationSignalReference.get();
+ final IDataShareWriteAdapter clientAdapter = mClientAdapterReference.get();
+ if (parentService == null || clientCancellationSignal == null
+ || clientAdapter == null) {
+ Slog.w(TAG, "Can't fulfill accept() request, because remote objects have been "
+ + "GC'ed");
+ return;
+ }
Pair<ParcelFileDescriptor, ParcelFileDescriptor> clientPipe = createPipe();
if (clientPipe == null) {
- mClientAdapter.error(DataShareWriteAdapter.ERROR_UNKNOWN);
+ clientAdapter.error(DataShareWriteAdapter.ERROR_UNKNOWN);
serviceAdapter.error(DataShareWriteAdapter.ERROR_UNKNOWN);
return;
}
- ParcelFileDescriptor source_in = clientPipe.second;
- ParcelFileDescriptor sink_in = clientPipe.first;
+ ParcelFileDescriptor sourceIn = clientPipe.second;
+ ParcelFileDescriptor sinkIn = clientPipe.first;
Pair<ParcelFileDescriptor, ParcelFileDescriptor> servicePipe = createPipe();
if (servicePipe == null) {
- bestEffortCloseFileDescriptors(source_in, sink_in);
+ bestEffortCloseFileDescriptors(sourceIn, sinkIn);
- mClientAdapter.error(DataShareWriteAdapter.ERROR_UNKNOWN);
+ clientAdapter.error(DataShareWriteAdapter.ERROR_UNKNOWN);
serviceAdapter.error(DataShareWriteAdapter.ERROR_UNKNOWN);
return;
}
- ParcelFileDescriptor source_out = servicePipe.second;
- ParcelFileDescriptor sink_out = servicePipe.first;
+ ParcelFileDescriptor sourceOut = servicePipe.second;
+ ParcelFileDescriptor sinkOut = servicePipe.first;
ICancellationSignal cancellationSignalTransport =
CancellationSignal.createTransport();
- mPackagesWithShareRequests.add(mDataShareRequest.getPackageName());
+ parentService.mPackagesWithShareRequests.add(mDataShareRequest.getPackageName());
- mClientAdapter.write(source_in);
- serviceAdapter.start(sink_out, cancellationSignalTransport);
+ clientAdapter.write(sourceIn);
+ serviceAdapter.start(sinkOut, cancellationSignalTransport);
- // TODO(b/148264965): use cancellation signals for timeouts and cancelling
CancellationSignal cancellationSignal =
CancellationSignal.fromTransport(cancellationSignalTransport);
cancellationSignal.setOnCancelListener(() -> {
try {
- // TODO(b/148264965): this should propagate with the cancellation signal to the
- // client
- mClientAdapter.error(DataShareWriteAdapter.ERROR_UNKNOWN);
+ clientCancellationSignal.cancel();
} catch (RemoteException e) {
- Slog.e(mTag, "Failed to propagate cancel operation to the caller", e);
+ Slog.e(TAG, "Failed to propagate cancel operation to the caller", e);
}
});
// File descriptor received by the client app will be a copy of the current one. Close
// the one that belongs to the system server, so there's only 1 open left for the
// current pipe.
- bestEffortCloseFileDescriptor(source_in);
+ bestEffortCloseFileDescriptor(sourceIn);
- mDataShareExecutor.execute(() -> {
+ parentService.mDataShareExecutor.execute(() -> {
try (InputStream fis =
- new ParcelFileDescriptor.AutoCloseInputStream(sink_in);
+ new ParcelFileDescriptor.AutoCloseInputStream(sinkIn);
OutputStream fos =
- new ParcelFileDescriptor.AutoCloseOutputStream(source_out)) {
+ new ParcelFileDescriptor.AutoCloseOutputStream(sourceOut)) {
byte[] byteBuffer = new byte[DATA_SHARE_BYTE_BUFFER_LENGTH];
while (true) {
@@ -998,56 +1015,90 @@ public final class ContentCaptureManagerService extends
fos.write(byteBuffer, 0 /* offset */, readBytes);
}
} catch (IOException e) {
- Slog.e(mTag, "Failed to pipe client and service streams", e);
+ Slog.e(TAG, "Failed to pipe client and service streams", e);
}
});
- mHandler.postDelayed(() -> {
- synchronized (mLock) {
- mPackagesWithShareRequests.remove(mDataShareRequest.getPackageName());
-
- // Interaction finished successfully <=> all data has been written to Content
- // Capture Service. If it hasn't been read successfully, service would be able
- // to signal through the cancellation signal.
- boolean finishedSuccessfully = !sink_in.getFileDescriptor().valid()
- && !source_out.getFileDescriptor().valid();
-
- if (finishedSuccessfully) {
- Slog.i(mTag, "Content capture data sharing session terminated "
- + "successfully for package '"
- + mDataShareRequest.getPackageName()
- + "'");
- } else {
- Slog.i(mTag, "Reached the timeout of Content Capture data sharing session "
- + "for package '"
- + mDataShareRequest.getPackageName()
- + "', terminating the pipe.");
- }
-
- // Ensure all the descriptors are closed after the session.
- bestEffortCloseFileDescriptors(source_in, sink_in, source_out, sink_out);
-
- if (!finishedSuccessfully) {
- try {
- mClientAdapter.error(DataShareWriteAdapter.ERROR_UNKNOWN);
- } catch (RemoteException e) {
- Slog.e(mTag, "Failed to call error() to client", e);
- }
- try {
- serviceAdapter.error(DataShareWriteAdapter.ERROR_UNKNOWN);
- } catch (RemoteException e) {
- Slog.e(mTag, "Failed to call error() to service", e);
- }
- }
- }
- }, MAX_DATA_SHARE_FILE_DESCRIPTORS_TTL_MS);
+ parentService.mHandler.postDelayed(() ->
+ enforceDataSharingTtl(
+ sourceIn,
+ sinkIn,
+ sourceOut,
+ sinkOut,
+ new WeakReference<>(serviceCancellationSignal)),
+ MAX_DATA_SHARE_FILE_DESCRIPTORS_TTL_MS);
}
@Override
public void reject() throws RemoteException {
- Slog.i(mTag, "Data share request rejected by Content Capture service");
+ Slog.i(TAG, "Data share request rejected by Content Capture service");
+
+ IDataShareWriteAdapter clientAdapter = mClientAdapterReference.get();
+ if (clientAdapter == null) {
+ Slog.w(TAG, "Can't fulfill reject() request, because remote objects have been "
+ + "GC'ed");
+ return;
+ }
+
+ clientAdapter.rejected();
+ }
- mClientAdapter.rejected();
+ private void enforceDataSharingTtl(ParcelFileDescriptor sourceIn,
+ ParcelFileDescriptor sinkIn,
+ ParcelFileDescriptor sourceOut,
+ ParcelFileDescriptor sinkOut,
+ WeakReference<ICancellationSignal> serviceCancellationSignalReference) {
+
+ final ContentCaptureManagerService parentService = mParentServiceReference.get();
+ final ICancellationSignal clientCancellationSignal =
+ mClientCancellationSignalReference.get();
+ final ICancellationSignal serviceCancellationSignal =
+ serviceCancellationSignalReference.get();
+ if (parentService == null || clientCancellationSignal == null
+ || serviceCancellationSignal == null) {
+ Slog.w(TAG, "Can't enforce data sharing TTL, because remote objects have been "
+ + "GC'ed");
+ return;
+ }
+
+ synchronized (parentService.mLock) {
+ parentService.mPackagesWithShareRequests
+ .remove(mDataShareRequest.getPackageName());
+
+ // Interaction finished successfully <=> all data has been written to Content
+ // Capture Service. If it hasn't been read successfully, service would be able
+ // to signal through the cancellation signal.
+ boolean finishedSuccessfully = !sinkIn.getFileDescriptor().valid()
+ && !sourceOut.getFileDescriptor().valid();
+
+ if (finishedSuccessfully) {
+ Slog.i(TAG, "Content capture data sharing session terminated "
+ + "successfully for package '"
+ + mDataShareRequest.getPackageName()
+ + "'");
+ } else {
+ Slog.i(TAG, "Reached the timeout of Content Capture data sharing session "
+ + "for package '"
+ + mDataShareRequest.getPackageName()
+ + "', terminating the pipe.");
+ }
+
+ // Ensure all the descriptors are closed after the session.
+ bestEffortCloseFileDescriptors(sourceIn, sinkIn, sourceOut, sinkOut);
+
+ if (!finishedSuccessfully) {
+ try {
+ clientCancellationSignal.cancel();
+ } catch (RemoteException e) {
+ Slog.e(TAG, "Failed to cancel() the client operation", e);
+ }
+ try {
+ serviceCancellationSignal.cancel();
+ } catch (RemoteException e) {
+ Slog.e(TAG, "Failed to cancel() the service operation", e);
+ }
+ }
+ }
}
private Pair<ParcelFileDescriptor, ParcelFileDescriptor> createPipe() {
@@ -1055,19 +1106,19 @@ public final class ContentCaptureManagerService extends
try {
fileDescriptors = ParcelFileDescriptor.createPipe();
} catch (IOException e) {
- Slog.e(mTag, "Failed to create a content capture data-sharing pipe", e);
+ Slog.e(TAG, "Failed to create a content capture data-sharing pipe", e);
return null;
}
if (fileDescriptors.length != 2) {
- Slog.e(mTag, "Failed to create a content capture data-sharing pipe, "
+ Slog.e(TAG, "Failed to create a content capture data-sharing pipe, "
+ "unexpected number of file descriptors");
return null;
}
if (!fileDescriptors[0].getFileDescriptor().valid()
|| !fileDescriptors[1].getFileDescriptor().valid()) {
- Slog.e(mTag, "Failed to create a content capture data-sharing pipe, didn't "
+ Slog.e(TAG, "Failed to create a content capture data-sharing pipe, didn't "
+ "receive a pair of valid file descriptors.");
return null;
}
@@ -1079,7 +1130,7 @@ public final class ContentCaptureManagerService extends
try {
fd.close();
} catch (IOException e) {
- Slog.e(mTag, "Failed to close a file descriptor", e);
+ Slog.e(TAG, "Failed to close a file descriptor", e);
}
}
diff --git a/services/core/java/com/android/server/ConnectivityService.java b/services/core/java/com/android/server/ConnectivityService.java
index dd33566322e2..d933e9d8e9da 100644
--- a/services/core/java/com/android/server/ConnectivityService.java
+++ b/services/core/java/com/android/server/ConnectivityService.java
@@ -2962,6 +2962,11 @@ public class ConnectivityService extends IConnectivityManager.Stub
public int getInterfaceVersion() {
return this.VERSION;
}
+
+ @Override
+ public String getInterfaceHash() {
+ return this.HASH;
+ }
}
private boolean networkRequiresPrivateDnsValidation(NetworkAgentInfo nai) {
diff --git a/services/core/java/com/android/server/LocationManagerService.java b/services/core/java/com/android/server/LocationManagerService.java
index 3b6ff26d5f03..f1f2d2abea3f 100644
--- a/services/core/java/com/android/server/LocationManagerService.java
+++ b/services/core/java/com/android/server/LocationManagerService.java
@@ -104,6 +104,7 @@ import com.android.server.location.MockProvider;
import com.android.server.location.MockableLocationProvider;
import com.android.server.location.PassiveProvider;
import com.android.server.location.UserInfoStore;
+import com.android.server.location.gnss.GnssManagerService;
import com.android.server.pm.permission.PermissionManagerServiceInternal;
import java.io.ByteArrayOutputStream;
@@ -859,13 +860,7 @@ public class LocationManagerService extends ILocationManager.Stub {
pw.increaseIndent();
- boolean enabled = isEnabled();
- pw.println("enabled=" + enabled);
- if (!enabled) {
- pw.println("allowed=" + mProvider.getState().allowed);
- }
-
- pw.println("properties=" + mProvider.getState().properties);
+ pw.println("enabled=" + isEnabled());
}
mProvider.dump(fd, pw, args);
diff --git a/services/core/java/com/android/server/NetworkManagementService.java b/services/core/java/com/android/server/NetworkManagementService.java
index 05d83606b2d0..9d4c78383183 100644
--- a/services/core/java/com/android/server/NetworkManagementService.java
+++ b/services/core/java/com/android/server/NetworkManagementService.java
@@ -735,6 +735,11 @@ public class NetworkManagementService extends INetworkManagementService.Stub {
public int getInterfaceVersion() {
return INetdUnsolicitedEventListener.VERSION;
}
+
+ @Override
+ public String getInterfaceHash() {
+ return INetdUnsolicitedEventListener.HASH;
+ }
}
//
diff --git a/services/core/java/com/android/server/ServiceWatcher.java b/services/core/java/com/android/server/ServiceWatcher.java
index 8564cb456ba6..14cd3a5d5c0d 100644
--- a/services/core/java/com/android/server/ServiceWatcher.java
+++ b/services/core/java/com/android/server/ServiceWatcher.java
@@ -167,7 +167,7 @@ public class ServiceWatcher implements ServiceConnection {
@Override
public String toString() {
- return component + "@" + version + "[u" + userId + "]";
+ return component.toShortString() + "@" + version + "[u" + userId + "]";
}
}
@@ -341,7 +341,7 @@ public class ServiceWatcher implements ServiceConnection {
Preconditions.checkState(Looper.myLooper() == mHandler.getLooper());
if (D) {
- Log.i(TAG, getLogPrefix() + " connected to " + component);
+ Log.i(TAG, getLogPrefix() + " connected to " + component.toShortString());
}
mBinder = binder;
@@ -351,11 +351,22 @@ public class ServiceWatcher implements ServiceConnection {
}
@Override
+ public void onBindingDied(ComponentName component) {
+ Preconditions.checkState(Looper.myLooper() == mHandler.getLooper());
+
+ if (D) {
+ Log.i(TAG, getLogPrefix() + " " + component.toShortString() + " died");
+ }
+
+ onBestServiceChanged(true);
+ }
+
+ @Override
public final void onServiceDisconnected(ComponentName component) {
Preconditions.checkState(Looper.myLooper() == mHandler.getLooper());
if (D) {
- Log.i(TAG, getLogPrefix() + " disconnected from " + component);
+ Log.i(TAG, getLogPrefix() + " disconnected from " + component.toShortString());
}
mBinder = null;
@@ -383,8 +394,8 @@ public class ServiceWatcher implements ServiceConnection {
}
/**
- * Runs the given function asynchronously if currently connected. Suppresses any RemoteException
- * thrown during execution.
+ * Runs the given function asynchronously if and only if currently connected. Suppresses any
+ * RemoteException thrown during execution.
*/
public final void runOnBinder(BinderRunner runner) {
runOnHandler(() -> {
@@ -473,4 +484,9 @@ public class ServiceWatcher implements ServiceConnection {
private String getLogPrefix() {
return "[" + mIntent.getAction() + "]";
}
+
+ @Override
+ public String toString() {
+ return mServiceInfo.toString();
+ }
}
diff --git a/services/core/java/com/android/server/VibratorService.java b/services/core/java/com/android/server/VibratorService.java
index 5a56a9fa5367..6bc090a4f7ba 100644
--- a/services/core/java/com/android/server/VibratorService.java
+++ b/services/core/java/com/android/server/VibratorService.java
@@ -16,6 +16,8 @@
package com.android.server;
+import static android.os.VibrationEffect.Composition.PrimitiveEffect;
+
import android.annotation.Nullable;
import android.app.ActivityManager;
import android.app.AppOpsManager;
@@ -73,8 +75,10 @@ import com.android.internal.util.DumpUtils;
import java.io.FileDescriptor;
import java.io.PrintWriter;
import java.util.ArrayList;
+import java.util.Arrays;
import java.util.Date;
import java.util.LinkedList;
+import java.util.List;
public class VibratorService extends IVibratorService.Stub
implements InputManager.InputDeviceListener {
@@ -128,10 +132,11 @@ public class VibratorService extends IVibratorService.Stub
private final boolean mAllowPriorityVibrationsInLowPowerMode;
private final boolean mSupportsAmplitudeControl;
private final boolean mSupportsExternalControl;
+ private final List<Integer> mSupportedEffects;
private final long mCapabilities;
private final int mDefaultVibrationAmplitude;
private final SparseArray<VibrationEffect> mFallbackEffects;
- private final SparseArray<Integer> mProcStatesCache = new SparseArray();
+ private final SparseArray<Integer> mProcStatesCache = new SparseArray<>();
private final WorkSource mTmpWorkSource = new WorkSource();
private final Handler mH = new Handler();
private final Object mLock = new Object();
@@ -150,7 +155,7 @@ public class VibratorService extends IVibratorService.Stub
// mInputDeviceVibrators lock should be acquired after mLock, if both are
// to be acquired
- private final ArrayList<Vibrator> mInputDeviceVibrators = new ArrayList<Vibrator>();
+ private final ArrayList<Vibrator> mInputDeviceVibrators = new ArrayList<>();
private boolean mVibrateInputDevicesSetting; // guarded by mInputDeviceVibrators
private boolean mInputDeviceListenerRegistered; // guarded by mInputDeviceVibrators
@@ -171,7 +176,10 @@ public class VibratorService extends IVibratorService.Stub
static native void vibratorOff();
static native boolean vibratorSupportsAmplitudeControl();
static native void vibratorSetAmplitude(int amplitude);
+ static native int[] vibratorGetSupportedEffects();
static native long vibratorPerformEffect(long effect, long strength, Vibration vibration);
+ static native void vibratorPerformComposedEffect(
+ VibrationEffect.Composition.PrimitiveEffect[] effect, Vibration vibration);
static native boolean vibratorSupportsExternalControl();
static native void vibratorSetExternalControl(boolean enabled);
static native long vibratorGetCapabilities();
@@ -238,6 +246,8 @@ public class VibratorService extends IVibratorService.Stub
}
}
+ // Called by native
+ @SuppressWarnings("unused")
private void onComplete() {
synchronized (mLock) {
if (this == mCurrentVibration) {
@@ -346,10 +356,11 @@ public class VibratorService extends IVibratorService.Stub
mSupportsAmplitudeControl = vibratorSupportsAmplitudeControl();
mSupportsExternalControl = vibratorSupportsExternalControl();
+ mSupportedEffects = asList(vibratorGetSupportedEffects());
mCapabilities = vibratorGetCapabilities();
mContext = context;
- PowerManager pm = (PowerManager)context.getSystemService(Context.POWER_SERVICE);
+ PowerManager pm = context.getSystemService(PowerManager.class);
mWakeLock = pm.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, "*vibrator*");
mWakeLock.setReferenceCounted(true);
@@ -510,12 +521,47 @@ public class VibratorService extends IVibratorService.Stub
}
@Override // Binder call
+ public boolean[] areEffectsSupported(int[] effectIds) {
+ // Return null to indicate that the HAL doesn't actually tell us what effects are
+ // supported.
+ if (mSupportedEffects == null) {
+ return null;
+ }
+ boolean[] supported = new boolean[effectIds.length];
+ for (int i = 0; i < effectIds.length; i++) {
+ supported[i] = mSupportedEffects.contains(effectIds[i]);
+ }
+ return supported;
+ }
+
+ @Override // Binder call
+ public boolean[] arePrimitivesSupported(int[] primitiveIds) {
+ boolean[] supported = new boolean[primitiveIds.length];
+ if (hasCapability(IVibrator.CAP_COMPOSE_EFFECTS)) {
+ Arrays.fill(supported, true);
+ }
+ return supported;
+ }
+
+
+ private static List<Integer> asList(int... vals) {
+ if (vals == null) {
+ return null;
+ }
+ List<Integer> l = new ArrayList<>(vals.length);
+ for (int val : vals) {
+ l.add(val);
+ }
+ return l;
+ }
+
+ @Override // Binder call
public boolean setAlwaysOnEffect(int uid, String opPkg, int alwaysOnId, VibrationEffect effect,
VibrationAttributes attrs) {
if (!hasPermission(android.Manifest.permission.VIBRATE_ALWAYS_ON)) {
throw new SecurityException("Requires VIBRATE_ALWAYS_ON permission");
}
- if ((mCapabilities & IVibrator.CAP_ALWAYS_ON_CONTROL) == 0) {
+ if (!hasCapability(IVibrator.CAP_ALWAYS_ON_CONTROL)) {
Slog.e(TAG, "Always-on effects not supported.");
return false;
}
@@ -817,6 +863,14 @@ public class VibratorService extends IVibratorService.Stub
if (timeout > 0) {
mH.postDelayed(mVibrationEndRunnable, timeout);
}
+ } else if (vib.effect instanceof VibrationEffect.Composed) {
+ Trace.asyncTraceBegin(Trace.TRACE_TAG_VIBRATOR, "vibration", 0);
+ doVibratorComposedEffectLocked(vib);
+ // FIXME: We rely on the completion callback here, but I don't think we require that
+ // devices which support composition also support the completion callback. If we
+ // ever get a device that supports the former but not the latter, then we have no
+ // real way of knowing how long a given effect should last.
+ mH.postDelayed(mVibrationEndRunnable, 10000);
} else {
Slog.e(TAG, "Unknown vibration type, ignoring");
}
@@ -1148,7 +1202,7 @@ public class VibratorService extends IVibratorService.Stub
}
} else {
// Note: ordering is important here! Many haptic drivers will reset their
- // amplitude when enabled, so we always have to enable frst, then set the
+ // amplitude when enabled, so we always have to enable first, then set the
// amplitude.
vibratorOn(millis);
doVibratorSetAmplitude(amplitude);
@@ -1201,7 +1255,7 @@ public class VibratorService extends IVibratorService.Stub
long duration = vibratorPerformEffect(prebaked.getId(),
prebaked.getEffectStrength(), vib);
long timeout = duration;
- if ((mCapabilities & IVibrator.CAP_PERFORM_CALLBACK) != 0) {
+ if (hasCapability(IVibrator.CAP_PERFORM_CALLBACK)) {
timeout *= ASYNC_TIMEOUT_MULTIPLIER;
}
if (timeout > 0) {
@@ -1229,6 +1283,41 @@ public class VibratorService extends IVibratorService.Stub
}
}
+ @GuardedBy("mLock")
+ private void doVibratorComposedEffectLocked(Vibration vib) {
+ Trace.traceBegin(Trace.TRACE_TAG_VIBRATOR, "doVibratorComposedEffectLocked");
+
+ try {
+ final VibrationEffect.Composed composed = (VibrationEffect.Composed) vib.effect;
+ final boolean usingInputDeviceVibrators;
+ synchronized (mInputDeviceVibrators) {
+ usingInputDeviceVibrators = !mInputDeviceVibrators.isEmpty();
+ }
+ // Input devices don't support composed effect, so skip trying it with them.
+ if (usingInputDeviceVibrators) {
+ return;
+ }
+
+ if (!hasCapability(IVibrator.CAP_COMPOSE_EFFECTS)) {
+ return;
+ }
+
+ PrimitiveEffect[] primitiveEffects =
+ composed.getPrimitiveEffects().toArray(new PrimitiveEffect[0]);
+ vibratorPerformComposedEffect(primitiveEffects, vib);
+
+ // Composed effects don't actually give us an estimated duration, so we just guess here.
+ noteVibratorOnLocked(vib.uid, 10 * primitiveEffects.length);
+ } finally {
+ Trace.traceEnd(Trace.TRACE_TAG_VIBRATOR);
+ }
+
+ }
+
+ private boolean hasCapability(long capability) {
+ return (mCapabilities & capability) == capability;
+ }
+
private VibrationEffect getFallbackEffect(int effectId) {
return mFallbackEffects.get(effectId);
}
@@ -1424,7 +1513,7 @@ public class VibratorService extends IVibratorService.Stub
long[] timings, int[] amplitudes, int startIndex, int repeatIndex) {
int i = startIndex;
long timing = 0;
- while(amplitudes[i] != 0) {
+ while (amplitudes[i] != 0) {
timing += timings[i++];
if (i >= timings.length) {
if (repeatIndex >= 0) {
@@ -1475,18 +1564,14 @@ public class VibratorService extends IVibratorService.Stub
} else {
pw.println("null");
}
- pw.print(" mCurrentExternalVibration=");
- if (mCurrentExternalVibration != null) {
- pw.println(mCurrentExternalVibration.toString());
- } else {
- pw.println("null");
- }
+ pw.print(" mCurrentExternalVibration=" + mCurrentExternalVibration);
pw.println(" mVibratorUnderExternalControl=" + mVibratorUnderExternalControl);
pw.println(" mLowPowerMode=" + mLowPowerMode);
pw.println(" mHapticFeedbackIntensity=" + mHapticFeedbackIntensity);
pw.println(" mNotificationIntensity=" + mNotificationIntensity);
pw.println(" mRingIntensity=" + mRingIntensity);
- pw.println("");
+ pw.println(" mSupportedEffects=" + mSupportedEffects);
+ pw.println();
pw.println(" Previous ring vibrations:");
for (VibrationInfo info : mPreviousRingVibrations) {
pw.print(" ");
@@ -1495,34 +1580,29 @@ public class VibratorService extends IVibratorService.Stub
pw.println(" Previous notification vibrations:");
for (VibrationInfo info : mPreviousNotificationVibrations) {
- pw.print(" ");
- pw.println(info.toString());
+ pw.println(" " + info);
}
pw.println(" Previous alarm vibrations:");
for (VibrationInfo info : mPreviousAlarmVibrations) {
- pw.print(" ");
- pw.println(info.toString());
+ pw.println(" " + info);
}
pw.println(" Previous vibrations:");
for (VibrationInfo info : mPreviousVibrations) {
- pw.print(" ");
- pw.println(info.toString());
+ pw.println(" " + info);
}
pw.println(" Previous external vibrations:");
for (ExternalVibration vib : mPreviousExternalVibrations) {
- pw.print(" ");
- pw.println(vib.toString());
+ pw.println(" " + vib);
}
}
}
@Override
public void onShellCommand(FileDescriptor in, FileDescriptor out, FileDescriptor err,
- String[] args, ShellCallback callback, ResultReceiver resultReceiver)
- throws RemoteException {
+ String[] args, ShellCallback callback, ResultReceiver resultReceiver) {
new VibratorShellCommand(this).exec(this, in, out, err, args, callback, resultReceiver);
}
diff --git a/services/core/java/com/android/server/am/ActiveServices.java b/services/core/java/com/android/server/am/ActiveServices.java
index 0a91f9af1cae..ffeea3d12781 100644
--- a/services/core/java/com/android/server/am/ActiveServices.java
+++ b/services/core/java/com/android/server/am/ActiveServices.java
@@ -287,6 +287,7 @@ public final class ActiveServices {
static final int MSG_BG_START_TIMEOUT = 1;
static final int MSG_UPDATE_FOREGROUND_APPS = 2;
+ static final int MSG_ENSURE_NOT_START_BG = 3;
ServiceMap(Looper looper, int userId) {
super(looper);
@@ -304,6 +305,11 @@ public final class ActiveServices {
case MSG_UPDATE_FOREGROUND_APPS: {
updateForegroundApps(this);
} break;
+ case MSG_ENSURE_NOT_START_BG: {
+ synchronized (mAm) {
+ rescheduleDelayedStartsLocked();
+ }
+ } break;
}
}
@@ -311,7 +317,9 @@ public final class ActiveServices {
if (mStartingBackground.remove(r)) {
if (DEBUG_DELAYED_STARTS) Slog.v(TAG_SERVICE,
"No longer background starting: " + r);
- rescheduleDelayedStartsLocked();
+ removeMessages(MSG_ENSURE_NOT_START_BG);
+ Message msg = obtainMessage(MSG_ENSURE_NOT_START_BG);
+ sendMessage(msg);
}
if (mDelayedStartList.remove(r)) {
if (DEBUG_DELAYED_STARTS) Slog.v(TAG_SERVICE, "No longer delaying start: " + r);
diff --git a/services/core/java/com/android/server/am/BatteryExternalStatsWorker.java b/services/core/java/com/android/server/am/BatteryExternalStatsWorker.java
index cf996a50d5c7..fa916202d553 100644
--- a/services/core/java/com/android/server/am/BatteryExternalStatsWorker.java
+++ b/services/core/java/com/android/server/am/BatteryExternalStatsWorker.java
@@ -567,39 +567,41 @@ class BatteryExternalStatsWorker implements BatteryStatsImpl.ExternalStatsSync {
final long lastRxMs = mLastInfo.getControllerRxDurationMillis();
final long lastEnergy = mLastInfo.getControllerEnergyUsedMicroJoules();
- // We will modify the last info object to be the delta, and store the new
- // WifiActivityEnergyInfo object as our last one.
- final WifiActivityEnergyInfo delta = mLastInfo;
- delta.setTimeSinceBootMillis(latest.getTimeSinceBootMillis());
- delta.setStackState(latest.getStackState());
+ final long deltaTimeSinceBootMillis = latest.getTimeSinceBootMillis();
+ final int deltaStackState = latest.getStackState();
+ final long deltaControllerTxDurationMillis;
+ final long deltaControllerRxDurationMillis;
+ final long deltaControllerScanDurationMillis;
+ final long deltaControllerIdleDurationMillis;
+ final long deltaControllerEnergyUsedMicroJoules;
final long txTimeMs = latest.getControllerTxDurationMillis() - lastTxMs;
final long rxTimeMs = latest.getControllerRxDurationMillis() - lastRxMs;
final long idleTimeMs = latest.getControllerIdleDurationMillis() - lastIdleMs;
final long scanTimeMs = latest.getControllerScanDurationMillis() - lastScanMs;
+ final boolean wasReset;
if (txTimeMs < 0 || rxTimeMs < 0 || scanTimeMs < 0 || idleTimeMs < 0) {
// The stats were reset by the WiFi system (which is why our delta is negative).
// Returns the unaltered stats. The total on time should not exceed the time
- // duartion between reports.
+ // duration between reports.
final long totalOnTimeMs = latest.getControllerTxDurationMillis()
+ latest.getControllerRxDurationMillis()
+ latest.getControllerIdleDurationMillis();
if (totalOnTimeMs <= timePeriodMs + MAX_WIFI_STATS_SAMPLE_ERROR_MILLIS) {
- delta.setControllerEnergyUsedMicroJoules(
- latest.getControllerEnergyUsedMicroJoules());
- delta.setControllerRxDurationMillis(latest.getControllerRxDurationMillis());
- delta.setControllerTxDurationMillis(latest.getControllerTxDurationMillis());
- delta.setControllerIdleDurationMillis(latest.getControllerIdleDurationMillis());
- delta.setControllerScanDurationMillis(latest.getControllerScanDurationMillis());
+ deltaControllerEnergyUsedMicroJoules = latest.getControllerEnergyUsedMicroJoules();
+ deltaControllerRxDurationMillis = latest.getControllerRxDurationMillis();
+ deltaControllerTxDurationMillis = latest.getControllerTxDurationMillis();
+ deltaControllerIdleDurationMillis = latest.getControllerIdleDurationMillis();
+ deltaControllerScanDurationMillis = latest.getControllerScanDurationMillis();
} else {
- delta.setControllerEnergyUsedMicroJoules(0);
- delta.setControllerRxDurationMillis(0);
- delta.setControllerTxDurationMillis(0);
- delta.setControllerIdleDurationMillis(0);
- delta.setControllerScanDurationMillis(0);
+ deltaControllerEnergyUsedMicroJoules = 0;
+ deltaControllerRxDurationMillis = 0;
+ deltaControllerTxDurationMillis = 0;
+ deltaControllerIdleDurationMillis = 0;
+ deltaControllerScanDurationMillis = 0;
}
- Slog.v(TAG, "WiFi energy data was reset, new WiFi energy data is " + delta);
+ wasReset = true;
} else {
final long totalActiveTimeMs = txTimeMs + rxTimeMs;
long maxExpectedIdleTimeMs;
@@ -634,21 +636,33 @@ class BatteryExternalStatsWorker implements BatteryStatsImpl.ExternalStatsSync {
maxExpectedIdleTimeMs = timePeriodMs - totalActiveTimeMs;
}
// These times seem to be the most reliable.
- delta.setControllerTxDurationMillis(txTimeMs);
- delta.setControllerRxDurationMillis(rxTimeMs);
- delta.setControllerScanDurationMillis(scanTimeMs);
+ deltaControllerTxDurationMillis = txTimeMs;
+ deltaControllerRxDurationMillis = rxTimeMs;
+ deltaControllerScanDurationMillis = scanTimeMs;
// WiFi calculates the idle time as a difference from the on time and the various
// Rx + Tx times. There seems to be some missing time there because this sometimes
// becomes negative. Just cap it at 0 and ensure that it is less than the expected idle
// time from the difference in timestamps.
// b/21613534
- delta.setControllerIdleDurationMillis(
- Math.min(maxExpectedIdleTimeMs, Math.max(0, idleTimeMs)));
- delta.setControllerEnergyUsedMicroJoules(
- Math.max(0, latest.getControllerEnergyUsedMicroJoules() - lastEnergy));
+ deltaControllerIdleDurationMillis =
+ Math.min(maxExpectedIdleTimeMs, Math.max(0, idleTimeMs));
+ deltaControllerEnergyUsedMicroJoules =
+ Math.max(0, latest.getControllerEnergyUsedMicroJoules() - lastEnergy);
+ wasReset = false;
}
mLastInfo = latest;
+ WifiActivityEnergyInfo delta = new WifiActivityEnergyInfo(
+ deltaTimeSinceBootMillis,
+ deltaStackState,
+ deltaControllerTxDurationMillis,
+ deltaControllerRxDurationMillis,
+ deltaControllerScanDurationMillis,
+ deltaControllerIdleDurationMillis,
+ deltaControllerEnergyUsedMicroJoules);
+ if (wasReset) {
+ Slog.v(TAG, "WiFi energy data was reset, new WiFi energy data is " + delta);
+ }
return delta;
}
}
diff --git a/services/core/java/com/android/server/am/OomAdjuster.java b/services/core/java/com/android/server/am/OomAdjuster.java
index e9d64913a354..3bd7d5c0d075 100644
--- a/services/core/java/com/android/server/am/OomAdjuster.java
+++ b/services/core/java/com/android/server/am/OomAdjuster.java
@@ -17,8 +17,8 @@
package com.android.server.am;
import static android.app.ActivityManager.PROCESS_CAPABILITY_ALL;
-import static android.app.ActivityManager.PROCESS_CAPABILITY_FOREGROUND_CAMERA;
import static android.app.ActivityManager.PROCESS_CAPABILITY_ALL_IMPLICIT;
+import static android.app.ActivityManager.PROCESS_CAPABILITY_FOREGROUND_CAMERA;
import static android.app.ActivityManager.PROCESS_CAPABILITY_FOREGROUND_LOCATION;
import static android.app.ActivityManager.PROCESS_CAPABILITY_FOREGROUND_MICROPHONE;
import static android.app.ActivityManager.PROCESS_CAPABILITY_NONE;
@@ -36,9 +36,7 @@ import static android.app.ActivityManager.PROCESS_STATE_NONEXISTENT;
import static android.app.ActivityManager.PROCESS_STATE_SERVICE;
import static android.app.ActivityManager.PROCESS_STATE_TOP;
import static android.app.ActivityManager.PROCESS_STATE_TRANSIENT_BACKGROUND;
-import static android.content.pm.ServiceInfo.FOREGROUND_SERVICE_TYPE_CAMERA;
import static android.content.pm.ServiceInfo.FOREGROUND_SERVICE_TYPE_LOCATION;
-import static android.content.pm.ServiceInfo.FOREGROUND_SERVICE_TYPE_MICROPHONE;
import static android.os.Process.SCHED_OTHER;
import static android.os.Process.THREAD_GROUP_BACKGROUND;
import static android.os.Process.THREAD_GROUP_DEFAULT;
@@ -78,7 +76,6 @@ import android.compat.annotation.Disabled;
import android.compat.annotation.EnabledAfter;
import android.content.Context;
import android.content.pm.ServiceInfo;
-import android.os.Build;
import android.os.Debug;
import android.os.Handler;
import android.os.IBinder;
@@ -151,6 +148,9 @@ public final class OomAdjuster {
@Disabled
static final long CAMERA_MICROPHONE_CAPABILITY_CHANGE_ID = 136219221L;
+ //TODO: remove this when development is done.
+ private static final int TEMP_PROCESS_CAPABILITY_FOREGROUND_LOCATION = 1 << 31;
+
/**
* For some direct access we need to power manager.
*/
@@ -1477,13 +1477,24 @@ public final class OomAdjuster {
}
}
- if (s.isForeground && s.mAllowWhileInUsePermissionInFgs) {
+ if (s.isForeground) {
final int fgsType = s.foregroundServiceType;
- capabilityFromFGS |=
- (fgsType & FOREGROUND_SERVICE_TYPE_LOCATION)
- != 0 ? PROCESS_CAPABILITY_FOREGROUND_LOCATION : 0;
- capabilityFromFGS |= PROCESS_CAPABILITY_FOREGROUND_CAMERA
- | PROCESS_CAPABILITY_FOREGROUND_MICROPHONE;
+ if (s.mAllowWhileInUsePermissionInFgs) {
+ capabilityFromFGS |=
+ (fgsType & FOREGROUND_SERVICE_TYPE_LOCATION)
+ != 0 ? PROCESS_CAPABILITY_FOREGROUND_LOCATION : 0;
+ } else {
+ //The FGS has the location capability, but due to FGS BG start restriction it
+ //lost the capability, use temp location capability to mark this case.
+ //TODO: remove this block when development is done.
+ capabilityFromFGS |=
+ (fgsType & FOREGROUND_SERVICE_TYPE_LOCATION)
+ != 0 ? TEMP_PROCESS_CAPABILITY_FOREGROUND_LOCATION : 0;
+ }
+ if (s.mAllowWhileInUsePermissionInFgs) {
+ capabilityFromFGS |= PROCESS_CAPABILITY_FOREGROUND_CAMERA
+ | PROCESS_CAPABILITY_FOREGROUND_MICROPHONE;
+ }
}
ArrayMap<IBinder, ArrayList<ConnectionRecord>> serviceConnections = s.getConnections();
diff --git a/services/core/java/com/android/server/am/ProcessList.java b/services/core/java/com/android/server/am/ProcessList.java
index b269c38307e7..954be20122b4 100644
--- a/services/core/java/com/android/server/am/ProcessList.java
+++ b/services/core/java/com/android/server/am/ProcessList.java
@@ -1551,6 +1551,45 @@ public final class ProcessList {
}
}
+ private int[] computeGidsForProcess(int mountExternal, int uid, int[] permGids) {
+ ArrayList<Integer> gidList = new ArrayList<>(permGids.length + 5);
+
+ final int sharedAppGid = UserHandle.getSharedAppGid(UserHandle.getAppId(uid));
+ final int cacheAppGid = UserHandle.getCacheAppGid(UserHandle.getAppId(uid));
+ final int userGid = UserHandle.getUserGid(UserHandle.getUserId(uid));
+
+ // Add shared application and profile GIDs so applications can share some
+ // resources like shared libraries and access user-wide resources
+ for (int permGid : permGids) {
+ gidList.add(permGid);
+ }
+ if (sharedAppGid != UserHandle.ERR_GID) {
+ gidList.add(sharedAppGid);
+ }
+ if (cacheAppGid != UserHandle.ERR_GID) {
+ gidList.add(cacheAppGid);
+ }
+ if (userGid != UserHandle.ERR_GID) {
+ gidList.add(userGid);
+ }
+ if (mountExternal == Zygote.MOUNT_EXTERNAL_ANDROID_WRITABLE) {
+ // For DownloadProviders and MTP: To grant access to /sdcard/Android/
+ gidList.add(Process.SDCARD_RW_GID);
+ }
+ if (mountExternal == Zygote.MOUNT_EXTERNAL_PASS_THROUGH) {
+ // For the FUSE daemon: To grant access to the lower filesystem.
+ // EmulatedVolumes: /data/media and /mnt/expand/<volume>/data/media
+ // PublicVolumes: /mnt/media_rw/<volume>
+ gidList.add(Process.MEDIA_RW_GID);
+ }
+
+ int[] gidArray = new int[gidList.size()];
+ for (int i = 0; i < gidArray.length; i++) {
+ gidArray[i] = gidList.get(i);
+ }
+ return gidArray;
+ }
+
/**
* @return {@code true} if process start is successful, false otherwise.
*/
@@ -1625,38 +1664,7 @@ public final class ProcessList {
}
}
- int numGids = 3;
- if (mountExternal == Zygote.MOUNT_EXTERNAL_ANDROID_WRITABLE
- || app.info.packageName.equals("com.android.externalstorage")) {
- numGids++;
- }
-
- /*
- * Add shared application and profile GIDs so applications can share some
- * resources like shared libraries and access user-wide resources
- */
- if (ArrayUtils.isEmpty(permGids)) {
- gids = new int[numGids];
- } else {
- gids = new int[permGids.length + numGids];
- System.arraycopy(permGids, 0, gids, numGids, permGids.length);
- }
- gids[0] = UserHandle.getSharedAppGid(UserHandle.getAppId(uid));
- gids[1] = UserHandle.getCacheAppGid(UserHandle.getAppId(uid));
- gids[2] = UserHandle.getUserGid(UserHandle.getUserId(uid));
-
- if (numGids > 3) {
- if (app.info.packageName.equals("com.android.externalstorage")) {
- // Allows access to 'unreliable' (USB OTG) volumes via SAF
- gids[3] = Process.MEDIA_RW_GID;
- } else if (mountExternal == Zygote.MOUNT_EXTERNAL_ANDROID_WRITABLE) {
- gids[3] = Process.SDCARD_RW_GID;
- }
- }
-
- // Replace any invalid GIDs
- if (gids[0] == UserHandle.ERR_GID) gids[0] = gids[2];
- if (gids[1] == UserHandle.ERR_GID) gids[1] = gids[2];
+ gids = computeGidsForProcess(mountExternal, uid, permGids);
}
app.mountMode = mountExternal;
checkSlow(startTime, "startProcess: building args");
diff --git a/services/core/java/com/android/server/appop/AppOpsService.java b/services/core/java/com/android/server/appop/AppOpsService.java
index 62596de7b9a6..1d714a2c99c7 100644
--- a/services/core/java/com/android/server/appop/AppOpsService.java
+++ b/services/core/java/com/android/server/appop/AppOpsService.java
@@ -213,6 +213,9 @@ public class AppOpsService extends IAppOpsService.Stub {
private static final int MAX_UNFORWARED_OPS = 10;
private static final int MAX_UNUSED_POOLED_OBJECTS = 3;
+ //TODO: remove this when development is done.
+ private static final int TEMP_PROCESS_CAPABILITY_FOREGROUND_LOCATION = 1 << 31;
+
Context mContext;
final AtomicFile mFile;
final Handler mHandler;
@@ -480,8 +483,15 @@ public class AppOpsService extends IAppOpsService.Stub {
case AppOpsManager.OP_MONITOR_HIGH_POWER_LOCATION:
if ((capability & PROCESS_CAPABILITY_FOREGROUND_LOCATION) != 0) {
return AppOpsManager.MODE_ALLOWED;
- } else {
+ } else if ((capability
+ & TEMP_PROCESS_CAPABILITY_FOREGROUND_LOCATION) != 0) {
+ // The FGS has the location capability, but due to FGS BG start
+ // restriction it lost the capability, use temp location capability
+ // to mark this case.
+ // TODO change to MODE_IGNORED when enforcing the feature.
maybeShowWhileInUseDebugToast(op, mode);
+ return AppOpsManager.MODE_ALLOWED;
+ } else {
return AppOpsManager.MODE_IGNORED;
}
case OP_CAMERA:
@@ -586,7 +596,7 @@ public class AppOpsService extends IAppOpsService.Stub {
return;
}
final long now = System.currentTimeMillis();
- if (lastTimeShowDebugToast == 0 || now - lastTimeShowDebugToast > 600000) {
+ if (lastTimeShowDebugToast == 0 || now - lastTimeShowDebugToast > 3600000) {
lastTimeShowDebugToast = now;
mHandler.sendMessage(PooledLambda.obtainMessage(
ActivityManagerInternal::showWhileInUseDebugToast,
diff --git a/services/core/java/com/android/server/compat/PlatformCompat.java b/services/core/java/com/android/server/compat/PlatformCompat.java
index bb8b12e86e16..4d5af9ac5d5c 100644
--- a/services/core/java/com/android/server/compat/PlatformCompat.java
+++ b/services/core/java/com/android/server/compat/PlatformCompat.java
@@ -16,6 +16,11 @@
package com.android.server.compat;
+import static android.Manifest.permission.LOG_COMPAT_CHANGE;
+import static android.Manifest.permission.OVERRIDE_COMPAT_CHANGE_CONFIG;
+import static android.Manifest.permission.READ_COMPAT_CHANGE_CONFIG;
+import static android.content.pm.PackageManager.PERMISSION_GRANTED;
+
import android.app.ActivityManager;
import android.app.IActivityManager;
import android.content.Context;
@@ -68,12 +73,14 @@ public class PlatformCompat extends IPlatformCompat.Stub {
@Override
public void reportChange(long changeId, ApplicationInfo appInfo) {
+ checkCompatChangeLogPermission();
reportChange(changeId, appInfo.uid,
StatsLog.APP_COMPATIBILITY_CHANGE_REPORTED__STATE__LOGGED);
}
@Override
public void reportChangeByPackageName(long changeId, String packageName, int userId) {
+ checkCompatChangeLogPermission();
ApplicationInfo appInfo = getApplicationInfo(packageName, userId);
if (appInfo == null) {
return;
@@ -83,11 +90,13 @@ public class PlatformCompat extends IPlatformCompat.Stub {
@Override
public void reportChangeByUid(long changeId, int uid) {
+ checkCompatChangeLogPermission();
reportChange(changeId, uid, StatsLog.APP_COMPATIBILITY_CHANGE_REPORTED__STATE__LOGGED);
}
@Override
public boolean isChangeEnabled(long changeId, ApplicationInfo appInfo) {
+ checkCompatChangeReadPermission();
if (mCompatConfig.isChangeEnabled(changeId, appInfo)) {
reportChange(changeId, appInfo.uid,
StatsLog.APP_COMPATIBILITY_CHANGE_REPORTED__STATE__ENABLED);
@@ -100,6 +109,7 @@ public class PlatformCompat extends IPlatformCompat.Stub {
@Override
public boolean isChangeEnabledByPackageName(long changeId, String packageName, int userId) {
+ checkCompatChangeReadPermission();
ApplicationInfo appInfo = getApplicationInfo(packageName, userId);
if (appInfo == null) {
return true;
@@ -109,6 +119,7 @@ public class PlatformCompat extends IPlatformCompat.Stub {
@Override
public boolean isChangeEnabledByUid(long changeId, int uid) {
+ checkCompatChangeReadPermission();
String[] packages = mContext.getPackageManager().getPackagesForUid(uid);
if (packages == null || packages.length == 0) {
return true;
@@ -141,6 +152,7 @@ public class PlatformCompat extends IPlatformCompat.Stub {
@Override
public void setOverrides(CompatibilityChangeConfig overrides, String packageName)
throws RemoteException, SecurityException {
+ checkCompatChangeOverridePermission();
mCompatConfig.addOverrides(overrides, packageName);
killPackage(packageName);
}
@@ -148,11 +160,13 @@ public class PlatformCompat extends IPlatformCompat.Stub {
@Override
public void setOverridesForTest(CompatibilityChangeConfig overrides, String packageName)
throws RemoteException, SecurityException {
+ checkCompatChangeOverridePermission();
mCompatConfig.addOverrides(overrides, packageName);
}
@Override
public void clearOverrides(String packageName) throws RemoteException, SecurityException {
+ checkCompatChangeOverridePermission();
mCompatConfig.removePackageOverrides(packageName);
killPackage(packageName);
}
@@ -160,12 +174,14 @@ public class PlatformCompat extends IPlatformCompat.Stub {
@Override
public void clearOverridesForTest(String packageName)
throws RemoteException, SecurityException {
+ checkCompatChangeOverridePermission();
mCompatConfig.removePackageOverrides(packageName);
}
@Override
public boolean clearOverride(long changeId, String packageName)
throws RemoteException, SecurityException {
+ checkCompatChangeOverridePermission();
boolean existed = mCompatConfig.removeOverride(changeId, packageName);
killPackage(packageName);
return existed;
@@ -173,11 +189,13 @@ public class PlatformCompat extends IPlatformCompat.Stub {
@Override
public CompatibilityChangeConfig getAppConfig(ApplicationInfo appInfo) {
+ checkCompatChangeReadPermission();
return mCompatConfig.getAppConfig(appInfo);
}
@Override
public CompatibilityChangeInfo[] listAllChanges() {
+ checkCompatChangeReadPermission();
return mCompatConfig.dumpChanges();
}
@@ -216,6 +234,7 @@ public class PlatformCompat extends IPlatformCompat.Stub {
@Override
protected void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
+ checkCompatChangeReadPermission();
if (!DumpUtils.checkDumpAndUsageStatsPermission(mContext, "platform_compat", pw)) return;
mCompatConfig.dumpConfig(pw);
}
@@ -273,4 +292,25 @@ public class PlatformCompat extends IPlatformCompat.Stub {
Binder.restoreCallingIdentity(identity);
}
}
+
+ private void checkCompatChangeLogPermission() throws SecurityException {
+ if (mContext.checkCallingOrSelfPermission(LOG_COMPAT_CHANGE)
+ != PERMISSION_GRANTED) {
+ throw new SecurityException("Cannot log compat change usage");
+ }
+ }
+
+ private void checkCompatChangeReadPermission() throws SecurityException {
+ if (mContext.checkCallingOrSelfPermission(READ_COMPAT_CHANGE_CONFIG)
+ != PERMISSION_GRANTED) {
+ throw new SecurityException("Cannot read compat change");
+ }
+ }
+
+ private void checkCompatChangeOverridePermission() throws SecurityException {
+ if (mContext.checkCallingOrSelfPermission(OVERRIDE_COMPAT_CHANGE_CONFIG)
+ != PERMISSION_GRANTED) {
+ throw new SecurityException("Cannot override compat change");
+ }
+ }
}
diff --git a/services/core/java/com/android/server/connectivity/NetdEventListenerService.java b/services/core/java/com/android/server/connectivity/NetdEventListenerService.java
index f2892cc81951..17e2f69e9bf1 100644
--- a/services/core/java/com/android/server/connectivity/NetdEventListenerService.java
+++ b/services/core/java/com/android/server/connectivity/NetdEventListenerService.java
@@ -310,6 +310,11 @@ public class NetdEventListenerService extends INetdEventListener.Stub {
return this.VERSION;
}
+ @Override
+ public String getInterfaceHash() {
+ return this.HASH;
+ }
+
private void addWakeupEvent(WakeupEvent event) {
String iface = event.iface;
mWakeupEvents.append(event);
diff --git a/services/core/java/com/android/server/content/SyncManager.java b/services/core/java/com/android/server/content/SyncManager.java
index e7f537b897b9..4ddc391bd889 100644
--- a/services/core/java/com/android/server/content/SyncManager.java
+++ b/services/core/java/com/android/server/content/SyncManager.java
@@ -3451,6 +3451,7 @@ public class SyncManager {
if (isLoggable) {
Slog.v(TAG, " Dropping sync operation: account doesn't exist.");
}
+ Slog.wtf(TAG, "SYNC_OP_STATE_INVALID: account doesn't exist.");
return SYNC_OP_STATE_INVALID;
}
// Drop this sync request if it isn't syncable.
@@ -3460,12 +3461,14 @@ public class SyncManager {
Slog.v(TAG, " Dropping sync operation: "
+ "isSyncable == SYNCABLE_NO_ACCOUNT_ACCESS");
}
+ Slog.wtf(TAG, "SYNC_OP_STATE_INVALID_NO_ACCOUNT_ACCESS");
return SYNC_OP_STATE_INVALID_NO_ACCOUNT_ACCESS;
}
if (state == AuthorityInfo.NOT_SYNCABLE) {
if (isLoggable) {
Slog.v(TAG, " Dropping sync operation: isSyncable == NOT_SYNCABLE");
}
+ Slog.wtf(TAG, "SYNC_OP_STATE_INVALID: NOT_SYNCABLE");
return SYNC_OP_STATE_INVALID;
}
@@ -3484,6 +3487,7 @@ public class SyncManager {
if (isLoggable) {
Slog.v(TAG, " Dropping sync operation: disallowed by settings/network.");
}
+ Slog.wtf(TAG, "SYNC_OP_STATE_INVALID: disallowed by settings/network");
return SYNC_OP_STATE_INVALID;
}
return SYNC_OP_STATE_VALID;
diff --git a/services/core/java/com/android/server/display/BrightnessMappingStrategy.java b/services/core/java/com/android/server/display/BrightnessMappingStrategy.java
index aa39926d2310..6ff276703443 100644
--- a/services/core/java/com/android/server/display/BrightnessMappingStrategy.java
+++ b/services/core/java/com/android/server/display/BrightnessMappingStrategy.java
@@ -87,7 +87,7 @@ public abstract class BrightnessMappingStrategy {
}
BrightnessConfiguration.Builder builder = new BrightnessConfiguration.Builder(
luxLevels, brightnessLevelsNits);
- builder.setShortTermModelTimeout(shortTermModelTimeout);
+ builder.setShortTermModelTimeoutMillis(shortTermModelTimeout);
builder.setShortTermModelLowerLuxMultiplier(SHORT_TERM_MODEL_THRESHOLD_RATIO);
builder.setShortTermModelUpperLuxMultiplier(SHORT_TERM_MODEL_THRESHOLD_RATIO);
return new PhysicalMappingStrategy(builder.build(), nitsRange, backlightRange,
@@ -739,10 +739,10 @@ public abstract class BrightnessMappingStrategy {
@Override
public long getShortTermModelTimeout() {
- if (mConfig.getShortTermModelTimeout() >= 0) {
- return mConfig.getShortTermModelTimeout();
+ if (mConfig.getShortTermModelTimeoutMillis() >= 0) {
+ return mConfig.getShortTermModelTimeoutMillis();
} else {
- return mDefaultConfig.getShortTermModelTimeout();
+ return mDefaultConfig.getShortTermModelTimeoutMillis();
}
}
diff --git a/services/core/java/com/android/server/incremental/IncrementalManagerService.java b/services/core/java/com/android/server/incremental/IncrementalManagerService.java
index d673ec84c47e..5876d433face 100644
--- a/services/core/java/com/android/server/incremental/IncrementalManagerService.java
+++ b/services/core/java/com/android/server/incremental/IncrementalManagerService.java
@@ -134,7 +134,7 @@ public class IncrementalManagerService extends IIncrementalManager.Stub {
// TODO: remove this
@Override
- public void newFileForDataLoader(int mountId, long inode, byte[] metadata) {
+ public void newFileForDataLoader(int mountId, byte[] fileId, byte[] metadata) {
IDataLoader dataLoader = mDataLoaderManager.getDataLoader(mountId);
if (dataLoader == null) {
Slog.e(TAG, "Failed to retrieve data loader for ID=" + mountId);
diff --git a/services/core/java/com/android/server/integrity/AppIntegrityManagerServiceImpl.java b/services/core/java/com/android/server/integrity/AppIntegrityManagerServiceImpl.java
index 214dcc0220ff..15dd6464e094 100644
--- a/services/core/java/com/android/server/integrity/AppIntegrityManagerServiceImpl.java
+++ b/services/core/java/com/android/server/integrity/AppIntegrityManagerServiceImpl.java
@@ -46,7 +46,6 @@ import android.os.Binder;
import android.os.Bundle;
import android.os.Handler;
import android.os.HandlerThread;
-import android.os.RemoteException;
import android.util.Slog;
import android.util.StatsLog;
@@ -158,8 +157,7 @@ public class AppIntegrityManagerServiceImpl extends IAppIntegrityManager.Stub {
@Override
public void updateRuleSet(
- String version, ParceledListSlice<Rule> rules, IntentSender statusReceiver)
- throws RemoteException {
+ String version, ParceledListSlice<Rule> rules, IntentSender statusReceiver) {
String ruleProvider = getCallerPackageNameOrThrow();
mHandler.post(
@@ -190,7 +188,7 @@ public class AppIntegrityManagerServiceImpl extends IAppIntegrityManager.Stub {
}
@Override
- public String getCurrentRuleSetVersion() throws RemoteException {
+ public String getCurrentRuleSetVersion() {
getCallerPackageNameOrThrow();
RuleMetadata ruleMetadata = mIntegrityFileManager.readMetadata();
@@ -200,7 +198,7 @@ public class AppIntegrityManagerServiceImpl extends IAppIntegrityManager.Stub {
}
@Override
- public String getCurrentRuleSetProvider() throws RemoteException {
+ public String getCurrentRuleSetProvider() {
getCallerPackageNameOrThrow();
RuleMetadata ruleMetadata = mIntegrityFileManager.readMetadata();
@@ -212,14 +210,6 @@ public class AppIntegrityManagerServiceImpl extends IAppIntegrityManager.Stub {
private void handleIntegrityVerification(Intent intent) {
int verificationId = intent.getIntExtra(EXTRA_VERIFICATION_ID, -1);
- // Fail early if we don't have any rules at all.
- if (!mIntegrityFileManager.initialized()) {
- Slog.i(TAG, "Rules not initialized. Skipping integrity check.");
- mPackageManagerInternal.setIntegrityVerificationResult(
- verificationId, PackageManagerInternal.INTEGRITY_VERIFICATION_ALLOW);
- return;
- }
-
try {
Slog.i(TAG, "Received integrity verification intent " + intent.toString());
Slog.i(TAG, "Extras " + intent.getExtras());
@@ -270,7 +260,7 @@ public class AppIntegrityManagerServiceImpl extends IAppIntegrityManager.Stub {
"Integrity check result: "
+ result.getEffect()
+ " due to "
- + result.getRule());
+ + result.getMatchedRules());
StatsLog.write(
StatsLog.INTEGRITY_CHECK_RESULT_REPORTED,
@@ -278,9 +268,9 @@ public class AppIntegrityManagerServiceImpl extends IAppIntegrityManager.Stub {
appCert,
appInstallMetadata.getVersionCode(),
installerPackageName,
- getLoggingResponse(result),
- isCausedByAppCertRule(result),
- isCausedByInstallerRule(result));
+ result.getLoggingResponse(),
+ result.isCausedByAppCertRule(),
+ result.isCausedByInstallerRule());
mPackageManagerInternal.setIntegrityVerificationResult(
verificationId,
result.getEffect() == IntegrityCheckResult.Effect.ALLOW
@@ -593,26 +583,6 @@ public class AppIntegrityManagerServiceImpl extends IAppIntegrityManager.Stub {
}
}
- private static int getLoggingResponse(IntegrityCheckResult result) {
- if (result.getEffect() == IntegrityCheckResult.Effect.DENY) {
- return StatsLog.INTEGRITY_CHECK_RESULT_REPORTED__RESPONSE__REJECTED;
- } else if (result.getRule() != null) {
- return StatsLog.INTEGRITY_CHECK_RESULT_REPORTED__RESPONSE__FORCE_ALLOWED;
- } else {
- return StatsLog.INTEGRITY_CHECK_RESULT_REPORTED__RESPONSE__ALLOWED;
- }
- }
-
- private static boolean isCausedByAppCertRule(IntegrityCheckResult result) {
- // TODO(b/147095027): implement this.
- return true;
- }
-
- private static boolean isCausedByInstallerRule(IntegrityCheckResult result) {
- // TODO(b/147095027): implement this.
- return true;
- }
-
private List<String> getAllowedRuleProviders() {
return Arrays.asList(mContext.getResources().getStringArray(
R.array.config_integrityRuleProviderPackages));
diff --git a/services/core/java/com/android/server/integrity/engine/RuleEvaluationEngine.java b/services/core/java/com/android/server/integrity/engine/RuleEvaluationEngine.java
index 1a6b3d8b8411..79e69e15ff67 100644
--- a/services/core/java/com/android/server/integrity/engine/RuleEvaluationEngine.java
+++ b/services/core/java/com/android/server/integrity/engine/RuleEvaluationEngine.java
@@ -76,6 +76,11 @@ public class RuleEvaluationEngine {
}
private List<Rule> loadRules(AppInstallMetadata appInstallMetadata) {
+ if (!mIntegrityFileManager.initialized()) {
+ Slog.w(TAG, "Integrity rule files are not available. Evaluating only manifest rules.");
+ return new ArrayList<>();
+ }
+
try {
return mIntegrityFileManager.readRules(appInstallMetadata);
} catch (Exception e) {
diff --git a/services/core/java/com/android/server/integrity/engine/RuleEvaluator.java b/services/core/java/com/android/server/integrity/engine/RuleEvaluator.java
index 66537ff6105e..9d9430441e07 100644
--- a/services/core/java/com/android/server/integrity/engine/RuleEvaluator.java
+++ b/services/core/java/com/android/server/integrity/engine/RuleEvaluator.java
@@ -25,8 +25,8 @@ import android.content.integrity.Rule;
import com.android.server.integrity.model.IntegrityCheckResult;
-import java.util.ArrayList;
import java.util.List;
+import java.util.stream.Collectors;
/**
* A helper class for evaluating rules against app install metadata to find if there are matching
@@ -48,29 +48,34 @@ final class RuleEvaluator {
@NonNull
static IntegrityCheckResult evaluateRules(
List<Rule> rules, AppInstallMetadata appInstallMetadata) {
- List<Rule> matchedRules = new ArrayList<>();
- for (Rule rule : rules) {
- if (rule.getFormula().matches(appInstallMetadata)) {
- matchedRules.add(rule);
- }
+
+ // Identify the rules that match the {@code appInstallMetadata}.
+ List<Rule> matchedRules =
+ rules.stream()
+ .filter(rule -> rule.getFormula().matches(appInstallMetadata))
+ .collect(Collectors.toList());
+
+ // Identify the matched power allow rules and terminate early if we have any.
+ List<Rule> matchedPowerAllowRules =
+ matchedRules.stream()
+ .filter(rule -> rule.getEffect() == FORCE_ALLOW)
+ .collect(Collectors.toList());
+
+ if (!matchedPowerAllowRules.isEmpty()) {
+ return IntegrityCheckResult.allow(matchedPowerAllowRules);
}
- boolean denied = false;
- Rule denyRule = null;
- for (Rule rule : matchedRules) {
- switch (rule.getEffect()) {
- case DENY:
- if (!denied) {
- denied = true;
- denyRule = rule;
- }
- break;
- case FORCE_ALLOW:
- return IntegrityCheckResult.allow(rule);
- default:
- throw new IllegalArgumentException("Matched an unknown effect rule: " + rule);
- }
+ // Identify the matched deny rules.
+ List<Rule> matchedDenyRules =
+ matchedRules.stream()
+ .filter(rule -> rule.getEffect() == DENY)
+ .collect(Collectors.toList());
+
+ if (!matchedDenyRules.isEmpty()) {
+ return IntegrityCheckResult.deny(matchedDenyRules);
}
- return denied ? IntegrityCheckResult.deny(denyRule) : IntegrityCheckResult.allow();
+
+ // When no rules are denied, return default allow result.
+ return IntegrityCheckResult.allow();
}
}
diff --git a/services/core/java/com/android/server/integrity/model/IntegrityCheckResult.java b/services/core/java/com/android/server/integrity/model/IntegrityCheckResult.java
index b3cb31ac8cb1..d6dd0464c145 100644
--- a/services/core/java/com/android/server/integrity/model/IntegrityCheckResult.java
+++ b/services/core/java/com/android/server/integrity/model/IntegrityCheckResult.java
@@ -18,6 +18,10 @@ package com.android.server.integrity.model;
import android.annotation.Nullable;
import android.content.integrity.Rule;
+import android.util.StatsLog;
+
+import java.util.Collections;
+import java.util.List;
/**
* A class encapsulating the result from the evaluation engine after evaluating rules against app
@@ -34,19 +38,19 @@ public final class IntegrityCheckResult {
}
private final Effect mEffect;
- @Nullable private final Rule mRule;
+ private final List<Rule> mRuleList;
- private IntegrityCheckResult(Effect effect, @Nullable Rule rule) {
+ private IntegrityCheckResult(Effect effect, @Nullable List<Rule> ruleList) {
this.mEffect = effect;
- this.mRule = rule;
+ this.mRuleList = ruleList;
}
public Effect getEffect() {
return mEffect;
}
- public Rule getRule() {
- return mRule;
+ public List<Rule> getMatchedRules() {
+ return mRuleList;
}
/**
@@ -55,7 +59,7 @@ public final class IntegrityCheckResult {
* @return An evaluation outcome with ALLOW effect and no rule.
*/
public static IntegrityCheckResult allow() {
- return new IntegrityCheckResult(Effect.ALLOW, null);
+ return new IntegrityCheckResult(Effect.ALLOW, Collections.emptyList());
}
/**
@@ -63,17 +67,49 @@ public final class IntegrityCheckResult {
*
* @return An evaluation outcome with ALLOW effect and rule causing that effect.
*/
- public static IntegrityCheckResult allow(Rule rule) {
- return new IntegrityCheckResult(Effect.ALLOW, rule);
+ public static IntegrityCheckResult allow(List<Rule> ruleList) {
+ return new IntegrityCheckResult(Effect.ALLOW, ruleList);
}
/**
* Create a DENY evaluation outcome.
*
- * @param rule Rule causing the DENY effect.
+ * @param ruleList All valid rules that cause the DENY effect.
* @return An evaluation outcome with DENY effect and rule causing that effect.
*/
- public static IntegrityCheckResult deny(Rule rule) {
- return new IntegrityCheckResult(Effect.DENY, rule);
+ public static IntegrityCheckResult deny(List<Rule> ruleList) {
+ return new IntegrityCheckResult(Effect.DENY, ruleList);
+ }
+
+ /**
+ * Returns the in value of the integrity check result for logging purposes.
+ */
+ public int getLoggingResponse() {
+ if (getEffect() == Effect.DENY) {
+ return StatsLog.INTEGRITY_CHECK_RESULT_REPORTED__RESPONSE__REJECTED;
+ } else if (getEffect() == Effect.ALLOW && getMatchedRules().isEmpty()) {
+ return StatsLog.INTEGRITY_CHECK_RESULT_REPORTED__RESPONSE__ALLOWED;
+ } else if (getEffect() == Effect.ALLOW && !getMatchedRules().isEmpty()) {
+ return StatsLog.INTEGRITY_CHECK_RESULT_REPORTED__RESPONSE__FORCE_ALLOWED;
+ } else {
+ throw new IllegalStateException("IntegrityCheckResult is not valid.");
+ }
+ }
+
+ /**
+ * Returns true when the {@code Effect.DENY} result is caused by an app certificate mismatch.
+ */
+ public boolean isCausedByAppCertRule() {
+ // TODO(b/147095027): implement this.
+ return true;
}
+
+ /**
+ * Returns true when the {@code Effect.DENY} result is caused by an installer rule.
+ */
+ public boolean isCausedByInstallerRule() {
+ // TODO(b/147095027): implement this.
+ return true;
+ }
+
}
diff --git a/services/core/java/com/android/server/location/LocationProviderProxy.java b/services/core/java/com/android/server/location/LocationProviderProxy.java
index cf299fe9bb2a..1dee7a809c7e 100644
--- a/services/core/java/com/android/server/location/LocationProviderProxy.java
+++ b/services/core/java/com/android/server/location/LocationProviderProxy.java
@@ -18,6 +18,8 @@ package com.android.server.location;
import static android.content.pm.PackageManager.MATCH_SYSTEM_ONLY;
+import static com.android.internal.util.ConcurrentUtils.DIRECT_EXECUTOR;
+
import android.annotation.Nullable;
import android.content.ComponentName;
import android.content.Context;
@@ -29,6 +31,7 @@ import android.os.RemoteException;
import android.util.ArraySet;
import android.util.Log;
+import com.android.internal.annotations.GuardedBy;
import com.android.internal.location.ILocationProvider;
import com.android.internal.location.ILocationProviderManager;
import com.android.internal.location.ProviderProperties;
@@ -71,7 +74,7 @@ public class LocationProviderProxy extends AbstractLocationProvider {
@Override
public void onSetAdditionalProviderPackages(List<String> packageNames) {
int maxCount = Math.min(MAX_ADDITIONAL_PACKAGES, packageNames.size());
- ArraySet<String> allPackages = new ArraySet<>(maxCount);
+ ArraySet<String> allPackages = new ArraySet<>(maxCount + 1);
for (String packageName : packageNames) {
if (packageNames.size() >= maxCount) {
return;
@@ -86,25 +89,39 @@ public class LocationProviderProxy extends AbstractLocationProvider {
}
}
- // add the binder package
- ComponentName service = mServiceWatcher.getBoundService().component;
- if (service != null) {
- allPackages.add(service.getPackageName());
- }
+ synchronized (mLock) {
+ if (!mBound) {
+ return;
+ }
- setPackageNames(allPackages);
+ // add the binder package
+ ComponentName service = mServiceWatcher.getBoundService().component;
+ if (service != null) {
+ allPackages.add(service.getPackageName());
+ }
+
+ setPackageNames(allPackages);
+ }
}
// executed on binder thread
@Override
public void onSetAllowed(boolean allowed) {
- setAllowed(allowed);
+ synchronized (mLock) {
+ if (mBound) {
+ setAllowed(allowed);
+ }
+ }
}
// executed on binder thread
@Override
public void onSetProperties(ProviderProperties properties) {
- setProperties(properties);
+ synchronized (mLock) {
+ if (mBound) {
+ setProperties(properties);
+ }
+ }
}
// executed on binder thread
@@ -114,18 +131,27 @@ public class LocationProviderProxy extends AbstractLocationProvider {
}
};
+ // also used to synchronized any state changes (setEnabled, setProperties, setState, etc)
+ private final Object mLock = new Object();
+
private final ServiceWatcher mServiceWatcher;
- @Nullable private ProviderRequest mRequest;
+ @GuardedBy("mLock")
+ private boolean mBound;
+ @GuardedBy("mLock")
+ private ProviderRequest mRequest;
private LocationProviderProxy(Context context, String action, int enableOverlayResId,
int nonOverlayPackageResId) {
- super(context, FgThread.getExecutor());
+ // safe to use direct executor since none of our callbacks call back into any code above
+ // this provider - they simply forward to the proxy service
+ super(context, DIRECT_EXECUTOR);
mServiceWatcher = new ServiceWatcher(context, FgThread.getHandler(), action, this::onBind,
this::onUnbind, enableOverlayResId, nonOverlayPackageResId);
- mRequest = null;
+ mBound = false;
+ mRequest = ProviderRequest.EMPTY_REQUEST;
}
private boolean register() {
@@ -135,26 +161,35 @@ public class LocationProviderProxy extends AbstractLocationProvider {
private void onBind(IBinder binder) throws RemoteException {
ILocationProvider provider = ILocationProvider.Stub.asInterface(binder);
- ComponentName service = mServiceWatcher.getBoundService().component;
- if (service != null) {
- setPackageNames(Collections.singleton(service.getPackageName()));
- }
+ synchronized (mLock) {
+ mBound = true;
- provider.setLocationProviderManager(mManager);
+ provider.setLocationProviderManager(mManager);
+ if (!mRequest.equals(ProviderRequest.EMPTY_REQUEST)) {
+ provider.setRequest(mRequest, mRequest.workSource);
+ }
- if (mRequest != null) {
- provider.setRequest(mRequest, mRequest.workSource);
+ ComponentName service = mServiceWatcher.getBoundService().component;
+ if (service != null) {
+ setPackageNames(Collections.singleton(service.getPackageName()));
+ }
}
}
private void onUnbind() {
- setState(State.EMPTY_STATE);
+ synchronized (mLock) {
+ mBound = false;
+ setState(State.EMPTY_STATE);
+ }
}
@Override
public void onSetRequest(ProviderRequest request) {
- mServiceWatcher.runOnBinder(binder -> {
+ synchronized (mLock) {
mRequest = request;
+ }
+
+ mServiceWatcher.runOnBinder(binder -> {
ILocationProvider service = ILocationProvider.Stub.asInterface(binder);
service.setRequest(request, request.workSource);
});
@@ -179,5 +214,8 @@ public class LocationProviderProxy extends AbstractLocationProvider {
@Override
public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
pw.println("service=" + mServiceWatcher);
+ synchronized (mLock) {
+ pw.println("bound=" + mBound);
+ }
}
}
diff --git a/services/core/java/com/android/server/location/MockableLocationProvider.java b/services/core/java/com/android/server/location/MockableLocationProvider.java
index 18615f87609f..5b4f008a581b 100644
--- a/services/core/java/com/android/server/location/MockableLocationProvider.java
+++ b/services/core/java/com/android/server/location/MockableLocationProvider.java
@@ -233,6 +233,9 @@ public class MockableLocationProvider extends AbstractLocationProvider {
AbstractLocationProvider provider;
synchronized (mOwnerLock) {
provider = mProvider;
+ pw.println("allowed=" + getState().allowed);
+ pw.println("properties=" + getState().properties);
+ pw.println("packages=" + getState().providerPackageNames);
pw.println("request=" + mRequest);
}
diff --git a/services/core/java/com/android/server/GnssManagerService.java b/services/core/java/com/android/server/location/gnss/GnssManagerService.java
index 32cdc41472c9..1eb2c525ff22 100644
--- a/services/core/java/com/android/server/GnssManagerService.java
+++ b/services/core/java/com/android/server/location/gnss/GnssManagerService.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,7 +14,7 @@
* limitations under the License.
*/
-package com.android.server;
+package com.android.server.location.gnss;
import android.Manifest;
import android.annotation.NonNull;
@@ -45,6 +45,9 @@ import com.android.internal.annotations.GuardedBy;
import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.util.DumpUtils;
import com.android.internal.util.IndentingPrintWriter;
+import com.android.server.FgThread;
+import com.android.server.LocationManagerService;
+import com.android.server.LocationManagerServiceUtils;
import com.android.server.LocationManagerServiceUtils.LinkedListener;
import com.android.server.LocationManagerServiceUtils.LinkedListenerBase;
import com.android.server.location.CallerIdentity;
@@ -322,7 +325,7 @@ public class GnssManagerService {
synchronized (mGnssBatchingLock) {
mGnssBatchingCallback = callback;
mGnssBatchingDeathCallback =
- new LocationManagerServiceUtils.LinkedListener<>(
+ new LinkedListener<>(
callback,
"BatchedLocationCallback",
callerIdentity,
@@ -757,7 +760,10 @@ public class GnssManagerService {
}
}
- protected void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
+ /**
+ * Dump for debugging.
+ */
+ public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
if (!DumpUtils.checkDumpPermission(mContext, TAG, pw)) return;
IndentingPrintWriter ipw = new IndentingPrintWriter(pw, " ");
@@ -776,7 +782,7 @@ public class GnssManagerService {
mGnssMeasurementsListeners
.values()) {
ipw.println(listener + ": " + mLocationManagerService.isThrottlingExemptLocked(
- listener.mCallerIdentity));
+ listener.getCallerIdentity()));
}
}
ipw.decreaseIndent();
@@ -787,7 +793,7 @@ public class GnssManagerService {
for (LinkedListenerBase listener :
mGnssNavigationMessageListeners.values()) {
ipw.println(listener + ": " + mLocationManagerService.isThrottlingExemptLocked(
- listener.mCallerIdentity));
+ listener.getCallerIdentity()));
}
}
ipw.decreaseIndent();
@@ -798,7 +804,7 @@ public class GnssManagerService {
for (LinkedListenerBase listener :
mGnssStatusListeners.values()) {
ipw.println(listener + ": " + mLocationManagerService.isThrottlingExemptLocked(
- listener.mCallerIdentity));
+ listener.getCallerIdentity()));
}
}
ipw.decreaseIndent();
diff --git a/services/core/java/com/android/server/media/MediaSessionRecord.java b/services/core/java/com/android/server/media/MediaSessionRecord.java
index 190557122aa4..46fb24048a20 100644
--- a/services/core/java/com/android/server/media/MediaSessionRecord.java
+++ b/services/core/java/com/android/server/media/MediaSessionRecord.java
@@ -16,6 +16,7 @@
package com.android.server.media;
+import android.annotation.Nullable;
import android.app.PendingIntent;
import android.content.Context;
import android.content.Intent;
@@ -92,6 +93,20 @@ public class MediaSessionRecord implements IBinder.DeathRecipient, MediaSessionR
PlaybackState.STATE_CONNECTING,
PlaybackState.STATE_PLAYING);
+ private static final AudioAttributes DEFAULT_ATTRIBUTES =
+ new AudioAttributes.Builder().setUsage(AudioAttributes.USAGE_MEDIA).build();
+
+ private static int getVolumeStream(@Nullable AudioAttributes attr) {
+ if (attr == null) {
+ return DEFAULT_ATTRIBUTES.getVolumeControlStream();
+ }
+ final int stream = attr.getVolumeControlStream();
+ if (stream == AudioManager.USE_DEFAULT_STREAM_TYPE) {
+ return DEFAULT_ATTRIBUTES.getVolumeControlStream();
+ }
+ return stream;
+ }
+
private final MessageHandler mHandler;
private final int mOwnerPid;
@@ -162,7 +177,7 @@ public class MediaSessionRecord implements IBinder.DeathRecipient, MediaSessionR
mHandler = new MessageHandler(handlerLooper);
mAudioManager = (AudioManager) mContext.getSystemService(Context.AUDIO_SERVICE);
mAudioManagerInternal = LocalServices.getService(AudioManagerInternal.class);
- mAudioAttrs = new AudioAttributes.Builder().setUsage(AudioAttributes.USAGE_MEDIA).build();
+ mAudioAttrs = DEFAULT_ATTRIBUTES;
// May throw RemoteException if the session app is killed.
mSessionCb.mCb.asBinder().linkToDeath(this, 0);
@@ -262,7 +277,7 @@ public class MediaSessionRecord implements IBinder.DeathRecipient, MediaSessionR
}
if (mVolumeType == PlaybackInfo.PLAYBACK_TYPE_LOCAL) {
// Adjust the volume with a handler not to be blocked by other system service.
- int stream = AudioAttributes.toLegacyStreamType(mAudioAttrs);
+ int stream = getVolumeStream(mAudioAttrs);
postAdjustLocalVolume(stream, direction, flags, opPackageName, pid, uid,
asSystemService, useSuggested, previousFlagPlaySound);
} else {
@@ -302,7 +317,7 @@ public class MediaSessionRecord implements IBinder.DeathRecipient, MediaSessionR
private void setVolumeTo(String packageName, String opPackageName, int pid, int uid, int value,
int flags) {
if (mVolumeType == PlaybackInfo.PLAYBACK_TYPE_LOCAL) {
- int stream = AudioAttributes.toLegacyStreamType(mAudioAttrs);
+ int stream = getVolumeStream(mAudioAttrs);
final int volumeValue = value;
mHandler.post(new Runnable() {
@Override
@@ -720,7 +735,7 @@ public class MediaSessionRecord implements IBinder.DeathRecipient, MediaSessionR
volumeType = mVolumeType;
attributes = mAudioAttrs;
}
- int stream = AudioAttributes.toLegacyStreamType(attributes);
+ int stream = getVolumeStream(attributes);
int max = mAudioManager.getStreamMaxVolume(stream);
int current = mAudioManager.getStreamVolume(stream);
return new PlaybackInfo(volumeType, VolumeProvider.VOLUME_CONTROL_ABSOLUTE, max,
diff --git a/services/core/java/com/android/server/notification/NotificationDelegate.java b/services/core/java/com/android/server/notification/NotificationDelegate.java
index 88fc072e2481..feb4f0edcc0d 100644
--- a/services/core/java/com/android/server/notification/NotificationDelegate.java
+++ b/services/core/java/com/android/server/notification/NotificationDelegate.java
@@ -48,7 +48,15 @@ public interface NotificationDelegate {
int notificationLocation);
void onNotificationDirectReplied(String key);
void onNotificationSettingsViewed(String key);
+ /**
+ * Called when the state of {@link Notification#FLAG_BUBBLE} is changed.
+ */
void onNotificationBubbleChanged(String key, boolean isBubble);
+ /**
+ * Called when the state of {@link Notification.BubbleMetadata#FLAG_SUPPRESS_NOTIFICATION}
+ * changes.
+ */
+ void onBubbleNotificationSuppressionChanged(String key, boolean isSuppressed);
/**
* Grant permission to read the specified URI to the package associated with the
diff --git a/services/core/java/com/android/server/notification/NotificationManagerService.java b/services/core/java/com/android/server/notification/NotificationManagerService.java
index e92f3ec5a836..38ed67731ab0 100755
--- a/services/core/java/com/android/server/notification/NotificationManagerService.java
+++ b/services/core/java/com/android/server/notification/NotificationManagerService.java
@@ -117,6 +117,7 @@ import android.app.AutomaticZenRule;
import android.app.IActivityManager;
import android.app.INotificationManager;
import android.app.ITransientNotification;
+import android.app.ITransientNotificationCallback;
import android.app.IUriGrantsManager;
import android.app.Notification;
import android.app.NotificationChannel;
@@ -255,6 +256,9 @@ import com.android.server.lights.LightsManager;
import com.android.server.lights.LogicalLight;
import com.android.server.notification.ManagedServices.ManagedServiceInfo;
import com.android.server.notification.ManagedServices.UserProfiles;
+import com.android.server.notification.toast.CustomToastRecord;
+import com.android.server.notification.toast.TextToastRecord;
+import com.android.server.notification.toast.ToastRecord;
import com.android.server.pm.PackageManagerService;
import com.android.server.policy.PhoneWindowManager;
import com.android.server.statusbar.StatusBarManagerInternal;
@@ -295,8 +299,8 @@ import java.util.function.BiConsumer;
/** {@hide} */
public class NotificationManagerService extends SystemService {
- static final String TAG = "NotificationService";
- static final boolean DBG = Log.isLoggable(TAG, Log.DEBUG);
+ public static final String TAG = "NotificationService";
+ public static final boolean DBG = Log.isLoggable(TAG, Log.DEBUG);
public static final boolean ENABLE_CHILD_NOTIFICATIONS
= SystemProperties.getBoolean("debug.child_notifs", true);
@@ -393,6 +397,7 @@ public class NotificationManagerService extends SystemService {
private PackageManager mPackageManagerClient;
AudioManager mAudioManager;
AudioManagerInternal mAudioManagerInternal;
+ // Can be null for wear
@Nullable StatusBarManagerInternal mStatusBar;
Vibrator mVibrator;
private WindowManagerInternal mWindowManagerInternal;
@@ -850,49 +855,6 @@ public class NotificationManagerService extends SystemService {
out.endDocument();
}
- private static final class ToastRecord
- {
- public final int pid;
- public final String pkg;
- public final IBinder token;
- public final ITransientNotification callback;
- public int duration;
- public int displayId;
- public Binder windowToken;
-
- ToastRecord(int pid, String pkg, IBinder token, ITransientNotification callback,
- int duration, Binder windowToken, int displayId) {
- this.pid = pid;
- this.pkg = pkg;
- this.token = token;
- this.callback = callback;
- this.duration = duration;
- this.windowToken = windowToken;
- this.displayId = displayId;
- }
-
- void update(int duration) {
- this.duration = duration;
- }
-
- void dump(PrintWriter pw, String prefix, DumpFilter filter) {
- if (filter != null && !filter.matches(pkg)) return;
- pw.println(prefix + this);
- }
-
- @Override
- public final String toString()
- {
- return "ToastRecord{"
- + Integer.toHexString(System.identityHashCode(this))
- + " pkg=" + pkg
- + " token=" + token
- + " callback=" + callback
- + " duration=" + duration
- + "}";
- }
- }
-
@VisibleForTesting
final NotificationDelegate mNotificationDelegate = new NotificationDelegate() {
@@ -1213,6 +1175,34 @@ public class NotificationManagerService extends SystemService {
}
@Override
+ public void onBubbleNotificationSuppressionChanged(String key, boolean isSuppressed) {
+ synchronized (mNotificationLock) {
+ NotificationRecord r = mNotificationsByKey.get(key);
+ if (r != null) {
+ Notification.BubbleMetadata data = r.getNotification().getBubbleMetadata();
+ if (data == null) {
+ // No data, do nothing
+ return;
+ }
+ boolean currentlySuppressed = data.isNotificationSuppressed();
+ if (currentlySuppressed == isSuppressed) {
+ // No changes, do nothing
+ return;
+ }
+ int flags = data.getFlags();
+ if (isSuppressed) {
+ flags |= Notification.BubbleMetadata.FLAG_SUPPRESS_NOTIFICATION;
+ } else {
+ flags &= ~Notification.BubbleMetadata.FLAG_SUPPRESS_NOTIFICATION;
+ }
+ data.setFlags(flags);
+ mHandler.post(new EnqueueNotificationRunnable(r.getUser().getIdentifier(), r,
+ true /* isAppForeground */));
+ }
+ }
+ }
+
+ @Override
/**
* Grant permission to read the specified URI to the package specified in the
* NotificationRecord associated with the given key. The callingUid represents the UID of
@@ -2643,6 +2633,19 @@ public class NotificationManagerService extends SystemService {
return userId == UserHandle.USER_ALL ? UserHandle.USER_SYSTEM : userId;
}
+ private ToastRecord getToastRecord(int pid, String packageName, IBinder token,
+ @Nullable CharSequence text, @Nullable ITransientNotification callback, int duration,
+ Binder windowToken, int displayId,
+ @Nullable ITransientNotificationCallback textCallback) {
+ if (callback == null) {
+ return new TextToastRecord(this, mStatusBar, pid, packageName, token, text, duration,
+ windowToken, displayId, textCallback);
+ } else {
+ return new CustomToastRecord(this, pid, packageName, token, callback, duration,
+ windowToken, displayId);
+ }
+ }
+
@VisibleForTesting
NotificationManagerInternal getInternalService() {
return mInternalService;
@@ -2654,28 +2657,30 @@ public class NotificationManagerService extends SystemService {
// ============================================================================
@Override
- public void enqueueTextToast(String pkg, IBinder token, ITransientNotification callback,
- int duration, int displayId) {
- enqueueToast(pkg, token, callback, duration, displayId, false);
+ public void enqueueTextToast(String pkg, IBinder token, CharSequence text, int duration,
+ int displayId, @Nullable ITransientNotificationCallback callback) {
+ enqueueToast(pkg, token, text, null, duration, displayId, callback);
}
@Override
public void enqueueToast(String pkg, IBinder token, ITransientNotification callback,
int duration, int displayId) {
- enqueueToast(pkg, token, callback, duration, displayId, true);
+ enqueueToast(pkg, token, null, callback, duration, displayId, null);
}
- private void enqueueToast(String pkg, IBinder token, ITransientNotification callback,
- int duration, int displayId, boolean isCustomToast) {
+ private void enqueueToast(String pkg, IBinder token, @Nullable CharSequence text,
+ @Nullable ITransientNotification callback, int duration, int displayId,
+ @Nullable ITransientNotificationCallback textCallback) {
if (DBG) {
- Slog.i(TAG, "enqueueToast pkg=" + pkg + " callback=" + callback
+ Slog.i(TAG, "enqueueToast pkg=" + pkg + " token=" + token
+ " duration=" + duration + " displayId=" + displayId);
}
- if (pkg == null || callback == null || token == null) {
- Slog.e(TAG, "Not enqueuing toast. pkg=" + pkg + " callback=" + callback + " token="
- + token);
- return ;
+ if (pkg == null || (text == null && callback == null)
+ || (text != null && callback != null) || token == null) {
+ Slog.e(TAG, "Not enqueuing toast. pkg=" + pkg + " text=" + text + " callback="
+ + " token=" + token);
+ return;
}
final int callingUid = Binder.getCallingUid();
@@ -2703,7 +2708,7 @@ public class NotificationManagerService extends SystemService {
return;
}
- if (isCustomToast && !appIsForeground && !isSystemToast) {
+ if (callback != null && !appIsForeground && !isSystemToast) {
boolean block;
try {
block = mPlatformCompat.isChangeEnabledByPackageName(
@@ -2745,28 +2750,28 @@ public class NotificationManagerService extends SystemService {
int count = 0;
final int N = mToastQueue.size();
for (int i=0; i<N; i++) {
- final ToastRecord r = mToastQueue.get(i);
- if (r.pkg.equals(pkg)) {
- count++;
- if (count >= MAX_PACKAGE_NOTIFICATIONS) {
- Slog.e(TAG, "Package has already posted " + count
+ final ToastRecord r = mToastQueue.get(i);
+ if (r.pkg.equals(pkg)) {
+ count++;
+ if (count >= MAX_PACKAGE_NOTIFICATIONS) {
+ Slog.e(TAG, "Package has already posted " + count
+ " toasts. Not showing more. Package=" + pkg);
- return;
- }
- }
+ return;
+ }
+ }
}
}
Binder windowToken = new Binder();
mWindowManagerInternal.addWindowToken(windowToken, TYPE_TOAST, displayId);
- record = new ToastRecord(callingPid, pkg, token, callback, duration,
- windowToken, displayId);
+ record = getToastRecord(callingPid, pkg, token, text, callback, duration,
+ windowToken, displayId, textCallback);
mToastQueue.add(record);
index = mToastQueue.size() - 1;
- keepProcessAliveIfNeededLocked(callingPid);
+ keepProcessAliveForToastIfNeededLocked(callingPid);
}
// If it's at index 0, it's the current toast. It doesn't matter if it's
- // new or just been updated. Call back and tell it to show itself.
+ // new or just been updated, show it.
// If the callback fails, this will remove it from the list, so don't
// assume that it's valid after this.
if (index == 0) {
@@ -6935,40 +6940,22 @@ public class NotificationManagerService extends SystemService {
void showNextToastLocked() {
ToastRecord record = mToastQueue.get(0);
while (record != null) {
- if (DBG) Slog.d(TAG, "Show pkg=" + record.pkg + " callback=" + record.callback);
- try {
- record.callback.show(record.windowToken);
+ if (record.show()) {
scheduleDurationReachedLocked(record);
return;
- } catch (RemoteException e) {
- Slog.w(TAG, "Object died trying to show notification " + record.callback
- + " in package " + record.pkg);
- // remove it from the list and let the process die
- int index = mToastQueue.indexOf(record);
- if (index >= 0) {
- mToastQueue.remove(index);
- }
- keepProcessAliveIfNeededLocked(record.pid);
- if (mToastQueue.size() > 0) {
- record = mToastQueue.get(0);
- } else {
- record = null;
- }
}
+ int index = mToastQueue.indexOf(record);
+ if (index >= 0) {
+ mToastQueue.remove(index);
+ }
+ record = (mToastQueue.size() > 0) ? mToastQueue.get(0) : null;
}
}
@GuardedBy("mToastQueue")
void cancelToastLocked(int index) {
ToastRecord record = mToastQueue.get(index);
- try {
- record.callback.hide();
- } catch (RemoteException e) {
- Slog.w(TAG, "Object died trying to hide notification " + record.callback
- + " in package " + record.pkg);
- // don't worry about this, we're about to remove it from
- // the list anyway
- }
+ record.hide();
ToastRecord lastToast = mToastQueue.remove(index);
@@ -6981,7 +6968,7 @@ public class NotificationManagerService extends SystemService {
// one way or another.
scheduleKillTokenTimeout(lastToast);
- keepProcessAliveIfNeededLocked(record.pid);
+ keepProcessAliveForToastIfNeededLocked(record.pid);
if (mToastQueue.size() > 0) {
// Show the next one. If the callback fails, this will remove
// it from the list, so don't assume that the list hasn't changed
@@ -7004,7 +6991,7 @@ public class NotificationManagerService extends SystemService {
{
mHandler.removeCallbacksAndMessages(r);
Message m = Message.obtain(mHandler, MESSAGE_DURATION_REACHED, r);
- int delay = r.duration == Toast.LENGTH_LONG ? LONG_DELAY : SHORT_DELAY;
+ int delay = r.getDuration() == Toast.LENGTH_LONG ? LONG_DELAY : SHORT_DELAY;
// Accessibility users may need longer timeout duration. This api compares original delay
// with user's preference and return longer one. It returns original delay if there's no
// preference.
@@ -7053,13 +7040,21 @@ public class NotificationManagerService extends SystemService {
return -1;
}
+ /**
+ * Adjust process {@code pid} importance according to whether it has toasts in the queue or not.
+ */
+ public void keepProcessAliveForToastIfNeeded(int pid) {
+ synchronized (mToastQueue) {
+ keepProcessAliveForToastIfNeededLocked(pid);
+ }
+ }
+
@GuardedBy("mToastQueue")
- void keepProcessAliveIfNeededLocked(int pid)
- {
+ private void keepProcessAliveForToastIfNeededLocked(int pid) {
int toastCount = 0; // toasts from this pid
ArrayList<ToastRecord> list = mToastQueue;
- int N = list.size();
- for (int i=0; i<N; i++) {
+ int n = list.size();
+ for (int i = 0; i < n; i++) {
ToastRecord r = list.get(i);
if (r.pid == pid) {
toastCount++;
diff --git a/services/core/java/com/android/server/notification/toast/CustomToastRecord.java b/services/core/java/com/android/server/notification/toast/CustomToastRecord.java
new file mode 100644
index 000000000000..aca6f4853597
--- /dev/null
+++ b/services/core/java/com/android/server/notification/toast/CustomToastRecord.java
@@ -0,0 +1,83 @@
+/*
+ * 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.notification.toast;
+
+import static com.android.internal.util.Preconditions.checkNotNull;
+import static com.android.server.notification.NotificationManagerService.DBG;
+
+import android.app.ITransientNotification;
+import android.os.Binder;
+import android.os.IBinder;
+import android.os.RemoteException;
+import android.util.Slog;
+
+import com.android.server.notification.NotificationManagerService;
+
+/**
+ * Represents a custom toast, a toast whose view is provided by the app.
+ */
+public class CustomToastRecord extends ToastRecord {
+ private static final String TAG = NotificationManagerService.TAG;
+
+ public final ITransientNotification callback;
+
+ public CustomToastRecord(
+ NotificationManagerService notificationManager, int pid, String packageName,
+ IBinder token, ITransientNotification callback, int duration, Binder windowToken,
+ int displayId) {
+ super(notificationManager, pid, packageName, token, duration, windowToken, displayId);
+ this.callback = checkNotNull(callback);
+ }
+
+ @Override
+ public boolean show() {
+ if (DBG) {
+ Slog.d(TAG, "Show pkg=" + pkg + " callback=" + callback);
+ }
+ try {
+ callback.show(windowToken);
+ return true;
+ } catch (RemoteException e) {
+ Slog.w(TAG, "Object died trying to show custom toast " + token + " in package "
+ + pkg);
+ mNotificationManager.keepProcessAliveForToastIfNeeded(pid);
+ return false;
+ }
+ }
+
+ @Override
+ public void hide() {
+ try {
+ callback.hide();
+ } catch (RemoteException e) {
+ Slog.w(TAG, "Object died trying to hide custom toast " + token + " in package "
+ + pkg);
+
+ }
+ }
+
+ @Override
+ public String toString() {
+ return "CustomToastRecord{"
+ + Integer.toHexString(System.identityHashCode(this))
+ + " token=" + token
+ + " packageName=" + pkg
+ + " callback=" + callback
+ + " duration=" + getDuration()
+ + "}";
+ }
+}
diff --git a/services/core/java/com/android/server/notification/toast/TextToastRecord.java b/services/core/java/com/android/server/notification/toast/TextToastRecord.java
new file mode 100644
index 000000000000..3c231b445f62
--- /dev/null
+++ b/services/core/java/com/android/server/notification/toast/TextToastRecord.java
@@ -0,0 +1,84 @@
+/*
+ * 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.notification.toast;
+
+import static com.android.internal.util.Preconditions.checkNotNull;
+import static com.android.server.notification.NotificationManagerService.DBG;
+
+import android.annotation.Nullable;
+import android.app.ITransientNotificationCallback;
+import android.os.Binder;
+import android.os.IBinder;
+import android.util.Slog;
+
+import com.android.server.notification.NotificationManagerService;
+import com.android.server.statusbar.StatusBarManagerInternal;
+
+/**
+ * Represents a text toast, a toast rendered by the system that contains only text.
+ */
+public class TextToastRecord extends ToastRecord {
+ private static final String TAG = NotificationManagerService.TAG;
+
+ public final CharSequence text;
+ @Nullable
+ private final StatusBarManagerInternal mStatusBar;
+ @Nullable
+ private final ITransientNotificationCallback mCallback;
+
+ public TextToastRecord(NotificationManagerService notificationManager,
+ @Nullable StatusBarManagerInternal statusBarManager, int pid, String packageName,
+ IBinder token, CharSequence text, int duration, Binder windowToken, int displayId,
+ @Nullable ITransientNotificationCallback callback) {
+ super(notificationManager, pid, packageName, token, duration, windowToken, displayId);
+ mStatusBar = statusBarManager;
+ mCallback = callback;
+ this.text = checkNotNull(text);
+ }
+
+ @Override
+ public boolean show() {
+ if (DBG) {
+ Slog.d(TAG, "Show pkg=" + pkg + " text=" + text);
+ }
+ if (mStatusBar == null) {
+ Slog.w(TAG, "StatusBar not available to show text toast for package " + pkg);
+ return false;
+ }
+ mStatusBar.showToast(pkg, token, text, windowToken, getDuration(), mCallback);
+ return true;
+ }
+
+ @Override
+ public void hide() {
+ // If it's null, show() would have returned false
+ checkNotNull(mStatusBar, "Cannot hide toast that wasn't shown");
+
+ mStatusBar.hideToast(pkg, token);
+ }
+
+ @Override
+ public String toString() {
+ return "TextToastRecord{"
+ + Integer.toHexString(System.identityHashCode(this))
+ + " token=" + token
+ + " packageName=" + pkg
+ + " text=" + text
+ + " duration=" + getDuration()
+ + "}";
+ }
+}
diff --git a/services/core/java/com/android/server/notification/toast/ToastRecord.java b/services/core/java/com/android/server/notification/toast/ToastRecord.java
new file mode 100644
index 000000000000..ef75a6f5dd7b
--- /dev/null
+++ b/services/core/java/com/android/server/notification/toast/ToastRecord.java
@@ -0,0 +1,88 @@
+/*
+ * 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.notification.toast;
+
+import android.os.Binder;
+import android.os.IBinder;
+
+import com.android.server.notification.NotificationManagerService;
+import com.android.server.notification.NotificationManagerService.DumpFilter;
+
+import java.io.PrintWriter;
+
+/**
+ * Represents a toast, a transient notification.
+ */
+public abstract class ToastRecord {
+ public final int pid;
+ public final String pkg;
+ public final IBinder token;
+ public final int displayId;
+ public final Binder windowToken;
+ protected final NotificationManagerService mNotificationManager;
+ private int mDuration;
+
+ protected ToastRecord(
+ NotificationManagerService notificationManager,
+ int pid, String pkg, IBinder token, int duration,
+ Binder windowToken, int displayId) {
+ this.mNotificationManager = notificationManager;
+ this.pid = pid;
+ this.pkg = pkg;
+ this.token = token;
+ this.windowToken = windowToken;
+ this.displayId = displayId;
+ mDuration = duration;
+ }
+
+ /**
+ * This method is responsible for showing the toast represented by this object.
+ *
+ * @return True if it was successfully shown.
+ */
+ public abstract boolean show();
+
+ /**
+ * This method is responsible for hiding the toast represented by this object.
+ */
+ public abstract void hide();
+
+ /**
+ * Returns the duration of this toast, which can be {@link android.widget.Toast#LENGTH_SHORT}
+ * or {@link android.widget.Toast#LENGTH_LONG}.
+ */
+ public int getDuration() {
+ return mDuration;
+ }
+
+ /**
+ * Updates toast duration.
+ */
+ public void update(int duration) {
+ mDuration = duration;
+ }
+
+ /**
+ * Dumps a textual representation of this object.
+ */
+ public void dump(PrintWriter pw, String prefix, DumpFilter filter) {
+ if (filter != null && !filter.matches(pkg)) {
+ return;
+ }
+ pw.println(prefix + this);
+ }
+}
diff --git a/services/core/java/com/android/server/om/OverlayActorEnforcer.java b/services/core/java/com/android/server/om/OverlayActorEnforcer.java
index 0a9f923ac817..4c85603a719c 100644
--- a/services/core/java/com/android/server/om/OverlayActorEnforcer.java
+++ b/services/core/java/com/android/server/om/OverlayActorEnforcer.java
@@ -53,10 +53,6 @@ public class OverlayActorEnforcer {
*/
static Pair<String, ActorState> getPackageNameForActor(String actorUriString,
Map<String, Map<String, String>> namedActors) {
- if (namedActors.isEmpty()) {
- return Pair.create(null, ActorState.NO_NAMED_ACTORS);
- }
-
Uri actorUri = Uri.parse(actorUriString);
String actorScheme = actorUri.getScheme();
@@ -65,6 +61,10 @@ public class OverlayActorEnforcer {
return Pair.create(null, ActorState.INVALID_OVERLAYABLE_ACTOR_NAME);
}
+ if (namedActors.isEmpty()) {
+ return Pair.create(null, ActorState.NO_NAMED_ACTORS);
+ }
+
String actorNamespace = actorUri.getAuthority();
Map<String, String> namespace = namedActors.get(actorNamespace);
if (namespace == null) {
diff --git a/services/core/java/com/android/server/om/TEST_MAPPING b/services/core/java/com/android/server/om/TEST_MAPPING
index 52163a09e387..75229a1adccc 100644
--- a/services/core/java/com/android/server/om/TEST_MAPPING
+++ b/services/core/java/com/android/server/om/TEST_MAPPING
@@ -7,6 +7,20 @@
"include-filter": "com.android.server.om."
}
]
+ },
+ {
+ "name": "OverlayDeviceTests"
+ },
+ {
+ "name": "OverlayHostTests"
+ },
+ {
+ "name": "CtsAppSecurityHostTestCases",
+ "options": [
+ {
+ "include-filter": "android.appsecurity.cts.OverlayHostTest"
+ }
+ ]
}
]
}
diff --git a/services/core/java/com/android/server/pm/AppsFilter.java b/services/core/java/com/android/server/pm/AppsFilter.java
index 6e7e5d884a4a..5c17bec0db47 100644
--- a/services/core/java/com/android/server/pm/AppsFilter.java
+++ b/services/core/java/com/android/server/pm/AppsFilter.java
@@ -20,10 +20,12 @@ import static android.os.Trace.TRACE_TAG_PACKAGE_MANAGER;
import static android.provider.DeviceConfig.NAMESPACE_PACKAGE_MANAGER_SERVICE;
import android.Manifest;
+import android.annotation.NonNull;
import android.annotation.Nullable;
import android.content.Intent;
import android.content.IntentFilter;
import android.content.pm.PackageManager;
+import android.content.pm.PackageParser;
import android.content.pm.parsing.AndroidPackage;
import android.content.pm.parsing.ComponentParseUtils;
import android.content.pm.parsing.ComponentParseUtils.ParsedActivity;
@@ -108,6 +110,7 @@ public class AppsFilter {
private final FeatureConfig mFeatureConfig;
private final OverlayReferenceMapper mOverlayReferenceMapper;
+ private PackageParser.SigningDetails mSystemSigningDetails;
AppsFilter(FeatureConfig featureConfig, String[] forceQueryableWhitelist,
boolean systemAppsQueryable,
@@ -321,6 +324,17 @@ public class AppsFilter {
*/
public void addPackage(PackageSetting newPkgSetting,
ArrayMap<String, PackageSetting> existingSettings) {
+ if (Objects.equals("android", newPkgSetting.name)) {
+ // let's set aside the framework signatures
+ mSystemSigningDetails = newPkgSetting.signatures.mSigningDetails;
+ // and since we add overlays before we add the framework, let's revisit already added
+ // packages for signature matches
+ for (PackageSetting setting : existingSettings.values()) {
+ if (isSystemSigned(mSystemSigningDetails, setting)) {
+ mForceQueryable.add(setting.appId);
+ }
+ }
+ }
Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "filter.addPackage");
try {
final AndroidPackage newPkg = newPkgSetting.pkg;
@@ -336,7 +350,9 @@ public class AppsFilter {
|| (newPkgSetting.isSystem() && (mSystemAppsQueryable
|| ArrayUtils.contains(mForceQueryableByDevicePackageNames,
newPkg.getPackageName())));
- if (newIsForceQueryable) {
+ if (newIsForceQueryable
+ || (mSystemSigningDetails != null
+ && isSystemSigned(mSystemSigningDetails, newPkgSetting))) {
mForceQueryable.add(newPkgSetting.appId);
}
@@ -382,6 +398,12 @@ public class AppsFilter {
}
}
+ private static boolean isSystemSigned(@NonNull PackageParser.SigningDetails sysSigningDetails,
+ PackageSetting pkgSetting) {
+ return pkgSetting.isSystem()
+ && pkgSetting.signatures.mSigningDetails.signaturesMatchExactly(sysSigningDetails);
+ }
+
/**
* Removes a package for consideration when filtering visibility between apps.
*
diff --git a/services/core/java/com/android/server/pm/PackageInstallerSession.java b/services/core/java/com/android/server/pm/PackageInstallerSession.java
index a22332648e0b..43ed25f944c6 100644
--- a/services/core/java/com/android/server/pm/PackageInstallerSession.java
+++ b/services/core/java/com/android/server/pm/PackageInstallerSession.java
@@ -75,11 +75,13 @@ import android.content.pm.PackageInstaller.SessionInfo;
import android.content.pm.PackageInstaller.SessionInfo.StagedSessionErrorCode;
import android.content.pm.PackageInstaller.SessionParams;
import android.content.pm.PackageManager;
+import android.content.pm.PackageManagerInternal;
import android.content.pm.PackageParser;
import android.content.pm.PackageParser.ApkLite;
import android.content.pm.PackageParser.PackageLite;
import android.content.pm.PackageParser.PackageParserException;
import android.content.pm.dex.DexMetadataHelper;
+import android.content.pm.parsing.AndroidPackage;
import android.content.pm.parsing.ApkLiteParseUtils;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
@@ -101,6 +103,7 @@ import android.os.UserHandle;
import android.os.incremental.IncrementalFileStorages;
import android.os.incremental.IncrementalManager;
import android.os.storage.StorageManager;
+import android.provider.Settings.Secure;
import android.stats.devicepolicy.DevicePolicyEnums;
import android.system.ErrnoException;
import android.system.Int64Ref;
@@ -154,6 +157,7 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub {
private static final int MSG_COMMIT = 1;
private static final int MSG_ON_PACKAGE_INSTALLED = 2;
private static final int MSG_SEAL = 3;
+ private static final int MSG_STREAM_AND_VALIDATE = 4;
/** XML constants used for persisting a session */
static final String TAG_SESSION = "session";
@@ -369,6 +373,8 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub {
@GuardedBy("mLock")
private boolean mVerityFound;
+ private boolean mDataLoaderFinished = false;
+
// TODO(b/146080380): merge file list with Callback installation.
private IncrementalFileStorages mIncrementalFileStorages;
@@ -412,6 +418,9 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub {
case MSG_SEAL:
handleSeal((IntentSender) msg.obj);
break;
+ case MSG_STREAM_AND_VALIDATE:
+ handleStreamAndValidate();
+ break;
case MSG_COMMIT:
handleCommit();
break;
@@ -1019,11 +1028,7 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub {
}
private void handleSeal(@NonNull IntentSender statusReceiver) {
- // TODO(b/136132412): update with new APIs
- if (mIncrementalFileStorages != null) {
- mIncrementalFileStorages.startLoading();
- }
- if (!markAsCommitted(statusReceiver)) {
+ if (!markAsSealed(statusReceiver)) {
return;
}
if (isMultiPackage()) {
@@ -1031,21 +1036,52 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub {
final IntentSender childIntentSender =
new ChildStatusIntentReceiver(remainingSessions, statusReceiver)
.getIntentSender();
- boolean commitFailed = false;
+ boolean sealFailed = false;
for (int i = mChildSessionIds.size() - 1; i >= 0; --i) {
final int childSessionId = mChildSessionIds.keyAt(i);
- // commit all children, regardless if any of them fail; we'll throw/return
+ // seal all children, regardless if any of them fail; we'll throw/return
// as appropriate once all children have been processed
if (!mSessionProvider.getSession(childSessionId)
- .markAsCommitted(childIntentSender)) {
- commitFailed = true;
+ .markAsSealed(childIntentSender)) {
+ sealFailed = true;
}
}
- if (commitFailed) {
+ if (sealFailed) {
return;
}
}
+ dispatchStreamAndValidate();
+ }
+
+ private void dispatchStreamAndValidate() {
+ mHandler.obtainMessage(MSG_STREAM_AND_VALIDATE).sendToTarget();
+ }
+
+ private void handleStreamAndValidate() {
+ // TODO(b/136132412): update with new APIs
+ if (mIncrementalFileStorages != null) {
+ mIncrementalFileStorages.startLoading();
+ }
+
+ boolean commitFailed = !markAsCommitted();
+
+ if (isMultiPackage()) {
+ for (int i = mChildSessionIds.size() - 1; i >= 0; --i) {
+ final int childSessionId = mChildSessionIds.keyAt(i);
+ // commit all children, regardless if any of them fail; we'll throw/return
+ // as appropriate once all children have been processed
+ if (!mSessionProvider.getSession(childSessionId)
+ .markAsCommitted()) {
+ commitFailed = true;
+ }
+ }
+ }
+
+ if (commitFailed) {
+ return;
+ }
+
mHandler.obtainMessage(MSG_COMMIT).sendToTarget();
}
@@ -1141,6 +1177,28 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub {
}
}
+ /**
+ * Returns whether or not a package can be installed while Secure FRP is enabled.
+ * <p>
+ * Only callers with the INSTALL_PACKAGES permission are allowed to install. However,
+ * prevent the package installer from installing anything because, while it has the
+ * permission, it will allows packages to be installed from anywhere.
+ */
+ private static boolean isSecureFrpInstallAllowed(Context context, int callingUid) {
+ final PackageManagerInternal pmi = LocalServices.getService(PackageManagerInternal.class);
+ final String[] systemInstaller = pmi.getKnownPackageNames(
+ PackageManagerInternal.PACKAGE_INSTALLER, UserHandle.USER_SYSTEM);
+ final AndroidPackage callingInstaller = pmi.getPackage(callingUid);
+ if (callingInstaller != null
+ && ArrayUtils.contains(systemInstaller, callingInstaller.getPackageName())) {
+ // don't allow the system package installer to install while under secure FRP
+ return false;
+ }
+
+ // require caller to hold the INSTALL_PACKAGES permission
+ return context.checkCallingOrSelfPermission(Manifest.permission.INSTALL_PACKAGES)
+ == PackageManager.PERMISSION_GRANTED;
+ }
/**
* Sanity checks to make sure it's ok to commit the session.
@@ -1151,13 +1209,15 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub {
assertPreparedAndNotDestroyedLocked("commit");
assertNoWriteFileTransfersOpenLocked();
- final boolean enforceInstallPackages = forTransfer
- || (android.provider.Settings.Secure.getInt(mContext.getContentResolver(),
- android.provider.Settings.Secure.SECURE_FRP_MODE, 0) == 1);
- if (enforceInstallPackages) {
- mContext.enforceCallingOrSelfPermission(Manifest.permission.INSTALL_PACKAGES, null);
+ final boolean isSecureFrpEnabled =
+ (Secure.getInt(mContext.getContentResolver(), Secure.SECURE_FRP_MODE, 0) == 1);
+ if (isSecureFrpEnabled
+ && !isSecureFrpInstallAllowed(mContext, Binder.getCallingUid())) {
+ throw new SecurityException("Can't install packages while in secure FRP");
}
+
if (forTransfer) {
+ mContext.enforceCallingOrSelfPermission(Manifest.permission.INSTALL_PACKAGES, null);
if (mInstallerUid == mOriginalInstallerUid) {
throw new IllegalArgumentException("Session has not been transferred");
}
@@ -1170,46 +1230,50 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub {
}
/**
- * Do everything but actually commit the session. If this was not already called, the session
- * will be sealed and marked as committed. The caller of this method is responsible for
- * subsequently submitting this session for processing.
+ * If this was not already called, the session will be sealed.
*
* This method may be called multiple times to update the status receiver validate caller
* permissions.
*/
- private boolean markAsCommitted(@NonNull IntentSender statusReceiver) {
+ private boolean markAsSealed(@NonNull IntentSender statusReceiver) {
Objects.requireNonNull(statusReceiver);
List<PackageInstallerSession> childSessions = getChildSessions();
- final boolean wasSealed;
synchronized (mLock) {
mRemoteStatusReceiver = statusReceiver;
- // After validations and updating the observer, we can skip re-sealing, etc. because we
- // have already marked ourselves as committed.
- if (mCommitted) {
+ // After updating the observer, we can skip re-sealing.
+ if (mSealed) {
return true;
}
- wasSealed = mSealed;
try {
- if (!mSealed) {
- sealLocked(childSessions);
- }
-
- try {
- streamAndValidateLocked();
- } catch (StreamingException e) {
- // In case of streaming failure we don't want to fail or commit the session.
- // Just return from this method and allow caller to commit again.
- PackageInstallerService.sendPendingStreaming(mContext, mRemoteStatusReceiver,
- sessionId, e);
- return false;
- }
+ sealLocked(childSessions);
} catch (PackageManagerException e) {
return false;
}
+ }
+
+ // Persist the fact that we've sealed ourselves to prevent
+ // mutations of any hard links we create. We do this without holding
+ // the session lock, since otherwise it's a lock inversion.
+ mCallback.onSessionSealedBlocking(this);
+
+ return true;
+ }
+
+ private boolean markAsCommitted() {
+ synchronized (mLock) {
+ Objects.requireNonNull(mRemoteStatusReceiver);
+
+ if (mCommitted) {
+ return true;
+ }
+
+ if (!streamAndValidateLocked()) {
+ return false;
+ }
// Client staging is fully done at this point
mClientProgress = 1f;
@@ -1222,12 +1286,6 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub {
mCommitted = true;
}
- if (!wasSealed) {
- // Persist the fact that we've sealed ourselves to prevent
- // mutations of any hard links we create. We do this without holding
- // the session lock, since otherwise it's a lock inversion.
- mCallback.onSessionSealedBlocking(this);
- }
return true;
}
@@ -1295,17 +1353,6 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub {
}
/**
- * Convenience wrapper, see {@link #sealLocked(List<PackageInstallerSession>) seal} and
- * {@link #streamAndValidateLocked()}.
- */
- @GuardedBy("mLock")
- private void sealAndValidateLocked(List<PackageInstallerSession> childSessions)
- throws PackageManagerException, StreamingException {
- sealLocked(childSessions);
- streamAndValidateLocked();
- }
-
- /**
* Seal the session to prevent further modification.
*
* <p>The session will be sealed after calling this method even if it failed.
@@ -1338,23 +1385,23 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub {
* Prepare DataLoader and stream content for DataLoader sessions.
* Validate the contents of all session.
*
- * @throws StreamingException if streaming failed.
- * @throws PackageManagerException if validation failed.
+ * @return false if validation failed.
*/
@GuardedBy("mLock")
- private void streamAndValidateLocked()
- throws PackageManagerException, StreamingException {
+ private boolean streamAndValidateLocked() {
try {
// Read transfers from the original owner stay open, but as the session's data cannot
// be modified anymore, there is no leak of information. For staged sessions, further
// validation is performed by the staging manager.
if (!params.isMultiPackage) {
+ if (!prepareDataLoaderLocked()) {
+ return false;
+ }
+
final PackageInfo pkgInfo = mPm.getPackageInfo(
params.appPackageName, PackageManager.GET_SIGNATURES
| PackageManager.MATCH_STATIC_SHARED_LIBRARIES /*flags*/, userId);
- prepareDataLoader();
-
if (isApexInstallation()) {
validateApexInstallLocked();
} else {
@@ -1365,15 +1412,16 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub {
if (params.isStaged) {
mStagingManager.checkNonOverlappingWithStagedSessions(this);
}
+
+ return true;
} catch (PackageManagerException e) {
- throw onSessionVerificationFailure(e);
- } catch (StreamingException e) {
- throw e;
+ onSessionVerificationFailure(e);
} catch (Throwable e) {
// Convert all exceptions into package manager exceptions as only those are handled
// in the code above.
- throw onSessionVerificationFailure(new PackageManagerException(e));
+ onSessionVerificationFailure(new PackageManagerException(e));
}
+ return false;
}
private PackageManagerException onSessionVerificationFailure(PackageManagerException e) {
@@ -2145,8 +2193,12 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub {
}
String getInstallerPackageName() {
+ return getInstallSource().installerPackageName;
+ }
+
+ InstallSource getInstallSource() {
synchronized (mLock) {
- return mInstallSource.installerPackageName;
+ return mInstallSource;
}
}
@@ -2387,6 +2439,7 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub {
synchronized (mLock) {
assertCallerIsOwnerOrRootLocked();
assertPreparedAndNotSealedLocked("addFile");
+
mFiles.add(FileInfo.added(location, name, lengthBytes, metadata, signature));
}
}
@@ -2409,48 +2462,30 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub {
}
}
- static class Notificator {
- private int mValue = 0;
-
- void setValue(int value) {
- synchronized (this) {
- mValue = value;
- this.notify();
- }
- }
- int waitForValue() {
- synchronized (this) {
- while (mValue == 0) {
- try {
- this.wait();
- } catch (InterruptedException e) {
- // Happens if someone interrupts your thread.
- }
- }
- return mValue;
- }
- }
- }
-
/**
* Makes sure files are present in staging location.
*/
- private void prepareDataLoader()
- throws PackageManagerException, StreamingException {
+ @GuardedBy("mLock")
+ private boolean prepareDataLoaderLocked()
+ throws PackageManagerException {
if (!isDataLoaderInstallation()) {
- return;
+ return true;
+ }
+ if (mDataLoaderFinished) {
+ return true;
}
- List<InstallationFile> addedFiles = mFiles.stream().filter(
+ final List<InstallationFile> addedFiles = mFiles.stream().filter(
file -> sAddedFilter.accept(new File(file.name))).map(
file -> new InstallationFile(
- file.name, file.lengthBytes, file.metadata)).collect(
+ file.name, file.lengthBytes, file.metadata)).collect(
Collectors.toList());
- List<String> removedFiles = mFiles.stream().filter(
+ final List<String> removedFiles = mFiles.stream().filter(
file -> sRemovedFilter.accept(new File(file.name))).map(
file -> file.name.substring(
- 0, file.name.length() - REMOVE_MARKER_EXTENSION.length())).collect(
+ 0, file.name.length() - REMOVE_MARKER_EXTENSION.length())).collect(
Collectors.toList());
+
if (mIncrementalFileStorages != null) {
for (InstallationFile file : addedFiles) {
try {
@@ -2461,46 +2496,73 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub {
"Failed to add and configure Incremental File: " + file.getName(), ex);
}
}
- return;
+ return true;
}
- final FileSystemConnector connector = new FileSystemConnector(addedFiles);
-
- DataLoaderManager dataLoaderManager = mContext.getSystemService(DataLoaderManager.class);
+ final DataLoaderManager dataLoaderManager = mContext.getSystemService(
+ DataLoaderManager.class);
if (dataLoaderManager == null) {
throw new PackageManagerException(INSTALL_FAILED_MEDIA_UNAVAILABLE,
"Failed to find data loader manager service");
}
- // TODO(b/146080380): make this code async.
- final Notificator created = new Notificator();
- final Notificator started = new Notificator();
- final Notificator imageReady = new Notificator();
-
IDataLoaderStatusListener listener = new IDataLoaderStatusListener.Stub() {
@Override
public void onStatusChanged(int dataLoaderId, int status) {
- switch (status) {
- case IDataLoaderStatusListener.DATA_LOADER_CREATED: {
- created.setValue(1);
- break;
- }
- case IDataLoaderStatusListener.DATA_LOADER_STARTED: {
- started.setValue(1);
- break;
+ try {
+ if (status == IDataLoaderStatusListener.DATA_LOADER_DESTROYED) {
+ return;
}
- case IDataLoaderStatusListener.DATA_LOADER_IMAGE_READY: {
- imageReady.setValue(1);
- break;
+
+ IDataLoader dataLoader = dataLoaderManager.getDataLoader(dataLoaderId);
+ if (dataLoader == null) {
+ mDataLoaderFinished = true;
+ onSessionVerificationFailure(
+ new PackageManagerException(INSTALL_FAILED_MEDIA_UNAVAILABLE,
+ "Failure to obtain data loader"));
+ return;
}
- case IDataLoaderStatusListener.DATA_LOADER_IMAGE_NOT_READY: {
- imageReady.setValue(2);
- break;
+
+ switch (status) {
+ case IDataLoaderStatusListener.DATA_LOADER_CREATED: {
+ dataLoader.start();
+ break;
+ }
+ case IDataLoaderStatusListener.DATA_LOADER_STARTED: {
+ dataLoader.prepareImage(addedFiles, removedFiles);
+ break;
+ }
+ case IDataLoaderStatusListener.DATA_LOADER_IMAGE_READY: {
+ mDataLoaderFinished = true;
+ if (hasParentSessionId()) {
+ mSessionProvider.getSession(
+ mParentSessionId).dispatchStreamAndValidate();
+ } else {
+ dispatchStreamAndValidate();
+ }
+ dataLoader.destroy();
+ break;
+ }
+ case IDataLoaderStatusListener.DATA_LOADER_IMAGE_NOT_READY: {
+ mDataLoaderFinished = true;
+ onSessionVerificationFailure(
+ new PackageManagerException(INSTALL_FAILED_MEDIA_UNAVAILABLE,
+ "Failed to prepare image."));
+ dataLoader.destroy();
+ break;
+ }
}
+ } catch (RemoteException e) {
+ // In case of streaming failure we don't want to fail or commit the session.
+ // Just return from this method and allow caller to commit again.
+ PackageInstallerService.sendPendingStreaming(mContext,
+ mRemoteStatusReceiver,
+ sessionId, new StreamingException(e));
}
}
};
+ final FileSystemConnector connector = new FileSystemConnector(addedFiles);
final FileSystemControlParcel control = new FileSystemControlParcel();
control.callback = connector;
@@ -2515,28 +2577,8 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub {
throw new PackageManagerException(INSTALL_FAILED_MEDIA_UNAVAILABLE,
"Failed to initialize data loader");
}
- created.waitForValue();
- IDataLoader dataLoader = dataLoaderManager.getDataLoader(sessionId);
- if (dataLoader == null) {
- throw new PackageManagerException(INSTALL_FAILED_MEDIA_UNAVAILABLE,
- "Failure to obtain data loader");
- }
-
- try {
- dataLoader.start();
- started.waitForValue();
-
- dataLoader.prepareImage(addedFiles, removedFiles);
- if (imageReady.waitForValue() == 2) {
- throw new PackageManagerException(INSTALL_FAILED_MEDIA_UNAVAILABLE,
- "Failed to prepare image.");
- }
-
- dataLoader.destroy();
- } catch (RemoteException e) {
- throw new StreamingException(e);
- }
+ return false;
}
@Override
diff --git a/services/core/java/com/android/server/pm/PackageManagerService.java b/services/core/java/com/android/server/pm/PackageManagerService.java
index 6c8e7b058346..de6b7d5af397 100644
--- a/services/core/java/com/android/server/pm/PackageManagerService.java
+++ b/services/core/java/com/android/server/pm/PackageManagerService.java
@@ -2725,7 +2725,7 @@ public class PackageManagerService extends IPackageManager.Stub
t.traceBegin("get system config");
SystemConfig systemConfig = SystemConfig.getInstance();
mAvailableFeatures = systemConfig.getAvailableFeatures();
- ApplicationPackageManager.invalidateHasSystemFeatureCache();
+ ApplicationPackageManager.invalidateSysFeatureCache();
t.traceEnd();
mProtectedPackages = new ProtectedPackages(mContext);
diff --git a/services/core/java/com/android/server/pm/PackageManagerShellCommandDataLoader.java b/services/core/java/com/android/server/pm/PackageManagerShellCommandDataLoader.java
index 281c44a61153..5dca9e147e31 100644
--- a/services/core/java/com/android/server/pm/PackageManagerShellCommandDataLoader.java
+++ b/services/core/java/com/android/server/pm/PackageManagerShellCommandDataLoader.java
@@ -152,6 +152,7 @@ public class PackageManagerShellCommandDataLoader extends DataLoaderService {
}
return true;
} catch (IOException e) {
+ Slog.e(TAG, "Exception while streaming files", e);
return false;
}
}
diff --git a/services/core/java/com/android/server/policy/PhoneWindowManager.java b/services/core/java/com/android/server/policy/PhoneWindowManager.java
index da0d82047cbe..7c76656e1e6f 100644
--- a/services/core/java/com/android/server/policy/PhoneWindowManager.java
+++ b/services/core/java/com/android/server/policy/PhoneWindowManager.java
@@ -3075,7 +3075,7 @@ public class PhoneWindowManager implements WindowManagerPolicy {
event.getAction(), fallbackAction.keyCode,
event.getRepeatCount(), fallbackAction.metaState,
event.getDeviceId(), event.getScanCode(),
- flags, event.getSource(), event.getDisplayId(), null);
+ flags, event.getSource(), event.getDisplayId(), null /* hmac */, null);
if (!interceptFallback(focusedToken, fallbackEvent, policyFlags)) {
fallbackEvent.recycle();
diff --git a/services/core/java/com/android/server/power/Notifier.java b/services/core/java/com/android/server/power/Notifier.java
index df8e30f9387a..b45522d11a5d 100644
--- a/services/core/java/com/android/server/power/Notifier.java
+++ b/services/core/java/com/android/server/power/Notifier.java
@@ -62,6 +62,8 @@ import com.android.server.inputmethod.InputMethodManagerInternal;
import com.android.server.policy.WindowManagerPolicy;
import com.android.server.statusbar.StatusBarManagerInternal;
+import java.io.PrintWriter;
+
/**
* Sends broadcasts about important power state changes.
* <p>
@@ -123,6 +125,7 @@ public class Notifier {
@Nullable private final StatusBarManagerInternal mStatusBarManagerInternal;
private final TrustManager mTrustManager;
private final Vibrator mVibrator;
+ private final WakeLockLog mWakeLockLog;
private final NotifierHandler mHandler;
private final Intent mScreenOnIntent;
@@ -190,6 +193,8 @@ public class Notifier {
mShowWirelessChargingAnimationConfig = context.getResources().getBoolean(
com.android.internal.R.bool.config_showBuiltinWirelessChargingAnim);
+ mWakeLockLog = new WakeLockLog();
+
// Initialize interactive state for battery stats.
try {
mBatteryStats.noteInteractive(true);
@@ -228,6 +233,8 @@ public class Notifier {
// Ignore
}
}
+
+ mWakeLockLog.onWakeLockAcquired(tag, ownerUid, flags);
}
public void onLongPartialWakeLockStart(String tag, int ownerUid, WorkSource workSource,
@@ -338,6 +345,7 @@ public class Notifier {
// Ignore
}
}
+ mWakeLockLog.onWakeLockReleased(tag, ownerUid);
}
private int getBatteryStatsWakeLockMonitorType(int flags) {
@@ -647,6 +655,17 @@ public class Notifier {
mHandler.sendMessage(msg);
}
+ /**
+ * Dumps data for bugreports.
+ *
+ * @param pw The stream to print to.
+ */
+ public void dump(PrintWriter pw) {
+ if (mWakeLockLog != null) {
+ mWakeLockLog.dump(pw);
+ }
+ }
+
private void updatePendingBroadcastLocked() {
if (!mBroadcastInProgress
&& mPendingInteractiveState != INTERACTIVE_STATE_UNKNOWN
diff --git a/services/core/java/com/android/server/power/PowerManagerService.java b/services/core/java/com/android/server/power/PowerManagerService.java
index 3f3a13368ffc..4d13658c85b7 100644
--- a/services/core/java/com/android/server/power/PowerManagerService.java
+++ b/services/core/java/com/android/server/power/PowerManagerService.java
@@ -3810,6 +3810,10 @@ public final class PowerManagerService extends SystemService
if (wcd != null) {
wcd.dump(pw);
}
+
+ if (mNotifier != null) {
+ mNotifier.dump(pw);
+ }
}
private void dumpProto(FileDescriptor fd) {
@@ -4268,7 +4272,7 @@ public final class PowerManagerService extends SystemService
/**
* Represents a wake lock that has been acquired by an application.
*/
- private final class WakeLock implements IBinder.DeathRecipient {
+ /* package */ final class WakeLock implements IBinder.DeathRecipient {
public final IBinder mLock;
public int mFlags;
public String mTag;
diff --git a/services/core/java/com/android/server/power/WakeLockLog.java b/services/core/java/com/android/server/power/WakeLockLog.java
new file mode 100644
index 000000000000..d6060fa419b9
--- /dev/null
+++ b/services/core/java/com/android/server/power/WakeLockLog.java
@@ -0,0 +1,1355 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.power;
+
+import android.os.Handler;
+import android.os.Looper;
+import android.os.Message;
+import android.os.PowerManager;
+import android.text.TextUtils;
+import android.util.Slog;
+
+import com.android.internal.annotations.VisibleForTesting;
+import com.android.internal.os.BackgroundThread;
+import com.android.internal.os.SomeArgs;
+
+import java.io.PrintWriter;
+import java.text.SimpleDateFormat;
+import java.util.Arrays;
+import java.util.ConcurrentModificationException;
+import java.util.Date;
+import java.util.Iterator;
+import java.util.NoSuchElementException;
+
+/**
+ * Simple Log for wake lock events. Optimized to reduce memory usage.
+ *
+ * The wake lock events are ultimately saved in-memory in a pre-allocated byte-based ring-buffer.
+ *
+ * Most of the work of this log happens in the {@link BackgroundThread}.
+ *
+ * The main log is basically just a sequence of the two wake lock events (ACQUIRE and RELEASE).
+ * Each entry in the log stores the following data:
+ * {
+ * event type (RELEASE | ACQUIRE),
+ * time (64-bit from System.currentTimeMillis()),
+ * wake-lock ID {ownerUID (int) + tag (String)},
+ * wake-lock flags
+ * }
+ *
+ * In order to maximize the number of entries that fit into the log, there are various efforts made
+ * to compress what we store; of which two are fairly significant and contribute the most to the
+ * complexity of this code:
+ * A) Relative Time
+ * - Time in each log entry is stored as an 8-bit value and is relative to the time of the
+ * previous event. When relative time is too large for 8-bits, we add a third type of event
+ * called TIME_RESET, which is used to add a new 64-bit reference-time event to the log.
+ * In practice, TIME_RESETs seem to make up about 10% or less of the total events depending
+ * on the device usage.
+ * B) Wake-lock tag/ID as indexes
+ * - Wake locks are often reused many times. To avoid storing large strings in the ring buffer,
+ * we maintain a {@link TagDatabase} that associates each wakelock tag with an 7-bit index.
+ * The main log stores only these 7-bit indexes instead of whole strings.
+ *
+ * To make the code a bit more organized, there exists a class {@link EntryByteTranslator} which
+ * uses the tag database, and reference-times to convert between a {@link LogEntry} and the
+ * byte sequence that is ultimately stored in the main log, {@link TheLog}.
+ */
+final class WakeLockLog {
+ private static final String TAG = "PowerManagerService.WLLog";
+
+ private static final boolean DEBUG = false;
+
+ private static final int MSG_ON_WAKE_LOCK_EVENT = 1;
+
+ private static final int TYPE_TIME_RESET = 0x0;
+ private static final int TYPE_ACQUIRE = 0x1;
+ private static final int TYPE_RELEASE = 0x2;
+ private static final int MAX_LOG_ENTRY_BYTE_SIZE = 9;
+ private static final int LOG_SIZE = 1024 * 10;
+ private static final int LOG_SIZE_MIN = MAX_LOG_ENTRY_BYTE_SIZE + 1;
+
+ private static final int TAG_DATABASE_SIZE = 128;
+ private static final int TAG_DATABASE_SIZE_MAX = 128;
+
+ private static final int LEVEL_UNKNOWN = 0;
+ private static final int LEVEL_PARTIAL_WAKE_LOCK = 1;
+ private static final int LEVEL_FULL_WAKE_LOCK = 2;
+ private static final int LEVEL_SCREEN_DIM_WAKE_LOCK = 3;
+ private static final int LEVEL_SCREEN_BRIGHT_WAKE_LOCK = 4;
+ private static final int LEVEL_PROXIMITY_SCREEN_OFF_WAKE_LOCK = 5;
+ private static final int LEVEL_DOZE_WAKE_LOCK = 6;
+ private static final int LEVEL_DRAW_WAKE_LOCK = 7;
+
+ private static final String[] LEVEL_TO_STRING = {
+ "unknown",
+ "partial",
+ "full",
+ "screen-dim",
+ "screen-bright",
+ "prox",
+ "doze",
+ "draw"
+ };
+
+ /**
+ * Flags use the same bit field as the level, so must start at the next available bit
+ * after the largest level.
+ */
+ private static final int FLAG_ON_AFTER_RELEASE = 0x8;
+ private static final int FLAG_ACQUIRE_CAUSES_WAKEUP = 0x10;
+
+ private static final int MASK_LOWER_6_BITS = 0x3F;
+ private static final int MASK_LOWER_7_BITS = 0x7F;
+
+ private static final String[] REDUCED_TAG_PREFIXES =
+ {"*job*/", "*gms_scheduler*/", "IntentOp:"};
+
+ private static final SimpleDateFormat DATE_FORMAT = new SimpleDateFormat("MM-dd HH:mm:ss.SSS");
+
+ /**
+ * Lock protects WakeLockLock.dump (binder thread) from conflicting with changes to the log
+ * happening on the background thread.
+ */
+ private final Object mLock = new Object();
+
+ private final Injector mInjector;
+ private final TheLog mLog;
+ private final TagDatabase mTagDatabase;
+ private final Handler mHandler;
+ private final SimpleDateFormat mDumpsysDateFormat;
+
+ WakeLockLog() {
+ this(new Injector());
+ }
+
+ @VisibleForTesting
+ WakeLockLog(Injector injector) {
+ mInjector = injector;
+ mHandler = new WakeLockLogHandler(injector.getLooper());
+ mTagDatabase = new TagDatabase(injector);
+ EntryByteTranslator translator = new EntryByteTranslator(mTagDatabase);
+ mLog = new TheLog(injector, translator, mTagDatabase);
+ mDumpsysDateFormat = injector.getDateFormat();
+ }
+
+ /**
+ * Receives notifications of an ACQUIRE wake lock event from PowerManager.
+ *
+ * @param tag The wake lock tag
+ * @param ownerUid The owner UID of the wake lock.
+ * @param flags Flags used for the wake lock.
+ */
+ public void onWakeLockAcquired(String tag, int ownerUid, int flags) {
+ onWakeLockEvent(TYPE_ACQUIRE, tag, ownerUid, flags);
+ }
+
+ /**
+ * Receives notifications of a RELEASE wake lock event from PowerManager.
+ *
+ * @param tag The wake lock tag
+ * @param ownerUid The owner UID of the wake lock.
+ */
+ public void onWakeLockReleased(String tag, int ownerUid) {
+ onWakeLockEvent(TYPE_RELEASE, tag, ownerUid, 0 /* flags */);
+ }
+
+ /**
+ * Dumps all the wake lock data currently saved in the wake lock log to the specified
+ * {@code PrintWriter}.
+ *
+ * @param The {@code PrintWriter} to write to.
+ */
+ public void dump(PrintWriter pw) {
+ dump(pw, false);
+ }
+
+ @VisibleForTesting
+ void dump(PrintWriter pw, boolean includeTagDb) {
+ try {
+ synchronized (mLock) {
+ pw.println("Wake Lock Log");
+ LogEntry tempEntry = new LogEntry(); // Temporary entry for the iterator to reuse.
+ final Iterator<LogEntry> iterator = mLog.getAllItems(tempEntry);
+ int numEvents = 0;
+ int numResets = 0;
+ while (iterator.hasNext()) {
+ String address = null;
+ if (DEBUG) {
+ // Gets the byte index in the log for the current entry.
+ address = iterator.toString();
+ }
+ LogEntry entry = iterator.next();
+ if (entry != null) {
+ if (entry.type == TYPE_TIME_RESET) {
+ numResets++;
+ } else {
+ numEvents++;
+ if (DEBUG) {
+ pw.print(address);
+ }
+ entry.dump(pw, mDumpsysDateFormat);
+ }
+ }
+ }
+ pw.println(" -");
+ pw.println(" Events: " + numEvents + ", Time-Resets: " + numResets);
+ pw.println(" Buffer, Bytes used: " + mLog.getUsedBufferSize());
+ if (DEBUG || includeTagDb) {
+ pw.println(" " + mTagDatabase);
+ }
+ }
+ } catch (Exception e) {
+ pw.println("Exception dumping wake-lock log: " + e.toString());
+ }
+ }
+
+ /**
+ * Adds a new entry to the log based on the specified wake lock parameters.
+ *
+ * Grabs the current time for the event and then posts the rest of the logic (actually
+ * adding it to the log) to a background thread.
+ *
+ * @param eventType The type of event (ACQUIRE, RELEASE);
+ * @param tag The wake lock's identifying tag.
+ * @param ownerUid The owner UID of the wake lock.
+ * @param flags The flags used with the wake lock.
+ */
+ private void onWakeLockEvent(int eventType, String tag, int ownerUid,
+ int flags) {
+ if (tag == null) {
+ Slog.w(TAG, "Insufficient data to log wakelock [tag: " + tag
+ + ", ownerUid: " + ownerUid
+ + ", flags: 0x" + Integer.toHexString(flags));
+ return;
+ }
+
+ final long time = mInjector.currentTimeMillis();
+
+ SomeArgs args = SomeArgs.obtain();
+ args.arg1 = tagNameReducer(tag);
+ args.argi1 = eventType;
+ args.argi2 = ownerUid;
+ args.argi3 = eventType == TYPE_ACQUIRE ? translateFlagsFromPowerManager(flags) : 0;
+ args.argi4 = (int) ((time >> 32) & 0xFFFFFFFFL);
+ args.argi5 = (int) (time & 0xFFFFFFFFL);
+ mHandler.obtainMessage(MSG_ON_WAKE_LOCK_EVENT, args).sendToTarget();
+ }
+
+ /**
+ * Handles a new wakelock event in the background thread.
+ *
+ * @param eventType The type of event (ACQUIRE, RELEASE)
+ * @param tag The wake lock's identifying tag.
+ * @param ownerUid The owner UID of the wake lock.
+ * @param flags the flags used with the wake lock.
+ */
+ private void handleWakeLockEventInternal(int eventType, String tag, int ownerUid, int flags,
+ long time) {
+ synchronized (mLock) {
+ final TagData tagData = mTagDatabase.findOrCreateTag(
+ tag, ownerUid, true /* shouldCreate */);
+ mLog.addEntry(new LogEntry(time, eventType, tagData, flags));
+ }
+ }
+
+ /**
+ * Translates wake lock flags from PowerManager into a redefined set that fits
+ * in the lower 6-bits of the return value. The results are an OR-ed combination of the
+ * flags, {@code WakeLockLog.FLAG_*}, and a log-level, {@code WakeLockLog.LEVEL_*}.
+ *
+ * @param flags Wake lock flags including {@code PowerManager.*_WAKE_LOCK}
+ * {@link PowerManager.ACQUIRE_CAUSES_WAKEUP}, and
+ * {@link PowerManager.ON_AFTER_RELEASE}.
+ * @return The compressed flags value.
+ */
+ int translateFlagsFromPowerManager(int flags) {
+ int newFlags = 0;
+ switch(PowerManager.WAKE_LOCK_LEVEL_MASK & flags) {
+ case PowerManager.PARTIAL_WAKE_LOCK:
+ newFlags = LEVEL_PARTIAL_WAKE_LOCK;
+ break;
+ case PowerManager.SCREEN_DIM_WAKE_LOCK:
+ newFlags = LEVEL_SCREEN_DIM_WAKE_LOCK;
+ break;
+ case PowerManager.SCREEN_BRIGHT_WAKE_LOCK:
+ newFlags = LEVEL_SCREEN_BRIGHT_WAKE_LOCK;
+ break;
+ case PowerManager.FULL_WAKE_LOCK:
+ newFlags = LEVEL_FULL_WAKE_LOCK;
+ break;
+ case PowerManager.DOZE_WAKE_LOCK:
+ newFlags = LEVEL_DOZE_WAKE_LOCK;
+ break;
+ case PowerManager.DRAW_WAKE_LOCK:
+ newFlags = LEVEL_DRAW_WAKE_LOCK;
+ break;
+ case PowerManager.PROXIMITY_SCREEN_OFF_WAKE_LOCK:
+ newFlags = LEVEL_PROXIMITY_SCREEN_OFF_WAKE_LOCK;
+ break;
+ default:
+ Slog.w(TAG, "Unsupported lock level for logging, flags: " + flags);
+ break;
+ }
+ if ((flags & PowerManager.ACQUIRE_CAUSES_WAKEUP) != 0) {
+ newFlags |= FLAG_ACQUIRE_CAUSES_WAKEUP;
+ }
+ if ((flags & PowerManager.ON_AFTER_RELEASE) != 0) {
+ newFlags |= FLAG_ON_AFTER_RELEASE;
+ }
+ return newFlags;
+ }
+
+ /**
+ * Reduce certain wakelock tags to something smaller.
+ * e.g. "*job* /com.aye.bee.cee/dee.ee.eff.Gee$Eich" -> "*job* /c.a.b.c/d.e.e.Gee$Eich"
+ * This is used to save space when storing the tags in the Tag Database.
+ *
+ * @param tag The tag name to reduce
+ * @return A reduced version of the tag name.
+ */
+ private String tagNameReducer(String tag) {
+ if (tag == null) {
+ return null;
+ }
+
+ String reduciblePrefix = null;
+ for (int tp = 0; tp < REDUCED_TAG_PREFIXES.length; tp++) {
+ if (tag.startsWith(REDUCED_TAG_PREFIXES[tp])) {
+ reduciblePrefix = REDUCED_TAG_PREFIXES[tp];
+ break;
+ }
+ }
+
+ if (reduciblePrefix != null) {
+ final StringBuilder sb = new StringBuilder();
+
+ // add prefix first
+ sb.append(tag.substring(0, reduciblePrefix.length()));
+
+ // Stop looping on final marker
+ final int end = Math.max(tag.lastIndexOf("/"), tag.lastIndexOf("."));
+ boolean printNext = true;
+ int index = sb.length(); // Start looping after the prefix
+ for (; index < end; index++) {
+ char c = tag.charAt(index);
+ boolean isMarker = (c == '.' || c == '/');
+ // We print all markers and the character that follows each marker
+ if (isMarker || printNext) {
+ sb.append(c);
+ }
+ printNext = isMarker;
+ }
+ sb.append(tag.substring(index)); // append everything that is left
+ return sb.toString();
+ }
+ return tag;
+ }
+
+ /**
+ * Represents a wakelock-event entry in the log.
+ * Holds all the data of a wakelock. The lifetime of this class is fairly short as the data
+ * within this type is eventually written to the log as bytes and the instances discarded until
+ * the log is read again which is fairly infrequent.
+ *
+ * At the time of this writing, this can be one of three types of entries:
+ * 1) Wake lock acquire
+ * 2) Wake lock release
+ * 3) Time reset
+ */
+ static class LogEntry {
+ /**
+ * Type of wake lock from the {@code WakeLockLog.TYPE_*} set.
+ */
+ public int type;
+
+ /**
+ * Time of the wake lock entry as taken from System.currentTimeMillis().
+ */
+ public long time;
+
+ /**
+ * Data about the wake lock tag.
+ */
+ public TagData tag;
+
+ /**
+ * Flags used with the wake lock.
+ */
+ public int flags;
+
+ LogEntry() {}
+
+ LogEntry(long time, int type, TagData tag, int flags) {
+ set(time, type, tag, flags);
+ }
+
+ /**
+ * Sets the values of the log entry.
+ * This is exposed to ease the reuse of {@code LogEntry} instances.
+ *
+ * @param time Time of the entry.
+ * @param type Type of entry.
+ * @param tag Tag data of the wake lock.
+ * @param flags Flags used with the wake lock.
+ */
+ public void set(long time, int type, TagData tag, int flags) {
+ this.time = time;
+ this.type = type;
+ this.tag = tag;
+ this.flags = flags;
+ }
+
+ /**
+ * Dumps this entry to the specified {@link PrintWriter}.
+ *
+ * @param pw The print-writer to dump to.
+ * @param dateFormat The date format to use for outputing times.
+ */
+ public void dump(PrintWriter pw, SimpleDateFormat dateFormat) {
+ pw.println(" " + toStringInternal(dateFormat));
+ }
+
+ /**
+ * Converts the entry to a string.
+ * date - ownerUid - (ACQ|REL) tag [(flags)]
+ * e.g., 1999-01-01 12:01:01.123 - 10012 - ACQ bluetooth_timer (partial)
+ */
+ @Override
+ public String toString() {
+ return toStringInternal(DATE_FORMAT);
+ }
+
+ /**
+ * Converts the entry to a string.
+ * date - ownerUid - (ACQ|REL) tag [(flags)]
+ * e.g., 1999-01-01 12:01:01.123 - 10012 - ACQ bluetooth_timer (partial)
+ *
+ * @param dateFormat The date format to use for outputing times.
+ * @return The string output of this class instance.
+ */
+ private String toStringInternal(SimpleDateFormat dateFormat) {
+ StringBuilder sb = new StringBuilder();
+ if (type == TYPE_TIME_RESET) {
+ return dateFormat.format(new Date(time)) + " - RESET";
+ }
+ sb.append(dateFormat.format(new Date(time)))
+ .append(" - ")
+ .append(tag == null ? "---" : tag.ownerUid)
+ .append(" - ")
+ .append(type == TYPE_ACQUIRE ? "ACQ" : "REL")
+ .append(" ")
+ .append(tag == null ? "UNKNOWN" : tag.tag);
+ if (type == TYPE_ACQUIRE) {
+ sb.append(" (");
+ flagsToString(sb);
+ sb.append(")");
+ }
+ return sb.toString();
+ }
+
+ private void flagsToString(StringBuilder sb) {
+ sb.append(LEVEL_TO_STRING[flags & 0x7]);
+ if ((flags & FLAG_ON_AFTER_RELEASE) == FLAG_ON_AFTER_RELEASE) {
+ sb.append(",on-after-release");
+ }
+ if ((flags & FLAG_ACQUIRE_CAUSES_WAKEUP) == FLAG_ACQUIRE_CAUSES_WAKEUP) {
+ sb.append(",acq-causes-wake");
+ }
+ }
+ }
+
+ /**
+ * Converts between a {@link LogEntry} instance and a byte sequence.
+ *
+ * This is used to convert {@link LogEntry}s to a series of bytes before being written into
+ * the log, and vice-versa when reading from the log.
+ *
+ * This method employs the compression techniques that are mentioned in the header of
+ * {@link WakeLockLog}: Relative-time and Tag-indexing. Please see the header for the
+ * description of both.
+ *
+ * The specific byte formats used are explained more thoroughly in the method {@link #toBytes}.
+ */
+ static class EntryByteTranslator {
+
+ // Error codes that can be returned when converting to bytes.
+ static final int ERROR_TIME_IS_NEGATIVE = -1; // Relative time is negative
+ static final int ERROR_TIME_TOO_LARGE = -2; // Relative time is out of valid range (0-255)
+
+ private final TagDatabase mTagDatabase;
+
+ EntryByteTranslator(TagDatabase tagDatabase) {
+ mTagDatabase = tagDatabase;
+ }
+
+ /**
+ * Translates the specified bytes into a LogEntry instance, if possible.
+ *
+ * See {@link #toBytes} for an explanation of the byte formats.
+ *
+ * @param bytes The bytes to read.
+ * @param timeReference The reference time to use when reading the relative time from the
+ * bytes buffer.
+ * @param entryToReuse The entry instance to write to. If null, this method will create a
+ * new instance.
+ * @return The converted entry, or null if data is corrupt.
+ */
+ LogEntry fromBytes(byte[] bytes, long timeReference, LogEntry entryToReuse) {
+ if (bytes == null || bytes.length == 0) {
+ return null;
+ }
+
+ // Create an entry if non if passed in to use
+ LogEntry entry = entryToReuse != null ? entryToReuse : new LogEntry();
+
+ int type = (bytes[0] >> 6) & 0x3;
+ if ((type & 0x2) == 0x2) {
+ // As long as the highest order bit of the byte is set, it is a release
+ type = TYPE_RELEASE;
+ }
+ switch (type) {
+ case TYPE_ACQUIRE: {
+ if (bytes.length < 3) {
+ break;
+ }
+
+ int flags = bytes[0] & MASK_LOWER_6_BITS;
+ int tagIndex = bytes[1] & MASK_LOWER_7_BITS;
+ TagData tag = mTagDatabase.getTag(tagIndex);
+ long time = (bytes[2] & 0xFF) + timeReference;
+ entry.set(time, TYPE_ACQUIRE, tag, flags);
+ return entry;
+ }
+ case TYPE_RELEASE: {
+ if (bytes.length < 2) {
+ break;
+ }
+
+ int flags = 0;
+ int tagIndex = bytes[0] & MASK_LOWER_7_BITS;
+ TagData tag = mTagDatabase.getTag(tagIndex);
+ long time = (bytes[1] & 0xFF) + timeReference;
+ entry.set(time, TYPE_RELEASE, tag, flags);
+ return entry;
+ }
+ case TYPE_TIME_RESET: {
+ if (bytes.length < 9) {
+ break;
+ }
+
+ long time = ((bytes[1] & 0xFFL) << 56)
+ | ((bytes[2] & 0xFFL) << 48)
+ | ((bytes[3] & 0xFFL) << 40)
+ | ((bytes[4] & 0xFFL) << 32)
+ | ((bytes[5] & 0xFFL) << 24)
+ | ((bytes[6] & 0xFFL) << 16)
+ | ((bytes[7] & 0xFFL) << 8)
+ | (bytes[8] & 0xFFL);
+ entry.set(time, TYPE_TIME_RESET, null, 0);
+ return entry;
+ }
+ default:
+ Slog.w(TAG, "Type not recognized [" + type + "]", new Exception());
+ break;
+ }
+ return null;
+ }
+
+ /**
+ * Converts and writes the specified entry into the specified byte array.
+ * If the byte array is null or too small, then the method writes nothing, but still returns
+ * the number of bytes necessary to write the entry.
+ *
+ * Byte format used for each type:
+ *
+ * TYPE_RELEASE:
+ * bits
+ * 0 1 2 3 4 5 6 7
+ * bytes 0 [ 1 | 7-bit wake lock tag index ]
+ * 1 [ 8-bit relative time ]
+ *
+ *
+ * TYPE_ACQUIRE:
+ * bits
+ * 0 1 2 3 4 5 6 7
+ * 0 [ 0 1 | wake lock flags ]
+ * bytes 1 [unused| 7-bit wake lock tag index ]
+ * 2 [ 8-bit relative time ]
+ *
+ *
+ * TYPE_TIME_RESET:
+ * bits
+ * 0 1 2 3 4 5 6 7
+ * 0 [ 0 0 | unused ]
+ * bytes 1-9 [ 64-bit reference-time ]
+ *
+ * @param entry The entry to convert/write
+ * @param bytes The buffer to write to, or null to just return the necessary bytes.
+ * @param timeReference The reference-time used to calculate relative time of the entry.
+ * @return The number of bytes written to buffer, or required to write to the buffer.
+ */
+ int toBytes(LogEntry entry, byte[] bytes, long timeReference) {
+ int sizeNeeded = -1;
+ switch (entry.type) {
+ case TYPE_ACQUIRE: {
+ sizeNeeded = 3;
+ if (bytes != null && bytes.length >= sizeNeeded) {
+ int relativeTime = getRelativeTime(timeReference, entry.time);
+ if (relativeTime < 0) {
+ // Negative relative time indicates error code
+ return relativeTime;
+ }
+ bytes[0] = (byte) ((TYPE_ACQUIRE << 6)
+ | (entry.flags & MASK_LOWER_6_BITS));
+ bytes[1] = (byte) mTagDatabase.getTagIndex(entry.tag);
+ bytes[2] = (byte) (relativeTime & 0xFF); // Lower 8 bits of the time
+ if (DEBUG) {
+ Slog.d(TAG, "ACQ - Setting bytes: " + Arrays.toString(bytes));
+ }
+ }
+ break;
+ }
+ case TYPE_RELEASE: {
+ sizeNeeded = 2;
+ if (bytes != null && bytes.length >= sizeNeeded) {
+ int relativeTime = getRelativeTime(timeReference, entry.time);
+ if (relativeTime < 0) {
+ // Negative relative time indicates error code
+ return relativeTime;
+ }
+ bytes[0] = (byte) (0x80 | mTagDatabase.getTagIndex(entry.tag));
+ bytes[1] = (byte) (relativeTime & 0xFF); // Lower 8 bits of the time
+ if (DEBUG) {
+ Slog.d(TAG, "REL - Setting bytes: " + Arrays.toString(bytes));
+ }
+ }
+ break;
+ }
+ case TYPE_TIME_RESET: {
+ sizeNeeded = 9;
+ long time = entry.time;
+ if (bytes != null && bytes.length >= sizeNeeded) {
+ bytes[0] = (TYPE_TIME_RESET << 6);
+ bytes[1] = (byte) ((time >> 56) & 0xFF);
+ bytes[2] = (byte) ((time >> 48) & 0xFF);
+ bytes[3] = (byte) ((time >> 40) & 0xFF);
+ bytes[4] = (byte) ((time >> 32) & 0xFF);
+ bytes[5] = (byte) ((time >> 24) & 0xFF);
+ bytes[6] = (byte) ((time >> 16) & 0xFF);
+ bytes[7] = (byte) ((time >> 8) & 0xFF);
+ bytes[8] = (byte) (time & 0xFF);
+ }
+ break;
+ }
+ default:
+ throw new RuntimeException("Unknown type " + entry);
+ }
+
+ return sizeNeeded;
+ }
+
+ /**
+ * Calculates the relative time between the specified time and timeReference. The relative
+ * time is expected to be non-negative and fit within 8-bits (values between 0-255). If the
+ * relative time is outside of that range an error code will be returned instead.
+ *
+ * @param time
+ * @param timeReference
+ * @return The relative time between time and timeReference, or an error code.
+ */
+ private int getRelativeTime(long timeReference, long time) {
+ if (time < timeReference) {
+ if (DEBUG) {
+ Slog.w(TAG, "ERROR_TIME_IS_NEGATIVE");
+ }
+ return ERROR_TIME_IS_NEGATIVE;
+ }
+ long relativeTime = time - timeReference;
+ if (relativeTime > 255) {
+ if (DEBUG) {
+ Slog.w(TAG, "ERROR_TIME_TOO_LARGE");
+ }
+ return ERROR_TIME_TOO_LARGE;
+ }
+ return (int) relativeTime;
+ }
+ }
+
+ /**
+ * Main implementation of the ring buffer used to store the log entries. This class takes
+ * {@link LogEntry} instances and adds them to the ring buffer, utilizing
+ * {@link EntryByteTranslator} to convert byte {@link LogEntry} to bytes within the buffer.
+ *
+ * This class also implements the logic around TIME_RESET events. Since the LogEntries store
+ * their time (8-bit) relative to the previous event, this class can add {@link TYPE_TIME_RESET}
+ * LogEntries as necessary to allow a LogEntry's relative time to fit within that range.
+ */
+ static class TheLog {
+ private final EntryByteTranslator mTranslator;
+
+ /**
+ * Temporary buffer used when converting a new entry to bytes for writing to the buffer.
+ * Allocating once allows us to avoid allocating a buffer with each write.
+ */
+ private final byte[] mTempBuffer = new byte[MAX_LOG_ENTRY_BYTE_SIZE];
+
+ /**
+ * Second temporary buffer used when reading and writing bytes from the buffer.
+ * A second temporary buffer is necessary since additional items can be read concurrently
+ * from {@link mTempBuffer}. E.g., Adding an entry to a full buffer requires removing
+ * other entries from the buffer.
+ */
+ private final byte[] mReadWriteTempBuffer = new byte[MAX_LOG_ENTRY_BYTE_SIZE];
+
+ /**
+ * Main log buffer.
+ */
+ private final byte[] mBuffer;
+
+ /**
+ * Start index of the ring buffer.
+ */
+ private int mStart = 0;
+
+ /**
+ * Current end index of the ring buffer.
+ */
+ private int mEnd = 0;
+
+ /**
+ * Start time of the entries in the buffer. The first item stores an 8-bit time that is
+ * relative to this value.
+ */
+ private long mStartTime = 0;
+
+ /**
+ * The time of the last entry in the buffer. Reading the time from the last entry to
+ * calculate the relative time of a new one is sufficiently hard to prefer saving the value
+ * here instead.
+ */
+ private long mLatestTime = 0;
+
+ /**
+ * Counter for number of changes (adds or removes) that have been done to the buffer.
+ */
+ private long mChangeCount = 0;
+
+ private final TagDatabase mTagDatabase;
+
+ TheLog(Injector injector, EntryByteTranslator translator, TagDatabase tagDatabase) {
+ final int logSize = Math.max(injector.getLogSize(), LOG_SIZE_MIN);
+ mBuffer = new byte[logSize];
+
+ mTranslator = translator;
+ mTagDatabase = tagDatabase;
+
+ // Register to be notified when an older tag is removed from the TagDatabase to make
+ // room for a new entry.
+ mTagDatabase.setCallback(new TagDatabase.Callback() {
+ @Override public void onIndexRemoved(int index) {
+ removeTagIndex(index);
+ }
+ });
+ }
+
+ /**
+ * Returns the amount of space being used in the ring buffer (in bytes).
+ *
+ * @return Used buffer size in bytes.
+ */
+ int getUsedBufferSize() {
+ return mBuffer.length - getAvailableSpace();
+ }
+
+ /**
+ * Adds the specified {@link LogEntry} to the log by converting it to bytes and writing
+ * those bytes to the buffer.
+ *
+ * This method can have side effects of removing old values from the ring buffer and
+ * adding an extra TIME_RESET entry if necessary.
+ */
+ void addEntry(LogEntry entry) {
+ if (isBufferEmpty()) {
+ // First item being added, do initialization.
+ mStartTime = mLatestTime = entry.time;
+ }
+
+ int size = mTranslator.toBytes(entry, mTempBuffer, mLatestTime);
+ if (size == EntryByteTranslator.ERROR_TIME_IS_NEGATIVE) {
+ return; // Wholly unexpected circumstance...just break out now.
+ } else if (size == EntryByteTranslator.ERROR_TIME_TOO_LARGE) {
+ // The relative time between the last entry and this new one is too large
+ // to fit in our byte format...we need to create a new Time-Reset event and add
+ // that to the log first.
+ addEntry(new LogEntry(entry.time, TYPE_TIME_RESET, null, 0));
+ size = mTranslator.toBytes(entry, mTempBuffer, mLatestTime);
+ }
+
+ if (size > MAX_LOG_ENTRY_BYTE_SIZE || size <= 0) {
+ Slog.w(TAG, "Log entry size is out of expected range: " + size);
+ return;
+ }
+
+ // In case the buffer is full or nearly full, ensure there is a proper amount of space
+ // for the new entry.
+ if (!makeSpace(size)) {
+ return; // Doesn't fit
+ }
+
+ if (DEBUG) {
+ Slog.d(TAG, "Wrote New Entry @(" + mEnd + ") [" + entry + "] as "
+ + Arrays.toString(mTempBuffer));
+ }
+ // Set the bytes and update our end index & timestamp.
+ writeBytesAt(mEnd, mTempBuffer, size);
+ if (DEBUG) {
+ Slog.d(TAG, "Read written Entry @(" + mEnd + ") ["
+ + readEntryAt(mEnd, mLatestTime, null));
+ }
+ mEnd = (mEnd + size) % mBuffer.length;
+ mLatestTime = entry.time;
+
+ TagDatabase.updateTagTime(entry.tag, entry.time);
+ mChangeCount++;
+ }
+
+ /**
+ * Returns an {@link Iterator} of {@link LogEntry}s for all the entries in the log.
+ *
+ * If the log is modified while the entries are being read, the iterator will throw a
+ * {@link ConcurrentModificationExceptoin}.
+ *
+ * @param tempEntry A temporary {@link LogEntry} instance to use so that new instances
+ * aren't allocated with every call to {@code Iterator.next}.
+ */
+ Iterator<LogEntry> getAllItems(final LogEntry tempEntry) {
+ return new Iterator<LogEntry>() {
+ private int mCurrent = mStart; // Current read position in the log.
+ private long mCurrentTimeReference = mStartTime; // Current time-reference to use.
+ private final long mChangeValue = mChangeCount; // Used to track if buffer changed.
+
+ /**
+ * @return True if there are more elements to iterate through, false otherwise.\
+ * @throws ConcurrentModificationException if the buffer contents change.
+ */
+ @Override
+ public boolean hasNext() {
+ checkState();
+ return mCurrent != mEnd;
+ }
+
+ /**
+ * Returns the next element in the iterator.
+ *
+ * @return The next entry in the iterator
+ * @throws NoSuchElementException if iterator reaches the end.
+ * @throws ConcurrentModificationException if buffer contents change.
+ */
+ @Override
+ public LogEntry next() {
+ checkState();
+
+ if (!hasNext()) {
+ throw new NoSuchElementException("No more entries left.");
+ }
+
+ LogEntry entry = readEntryAt(mCurrent, mCurrentTimeReference, tempEntry);
+ int size = mTranslator.toBytes(entry, null, mStartTime);
+ mCurrent = (mCurrent + size) % mBuffer.length;
+ mCurrentTimeReference = entry.time;
+
+ return entry;
+ }
+
+ @Override public String toString() {
+ return "@" + mCurrent;
+ }
+
+ /**
+ * @throws ConcurrentModificationException if the underlying buffer has changed
+ * since this iterator was instantiated.
+ */
+ private void checkState() {
+ if (mChangeValue != mChangeCount) {
+ throw new ConcurrentModificationException("Buffer modified, old change: "
+ + mChangeValue + ", new change: " + mChangeCount);
+ }
+ }
+ };
+ }
+
+ /**
+ * Cleans up old tag index references from the entire log.
+ * Called when an older wakelock tag is removed from the tag database. This happens
+ * when the database needed additional room for newer tags.
+ *
+ * This is a fairly expensive operation. Reads all the entries from the buffer, which can
+ * be around 1500 for a 10Kb buffer. It will write back any entries that use the tag as
+ * well, but that's not many of them. Commonly-used tags dont ever make it to this part.
+ *
+ * If necessary, in the future we can keep track of the number of tag-users the same way we
+ * keep track of a tag's last-used-time to stop having to do this for old tags that dont
+ * have entries in the logs any more. Light testing has shown that for a 10Kb
+ * buffer, there are about 5 or fewer (of 1500) entries with "UNKNOWN" tag...which means
+ * this operation does happen, but not very much.
+ *
+ * @param tagIndex The index of the tag, as stored in the log
+ */
+ private void removeTagIndex(int tagIndex) {
+ if (isBufferEmpty()) {
+ return;
+ }
+
+ int readIndex = mStart;
+ long timeReference = mStartTime;
+ final LogEntry reusableEntryInstance = new LogEntry();
+ while (readIndex != mEnd) {
+ LogEntry entry = readEntryAt(readIndex, timeReference, reusableEntryInstance);
+ if (DEBUG) {
+ Slog.d(TAG, "Searching to remove tags @ " + readIndex + ": " + entry);
+ }
+ if (entry == null) {
+ Slog.w(TAG, "Entry is unreadable - Unexpected @ " + readIndex);
+ break; // cannot continue if entries are now corrupt
+ }
+ if (entry.tag != null && entry.tag.index == tagIndex) {
+ // We found an entry that uses the tag being removed. Re-write the
+ // entry back without a tag.
+ entry.tag = null; // remove the tag, and write it back
+ writeEntryAt(readIndex, entry, timeReference);
+ if (DEBUG) {
+ Slog.d(TAG, "Remove tag index: " + tagIndex + " @ " + readIndex);
+ }
+ }
+ timeReference = entry.time;
+ int entryByteSize = mTranslator.toBytes(entry, null, 0L);
+ readIndex = (readIndex + entryByteSize) % mBuffer.length;
+ }
+ }
+
+ /**
+ * Removes entries from the buffer until the specified amount of space is available for use.
+ *
+ * @param spaceNeeded The number of bytes needed in the buffer.
+ * @return True if there is space enough in the buffer, false otherwise.
+ */
+ private boolean makeSpace(int spaceNeeded) {
+ // Test the size of the buffer can fit it first, so that we dont loop forever in the
+ // following while loop.
+ if (mBuffer.length < spaceNeeded + 1) {
+ return false;
+ }
+
+ // We check spaceNeeded + 1 so that mStart + mEnd aren't equal. We use them being equal
+ // to mean that the buffer is empty...so avoid that.
+ while (getAvailableSpace() < (spaceNeeded + 1)) {
+ removeOldestItem();
+ }
+ return true;
+ }
+
+ /**
+ * Returns the available space of the ring buffer.
+ */
+ private int getAvailableSpace() {
+ return mEnd > mStart ? mBuffer.length - (mEnd - mStart) :
+ (mEnd < mStart ? mStart - mEnd :
+ mBuffer.length);
+ }
+
+ /**
+ * Removes the oldest item from the buffer if the buffer is not empty.
+ */
+ private void removeOldestItem() {
+ if (isBufferEmpty()) {
+ // No items to remove
+ return;
+ }
+
+ // Copy the contents of the start of the buffer to our temporary buffer.
+ LogEntry entry = readEntryAt(mStart, mStartTime, null);
+ if (DEBUG) {
+ Slog.d(TAG, "Removing oldest item at @ " + mStart + ", found: " + entry);
+ }
+ int size = mTranslator.toBytes(entry, null, mStartTime);
+ mStart = (mStart + size) % mBuffer.length;
+ mStartTime = entry.time; // new start time
+ mChangeCount++;
+ }
+
+ /**
+ * Returns true if the buffer is currently unused (contains zero entries).
+ *
+ * @return True if empty, false otherwise.
+ */
+ private boolean isBufferEmpty() {
+ return mStart == mEnd;
+ }
+
+ /**
+ * Reads an entry from the specified index in the buffer.
+ *
+ * @param index Index into the buffer from which to read.
+ * @param timeReference Reference time to use when creating the {@link LogEntry}.
+ * @param entryToSet Temporary entry to use instead of allocating a new one.
+ * @return the log-entry instance that was read.
+ */
+ private LogEntry readEntryAt(int index, long timeReference, LogEntry entryToSet) {
+ for (int i = 0; i < MAX_LOG_ENTRY_BYTE_SIZE; i++) {
+ int indexIntoMainBuffer = (index + i) % mBuffer.length;
+ if (indexIntoMainBuffer == mEnd) {
+ break;
+ }
+ mReadWriteTempBuffer[i] = mBuffer[indexIntoMainBuffer];
+ }
+ return mTranslator.fromBytes(mReadWriteTempBuffer, timeReference, entryToSet);
+ }
+
+ /**
+ * Write a specified {@link LogEntry} to the buffer at the specified index.
+ *
+ * @param index Index in which to write in the buffer.
+ * @param entry The entry to write into the buffer.
+ * @param timeReference The reference time to use when calculating the relative time.
+ */
+ private void writeEntryAt(int index, LogEntry entry, long timeReference) {
+ int size = mTranslator.toBytes(entry, mReadWriteTempBuffer, timeReference);
+ if (size > 0) {
+ if (DEBUG) {
+ Slog.d(TAG, "Writing Entry (" + index + ") [" + entry + "] as "
+ + Arrays.toString(mReadWriteTempBuffer));
+ }
+ writeBytesAt(index, mReadWriteTempBuffer, size);
+ }
+ }
+
+ /**
+ * Write the specified bytes into the buffer at the specified index.
+ * Handling wrap-around calculation for the ring-buffer.
+ *
+ * @param index The index from which to start writing.
+ * @param buffer The buffer of bytes to be written.
+ * @param size The amount of bytes to write from {@code buffer} to the log.
+ */
+ private void writeBytesAt(int index, byte[] buffer, int size) {
+ for (int i = 0; i < size; i++) {
+ int indexIntoMainBuffer = (index + i) % mBuffer.length;
+ mBuffer[indexIntoMainBuffer] = buffer[i];
+ }
+ if (DEBUG) {
+ Slog.d(TAG, "Write Byte: " + Arrays.toString(buffer));
+ }
+ }
+ }
+
+ /**
+ * An in-memory database of wake lock {@link TagData}. All tags stored in the database are given
+ * a 7-bit index. This index is then used by {@link TheLog} when translating {@link LogEntry}
+ * instanced into bytes.
+ *
+ * If a new tag is added when the database is full, the oldest tag is removed. The oldest tag
+ * is calcualted using {@link TagData.lastUsedTime}.
+ */
+ static class TagDatabase {
+ private final int mInvalidIndex;
+ private final TagData[] mArray;
+ private Callback mCallback;
+
+ TagDatabase(Injector injector) {
+ int size = Math.min(injector.getTagDatabaseSize(), TAG_DATABASE_SIZE_MAX);
+
+ // Largest possible index used as "INVALID", hence the (size - 1) sizing.
+ mArray = new TagData[size - 1];
+ mInvalidIndex = size - 1;
+ }
+
+ @Override
+ public String toString() {
+ StringBuilder sb = new StringBuilder();
+ sb.append("Tag Database: size(").append(mArray.length).append(")");
+ int entries = 0;
+ int byteEstimate = 0;
+ int tagSize = 0;
+ int tags = 0;
+ for (int i = 0; i < mArray.length; i++) {
+ byteEstimate += 8; // reference pointer
+ TagData data = mArray[i];
+ if (data != null) {
+ entries++;
+ byteEstimate += data.getByteSize();
+ if (data.tag != null) {
+ tags++;
+ tagSize += data.tag.length();
+ }
+ }
+ }
+ sb.append(", entries: ").append(entries);
+ sb.append(", Bytes used: ").append(byteEstimate);
+ if (DEBUG) {
+ sb.append(", Avg tag size: ").append(tagSize / tags);
+ sb.append("\n ").append(Arrays.toString(mArray));
+ }
+ return sb.toString();
+ }
+
+ /**
+ * Sets the callback.
+ *
+ * @param callback The callback to set.
+ */
+ public void setCallback(Callback callback) {
+ mCallback = callback;
+ }
+
+ /**
+ * Returns the tag corresponding to the specified index.
+ *
+ * @param index The index to search for.
+ */
+ public TagData getTag(int index) {
+ if (index < 0 || index >= mArray.length || index == mInvalidIndex) {
+ return null;
+ }
+ return mArray[index];
+ }
+
+ /**
+ * Returns an existing tag for the specified wake lock tag + ownerUid.
+ *
+ * @param tag The wake lock tag.
+ * @param ownerUid The wake lock's ownerUid.
+ * @return the TagData instance.
+ */
+ public TagData getTag(String tag, int ownerUid) {
+ return findOrCreateTag(tag, ownerUid, false /* shouldCreate */);
+ }
+
+ /**
+ * Returns the index for the corresponding tag.
+ *
+ * @param tagData The tag-data to search for.
+ * @return the corresponding index, or mInvalidIndex of none is found.
+ */
+ public int getTagIndex(TagData tagData) {
+ return tagData == null ? mInvalidIndex : tagData.index;
+ }
+
+ /**
+ * Returns a tag instance for the specified wake lock tag and ownerUid. If the data
+ * does not exist in the database, it will be created if so specified by
+ * {@code shouldCreate}.
+ *
+ * @param tagStr The wake lock's tag.
+ * @param ownerUid The wake lock's owner Uid.
+ * @param shouldCreate True when the tag should be created if it doesn't already exist.
+ * @return The tag-data instance that was found or created.
+ */
+ public TagData findOrCreateTag(String tagStr, int ownerUid, boolean shouldCreate) {
+ int firstAvailable = -1;
+ TagData oldest = null;
+ int oldestIndex = -1;
+
+ // Loop through and find the tag to be used.
+ TagData tag = new TagData(tagStr, ownerUid);
+ for (int i = 0; i < mArray.length; i++) {
+ TagData current = mArray[i];
+ if (tag.equals(current)) {
+ // found it
+ return current;
+ } else if (!shouldCreate) {
+ continue;
+ } else if (current != null) {
+ // See if this entry is the oldest entry, in case
+ // we need to replace it.
+ if (oldest == null || current.lastUsedTime < oldest.lastUsedTime) {
+ oldestIndex = i;
+ oldest = current;
+ }
+ } else if (firstAvailable == -1) {
+ firstAvailable = i;
+ }
+ }
+
+ // Item not found, and we shouldn't create one.
+ if (!shouldCreate) {
+ return null;
+ }
+
+ // If we need to remove an index, report to listeners that we are removing an index.
+ boolean useOldest = firstAvailable == -1;
+ if (useOldest && mCallback != null) {
+ if (DEBUG) {
+ Slog.d(TAG, "Removing tag index: " + oldestIndex + " = " + oldest);
+ }
+ mCallback.onIndexRemoved(oldestIndex);
+ }
+ setToIndex(tag, firstAvailable != -1 ? firstAvailable : oldestIndex);
+ return tag;
+ }
+
+ /**
+ * Updates the last-used-time of the specified tag.
+ *
+ * @param tag The tag to update.
+ * @param time The new last-used-time for the tag.
+ */
+ public static void updateTagTime(TagData tag, long time) {
+ if (tag != null) {
+ tag.lastUsedTime = time;
+ }
+ }
+
+ /**
+ * Sets a specified tag to the specified index.
+ */
+ private void setToIndex(TagData tag, int index) {
+ if (index < 0 || index >= mArray.length) {
+ return;
+ }
+ TagData current = mArray[index];
+ if (current != null) {
+ // clean up the reference in the TagData instance first.
+ current.index = mInvalidIndex;
+
+ if (DEBUG) {
+ Slog.d(TAG, "Replaced tag " + current.tag + " from index " + index + " with tag"
+ + tag);
+ }
+ }
+
+ mArray[index] = tag;
+ tag.index = index;
+ }
+
+ /**
+ * Callback on which to be notified of changes to {@link TagDatabase}.
+ */
+ interface Callback {
+
+ /**
+ * Handles removals of TagData indexes.
+ *
+ * @param index the index being removed.
+ */
+ void onIndexRemoved(int index);
+ }
+ }
+
+ /**
+ * This class represents unique wake lock tags that are stored in {@link TagDatabase}.
+ * Contains both the wake lock tag data (tag + ownerUid) as well as index and last-used
+ * time data as it relates to the tag-database.
+ */
+ static class TagData {
+ public String tag; // Wake lock tag
+ public int ownerUid; // Wake lock owner Uid
+ public int index; // Index of the tag in the tag-database
+ public long lastUsedTime; // Last time that this entry was used
+
+ TagData(String tag, int ownerUid) {
+ this.tag = tag;
+ this.ownerUid = ownerUid;
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ if (this == o) {
+ return true;
+ }
+ if (o instanceof TagData) {
+ TagData other = (TagData) o;
+ return TextUtils.equals(tag, other.tag) && ownerUid == other.ownerUid;
+ }
+ return false;
+ }
+
+ @Override
+ public String toString() {
+ StringBuilder sb = new StringBuilder();
+ if (DEBUG) {
+ sb.append("(").append(index).append(")");
+ }
+ return "[" + ownerUid + " ; " + tag + "]";
+ }
+
+ /**
+ * Returns an estimate of the number of bytes used by each instance of this class.
+ * Used for debug purposes.
+ *
+ * @return the size of this tag-data.
+ */
+ int getByteSize() {
+ int bytes = 0;
+ bytes += 8; // tag reference-pointer;
+ bytes += tag == null ? 0 : tag.length() * 2;
+ bytes += 4; // ownerUid
+ bytes += 4; // index
+ bytes += 8; // lastUsedTime
+ return bytes;
+ }
+ }
+
+ /**
+ * Injector used by {@link WakeLockLog} for testing purposes.
+ */
+ public static class Injector {
+ public Looper getLooper() {
+ return BackgroundThread.get().getLooper();
+ }
+
+ public int getTagDatabaseSize() {
+ return TAG_DATABASE_SIZE;
+ }
+
+ public int getLogSize() {
+ return LOG_SIZE;
+ }
+
+ public long currentTimeMillis() {
+ return System.currentTimeMillis();
+ }
+
+ public SimpleDateFormat getDateFormat() {
+ return DATE_FORMAT;
+ }
+ }
+
+ private class WakeLockLogHandler extends Handler {
+ WakeLockLogHandler(Looper looper) {
+ super(looper);
+ }
+
+ @Override
+ public void handleMessage(Message message) {
+ switch(message.what) {
+ case MSG_ON_WAKE_LOCK_EVENT:
+ final SomeArgs args = (SomeArgs) message.obj;
+ final String tag = (String) args.arg1;
+ final int eventType = args.argi1;
+ final int ownerUid = args.argi2;
+ final int flags = args.argi3;
+ final long time = (((long) args.argi4) << 32) + (args.argi5 & 0xFFFFFFFFL);
+ args.recycle();
+ handleWakeLockEventInternal(eventType, tag, ownerUid, flags, time);
+ break;
+ default:
+ break;
+ }
+ }
+ }
+}
diff --git a/services/core/java/com/android/server/stats/pull/ProcfsMemoryUtil.java b/services/core/java/com/android/server/stats/pull/ProcfsMemoryUtil.java
index 638dfd23c27f..67677c6cf17e 100644
--- a/services/core/java/com/android/server/stats/pull/ProcfsMemoryUtil.java
+++ b/services/core/java/com/android/server/stats/pull/ProcfsMemoryUtil.java
@@ -19,8 +19,7 @@ import static android.os.Process.PROC_OUT_STRING;
import android.annotation.Nullable;
import android.os.Process;
-
-import java.util.function.BiConsumer;
+import android.util.SparseArray;
public final class ProcfsMemoryUtil {
private static final int[] CMDLINE_OUT = new int[] { PROC_OUT_STRING };
@@ -71,19 +70,25 @@ public final class ProcfsMemoryUtil {
return cmdline[0];
}
- public static void forEachPid(BiConsumer<Integer, String> func) {
+ /**
+ * Scans all /proc/pid/cmdline entries and returns a mapping between pid and cmdline.
+ */
+ public static SparseArray<String> getProcessCmdlines() {
int[] pids = new int[1024];
pids = Process.getPids("/proc", pids);
+
+ SparseArray<String> cmdlines = new SparseArray<>(pids.length);
for (int pid : pids) {
if (pid < 0) {
- return;
+ break;
}
String cmdline = readCmdlineFromProcfs(pid);
if (cmdline.isEmpty()) {
continue;
}
- func.accept(pid, cmdline);
+ cmdlines.append(pid, cmdline);
}
+ return cmdlines;
}
public static final class MemorySnapshot {
diff --git a/services/core/java/com/android/server/stats/pull/StatsPullAtomService.java b/services/core/java/com/android/server/stats/pull/StatsPullAtomService.java
index 39d1a51c01de..3aa51fbf2313 100644
--- a/services/core/java/com/android/server/stats/pull/StatsPullAtomService.java
+++ b/services/core/java/com/android/server/stats/pull/StatsPullAtomService.java
@@ -27,7 +27,7 @@ import static android.os.storage.VolumeInfo.TYPE_PUBLIC;
import static com.android.server.am.MemoryStatUtil.readMemoryStatFromFilesystem;
import static com.android.server.stats.pull.IonMemoryUtil.readProcessSystemIonHeapSizesFromDebugfs;
import static com.android.server.stats.pull.IonMemoryUtil.readSystemIonHeapSizeFromDebugfs;
-import static com.android.server.stats.pull.ProcfsMemoryUtil.forEachPid;
+import static com.android.server.stats.pull.ProcfsMemoryUtil.getProcessCmdlines;
import static com.android.server.stats.pull.ProcfsMemoryUtil.readCmdlineFromProcfs;
import static com.android.server.stats.pull.ProcfsMemoryUtil.readMemorySnapshotFromProcfs;
@@ -93,6 +93,7 @@ import android.util.ArrayMap;
import android.util.ArraySet;
import android.util.Log;
import android.util.Slog;
+import android.util.SparseArray;
import android.util.StatsEvent;
import android.util.StatsLog;
import android.util.proto.ProtoOutputStream;
@@ -130,8 +131,6 @@ import com.android.server.stats.pull.ProcfsMemoryUtil.MemorySnapshot;
import com.android.server.storage.DiskStatsFileLogger;
import com.android.server.storage.DiskStatsLoggingService;
-import com.google.android.collect.Sets;
-
import libcore.io.IoUtils;
import org.json.JSONArray;
@@ -165,11 +164,24 @@ public class StatsPullAtomService extends SystemService {
private static final String TAG = "StatsPullAtomService";
private static final boolean DEBUG = true;
+ /**
+ * Lowest available uid for apps.
+ *
+ * <p>Used to quickly discard memory snapshots of the zygote forks from native process
+ * measurements.
+ */
+ private static final int MIN_APP_UID = 10_000;
+
private static final String RESULT_RECEIVER_CONTROLLER_KEY = "controller_activity";
/**
* How long to wait on an individual subsystem to return its stats.
*/
private static final long EXTERNAL_STATS_SYNC_TIMEOUT_MILLIS = 2000;
+ private static final long NS_PER_SEC = 1000000000;
+ private static final long MILLI_AMP_HR_TO_NANO_AMP_SECS = 1_000_000L * 3600L;
+
+ private static final int MAX_BATTERY_STATS_HELPER_FREQUENCY_MS = 1000;
+ private static final int CPU_TIME_PER_THREAD_FREQ_MAX_NUM_FREQUENCIES = 8;
private final Object mNetworkStatsLock = new Object();
@GuardedBy("mNetworkStatsLock")
@@ -191,40 +203,222 @@ public class StatsPullAtomService extends SystemService {
@GuardedBy("mProcessStatsLock")
private IProcessStats mProcessStatsService;
+ private final Object mCpuTrackerLock = new Object();
+ @GuardedBy("mCpuTrackerLock")
+ private ProcessCpuTracker mProcessCpuTracker;
+
+ private final Object mDebugElapsedClockLock = new Object();
+ @GuardedBy("mDebugElapsedClockLock")
+ private long mDebugElapsedClockPreviousValue = 0;
+ @GuardedBy("mDebugElapsedClockLock")
+ private long mDebugElapsedClockPullCount = 0;
+
+ private final Object mDebugFailingElapsedClockLock = new Object();
+ @GuardedBy("mDebugFailingElapsedClockLock")
+ private long mDebugFailingElapsedClockPreviousValue = 0;
+ @GuardedBy("mDebugFailingElapsedClockLock")
+ private long mDebugFailingElapsedClockPullCount = 0;
+
private final Context mContext;
private StatsManager mStatsManager;
private StorageManager mStorageManager;
+ private WifiManager mWifiManager;
+ private TelephonyManager mTelephony;
+
+ private KernelWakelockReader mKernelWakelockReader;
+ private KernelWakelockStats mTmpWakelockStats;
+
+ private StoragedUidIoStatsReader mStoragedUidIoStatsReader;
+
+ private KernelCpuSpeedReader[] mKernelCpuSpeedReaders;
+ // Disables throttler on CPU time readers.
+ private KernelCpuUidUserSysTimeReader mCpuUidUserSysTimeReader;
+ private KernelCpuUidFreqTimeReader mCpuUidFreqTimeReader;
+ private KernelCpuUidActiveTimeReader mCpuUidActiveTimeReader;
+ private KernelCpuUidClusterTimeReader mCpuUidClusterTimeReader;
+
+ private File mBaseDir;
+
+ @Nullable
+ private KernelCpuThreadReaderDiff mKernelCpuThreadReader;
+
+ private BatteryStatsHelper mBatteryStatsHelper = null;
+ private long mBatteryStatsHelperTimestampMs = -MAX_BATTERY_STATS_HELPER_FREQUENCY_MS;
+
+ private StatsPullAtomCallbackImpl mStatsCallbackImpl;
public StatsPullAtomService(Context context) {
super(context);
mContext = context;
}
+ /**
+ * Use of this StatsPullAtomCallbackImpl means we avoid one class per tagId, which we would
+ * get if we used lambdas.
+ *
+ * The pull methods are intentionally left to be package private to avoid the creation
+ * of synthetic methods to save unnecessary bytecode.
+ */
+ private class StatsPullAtomCallbackImpl implements StatsManager.StatsPullAtomCallback {
+ @Override
+ public int onPullAtom(int atomTag, List<StatsEvent> data) {
+ switch(atomTag) {
+ case StatsLog.WIFI_BYTES_TRANSFER:
+ return pullWifiBytesTransfer(atomTag, data);
+ case StatsLog.WIFI_BYTES_TRANSFER_BY_FG_BG:
+ return pullWifiBytesTransferBackground(atomTag, data);
+ case StatsLog.MOBILE_BYTES_TRANSFER:
+ return pullMobileBytesTransfer(atomTag, data);
+ case StatsLog.MOBILE_BYTES_TRANSFER_BY_FG_BG:
+ return pullMobileBytesTransferBackground(atomTag, data);
+ case StatsLog.BLUETOOTH_BYTES_TRANSFER:
+ return pullBluetoothBytesTransfer(atomTag, data);
+ case StatsLog.KERNEL_WAKELOCK:
+ return pullKernelWakelock(atomTag, data);
+ case StatsLog.CPU_TIME_PER_FREQ:
+ return pullCpuTimePerFreq(atomTag, data);
+ case StatsLog.CPU_TIME_PER_UID:
+ return pullCpuTimePerUid(atomTag, data);
+ case StatsLog.CPU_TIME_PER_UID_FREQ:
+ return pullCpuTimeperUidFreq(atomTag, data);
+ case StatsLog.CPU_ACTIVE_TIME:
+ return pullCpuActiveTime(atomTag, data);
+ case StatsLog.CPU_CLUSTER_TIME:
+ return pullCpuClusterTime(atomTag, data);
+ case StatsLog.WIFI_ACTIVITY_INFO:
+ return pullWifiActivityInfo(atomTag, data);
+ case StatsLog.MODEM_ACTIVITY_INFO:
+ return pullModemActivityInfo(atomTag, data);
+ case StatsLog.BLUETOOTH_ACTIVITY_INFO:
+ return pullBluetoothActivityInfo(atomTag, data);
+ case StatsLog.SYSTEM_ELAPSED_REALTIME:
+ return pullSystemElapsedRealtime(atomTag, data);
+ case StatsLog.SYSTEM_UPTIME:
+ return pullSystemUptime(atomTag, data);
+ case StatsLog.PROCESS_MEMORY_STATE:
+ return pullProcessMemoryState(atomTag, data);
+ case StatsLog.PROCESS_MEMORY_HIGH_WATER_MARK:
+ return pullProcessMemoryHighWaterMark(atomTag, data);
+ case StatsLog.PROCESS_MEMORY_SNAPSHOT:
+ return pullProcessMemorySnapshot(atomTag, data);
+ case StatsLog.SYSTEM_ION_HEAP_SIZE:
+ return pullSystemIonHeapSize(atomTag, data);
+ case StatsLog.ION_HEAP_SIZE:
+ return pullIonHeapSize(atomTag, data);
+ case StatsLog.PROCESS_SYSTEM_ION_HEAP_SIZE:
+ return pullProcessSystemIonHeapSize(atomTag, data);
+ case StatsLog.TEMPERATURE:
+ return pullTemperature(atomTag, data);
+ case StatsLog.COOLING_DEVICE:
+ return pullCooldownDevice(atomTag, data);
+ case StatsLog.BINDER_CALLS:
+ return pullBinderCallsStats(atomTag, data);
+ case StatsLog.BINDER_CALLS_EXCEPTIONS:
+ return pullBinderCallsStatsExceptions(atomTag, data);
+ case StatsLog.LOOPER_STATS:
+ return pullLooperStats(atomTag, data);
+ case StatsLog.DISK_STATS:
+ return pullDiskStats(atomTag, data);
+ case StatsLog.DIRECTORY_USAGE:
+ return pullDirectoryUsage(atomTag, data);
+ case StatsLog.APP_SIZE:
+ return pullAppSize(atomTag, data);
+ case StatsLog.CATEGORY_SIZE:
+ return pullCategorySize(atomTag, data);
+ case StatsLog.NUM_FINGERPRINTS_ENROLLED:
+ return pullNumBiometricsEnrolled(
+ BiometricsProtoEnums.MODALITY_FINGERPRINT, atomTag, data);
+ case StatsLog.NUM_FACES_ENROLLED:
+ return pullNumBiometricsEnrolled(
+ BiometricsProtoEnums.MODALITY_FACE, atomTag, data);
+ case StatsLog.PROC_STATS:
+ return pullProcStats(ProcessStats.REPORT_ALL, atomTag, data);
+ case StatsLog.PROC_STATS_PKG_PROC:
+ return pullProcStats(ProcessStats.REPORT_PKG_PROC_STATS, atomTag, data);
+ case StatsLog.DISK_IO:
+ return pullDiskIO(atomTag, data);
+ case StatsLog.POWER_PROFILE:
+ return pullPowerProfile(atomTag, data);
+ case StatsLog.PROCESS_CPU_TIME:
+ return pullProcessCpuTime(atomTag, data);
+ case StatsLog.CPU_TIME_PER_THREAD_FREQ:
+ return pullCpuTimePerThreadFreq(atomTag, data);
+ case StatsLog.DEVICE_CALCULATED_POWER_USE:
+ return pullDeviceCalculatedPowerUse(atomTag, data);
+ case StatsLog.DEVICE_CALCULATED_POWER_BLAME_UID:
+ return pullDeviceCalculatedPowerBlameUid(atomTag, data);
+ case StatsLog.DEVICE_CALCULATED_POWER_BLAME_OTHER:
+ return pullDeviceCalculatedPowerBlameOther(atomTag, data);
+ case StatsLog.DEBUG_ELAPSED_CLOCK:
+ return pullDebugElapsedClock(atomTag, data);
+ case StatsLog.DEBUG_FAILING_ELAPSED_CLOCK:
+ return pullDebugFailingElapsedClock(atomTag, data);
+ case StatsLog.BUILD_INFORMATION:
+ return pullBuildInformation(atomTag, data);
+ case StatsLog.ROLE_HOLDER:
+ return pullRoleHolder(atomTag, data);
+ case StatsLog.DANGEROUS_PERMISSION_STATE:
+ return pullDangerousPermissionState(atomTag, data);
+ case StatsLog.TIME_ZONE_DATA_INFO:
+ return pullTimeZoneDataInfo(atomTag, data);
+ case StatsLog.EXTERNAL_STORAGE_INFO:
+ return pullExternalStorageInfo(atomTag, data);
+ case StatsLog.APPS_ON_EXTERNAL_STORAGE_INFO:
+ return pullAppsOnExternalStorageInfo(atomTag, data);
+ case StatsLog.FACE_SETTINGS:
+ return pullFaceSettings(atomTag, data);
+ case StatsLog.APP_OPS:
+ return pullAppOps(atomTag, data);
+ case StatsLog.NOTIFICATION_REMOTE_VIEWS:
+ return pullNotificationRemoteViews(atomTag, data);
+ case StatsLog.DANGEROUS_PERMISSION_STATE_SAMPLED:
+ return pullDangerousPermissionState(atomTag, data);
+ default:
+ throw new UnsupportedOperationException("Unknown tagId=" + atomTag);
+ }
+ }
+ }
+
@Override
public void onStart() {
+ // no op
+ }
+
+ @Override
+ public void onBootPhase(int phase) {
+ super.onBootPhase(phase);
+ if (phase == PHASE_SYSTEM_SERVICES_READY) {
+ BackgroundThread.getHandler().post(() -> {
+ initializePullersState();
+ registerAllPullers();
+ registerEventListeners();
+ });
+ }
+ }
+
+ void initializePullersState() {
+ // Get Context Managers
mStatsManager = (StatsManager) mContext.getSystemService(Context.STATS_MANAGER);
mWifiManager = (WifiManager) mContext.getSystemService(Context.WIFI_SERVICE);
mTelephony = (TelephonyManager) mContext.getSystemService(Context.TELEPHONY_SERVICE);
mStorageManager = (StorageManager) mContext.getSystemService(StorageManager.class);
- final ConnectivityManager connectivityManager =
- (ConnectivityManager) mContext.getSystemService(Context.CONNECTIVITY_SERVICE);
- // Default NetworkRequest should cover all transport types.
- final NetworkRequest request = new NetworkRequest.Builder().build();
- connectivityManager.registerNetworkCallback(request, new ConnectivityStatsCallback());
+ // Initialize DiskIO
+ mStoragedUidIoStatsReader = new StoragedUidIoStatsReader();
- // Enable push notifications of throttling from vendor thermal
- // management subsystem via thermalservice.
- IThermalService thermalService = getIThermalService();
- if (thermalService != null) {
- try {
- thermalService.registerThermalEventListener(
- new ThermalEventListener());
- Slog.i(TAG, "register thermal listener successfully");
- } catch (RemoteException e) {
- Slog.i(TAG, "failed to register thermal listener");
- }
- }
+ // Initialize PROC_STATS
+ // TODO (b/148402814): Change this directory to stats_pull.
+ mBaseDir = new File(SystemServiceManager.ensureSystemDir(), "stats_companion");
+
+ // Disables throttler on CPU time readers.
+ mCpuUidUserSysTimeReader = new KernelCpuUidUserSysTimeReader(false);
+ mCpuUidFreqTimeReader = new KernelCpuUidFreqTimeReader(false);
+ mCpuUidActiveTimeReader = new KernelCpuUidActiveTimeReader(false);
+ mCpuUidClusterTimeReader = new KernelCpuUidClusterTimeReader(false);
+
+ // Initialize state for KERNEL_WAKELOCK
+ mKernelWakelockReader = new KernelWakelockReader();
+ mTmpWakelockStats = new KernelWakelockStats();
// Initialize state for CPU_TIME_PER_FREQ atom
PowerProfile powerProfile = new PowerProfile(mContext);
@@ -246,13 +440,23 @@ public class StatsPullAtomService extends SystemService {
mBaseDir.mkdirs();
}
- @Override
- public void onBootPhase(int phase) {
- super.onBootPhase(phase);
- if (phase == PHASE_SYSTEM_SERVICES_READY) {
- BackgroundThread.getHandler().post(() -> {
- registerAllPullers();
- });
+ void registerEventListeners() {
+ final ConnectivityManager connectivityManager =
+ (ConnectivityManager) mContext.getSystemService(Context.CONNECTIVITY_SERVICE);
+ // Default NetworkRequest should cover all transport types.
+ final NetworkRequest request = new NetworkRequest.Builder().build();
+ connectivityManager.registerNetworkCallback(request, new ConnectivityStatsCallback());
+
+ // Enable push notifications of throttling from vendor thermal
+ // management subsystem via thermalservice.
+ IThermalService thermalService = getIThermalService();
+ if (thermalService != null) {
+ try {
+ thermalService.registerThermalEventListener(new ThermalEventListener());
+ Slog.i(TAG, "register thermal listener successfully");
+ } catch (RemoteException e) {
+ Slog.i(TAG, "failed to register thermal listener");
+ }
}
}
@@ -260,6 +464,7 @@ public class StatsPullAtomService extends SystemService {
if (DEBUG) {
Slog.d(TAG, "Registering all pullers with statsd");
}
+ mStatsCallbackImpl = new StatsPullAtomCallbackImpl();
registerWifiBytesTransfer();
registerWifiBytesTransferBackground();
registerMobileBytesTransfer();
@@ -276,11 +481,6 @@ public class StatsPullAtomService extends SystemService {
registerBluetoothActivityInfo();
registerSystemElapsedRealtime();
registerSystemUptime();
- registerRemainingBatteryCapacity();
- registerFullBatteryCapacity();
- registerBatteryVoltage();
- registerBatteryLevel();
- registerBatteryCycleCount();
registerProcessMemoryState();
registerProcessMemoryHighWaterMark();
registerProcessMemorySnapshot();
@@ -441,11 +641,11 @@ public class StatsPullAtomService extends SystemService {
tagId,
metadata,
BackgroundThread.getExecutor(),
- (atomTag, data) -> pullWifiBytesTransfer(atomTag, data)
+ mStatsCallbackImpl
);
}
- private int pullWifiBytesTransfer(int atomTag, List<StatsEvent> pulledData) {
+ int pullWifiBytesTransfer(int atomTag, List<StatsEvent> pulledData) {
INetworkStatsService networkStatsService = getINetworkStatsService();
if (networkStatsService == null) {
Slog.e(TAG, "NetworkStats Service is not available!");
@@ -453,7 +653,7 @@ public class StatsPullAtomService extends SystemService {
}
long token = Binder.clearCallingIdentity();
try {
- // TODO: Consider caching the following call to get BatteryStatsInternal.
+ // TODO(b/148402814): Consider caching the following call to get BatteryStatsInternal.
BatteryStatsInternal bs = LocalServices.getService(BatteryStatsInternal.class);
String[] ifaces = bs.getWifiIfaces();
if (ifaces.length == 0) {
@@ -533,11 +733,11 @@ public class StatsPullAtomService extends SystemService {
tagId,
metadata,
BackgroundThread.getExecutor(),
- (atomTag, data) -> pullWifiBytesTransferBackground(atomTag, data)
+ mStatsCallbackImpl
);
}
- private int pullWifiBytesTransferBackground(int atomTag, List<StatsEvent> pulledData) {
+ int pullWifiBytesTransferBackground(int atomTag, List<StatsEvent> pulledData) {
INetworkStatsService networkStatsService = getINetworkStatsService();
if (networkStatsService == null) {
Slog.e(TAG, "NetworkStats Service is not available!");
@@ -571,11 +771,11 @@ public class StatsPullAtomService extends SystemService {
tagId,
metadata,
BackgroundThread.getExecutor(),
- (atomTag, data) -> pullMobileBytesTransfer(atomTag, data)
+ mStatsCallbackImpl
);
}
- private int pullMobileBytesTransfer(int atomTag, List<StatsEvent> pulledData) {
+ int pullMobileBytesTransfer(int atomTag, List<StatsEvent> pulledData) {
INetworkStatsService networkStatsService = getINetworkStatsService();
if (networkStatsService == null) {
Slog.e(TAG, "NetworkStats Service is not available!");
@@ -609,11 +809,11 @@ public class StatsPullAtomService extends SystemService {
tagId,
metadata,
BackgroundThread.getExecutor(),
- (atomTag, data) -> pullMobileBytesTransferBackground(atomTag, data)
+ mStatsCallbackImpl
);
}
- private int pullMobileBytesTransferBackground(int atomTag, List<StatsEvent> pulledData) {
+ int pullMobileBytesTransferBackground(int atomTag, List<StatsEvent> pulledData) {
INetworkStatsService networkStatsService = getINetworkStatsService();
if (networkStatsService == null) {
Slog.e(TAG, "NetworkStats Service is not available!");
@@ -647,7 +847,7 @@ public class StatsPullAtomService extends SystemService {
tagId,
metadata,
BackgroundThread.getExecutor(),
- (atomTag, data) -> pullBluetoothBytesTransfer(atomTag, data)
+ mStatsCallbackImpl
);
}
@@ -694,7 +894,7 @@ public class StatsPullAtomService extends SystemService {
}
}
- private int pullBluetoothBytesTransfer(int atomTag, List<StatsEvent> pulledData) {
+ int pullBluetoothBytesTransfer(int atomTag, List<StatsEvent> pulledData) {
BluetoothActivityEnergyInfo info = fetchBluetoothData();
if (info == null || info.getUidTraffic() == null) {
return StatsManager.PULL_SKIP;
@@ -711,20 +911,17 @@ public class StatsPullAtomService extends SystemService {
return StatsManager.PULL_SUCCESS;
}
- private final KernelWakelockReader mKernelWakelockReader = new KernelWakelockReader();
- private final KernelWakelockStats mTmpWakelockStats = new KernelWakelockStats();
-
private void registerKernelWakelock() {
int tagId = StatsLog.KERNEL_WAKELOCK;
mStatsManager.registerPullAtomCallback(
tagId,
/* PullAtomMetadata */ null,
BackgroundThread.getExecutor(),
- (atomTag, data) -> pullKernelWakelock(atomTag, data)
+ mStatsCallbackImpl
);
}
- private int pullKernelWakelock(int atomTag, List<StatsEvent> pulledData) {
+ int pullKernelWakelock(int atomTag, List<StatsEvent> pulledData) {
final KernelWakelockStats wakelockStats =
mKernelWakelockReader.readKernelWakelockStats(mTmpWakelockStats);
for (Map.Entry<String, KernelWakelockStats.Entry> ent : wakelockStats.entrySet()) {
@@ -742,17 +939,6 @@ public class StatsPullAtomService extends SystemService {
return StatsManager.PULL_SUCCESS;
}
- private KernelCpuSpeedReader[] mKernelCpuSpeedReaders;
- // Disables throttler on CPU time readers.
- private KernelCpuUidUserSysTimeReader mCpuUidUserSysTimeReader =
- new KernelCpuUidUserSysTimeReader(false);
- private KernelCpuUidFreqTimeReader mCpuUidFreqTimeReader =
- new KernelCpuUidFreqTimeReader(false);
- private KernelCpuUidActiveTimeReader mCpuUidActiveTimeReader =
- new KernelCpuUidActiveTimeReader(false);
- private KernelCpuUidClusterTimeReader mCpuUidClusterTimeReader =
- new KernelCpuUidClusterTimeReader(false);
-
private void registerCpuTimePerFreq() {
int tagId = StatsLog.CPU_TIME_PER_FREQ;
PullAtomMetadata metadata = new PullAtomMetadata.Builder()
@@ -762,11 +948,11 @@ public class StatsPullAtomService extends SystemService {
tagId,
metadata,
BackgroundThread.getExecutor(),
- (atomTag, data) -> pullCpuTimePerFreq(atomTag, data)
+ mStatsCallbackImpl
);
}
- private int pullCpuTimePerFreq(int atomTag, List<StatsEvent> pulledData) {
+ int pullCpuTimePerFreq(int atomTag, List<StatsEvent> pulledData) {
for (int cluster = 0; cluster < mKernelCpuSpeedReaders.length; cluster++) {
long[] clusterTimeMs = mKernelCpuSpeedReaders[cluster].readAbsolute();
if (clusterTimeMs != null) {
@@ -793,11 +979,11 @@ public class StatsPullAtomService extends SystemService {
tagId,
metadata,
BackgroundThread.getExecutor(),
- (atomTag, data) -> pullCpuTimePerUid(atomTag, data)
+ mStatsCallbackImpl
);
}
- private int pullCpuTimePerUid(int atomTag, List<StatsEvent> pulledData) {
+ int pullCpuTimePerUid(int atomTag, List<StatsEvent> pulledData) {
mCpuUidUserSysTimeReader.readAbsolute((uid, timesUs) -> {
long userTimeUs = timesUs[0], systemTimeUs = timesUs[1];
StatsEvent e = StatsEvent.newBuilder()
@@ -822,11 +1008,11 @@ public class StatsPullAtomService extends SystemService {
tagId,
metadata,
BackgroundThread.getExecutor(),
- (atomTag, data) -> pullCpuTimeperUidFreq(atomTag, data)
+ mStatsCallbackImpl
);
}
- private int pullCpuTimeperUidFreq(int atomTag, List<StatsEvent> pulledData) {
+ int pullCpuTimeperUidFreq(int atomTag, List<StatsEvent> pulledData) {
mCpuUidFreqTimeReader.readAbsolute((uid, cpuFreqTimeMs) -> {
for (int freqIndex = 0; freqIndex < cpuFreqTimeMs.length; ++freqIndex) {
if (cpuFreqTimeMs[freqIndex] != 0) {
@@ -854,11 +1040,11 @@ public class StatsPullAtomService extends SystemService {
tagId,
metadata,
BackgroundThread.getExecutor(),
- (atomTag, data) -> pullCpuActiveTime(atomTag, data)
+ mStatsCallbackImpl
);
}
- private int pullCpuActiveTime(int atomTag, List<StatsEvent> pulledData) {
+ int pullCpuActiveTime(int atomTag, List<StatsEvent> pulledData) {
mCpuUidActiveTimeReader.readAbsolute((uid, cpuActiveTimesMs) -> {
StatsEvent e = StatsEvent.newBuilder()
.setAtomId(atomTag)
@@ -881,11 +1067,11 @@ public class StatsPullAtomService extends SystemService {
tagId,
metadata,
BackgroundThread.getExecutor(),
- (atomTag, data) -> pullCpuClusterTime(atomTag, data)
+ mStatsCallbackImpl
);
}
- private int pullCpuClusterTime(int atomTag, List<StatsEvent> pulledData) {
+ int pullCpuClusterTime(int atomTag, List<StatsEvent> pulledData) {
mCpuUidClusterTimeReader.readAbsolute((uid, cpuClusterTimesMs) -> {
for (int i = 0; i < cpuClusterTimesMs.length; i++) {
StatsEvent e = StatsEvent.newBuilder()
@@ -905,15 +1091,12 @@ public class StatsPullAtomService extends SystemService {
mStatsManager.registerPullAtomCallback(
tagId,
null, // use default PullAtomMetadata values
- (atomTag, data) -> pullWifiActivityInfo(atomTag, data),
- BackgroundThread.getExecutor()
+ BackgroundThread.getExecutor(),
+ mStatsCallbackImpl
);
}
- private WifiManager mWifiManager;
- private TelephonyManager mTelephony;
-
- private int pullWifiActivityInfo(int atomTag, List<StatsEvent> pulledData) {
+ int pullWifiActivityInfo(int atomTag, List<StatsEvent> pulledData) {
long token = Binder.clearCallingIdentity();
try {
SynchronousResultReceiver wifiReceiver = new SynchronousResultReceiver("wifi");
@@ -960,12 +1143,12 @@ public class StatsPullAtomService extends SystemService {
mStatsManager.registerPullAtomCallback(
tagId,
null, // use default PullAtomMetadata values
- (atomTag, data) -> pullModemActivityInfo(atomTag, data),
- BackgroundThread.getExecutor()
+ BackgroundThread.getExecutor(),
+ mStatsCallbackImpl
);
}
- private int pullModemActivityInfo(int atomTag, List<StatsEvent> pulledData) {
+ int pullModemActivityInfo(int atomTag, List<StatsEvent> pulledData) {
long token = Binder.clearCallingIdentity();
try {
SynchronousResultReceiver modemReceiver = new SynchronousResultReceiver("telephony");
@@ -999,11 +1182,11 @@ public class StatsPullAtomService extends SystemService {
tagId,
/* metadata */ null,
BackgroundThread.getExecutor(),
- (atomTag, data) -> pullBluetoothActivityInfo(atomTag, data)
+ mStatsCallbackImpl
);
}
- private int pullBluetoothActivityInfo(int atomTag, List<StatsEvent> pulledData) {
+ int pullBluetoothActivityInfo(int atomTag, List<StatsEvent> pulledData) {
BluetoothActivityEnergyInfo info = fetchBluetoothData();
if (info == null) {
return StatsManager.PULL_SKIP;
@@ -1021,23 +1204,21 @@ public class StatsPullAtomService extends SystemService {
return StatsManager.PULL_SUCCESS;
}
- private static final long NS_PER_SEC = 1000000000;
-
private void registerSystemElapsedRealtime() {
int tagId = StatsLog.SYSTEM_ELAPSED_REALTIME;
- PullAtomMetadata metadata = PullAtomMetadata.newBuilder()
+ PullAtomMetadata metadata = new PullAtomMetadata.Builder()
.setCoolDownNs(NS_PER_SEC)
.setTimeoutNs(NS_PER_SEC / 2)
.build();
mStatsManager.registerPullAtomCallback(
tagId,
metadata,
- (atomTag, data) -> pullSystemElapsedRealtime(atomTag, data),
- BackgroundThread.getExecutor()
+ BackgroundThread.getExecutor(),
+ mStatsCallbackImpl
);
}
- private int pullSystemElapsedRealtime(int atomTag, List<StatsEvent> pulledData) {
+ int pullSystemElapsedRealtime(int atomTag, List<StatsEvent> pulledData) {
StatsEvent e = StatsEvent.newBuilder()
.setAtomId(atomTag)
.writeLong(SystemClock.elapsedRealtime())
@@ -1052,11 +1233,11 @@ public class StatsPullAtomService extends SystemService {
tagId,
null, // use default PullAtomMetadata values
BackgroundThread.getExecutor(),
- (atomTag, data) -> pullSystemUptime(atomTag, data)
+ mStatsCallbackImpl
);
}
- private int pullSystemUptime(int atomTag, List<StatsEvent> pulledData) {
+ int pullSystemUptime(int atomTag, List<StatsEvent> pulledData) {
StatsEvent e = StatsEvent.newBuilder()
.setAtomId(atomTag)
.writeLong(SystemClock.elapsedRealtime())
@@ -1065,60 +1246,20 @@ public class StatsPullAtomService extends SystemService {
return StatsManager.PULL_SUCCESS;
}
- private void registerRemainingBatteryCapacity() {
- // No op.
- }
-
- private void pullRemainingBatteryCapacity() {
- // No op.
- }
-
- private void registerFullBatteryCapacity() {
- // No op.
- }
-
- private void pullFullBatteryCapacity() {
- // No op.
- }
-
- private void registerBatteryVoltage() {
- // No op.
- }
-
- private void pullBatteryVoltage() {
- // No op.
- }
-
- private void registerBatteryLevel() {
- // No op.
- }
-
- private void pullBatteryLevel() {
- // No op.
- }
-
- private void registerBatteryCycleCount() {
- // No op.
- }
-
- private void pullBatteryCycleCount() {
- // No op.
- }
-
private void registerProcessMemoryState() {
int tagId = StatsLog.PROCESS_MEMORY_STATE;
- PullAtomMetadata metadata = PullAtomMetadata.newBuilder()
+ PullAtomMetadata metadata = new PullAtomMetadata.Builder()
.setAdditiveFields(new int[] {4, 5, 6, 7, 8})
.build();
mStatsManager.registerPullAtomCallback(
tagId,
metadata,
- (atomTag, data) -> pullProcessMemoryState(atomTag, data),
- BackgroundThread.getExecutor()
+ BackgroundThread.getExecutor(),
+ mStatsCallbackImpl
);
}
- private int pullProcessMemoryState(int atomTag, List<StatsEvent> pulledData) {
+ int pullProcessMemoryState(int atomTag, List<StatsEvent> pulledData) {
List<ProcessMemoryState> processMemoryStates =
LocalServices.getService(ActivityManagerInternal.class)
.getMemoryStateForProcesses();
@@ -1147,45 +1288,6 @@ public class StatsPullAtomService extends SystemService {
return StatsManager.PULL_SUCCESS;
}
- /**
- * Which native processes to snapshot memory for.
- *
- * <p>Processes are matched by their cmdline in procfs. Example: cat /proc/pid/cmdline returns
- * /system/bin/statsd for the stats daemon.
- */
- private static final Set<String> MEMORY_INTERESTING_NATIVE_PROCESSES = Sets.newHashSet(
- "/system/bin/statsd", // Stats daemon.
- "/system/bin/surfaceflinger",
- "/system/bin/apexd", // APEX daemon.
- "/system/bin/audioserver",
- "/system/bin/cameraserver",
- "/system/bin/drmserver",
- "/system/bin/healthd",
- "/system/bin/incidentd",
- "/system/bin/installd",
- "/system/bin/lmkd", // Low memory killer daemon.
- "/system/bin/logd",
- "media.codec",
- "media.extractor",
- "media.metrics",
- "/system/bin/mediadrmserver",
- "/system/bin/mediaserver",
- "/system/bin/performanced",
- "/system/bin/tombstoned",
- "/system/bin/traced", // Perfetto.
- "/system/bin/traced_probes", // Perfetto.
- "webview_zygote",
- "zygote",
- "zygote64");
-
- /**
- * Lowest available uid for apps.
- *
- * <p>Used to quickly discard memory snapshots of the zygote forks from native process
- * measurements.
- */
- private static final int MIN_APP_UID = 10_000;
-
private static boolean isAppUid(int uid) {
return uid >= MIN_APP_UID;
}
@@ -1195,12 +1297,12 @@ public class StatsPullAtomService extends SystemService {
mStatsManager.registerPullAtomCallback(
tagId,
null, // use default PullAtomMetadata values
- (atomTag, data) -> pullProcessMemoryHighWaterMark(atomTag, data),
- BackgroundThread.getExecutor()
+ BackgroundThread.getExecutor(),
+ mStatsCallbackImpl
);
}
- private int pullProcessMemoryHighWaterMark(int atomTag, List<StatsEvent> pulledData) {
+ int pullProcessMemoryHighWaterMark(int atomTag, List<StatsEvent> pulledData) {
List<ProcessMemoryState> managedProcessList =
LocalServices.getService(ActivityManagerInternal.class)
.getMemoryStateForProcesses();
@@ -1219,30 +1321,25 @@ public class StatsPullAtomService extends SystemService {
.build();
pulledData.add(e);
}
- forEachPid((pid, cmdLine) -> {
- if (!MEMORY_INTERESTING_NATIVE_PROCESSES.contains(cmdLine)) {
- return;
- }
- final MemorySnapshot snapshot = readMemorySnapshotFromProcfs(pid);
- if (snapshot == null) {
- return;
- }
- // Sometimes we get here a process that is not included in the whitelist. It comes
- // from forking the zygote for an app. We can ignore that sample because this process
- // is collected by ProcessMemoryState.
- if (isAppUid(snapshot.uid)) {
- return;
+ // Complement the data with native system processes
+ SparseArray<String> processCmdlines = getProcessCmdlines();
+ managedProcessList.forEach(managedProcess -> processCmdlines.delete(managedProcess.pid));
+ int size = processCmdlines.size();
+ for (int i = 0; i < size; ++i) {
+ final MemorySnapshot snapshot = readMemorySnapshotFromProcfs(processCmdlines.keyAt(i));
+ if (snapshot == null || isAppUid(snapshot.uid)) {
+ continue;
}
StatsEvent e = StatsEvent.newBuilder()
.setAtomId(atomTag)
.writeInt(snapshot.uid)
- .writeString(cmdLine)
+ .writeString(processCmdlines.valueAt(i))
// RSS high-water mark in bytes.
.writeLong(snapshot.rssHighWaterMarkInKilobytes * 1024L)
.writeInt(snapshot.rssHighWaterMarkInKilobytes)
.build();
pulledData.add(e);
- });
+ }
// Invoke rss_hwm_reset binary to reset RSS HWM counters for all processes.
SystemProperties.set("sys.rss_hwm_reset.on", "1");
return StatsManager.PULL_SUCCESS;
@@ -1253,12 +1350,12 @@ public class StatsPullAtomService extends SystemService {
mStatsManager.registerPullAtomCallback(
tagId,
null, // use default PullAtomMetadata values
- (atomTag, data) -> pullProcessMemorySnapshot(atomTag, data),
- BackgroundThread.getExecutor()
+ BackgroundThread.getExecutor(),
+ mStatsCallbackImpl
);
}
- private int pullProcessMemorySnapshot(int atomTag, List<StatsEvent> pulledData) {
+ int pullProcessMemorySnapshot(int atomTag, List<StatsEvent> pulledData) {
List<ProcessMemoryState> managedProcessList =
LocalServices.getService(ActivityManagerInternal.class)
.getMemoryStateForProcesses();
@@ -1268,6 +1365,7 @@ public class StatsPullAtomService extends SystemService {
continue;
}
StatsEvent e = StatsEvent.newBuilder()
+ .setAtomId(atomTag)
.writeInt(managedProcess.uid)
.writeString(managedProcess.processName)
.writeInt(managedProcess.pid)
@@ -1279,24 +1377,22 @@ public class StatsPullAtomService extends SystemService {
.build();
pulledData.add(e);
}
- forEachPid((pid, cmdLine) -> {
- if (!MEMORY_INTERESTING_NATIVE_PROCESSES.contains(cmdLine)) {
- return;
- }
+ // Complement the data with native system processes. Given these measurements can be taken
+ // in response to LMKs happening, we want to first collect the managed app stats (to
+ // maximize the probability that a heavyweight process will be sampled before it dies).
+ SparseArray<String> processCmdlines = getProcessCmdlines();
+ managedProcessList.forEach(managedProcess -> processCmdlines.delete(managedProcess.pid));
+ int size = processCmdlines.size();
+ for (int i = 0; i < size; ++i) {
+ int pid = processCmdlines.keyAt(i);
final MemorySnapshot snapshot = readMemorySnapshotFromProcfs(pid);
- if (snapshot == null) {
- return;
- }
- // Sometimes we get here a process that is not included in the whitelist. It comes
- // from forking the zygote for an app. We can ignore that sample because this process
- // is collected by ProcessMemoryState.
- if (isAppUid(snapshot.uid)) {
- return;
+ if (snapshot == null || isAppUid(snapshot.uid)) {
+ continue;
}
StatsEvent e = StatsEvent.newBuilder()
.setAtomId(atomTag)
.writeInt(snapshot.uid)
- .writeString(cmdLine)
+ .writeString(processCmdlines.valueAt(i))
.writeInt(pid)
.writeInt(-1001) // Placeholder for native processes, OOM_SCORE_ADJ_MIN - 1.
.writeInt(snapshot.rssInKilobytes)
@@ -1305,7 +1401,7 @@ public class StatsPullAtomService extends SystemService {
.writeInt(snapshot.anonRssInKilobytes + snapshot.swapInKilobytes)
.build();
pulledData.add(e);
- });
+ }
return StatsManager.PULL_SUCCESS;
}
@@ -1314,12 +1410,12 @@ public class StatsPullAtomService extends SystemService {
mStatsManager.registerPullAtomCallback(
tagId,
null, // use default PullAtomMetadata values
- (atomTag, data) -> pullSystemIonHeapSize(atomTag, data),
- BackgroundThread.getExecutor()
+ BackgroundThread.getExecutor(),
+ mStatsCallbackImpl
);
}
- private int pullSystemIonHeapSize(int atomTag, List<StatsEvent> pulledData) {
+ int pullSystemIonHeapSize(int atomTag, List<StatsEvent> pulledData) {
final long systemIonHeapSizeInBytes = readSystemIonHeapSizeFromDebugfs();
StatsEvent e = StatsEvent.newBuilder()
.setAtomId(atomTag)
@@ -1335,11 +1431,11 @@ public class StatsPullAtomService extends SystemService {
tagId,
/* PullAtomMetadata */ null,
BackgroundThread.getExecutor(),
- (atomTag, data) -> pullIonHeapSize(atomTag, data)
+ mStatsCallbackImpl
);
}
- private int pullIonHeapSize(int atomTag, List<StatsEvent> pulledData) {
+ int pullIonHeapSize(int atomTag, List<StatsEvent> pulledData) {
int ionHeapSizeInKilobytes = (int) getIonHeapsSizeKb();
StatsEvent e = StatsEvent.newBuilder()
.setAtomId(atomTag)
@@ -1354,12 +1450,12 @@ public class StatsPullAtomService extends SystemService {
mStatsManager.registerPullAtomCallback(
tagId,
null, // use default PullAtomMetadata values
- (atomTag, data) -> pullProcessSystemIonHeapSize(atomTag, data),
- BackgroundThread.getExecutor()
+ BackgroundThread.getExecutor(),
+ mStatsCallbackImpl
);
}
- private int pullProcessSystemIonHeapSize(int atomTag, List<StatsEvent> pulledData) {
+ int pullProcessSystemIonHeapSize(int atomTag, List<StatsEvent> pulledData) {
List<IonAllocations> result = readProcessSystemIonHeapSizesFromDebugfs();
for (IonAllocations allocations : result) {
StatsEvent e = StatsEvent.newBuilder()
@@ -1380,12 +1476,12 @@ public class StatsPullAtomService extends SystemService {
mStatsManager.registerPullAtomCallback(
tagId,
null, // use default PullAtomMetadata values
- (atomTag, data) -> pullTemperature(atomTag, data),
- BackgroundThread.getExecutor()
+ BackgroundThread.getExecutor(),
+ mStatsCallbackImpl
);
}
- private int pullTemperature(int atomTag, List<StatsEvent> pulledData) {
+ int pullTemperature(int atomTag, List<StatsEvent> pulledData) {
IThermalService thermalService = getIThermalService();
if (thermalService == null) {
return StatsManager.PULL_SKIP;
@@ -1418,12 +1514,12 @@ public class StatsPullAtomService extends SystemService {
mStatsManager.registerPullAtomCallback(
tagId,
null, // use default PullAtomMetadata values
- (atomTag, data) -> pullCooldownDevice(atomTag, data),
- BackgroundThread.getExecutor()
+ BackgroundThread.getExecutor(),
+ mStatsCallbackImpl
);
}
- private int pullCooldownDevice(int atomTag, List<StatsEvent> pulledData) {
+ int pullCooldownDevice(int atomTag, List<StatsEvent> pulledData) {
IThermalService thermalService = getIThermalService();
if (thermalService == null) {
return StatsManager.PULL_SKIP;
@@ -1452,18 +1548,18 @@ public class StatsPullAtomService extends SystemService {
private void registerBinderCallsStats() {
int tagId = StatsLog.BINDER_CALLS;
- PullAtomMetadata metadata = PullAtomMetadata.newBuilder()
+ PullAtomMetadata metadata = new PullAtomMetadata.Builder()
.setAdditiveFields(new int[] {4, 5, 6, 8, 12})
.build();
mStatsManager.registerPullAtomCallback(
tagId,
metadata,
- (atomTag, data) -> pullBinderCallsStats(atomTag, data),
- BackgroundThread.getExecutor()
+ BackgroundThread.getExecutor(),
+ mStatsCallbackImpl
);
}
- private int pullBinderCallsStats(int atomTag, List<StatsEvent> pulledData) {
+ int pullBinderCallsStats(int atomTag, List<StatsEvent> pulledData) {
BinderCallsStatsService.Internal binderStats =
LocalServices.getService(BinderCallsStatsService.Internal.class);
if (binderStats == null) {
@@ -1501,12 +1597,12 @@ public class StatsPullAtomService extends SystemService {
mStatsManager.registerPullAtomCallback(
tagId,
null, // use default PullAtomMetadata values
- (atomTag, data) -> pullBinderCallsStatsExceptions(atomTag, data),
- BackgroundThread.getExecutor()
+ BackgroundThread.getExecutor(),
+ mStatsCallbackImpl
);
}
- private int pullBinderCallsStatsExceptions(int atomTag, List<StatsEvent> pulledData) {
+ int pullBinderCallsStatsExceptions(int atomTag, List<StatsEvent> pulledData) {
BinderCallsStatsService.Internal binderStats =
LocalServices.getService(BinderCallsStatsService.Internal.class);
if (binderStats == null) {
@@ -1530,18 +1626,18 @@ public class StatsPullAtomService extends SystemService {
private void registerLooperStats() {
int tagId = StatsLog.LOOPER_STATS;
- PullAtomMetadata metadata = PullAtomMetadata.newBuilder()
+ PullAtomMetadata metadata = new PullAtomMetadata.Builder()
.setAdditiveFields(new int[] {5, 6, 7, 8, 9})
.build();
mStatsManager.registerPullAtomCallback(
tagId,
metadata,
- (atomTag, data) -> pullLooperStats(atomTag, data),
- BackgroundThread.getExecutor()
+ BackgroundThread.getExecutor(),
+ mStatsCallbackImpl
);
}
- private int pullLooperStats(int atomTag, List<StatsEvent> pulledData) {
+ int pullLooperStats(int atomTag, List<StatsEvent> pulledData) {
LooperStats looperStats = LocalServices.getService(LooperStats.class);
if (looperStats == null) {
return StatsManager.PULL_SKIP;
@@ -1578,12 +1674,12 @@ public class StatsPullAtomService extends SystemService {
mStatsManager.registerPullAtomCallback(
tagId,
null, // use default PullAtomMetadata values
- (atomTag, data) -> pullDiskStats(atomTag, data),
- BackgroundThread.getExecutor()
+ BackgroundThread.getExecutor(),
+ mStatsCallbackImpl
);
}
- private int pullDiskStats(int atomTag, List<StatsEvent> pulledData) {
+ int pullDiskStats(int atomTag, List<StatsEvent> pulledData) {
// Run a quick-and-dirty performance test: write 512 bytes
byte[] junk = new byte[512];
for (int i = 0; i < junk.length; i++) junk[i] = (byte) i; // Write nonzero bytes
@@ -1644,12 +1740,12 @@ public class StatsPullAtomService extends SystemService {
mStatsManager.registerPullAtomCallback(
tagId,
null, // use default PullAtomMetadata values
- (atomTag, data) -> pullDirectoryUsage(atomTag, data),
- BackgroundThread.getExecutor()
+ BackgroundThread.getExecutor(),
+ mStatsCallbackImpl
);
}
- private int pullDirectoryUsage(int atomTag, List<StatsEvent> pulledData) {
+ int pullDirectoryUsage(int atomTag, List<StatsEvent> pulledData) {
StatFs statFsData = new StatFs(Environment.getDataDirectory().getAbsolutePath());
StatFs statFsSystem = new StatFs(Environment.getRootDirectory().getAbsolutePath());
StatFs statFsCache = new StatFs(Environment.getDownloadCacheDirectory().getAbsolutePath());
@@ -1685,12 +1781,12 @@ public class StatsPullAtomService extends SystemService {
mStatsManager.registerPullAtomCallback(
tagId,
null, // use default PullAtomMetadata values
- (atomTag, data) -> pullAppSize(atomTag, data),
- BackgroundThread.getExecutor()
+ BackgroundThread.getExecutor(),
+ mStatsCallbackImpl
);
}
- private int pullAppSize(int atomTag, List<StatsEvent> pulledData) {
+ int pullAppSize(int atomTag, List<StatsEvent> pulledData) {
try {
String jsonStr = IoUtils.readFileAsString(DiskStatsLoggingService.DUMPSYS_CACHE_PATH);
JSONObject json = new JSONObject(jsonStr);
@@ -1729,12 +1825,12 @@ public class StatsPullAtomService extends SystemService {
mStatsManager.registerPullAtomCallback(
tagId,
null, // use default PullAtomMetadata values
- (atomTag, data) -> pullCategorySize(atomTag, data),
- BackgroundThread.getExecutor()
+ BackgroundThread.getExecutor(),
+ mStatsCallbackImpl
);
}
- private int pullCategorySize(int atomTag, List<StatsEvent> pulledData) {
+ int pullCategorySize(int atomTag, List<StatsEvent> pulledData) {
try {
String jsonStr = IoUtils.readFileAsString(DiskStatsLoggingService.DUMPSYS_CACHE_PATH);
JSONObject json = new JSONObject(jsonStr);
@@ -1832,9 +1928,8 @@ public class StatsPullAtomService extends SystemService {
mStatsManager.registerPullAtomCallback(
tagId,
null, // use default PullAtomMetadata values
- (atomTag, data) -> pullNumBiometricsEnrolled(
- BiometricsProtoEnums.MODALITY_FINGERPRINT, atomTag, data),
- BackgroundThread.getExecutor()
+ BackgroundThread.getExecutor(),
+ mStatsCallbackImpl
);
}
@@ -1843,9 +1938,8 @@ public class StatsPullAtomService extends SystemService {
mStatsManager.registerPullAtomCallback(
tagId,
null, // use default PullAtomMetadata values
- (atomTag, data) -> pullNumBiometricsEnrolled(
- BiometricsProtoEnums.MODALITY_FACE, atomTag, data),
- BackgroundThread.getExecutor()
+ BackgroundThread.getExecutor(),
+ mStatsCallbackImpl
);
}
@@ -1897,15 +1991,13 @@ public class StatsPullAtomService extends SystemService {
return StatsManager.PULL_SUCCESS;
}
- private File mBaseDir = new File(SystemServiceManager.ensureSystemDir(), "stats_companion");
-
private void registerProcStats() {
int tagId = StatsLog.PROC_STATS;
mStatsManager.registerPullAtomCallback(
tagId,
null, // use default PullAtomMetadata values
- (atomTag, data) -> pullProcStats(ProcessStats.REPORT_ALL, atomTag, data),
- BackgroundThread.getExecutor()
+ BackgroundThread.getExecutor(),
+ mStatsCallbackImpl
);
}
@@ -1914,8 +2006,8 @@ public class StatsPullAtomService extends SystemService {
mStatsManager.registerPullAtomCallback(
tagId,
null, // use default PullAtomMetadata values
- (atomTag, data) -> pullProcStats(ProcessStats.REPORT_PKG_PROC_STATS, atomTag, data),
- BackgroundThread.getExecutor()
+ BackgroundThread.getExecutor(),
+ mStatsCallbackImpl
);
}
@@ -1978,25 +2070,21 @@ public class StatsPullAtomService extends SystemService {
return 0;
}
-
- private StoragedUidIoStatsReader mStoragedUidIoStatsReader =
- new StoragedUidIoStatsReader();
-
private void registerDiskIO() {
int tagId = StatsLog.DISK_IO;
- PullAtomMetadata metadata = PullAtomMetadata.newBuilder()
+ PullAtomMetadata metadata = new PullAtomMetadata.Builder()
.setAdditiveFields(new int[] {2, 3, 4, 5, 6, 7, 8, 9, 10, 11})
.setCoolDownNs(3 * NS_PER_SEC)
.build();
mStatsManager.registerPullAtomCallback(
tagId,
metadata,
- (atomTag, data) -> pullDiskIO(atomTag, data),
- BackgroundThread.getExecutor()
+ BackgroundThread.getExecutor(),
+ mStatsCallbackImpl
);
}
- private int pullDiskIO(int atomTag, List<StatsEvent> pulledData) {
+ int pullDiskIO(int atomTag, List<StatsEvent> pulledData) {
mStoragedUidIoStatsReader.readAbsolute((uid, fgCharsRead, fgCharsWrite, fgBytesRead,
fgBytesWrite, bgCharsRead, bgCharsWrite, bgBytesRead, bgBytesWrite,
fgFsync, bgFsync) -> {
@@ -2025,11 +2113,11 @@ public class StatsPullAtomService extends SystemService {
tagId,
/* PullAtomMetadata */ null,
BackgroundThread.getExecutor(),
- (atomTag, data) -> pullPowerProfile(atomTag, data)
+ mStatsCallbackImpl
);
}
- private int pullPowerProfile(int atomTag, List<StatsEvent> pulledData) {
+ int pullPowerProfile(int atomTag, List<StatsEvent> pulledData) {
PowerProfile powerProfile = new PowerProfile(mContext);
ProtoOutputStream proto = new ProtoOutputStream();
powerProfile.dumpDebug(proto);
@@ -2042,25 +2130,21 @@ public class StatsPullAtomService extends SystemService {
return StatsManager.PULL_SUCCESS;
}
- private final Object mCpuTrackerLock = new Object();
- @GuardedBy("mCpuTrackerLock")
- private ProcessCpuTracker mProcessCpuTracker;
-
private void registerProcessCpuTime() {
int tagId = StatsLog.PROCESS_CPU_TIME;
// Min cool-down is 5 sec, in line with what ActivityManagerService uses.
- PullAtomMetadata metadata = PullAtomMetadata.newBuilder()
+ PullAtomMetadata metadata = new PullAtomMetadata.Builder()
.setCoolDownNs(5 * NS_PER_SEC)
.build();
mStatsManager.registerPullAtomCallback(
tagId,
metadata,
- (atomTag, data) -> pullProcessCpuTime(atomTag, data),
- BackgroundThread.getExecutor()
+ BackgroundThread.getExecutor(),
+ mStatsCallbackImpl
);
}
- private int pullProcessCpuTime(int atomTag, List<StatsEvent> pulledData) {
+ int pullProcessCpuTime(int atomTag, List<StatsEvent> pulledData) {
synchronized (mCpuTrackerLock) {
if (mProcessCpuTracker == null) {
mProcessCpuTracker = new ProcessCpuTracker(false);
@@ -2082,24 +2166,20 @@ public class StatsPullAtomService extends SystemService {
return StatsManager.PULL_SUCCESS;
}
- @Nullable
- private KernelCpuThreadReaderDiff mKernelCpuThreadReader;
- private static final int CPU_TIME_PER_THREAD_FREQ_MAX_NUM_FREQUENCIES = 8;
-
private void registerCpuTimePerThreadFreq() {
int tagId = StatsLog.CPU_TIME_PER_THREAD_FREQ;
- PullAtomMetadata metadata = PullAtomMetadata.newBuilder()
+ PullAtomMetadata metadata = new PullAtomMetadata.Builder()
.setAdditiveFields(new int[] {7, 9, 11, 13, 15, 17, 19, 21})
.build();
mStatsManager.registerPullAtomCallback(
tagId,
metadata,
- (atomTag, data) -> pullCpuTimePerThreadFreq(atomTag, data),
- BackgroundThread.getExecutor()
+ BackgroundThread.getExecutor(),
+ mStatsCallbackImpl
);
}
- private int pullCpuTimePerThreadFreq(int atomTag, List<StatsEvent> pulledData) {
+ int pullCpuTimePerThreadFreq(int atomTag, List<StatsEvent> pulledData) {
if (this.mKernelCpuThreadReader == null) {
Slog.e(TAG, "mKernelCpuThreadReader is null");
return StatsManager.PULL_SKIP;
@@ -2156,12 +2236,6 @@ public class StatsPullAtomService extends SystemService {
return StatsManager.PULL_SUCCESS;
}
- // TODO: move to top of file when all migrations are complete
- private BatteryStatsHelper mBatteryStatsHelper = null;
- private static final int MAX_BATTERY_STATS_HELPER_FREQUENCY_MS = 1000;
- private long mBatteryStatsHelperTimestampMs = -MAX_BATTERY_STATS_HELPER_FREQUENCY_MS;
- private static final long MILLI_AMP_HR_TO_NANO_AMP_SECS = 1_000_000L * 3600L;
-
private BatteryStatsHelper getBatteryStatsHelper() {
if (mBatteryStatsHelper == null) {
final long callingToken = Binder.clearCallingIdentity();
@@ -2193,12 +2267,12 @@ public class StatsPullAtomService extends SystemService {
mStatsManager.registerPullAtomCallback(
tagId,
null, // use default PullAtomMetadata values
- (atomTag, data) -> pullDeviceCalculatedPowerUse(atomTag, data),
- BackgroundThread.getExecutor()
+ BackgroundThread.getExecutor(),
+ mStatsCallbackImpl
);
}
- private int pullDeviceCalculatedPowerUse(int atomTag, List<StatsEvent> pulledData) {
+ int pullDeviceCalculatedPowerUse(int atomTag, List<StatsEvent> pulledData) {
BatteryStatsHelper bsHelper = getBatteryStatsHelper();
StatsEvent e = StatsEvent.newBuilder()
.setAtomId(atomTag)
@@ -2213,12 +2287,12 @@ public class StatsPullAtomService extends SystemService {
mStatsManager.registerPullAtomCallback(
tagId,
null, // use default PullAtomMetadata values
- (atomTag, data) -> pullDeviceCalculatedPowerBlameUid(atomTag, data),
- BackgroundThread.getExecutor()
+ BackgroundThread.getExecutor(),
+ mStatsCallbackImpl
);
}
- private int pullDeviceCalculatedPowerBlameUid(int atomTag, List<StatsEvent> pulledData) {
+ int pullDeviceCalculatedPowerBlameUid(int atomTag, List<StatsEvent> pulledData) {
final List<BatterySipper> sippers = getBatteryStatsHelper().getUsageList();
if (sippers == null) {
return StatsManager.PULL_SKIP;
@@ -2243,12 +2317,12 @@ public class StatsPullAtomService extends SystemService {
mStatsManager.registerPullAtomCallback(
tagId,
null, // use default PullAtomMetadata values
- (atomTag, data) -> pullDeviceCalculatedPowerBlameOther(atomTag, data),
- BackgroundThread.getExecutor()
+ BackgroundThread.getExecutor(),
+ mStatsCallbackImpl
);
}
- private int pullDeviceCalculatedPowerBlameOther(int atomTag, List<StatsEvent> pulledData) {
+ int pullDeviceCalculatedPowerBlameOther(int atomTag, List<StatsEvent> pulledData) {
final List<BatterySipper> sippers = getBatteryStatsHelper().getUsageList();
if (sippers == null) {
return StatsManager.PULL_SKIP;
@@ -2271,24 +2345,20 @@ public class StatsPullAtomService extends SystemService {
return StatsManager.PULL_SUCCESS;
}
- private final Object mDebugElapsedClockLock = new Object();
- private long mDebugElapsedClockPreviousValue = 0;
- private long mDebugElapsedClockPullCount = 0;
-
private void registerDebugElapsedClock() {
int tagId = StatsLog.DEBUG_ELAPSED_CLOCK;
- PullAtomMetadata metadata = PullAtomMetadata.newBuilder()
+ PullAtomMetadata metadata = new PullAtomMetadata.Builder()
.setAdditiveFields(new int[] {1, 2, 3, 4})
.build();
mStatsManager.registerPullAtomCallback(
tagId,
metadata,
- (atomTag, data) -> pullDebugElapsedClock(atomTag, data),
- BackgroundThread.getExecutor()
+ BackgroundThread.getExecutor(),
+ mStatsCallbackImpl
);
}
- private int pullDebugElapsedClock(int atomTag, List<StatsEvent> pulledData) {
+ int pullDebugElapsedClock(int atomTag, List<StatsEvent> pulledData) {
final long elapsedMillis = SystemClock.elapsedRealtime();
synchronized (mDebugElapsedClockLock) {
@@ -2326,24 +2396,20 @@ public class StatsPullAtomService extends SystemService {
return StatsManager.PULL_SUCCESS;
}
- private final Object mDebugFailingElapsedClockLock = new Object();
- private long mDebugFailingElapsedClockPreviousValue = 0;
- private long mDebugFailingElapsedClockPullCount = 0;
-
private void registerDebugFailingElapsedClock() {
int tagId = StatsLog.DEBUG_FAILING_ELAPSED_CLOCK;
- PullAtomMetadata metadata = PullAtomMetadata.newBuilder()
+ PullAtomMetadata metadata = new PullAtomMetadata.Builder()
.setAdditiveFields(new int[] {1, 2, 3, 4})
.build();
mStatsManager.registerPullAtomCallback(
tagId,
metadata,
- (atomTag, data) -> pullDebugFailingElapsedClock(atomTag, data),
- BackgroundThread.getExecutor()
+ BackgroundThread.getExecutor(),
+ mStatsCallbackImpl
);
}
- private int pullDebugFailingElapsedClock(int atomTag, List<StatsEvent> pulledData) {
+ int pullDebugFailingElapsedClock(int atomTag, List<StatsEvent> pulledData) {
final long elapsedMillis = SystemClock.elapsedRealtime();
synchronized (mDebugFailingElapsedClockLock) {
@@ -2377,11 +2443,11 @@ public class StatsPullAtomService extends SystemService {
tagId,
null, // use default PullAtomMetadata values
BackgroundThread.getExecutor(),
- (atomTag, data) -> pullBuildInformation(atomTag, data)
+ mStatsCallbackImpl
);
}
- private int pullBuildInformation(int atomTag, List<StatsEvent> pulledData) {
+ int pullBuildInformation(int atomTag, List<StatsEvent> pulledData) {
StatsEvent e = StatsEvent.newBuilder()
.setAtomId(atomTag)
.writeString(Build.FINGERPRINT)
@@ -2403,13 +2469,13 @@ public class StatsPullAtomService extends SystemService {
mStatsManager.registerPullAtomCallback(
tagId,
null, // use default PullAtomMetadata values
- (atomTag, data) -> pullRoleHolder(atomTag, data),
- BackgroundThread.getExecutor()
+ BackgroundThread.getExecutor(),
+ mStatsCallbackImpl
);
}
// Add a RoleHolder atom for each package that holds a role.
- private int pullRoleHolder(int atomTag, List<StatsEvent> pulledData) {
+ int pullRoleHolder(int atomTag, List<StatsEvent> pulledData) {
long callingToken = Binder.clearCallingIdentity();
try {
PackageManager pm = mContext.getPackageManager();
@@ -2461,12 +2527,12 @@ public class StatsPullAtomService extends SystemService {
mStatsManager.registerPullAtomCallback(
tagId,
null, // use default PullAtomMetadata values
- (atomTag, data) -> pullDangerousPermissionState(atomTag, data),
- BackgroundThread.getExecutor()
+ BackgroundThread.getExecutor(),
+ mStatsCallbackImpl
);
}
- private int pullDangerousPermissionState(int atomTag, List<StatsEvent> pulledData) {
+ int pullDangerousPermissionState(int atomTag, List<StatsEvent> pulledData) {
final long token = Binder.clearCallingIdentity();
Set<Integer> reportedUids = new HashSet<>();
try {
@@ -2547,12 +2613,12 @@ public class StatsPullAtomService extends SystemService {
mStatsManager.registerPullAtomCallback(
tagId,
null, // use default PullAtomMetadata values
- (atomTag, data) -> pullTimeZoneDataInfo(atomTag, data),
- BackgroundThread.getExecutor()
+ BackgroundThread.getExecutor(),
+ mStatsCallbackImpl
);
}
- private int pullTimeZoneDataInfo(int atomTag, List<StatsEvent> pulledData) {
+ int pullTimeZoneDataInfo(int atomTag, List<StatsEvent> pulledData) {
String tzDbVersion = "Unknown";
try {
tzDbVersion = android.icu.util.TimeZone.getTZDataVersion();
@@ -2574,12 +2640,12 @@ public class StatsPullAtomService extends SystemService {
mStatsManager.registerPullAtomCallback(
tagId,
null, // use default PullAtomMetadata values
- (atomTag, data) -> pullExternalStorageInfo(atomTag, data),
- BackgroundThread.getExecutor()
+ BackgroundThread.getExecutor(),
+ mStatsCallbackImpl
);
}
- private int pullExternalStorageInfo(int atomTag, List<StatsEvent> pulledData) {
+ int pullExternalStorageInfo(int atomTag, List<StatsEvent> pulledData) {
if (mStorageManager == null) {
return StatsManager.PULL_SKIP;
}
@@ -2624,12 +2690,12 @@ public class StatsPullAtomService extends SystemService {
mStatsManager.registerPullAtomCallback(
tagId,
null, // use default PullAtomMetadata values
- (atomTag, data) -> pullAppsOnExternalStorageInfo(atomTag, data),
- BackgroundThread.getExecutor()
+ BackgroundThread.getExecutor(),
+ mStatsCallbackImpl
);
}
- private int pullAppsOnExternalStorageInfo(int atomTag, List<StatsEvent> pulledData) {
+ int pullAppsOnExternalStorageInfo(int atomTag, List<StatsEvent> pulledData) {
if (mStorageManager == null) {
return StatsManager.PULL_SKIP;
}
@@ -2680,12 +2746,12 @@ public class StatsPullAtomService extends SystemService {
mStatsManager.registerPullAtomCallback(
tagId,
null, // use default PullAtomMetadata values
- (atomTag, data) -> pullFaceSettings(atomTag, data),
- BackgroundThread.getExecutor()
+ BackgroundThread.getExecutor(),
+ mStatsCallbackImpl
);
}
- private int pullFaceSettings(int atomTag, List<StatsEvent> pulledData) {
+ int pullFaceSettings(int atomTag, List<StatsEvent> pulledData) {
final long callingToken = Binder.clearCallingIdentity();
try {
List<UserInfo> users = mContext.getSystemService(UserManager.class).getUsers();
@@ -2734,13 +2800,13 @@ public class StatsPullAtomService extends SystemService {
mStatsManager.registerPullAtomCallback(
tagId,
null, // use default PullAtomMetadata values
- (atomTag, data) -> pullAppOps(atomTag, data),
- BackgroundThread.getExecutor()
+ BackgroundThread.getExecutor(),
+ mStatsCallbackImpl
);
}
- private int pullAppOps(int atomTag, List<StatsEvent> pulledData) {
+ int pullAppOps(int atomTag, List<StatsEvent> pulledData) {
final long token = Binder.clearCallingIdentity();
try {
AppOpsManager appOps = mContext.getSystemService(AppOpsManager.class);
@@ -2845,12 +2911,12 @@ public class StatsPullAtomService extends SystemService {
mStatsManager.registerPullAtomCallback(
tagId,
null, // use default PullAtomMetadata values
- (atomTag, data) -> pullNotificationRemoteViews(atomTag, data),
- BackgroundThread.getExecutor()
+ BackgroundThread.getExecutor(),
+ mStatsCallbackImpl
);
}
- private int pullNotificationRemoteViews(int atomTag, List<StatsEvent> pulledData) {
+ int pullNotificationRemoteViews(int atomTag, List<StatsEvent> pulledData) {
INotificationManager notificationManagerService = getINotificationManagerService();
if (notificationManagerService == null) {
return StatsManager.PULL_SKIP;
@@ -2889,8 +2955,8 @@ public class StatsPullAtomService extends SystemService {
mStatsManager.registerPullAtomCallback(
tagId,
null, // use default PullAtomMetadata values
- (atomTag, data) -> pullDangerousPermissionState(atomTag, data),
- BackgroundThread.getExecutor()
+ BackgroundThread.getExecutor(),
+ mStatsCallbackImpl
);
}
diff --git a/services/core/java/com/android/server/statusbar/StatusBarManagerInternal.java b/services/core/java/com/android/server/statusbar/StatusBarManagerInternal.java
index 95ffd8fe43d8..d88dccb9afeb 100644
--- a/services/core/java/com/android/server/statusbar/StatusBarManagerInternal.java
+++ b/services/core/java/com/android/server/statusbar/StatusBarManagerInternal.java
@@ -16,7 +16,10 @@
package com.android.server.statusbar;
+import android.annotation.Nullable;
+import android.app.ITransientNotificationCallback;
import android.os.Bundle;
+import android.os.IBinder;
import android.view.InsetsState.InternalInsetsType;
import android.view.WindowInsetsController.Appearance;
@@ -123,4 +126,15 @@ public interface StatusBarManagerInternal {
/** @see com.android.internal.statusbar.IStatusBar#abortTransient */
void abortTransient(int displayId, @InternalInsetsType int[] types);
+
+ /**
+ * @see com.android.internal.statusbar.IStatusBar#showToast(String, IBinder, CharSequence,
+ * IBinder, int, ITransientNotificationCallback)
+ */
+ void showToast(String packageName, IBinder token, CharSequence text,
+ IBinder windowToken, int duration,
+ @Nullable ITransientNotificationCallback textCallback);
+
+ /** @see com.android.internal.statusbar.IStatusBar#hideToast(String, IBinder) */
+ void hideToast(String packageName, IBinder token);
}
diff --git a/services/core/java/com/android/server/statusbar/StatusBarManagerService.java b/services/core/java/com/android/server/statusbar/StatusBarManagerService.java
index 870c81fb5dc2..57a6776ebc6c 100644
--- a/services/core/java/com/android/server/statusbar/StatusBarManagerService.java
+++ b/services/core/java/com/android/server/statusbar/StatusBarManagerService.java
@@ -21,6 +21,7 @@ import static android.view.Display.DEFAULT_DISPLAY;
import android.annotation.Nullable;
import android.app.ActivityThread;
+import android.app.ITransientNotificationCallback;
import android.app.Notification;
import android.app.StatusBarManager;
import android.content.ComponentName;
@@ -500,6 +501,26 @@ public class StatusBarManagerService extends IStatusBarService.Stub implements D
} catch (RemoteException ex) { }
}
}
+
+ @Override
+ public void showToast(String packageName, IBinder token, CharSequence text,
+ IBinder windowToken, int duration,
+ @Nullable ITransientNotificationCallback callback) {
+ if (mBar != null) {
+ try {
+ mBar.showToast(packageName, token, text, windowToken, duration, callback);
+ } catch (RemoteException ex) { }
+ }
+ }
+
+ @Override
+ public void hideToast(String packageName, IBinder token) {
+ if (mBar != null) {
+ try {
+ mBar.hideToast(packageName, token);
+ } catch (RemoteException ex) { }
+ }
+ }
};
private final GlobalActionsProvider mGlobalActionsProvider = new GlobalActionsProvider() {
@@ -1343,6 +1364,17 @@ public class StatusBarManagerService extends IStatusBarService.Stub implements D
}
@Override
+ public void onBubbleNotificationSuppressionChanged(String key, boolean isNotifSuppressed) {
+ enforceStatusBarService();
+ long identity = Binder.clearCallingIdentity();
+ try {
+ mNotificationDelegate.onBubbleNotificationSuppressionChanged(key, isNotifSuppressed);
+ } finally {
+ Binder.restoreCallingIdentity(identity);
+ }
+ }
+
+ @Override
public void grantInlineReplyUriPermission(String key, Uri uri, UserHandle user,
String packageName) {
enforceStatusBarService();
diff --git a/services/core/java/com/android/server/tv/TvInputManagerService.java b/services/core/java/com/android/server/tv/TvInputManagerService.java
index e8704ab789f2..4f010d5e0f2c 100755
--- a/services/core/java/com/android/server/tv/TvInputManagerService.java
+++ b/services/core/java/com/android/server/tv/TvInputManagerService.java
@@ -1681,7 +1681,8 @@ public final class TvInputManagerService extends SystemService {
}
@Override
- public void startRecording(IBinder sessionToken, @Nullable Uri programUri, int userId) {
+ public void startRecording(IBinder sessionToken, @Nullable Uri programUri,
+ @Nullable Bundle params, int userId) {
final int callingUid = Binder.getCallingUid();
final int resolvedUserId = resolveCallingUserId(Binder.getCallingPid(), callingUid,
userId, "startRecording");
@@ -1690,7 +1691,7 @@ public final class TvInputManagerService extends SystemService {
synchronized (mLock) {
try {
getSessionLocked(sessionToken, callingUid, resolvedUserId).startRecording(
- programUri);
+ programUri, params);
} catch (RemoteException | SessionNotFoundException e) {
Slog.e(TAG, "error in startRecording", e);
}
diff --git a/services/core/java/com/android/server/wm/ActivityStack.java b/services/core/java/com/android/server/wm/ActivityStack.java
index 663423f8728b..ddf0117987dd 100644
--- a/services/core/java/com/android/server/wm/ActivityStack.java
+++ b/services/core/java/com/android/server/wm/ActivityStack.java
@@ -660,6 +660,8 @@ class ActivityStack extends Task implements BoundsAnimationTarget {
super.onConfigurationChanged(newParentConfig);
+ updateTaskOrganizerState();
+
// Only need to update surface size here since the super method will handle updating
// surface position.
updateSurfaceSize(getPendingTransaction());
@@ -762,6 +764,22 @@ class ActivityStack extends Task implements BoundsAnimationTarget {
}
}
+ void updateTaskOrganizerState() {
+ if (!isRootTask()) {
+ return;
+ }
+
+ final int windowingMode = getWindowingMode();
+ /*
+ * Different windowing modes may be managed by different task organizers. If
+ * getTaskOrganizer returns null, we still call setTaskOrganizer to
+ * make sure we clear it.
+ */
+ final ITaskOrganizer org =
+ mWmService.mAtmService.mTaskOrganizerController.getTaskOrganizer(windowingMode);
+ setTaskOrganizer(org);
+ }
+
@Override
public void setWindowingMode(int windowingMode) {
// Calling Task#setWindowingMode() for leaf task since this is the a specialization of
@@ -774,15 +792,6 @@ class ActivityStack extends Task implements BoundsAnimationTarget {
setWindowingMode(windowingMode, false /* animate */, false /* showRecents */,
false /* enteringSplitScreenMode */, false /* deferEnsuringVisibility */,
false /* creating */);
- windowingMode = getWindowingMode();
- /*
- * Different windowing modes may be managed by different task organizers. If
- * getTaskOrganizer returns null, we still call transferToTaskOrganizer to
- * make sure we clear it.
- */
- final ITaskOrganizer org =
- mWmService.mAtmService.mTaskOrganizerController.getTaskOrganizer(windowingMode);
- transferToTaskOrganizer(org);
}
/**
@@ -1583,24 +1592,6 @@ class ActivityStack extends Task implements BoundsAnimationTarget {
return topActivity != null && topActivity.mVisibleRequested;
}
- private static void transferSingleTaskToOrganizer(Task tr, ITaskOrganizer organizer) {
- tr.setTaskOrganizer(organizer);
- }
-
- /**
- * Transfer control of the leashes and IWindowContainers to the given ITaskOrganizer.
- * This will (or shortly there-after) invoke the taskAppeared callbacks.
- * If the tasks had a previous TaskOrganizer, setTaskOrganizer will take care of
- * emitting the taskVanished callbacks.
- */
- void transferToTaskOrganizer(ITaskOrganizer organizer) {
- final PooledConsumer c = PooledLambda.obtainConsumer(
- ActivityStack::transferSingleTaskToOrganizer,
- PooledLambda.__(Task.class), organizer);
- forAllTasks(c, true /* traverseTopToBottom */, this);
- c.recycle();
- }
-
/**
* Returns true if the stack should be visible.
*
diff --git a/services/core/java/com/android/server/wm/ActivityStarter.java b/services/core/java/com/android/server/wm/ActivityStarter.java
index 26812f462b3f..9e3292b59402 100644
--- a/services/core/java/com/android/server/wm/ActivityStarter.java
+++ b/services/core/java/com/android/server/wm/ActivityStarter.java
@@ -1595,11 +1595,11 @@ class ActivityStarter {
mRootWindowContainer.resumeFocusedStacksTopActivities(
mTargetStack, mStartActivity, mOptions);
}
- } else if (mStartActivity != null) {
- mSupervisor.mRecentTasks.add(mStartActivity.getTask());
}
mRootWindowContainer.updateUserStack(mStartActivity.mUserId, mTargetStack);
+ // Update the recent tasks list immediately when the activity starts
+ mSupervisor.mRecentTasks.add(mStartActivity.getTask());
mSupervisor.handleNonResizableTaskIfNeeded(mStartActivity.getTask(),
preferredWindowingMode, mPreferredDisplayId, mTargetStack);
diff --git a/services/core/java/com/android/server/wm/DisplayContent.java b/services/core/java/com/android/server/wm/DisplayContent.java
index bf89bab53f30..c50048eeab64 100644
--- a/services/core/java/com/android/server/wm/DisplayContent.java
+++ b/services/core/java/com/android/server/wm/DisplayContent.java
@@ -112,7 +112,6 @@ import static com.android.server.wm.DisplayContentProto.ID;
import static com.android.server.wm.DisplayContentProto.IME_WINDOWS;
import static com.android.server.wm.DisplayContentProto.OPENING_APPS;
import static com.android.server.wm.DisplayContentProto.OVERLAY_WINDOWS;
-import static com.android.server.wm.DisplayContentProto.PINNED_STACK_CONTROLLER;
import static com.android.server.wm.DisplayContentProto.ROTATION;
import static com.android.server.wm.DisplayContentProto.SCREEN_ROTATION_ANIMATION;
import static com.android.server.wm.DisplayContentProto.STACKS;
@@ -2779,7 +2778,6 @@ class DisplayContent extends WindowContainer<DisplayContent.DisplayChildWindowCo
stack.dumpDebugInnerStackOnly(proto, STACKS, logLevel);
}
mDividerControllerLocked.dumpDebug(proto, DOCKED_STACK_DIVIDER_CONTROLLER);
- mPinnedStackControllerLocked.dumpDebug(proto, PINNED_STACK_CONTROLLER);
for (int i = mAboveAppWindowsContainers.getChildCount() - 1; i >= 0; --i) {
final WindowToken windowToken = mAboveAppWindowsContainers.getChildAt(i);
windowToken.dumpDebug(proto, ABOVE_APP_WINDOWS, logLevel);
@@ -4732,7 +4730,7 @@ class DisplayContent extends WindowContainer<DisplayContent.DisplayChildWindowCo
// exists so it get's layered above the starting window.
if (imeTarget != null && !(imeTarget.mActivityRecord != null
&& imeTarget.mActivityRecord.hasStartingWindow()) && (
- !(imeTarget.inSplitScreenWindowingMode()
+ !(imeTarget.inMultiWindowMode()
|| imeTarget.mToken.isAppTransitioning()) && (
imeTarget.getSurfaceControl() != null))) {
mImeWindowsContainers.assignRelativeLayer(t, imeTarget.getSurfaceControl(),
diff --git a/services/core/java/com/android/server/wm/DisplayPolicy.java b/services/core/java/com/android/server/wm/DisplayPolicy.java
index f8df883a3e1c..d211cfd83549 100644
--- a/services/core/java/com/android/server/wm/DisplayPolicy.java
+++ b/services/core/java/com/android/server/wm/DisplayPolicy.java
@@ -1014,8 +1014,19 @@ public class DisplayPolicy {
mNavigationBarController.setWindow(win);
mNavigationBarController.setOnBarVisibilityChangedListener(
mNavBarVisibilityListener, true);
- mDisplayContent.setInsetProvider(ITYPE_NAVIGATION_BAR,
- win, null /* frameProvider */);
+ mDisplayContent.setInsetProvider(ITYPE_NAVIGATION_BAR, win,
+ (displayFrames, windowState, inOutFrame) -> {
+
+ // In Gesture Nav, navigation bar frame is larger than frame to
+ // calculate inset.
+ if (mNavigationBarPosition == NAV_BAR_BOTTOM) {
+ sTmpRect.set(displayFrames.mUnrestricted);
+ sTmpRect.intersectUnchecked(displayFrames.mDisplayCutoutSafe);
+ inOutFrame.top = sTmpRect.bottom
+ - getNavigationBarHeight(displayFrames.mRotation,
+ mDisplayContent.getConfiguration().uiMode);
+ }
+ });
mDisplayContent.setInsetProvider(ITYPE_BOTTOM_GESTURES, win,
(displayFrames, windowState, inOutFrame) -> {
inOutFrame.top -= mBottomGestureAdditionalInset;
diff --git a/services/core/java/com/android/server/wm/InputMonitor.java b/services/core/java/com/android/server/wm/InputMonitor.java
index 4c5914bfd8c2..faa6e52da8bc 100644
--- a/services/core/java/com/android/server/wm/InputMonitor.java
+++ b/services/core/java/com/android/server/wm/InputMonitor.java
@@ -161,7 +161,13 @@ final class InputMonitor {
void onDisplayRemoved() {
mHandler.removeCallbacks(mUpdateInputWindows);
- mService.mInputManager.onDisplayRemoved(mDisplayId);
+ mHandler.post(() -> {
+ // Make sure any pending setInputWindowInfo transactions are completed. That prevents
+ // the timing of updating input info of removed display after cleanup.
+ mService.mTransactionFactory.get().syncInputWindows().apply();
+ // It calls InputDispatcher::setInputWindows directly.
+ mService.mInputManager.onDisplayRemoved(mDisplayId);
+ });
mDisplayRemoved = true;
}
diff --git a/services/core/java/com/android/server/wm/InsetsSourceProvider.java b/services/core/java/com/android/server/wm/InsetsSourceProvider.java
index 5a591ecd4746..2bb58ddc5b38 100644
--- a/services/core/java/com/android/server/wm/InsetsSourceProvider.java
+++ b/services/core/java/com/android/server/wm/InsetsSourceProvider.java
@@ -233,7 +233,7 @@ class InsetsSourceProvider {
// window crop of the surface controls (including the leash) until the client finishes
// drawing the new frame of the new orientation. Although we cannot defer the reparent
// operation, it is fine, because reparent won't cause any visual effect.
- final SurfaceControl barrier = mWin.mWinAnimator.mSurfaceController.mSurfaceControl;
+ final SurfaceControl barrier = mWin.getDeferTransactionBarrier();
t.deferTransactionUntil(mWin.getSurfaceControl(), barrier, frameNumber);
t.deferTransactionUntil(leash, barrier, frameNumber);
}
diff --git a/services/core/java/com/android/server/wm/PinnedStackController.java b/services/core/java/com/android/server/wm/PinnedStackController.java
index 1d5b5d1a46a4..668b6095738f 100644
--- a/services/core/java/com/android/server/wm/PinnedStackController.java
+++ b/services/core/java/com/android/server/wm/PinnedStackController.java
@@ -18,8 +18,6 @@ package com.android.server.wm;
import static android.app.WindowConfiguration.WINDOWING_MODE_PINNED;
-import static com.android.server.wm.PinnedStackControllerProto.DEFAULT_BOUNDS;
-import static com.android.server.wm.PinnedStackControllerProto.MOVEMENT_BOUNDS;
import static com.android.server.wm.WindowManagerDebugConfig.TAG_WITH_CLASS_NAME;
import static com.android.server.wm.WindowManagerDebugConfig.TAG_WM;
@@ -34,7 +32,6 @@ import android.os.RemoteException;
import android.util.DisplayMetrics;
import android.util.Log;
import android.util.Slog;
-import android.util.proto.ProtoOutputStream;
import android.view.DisplayInfo;
import android.view.IPinnedStackController;
import android.view.IPinnedStackListener;
@@ -97,11 +94,6 @@ class PinnedStackController {
// Temp vars for calculation
private final DisplayMetrics mTmpMetrics = new DisplayMetrics();
- // TODO(b/141200935): remove this when we have default/movement bounds tests in SysUI.
- // Keep record of the default and movement bounds
- private final Rect mLastReportedDefaultBounds = new Rect();
- private final Rect mLastReportedMovementBounds = new Rect();
-
/**
* The callback object passed to listeners for them to notify the controller of state changes.
*/
@@ -143,14 +135,6 @@ class PinnedStackController {
}
}
}
-
- @Override
- public void reportBounds(Rect defaultBounds, Rect movementBounds) {
- synchronized (mService.mGlobalLock) {
- mLastReportedDefaultBounds.set(defaultBounds);
- mLastReportedMovementBounds.set(movementBounds);
- }
- }
}
/**
@@ -421,8 +405,6 @@ class PinnedStackController {
void dump(String prefix, PrintWriter pw) {
pw.println(prefix + "PinnedStackController");
- pw.println(prefix + " mLastReportedDefaultBounds=" + mLastReportedDefaultBounds);
- pw.println(prefix + " mLastReportedMovementBounds=" + mLastReportedMovementBounds);
pw.println(prefix + " mDefaultAspectRatio=" + mDefaultAspectRatio);
pw.println(prefix + " mIsImeShowing=" + mIsImeShowing);
pw.println(prefix + " mImeHeight=" + mImeHeight);
@@ -443,11 +425,4 @@ class PinnedStackController {
}
pw.println(prefix + " mDisplayInfo=" + mDisplayInfo);
}
-
- void dumpDebug(ProtoOutputStream proto, long fieldId) {
- final long token = proto.start(fieldId);
- mLastReportedDefaultBounds.dumpDebug(proto, DEFAULT_BOUNDS);
- mLastReportedMovementBounds.dumpDebug(proto, MOVEMENT_BOUNDS);
- proto.end(token);
- }
}
diff --git a/services/core/java/com/android/server/wm/RecentTasks.java b/services/core/java/com/android/server/wm/RecentTasks.java
index 292e8aaa0c04..a9dc36d91a3f 100644
--- a/services/core/java/com/android/server/wm/RecentTasks.java
+++ b/services/core/java/com/android/server/wm/RecentTasks.java
@@ -173,6 +173,9 @@ class RecentTasks {
private final ArrayList<Task> mTasks = new ArrayList<>();
private final ArrayList<Callbacks> mCallbacks = new ArrayList<>();
+ /** The non-empty tasks that are removed from recent tasks (see {@link #removeForAddTask}). */
+ private final ArrayList<Task> mHiddenTasks = new ArrayList<>();
+
// These values are generally loaded from resources, but can be set dynamically in the tests
private boolean mHasVisibleRecentTasks;
private int mGlobalMaxNumTasks;
@@ -1024,6 +1027,12 @@ class RecentTasks {
void add(Task task) {
if (DEBUG_RECENTS_TRIM_TASKS) Slog.d(TAG, "add: task=" + task);
+ // Clean up the hidden tasks when going to home because the user may not be unable to return
+ // to the task from recents.
+ if (!mHiddenTasks.isEmpty() && task.isActivityTypeHome()) {
+ removeUnreachableHiddenTasks(task.getWindowingMode());
+ }
+
final boolean isAffiliated = task.mAffiliatedTaskId != task.mTaskId
|| task.mNextAffiliateTaskId != INVALID_TASK_ID
|| task.mPrevAffiliateTaskId != INVALID_TASK_ID;
@@ -1390,6 +1399,28 @@ class RecentTasks {
return display.getIndexOf(stack) < display.getIndexOf(display.getRootHomeTask());
}
+ /** Remove the tasks that user may not be able to return. */
+ private void removeUnreachableHiddenTasks(int windowingMode) {
+ for (int i = mHiddenTasks.size() - 1; i >= 0; i--) {
+ final Task hiddenTask = mHiddenTasks.get(i);
+ if (!hiddenTask.hasChild()) {
+ // The task was removed by other path.
+ mHiddenTasks.remove(i);
+ continue;
+ }
+ if (hiddenTask.getWindowingMode() != windowingMode
+ || hiddenTask.getTopVisibleActivity() != null) {
+ // The task may be reachable from the back stack of other windowing mode or it is
+ // currently in use. Keep the task in the hidden list to avoid losing track, e.g.
+ // after dismissing primary split screen.
+ continue;
+ }
+ mHiddenTasks.remove(i);
+ mSupervisor.removeTask(hiddenTask, false /* killProcess */,
+ !REMOVE_FROM_RECENTS, "remove-hidden-task");
+ }
+ }
+
/**
* If needed, remove oldest existing entries in recents that are for the same kind
* of task as the given one.
@@ -1406,6 +1437,14 @@ class RecentTasks {
// callbacks here.
final Task removedTask = mTasks.remove(removeIndex);
if (removedTask != task) {
+ // The added task is in recents so it is not hidden.
+ mHiddenTasks.remove(task);
+ if (removedTask.hasChild()) {
+ // A non-empty task is replaced by a new task. Because the removed task is no longer
+ // managed by the recent tasks list, add it to the hidden list to prevent the task
+ // from becoming dangling.
+ mHiddenTasks.add(removedTask);
+ }
notifyTaskRemoved(removedTask, false /* wasTrimmed */, false /* killProcess */);
if (DEBUG_RECENTS_TRIM_TASKS) Slog.d(TAG, "Trimming task=" + removedTask
+ " for addition of task=" + task);
@@ -1662,6 +1701,9 @@ class RecentTasks {
pw.println("mFreezeTaskListReordering=" + mFreezeTaskListReordering);
pw.println("mFreezeTaskListReorderingPendingTimeout="
+ mService.mH.hasCallbacks(mResetFreezeTaskListOnTimeoutRunnable));
+ if (!mHiddenTasks.isEmpty()) {
+ pw.println("mHiddenTasks=" + mHiddenTasks);
+ }
if (mTasks.isEmpty()) {
return;
}
diff --git a/services/core/java/com/android/server/wm/SeamlessRotator.java b/services/core/java/com/android/server/wm/SeamlessRotator.java
index ba31818d6331..c621c48b0028 100644
--- a/services/core/java/com/android/server/wm/SeamlessRotator.java
+++ b/services/core/java/com/android/server/wm/SeamlessRotator.java
@@ -101,9 +101,9 @@ public class SeamlessRotator {
t.setPosition(win.mSurfaceControl, win.mLastSurfacePosition.x, win.mLastSurfacePosition.y);
if (win.mWinAnimator.mSurfaceController != null && !timeout) {
t.deferTransactionUntil(win.mSurfaceControl,
- win.mWinAnimator.mSurfaceController.mSurfaceControl, win.getFrameNumber());
+ win.getDeferTransactionBarrier(), win.getFrameNumber());
t.deferTransactionUntil(win.mWinAnimator.mSurfaceController.mSurfaceControl,
- win.mWinAnimator.mSurfaceController.mSurfaceControl, win.getFrameNumber());
+ win.getDeferTransactionBarrier(), win.getFrameNumber());
}
}
diff --git a/services/core/java/com/android/server/wm/Session.java b/services/core/java/com/android/server/wm/Session.java
index 5babdafc856d..de7f9e41cac0 100644
--- a/services/core/java/com/android/server/wm/Session.java
+++ b/services/core/java/com/android/server/wm/Session.java
@@ -191,7 +191,7 @@ class Session extends IWindowSession.Stub implements IBinder.DeathRecipient {
Rect outStableInsets, Rect outBackdropFrame,
DisplayCutout.ParcelableWrapper cutout, MergedConfiguration mergedConfiguration,
SurfaceControl outSurfaceControl, InsetsState outInsetsState,
- Point outSurfaceSize) {
+ Point outSurfaceSize, SurfaceControl outBLASTSurfaceControl) {
if (false) Slog.d(TAG_WM, ">>>>>> ENTERED relayout from "
+ Binder.getCallingPid());
Trace.traceBegin(TRACE_TAG_WINDOW_MANAGER, mRelayoutTag);
@@ -199,7 +199,8 @@ class Session extends IWindowSession.Stub implements IBinder.DeathRecipient {
requestedWidth, requestedHeight, viewFlags, flags, frameNumber,
outFrame, outContentInsets, outVisibleInsets,
outStableInsets, outBackdropFrame, cutout,
- mergedConfiguration, outSurfaceControl, outInsetsState, outSurfaceSize);
+ mergedConfiguration, outSurfaceControl, outInsetsState, outSurfaceSize,
+ outBLASTSurfaceControl);
Trace.traceEnd(TRACE_TAG_WINDOW_MANAGER);
if (false) Slog.d(TAG_WM, "<<<<<< EXITING relayout to "
+ Binder.getCallingPid());
diff --git a/services/core/java/com/android/server/wm/Task.java b/services/core/java/com/android/server/wm/Task.java
index 7f96c5e45faa..36cae1fb9b36 100644
--- a/services/core/java/com/android/server/wm/Task.java
+++ b/services/core/java/com/android/server/wm/Task.java
@@ -3864,9 +3864,8 @@ class Task extends WindowContainer<WindowContainer> {
}
boolean isControlledByTaskOrganizer() {
- // TODO(b/147849315): Clean-up relationship between task-org and task-hierarchy. Ideally
- // we only give control of the root task.
- return getTopMostTask().mTaskOrganizer != null;
+ final Task rootTask = getRootTask();
+ return rootTask == this && rootTask.mTaskOrganizer != null;
}
@Override
@@ -3938,23 +3937,6 @@ class Task extends WindowContainer<WindowContainer> {
super.getRelativeDisplayedPosition(outPos);
}
- @Override
- public void setWindowingMode(int windowingMode) {
- super.setWindowingMode(windowingMode);
- windowingMode = getWindowingMode();
-
- // TODO(b/147849315): Clean-up relationship between task-org and task-hierarchy. Ideally
- // we only give control of the root task.
- // Different windowing modes may be managed by different task organizers. If
- // getTaskOrganizer returns null, we still call transferToTaskOrganizer to make sure we
- // clear it.
- if (!isRootTask()) {
- final ITaskOrganizer org =
- mAtmService.mTaskOrganizerController.getTaskOrganizer(windowingMode);
- setTaskOrganizer(org);
- }
- }
-
/**
* @return true if the task is currently focused.
*/
diff --git a/services/core/java/com/android/server/wm/TaskSnapshotSurface.java b/services/core/java/com/android/server/wm/TaskSnapshotSurface.java
index 57de7536409f..e47eaee5bce4 100644
--- a/services/core/java/com/android/server/wm/TaskSnapshotSurface.java
+++ b/services/core/java/com/android/server/wm/TaskSnapshotSurface.java
@@ -116,7 +116,11 @@ class TaskSnapshotSurface implements StartingSurface {
private static final String TAG = TAG_WITH_CLASS_NAME ? "SnapshotStartingWindow" : TAG_WM;
private static final int MSG_REPORT_DRAW = 0;
private static final String TITLE_FORMAT = "SnapshotStartingWindow for taskId=%s";
- private static final Point sSurfaceSize = new Point(); //tmp var for unused relayout param
+
+ //tmp vars for unused relayout params
+ private static final Point sTmpSurfaceSize = new Point();
+ private static final SurfaceControl sTmpSurfaceControl = new SurfaceControl();
+
private final Window mWindow;
private final Surface mSurface;
private SurfaceControl mSurfaceControl;
@@ -230,7 +234,7 @@ class TaskSnapshotSurface implements StartingSurface {
session.relayout(window, window.mSeq, layoutParams, -1, -1, View.VISIBLE, 0, -1,
tmpFrame, tmpContentInsets, tmpRect, tmpStableInsets, tmpRect,
tmpCutout, tmpMergedConfiguration, surfaceControl, mTmpInsetsState,
- sSurfaceSize);
+ sTmpSurfaceSize, sTmpSurfaceControl);
} catch (RemoteException e) {
// Local call.
}
diff --git a/services/core/java/com/android/server/wm/WindowManagerService.java b/services/core/java/com/android/server/wm/WindowManagerService.java
index e13083007237..2c6c756dda3b 100644
--- a/services/core/java/com/android/server/wm/WindowManagerService.java
+++ b/services/core/java/com/android/server/wm/WindowManagerService.java
@@ -2071,7 +2071,7 @@ public class WindowManagerService extends IWindowManager.Stub
Rect outVisibleInsets, Rect outStableInsets, Rect outBackdropFrame,
DisplayCutout.ParcelableWrapper outCutout, MergedConfiguration mergedConfiguration,
SurfaceControl outSurfaceControl, InsetsState outInsetsState,
- Point outSurfaceSize) {
+ Point outSurfaceSize, SurfaceControl outBLASTSurfaceControl) {
int result = 0;
boolean configChanged;
final int pid = Binder.getCallingPid();
@@ -2238,7 +2238,8 @@ public class WindowManagerService extends IWindowManager.Stub
result = win.relayoutVisibleWindow(result, attrChanges);
try {
- result = createSurfaceControl(outSurfaceControl, result, win, winAnimator);
+ result = createSurfaceControl(outSurfaceControl, outBLASTSurfaceControl,
+ result, win, winAnimator);
} catch (Exception e) {
displayContent.getInputMonitor().updateInputWindowsLw(true /*force*/);
@@ -2270,6 +2271,7 @@ public class WindowManagerService extends IWindowManager.Stub
// surface, let the client use that, but don't create new surface at this point.
Trace.traceBegin(TRACE_TAG_WINDOW_MANAGER, "relayoutWindow: getSurface");
winAnimator.mSurfaceController.getSurfaceControl(outSurfaceControl);
+ winAnimator.mSurfaceController.getBLASTSurfaceControl(outBLASTSurfaceControl);
Trace.traceEnd(TRACE_TAG_WINDOW_MANAGER);
} else {
if (DEBUG_VISIBILITY) Slog.i(TAG_WM, "Releasing surface in: " + win);
@@ -2451,7 +2453,8 @@ public class WindowManagerService extends IWindowManager.Stub
}
private int createSurfaceControl(SurfaceControl outSurfaceControl,
- int result, WindowState win, WindowStateAnimator winAnimator) {
+ SurfaceControl outBLASTSurfaceControl, int result,
+ WindowState win, WindowStateAnimator winAnimator) {
if (!win.mHasSurface) {
result |= RELAYOUT_RES_SURFACE_CHANGED;
}
@@ -2465,6 +2468,7 @@ public class WindowManagerService extends IWindowManager.Stub
}
if (surfaceController != null) {
surfaceController.getSurfaceControl(outSurfaceControl);
+ surfaceController.getBLASTSurfaceControl(outBLASTSurfaceControl);
ProtoLog.i(WM_SHOW_TRANSACTIONS, "OUT SURFACE %s: copied", outSurfaceControl);
} else {
diff --git a/services/core/java/com/android/server/wm/WindowState.java b/services/core/java/com/android/server/wm/WindowState.java
index 73984fd49f73..b9694c3b9bd3 100644
--- a/services/core/java/com/android/server/wm/WindowState.java
+++ b/services/core/java/com/android/server/wm/WindowState.java
@@ -5616,4 +5616,8 @@ class WindowState extends WindowContainer<WindowState> implements WindowManagerP
boolean isNonToastWindowVisibleForPid(int pid) {
return mSession.mPid == pid && isNonToastOrStarting() && isVisibleNow();
}
+
+ SurfaceControl getDeferTransactionBarrier() {
+ return mWinAnimator.getDeferTransactionBarrier();
+ }
}
diff --git a/services/core/java/com/android/server/wm/WindowStateAnimator.java b/services/core/java/com/android/server/wm/WindowStateAnimator.java
index 069ee4fea8ec..9552df7b5899 100644
--- a/services/core/java/com/android/server/wm/WindowStateAnimator.java
+++ b/services/core/java/com/android/server/wm/WindowStateAnimator.java
@@ -1010,7 +1010,7 @@ class WindowStateAnimator {
// the WS position is reset (so the stack position is shown) at the same
// time that the buffer size changes.
setOffsetPositionForStackResize(false);
- mSurfaceController.deferTransactionUntil(mSurfaceController.mSurfaceControl,
+ mSurfaceController.deferTransactionUntil(mWin.getDeferTransactionBarrier(),
mWin.getFrameNumber());
} else {
final ActivityStack stack = mWin.getRootTask();
@@ -1041,7 +1041,7 @@ class WindowStateAnimator {
// comes in at the new size (normally position and crop are unfrozen).
// deferTransactionUntil accomplishes this for us.
if (wasForceScaled && !mForceScaleUntilResize) {
- mSurfaceController.deferTransactionUntil(mSurfaceController.mSurfaceControl,
+ mSurfaceController.deferTransactionUntil(mWin.getDeferTransactionBarrier(),
mWin.getFrameNumber());
mSurfaceController.forceScaleableInTransaction(false);
}
@@ -1518,4 +1518,11 @@ class WindowStateAnimator {
void setOffsetPositionForStackResize(boolean offsetPositionForStackResize) {
mOffsetPositionForStackResize = offsetPositionForStackResize;
}
+
+ SurfaceControl getDeferTransactionBarrier() {
+ if (!hasSurface()) {
+ return null;
+ }
+ return mSurfaceController.getDeferTransactionBarrier();
+ }
}
diff --git a/services/core/java/com/android/server/wm/WindowSurfaceController.java b/services/core/java/com/android/server/wm/WindowSurfaceController.java
index 5b8015be1a7a..383c0d9ab3d4 100644
--- a/services/core/java/com/android/server/wm/WindowSurfaceController.java
+++ b/services/core/java/com/android/server/wm/WindowSurfaceController.java
@@ -51,6 +51,11 @@ class WindowSurfaceController {
SurfaceControl mSurfaceControl;
+ /**
+ * WM only uses for deferred transactions.
+ */
+ SurfaceControl mBLASTSurfaceControl;
+
// Should only be set from within setShown().
private boolean mSurfaceShown = false;
private float mSurfaceX = 0;
@@ -110,13 +115,22 @@ class WindowSurfaceController {
.setMetadata(METADATA_WINDOW_TYPE, windowType)
.setMetadata(METADATA_OWNER_UID, ownerUid);
- if ((win.getAttrs().privateFlags &
- WindowManager.LayoutParams.PRIVATE_FLAG_USE_BLAST) != 0) {
+ final boolean useBLAST = (win.getAttrs().privateFlags &
+ WindowManager.LayoutParams.PRIVATE_FLAG_USE_BLAST) != 0;
+ if (useBLAST) {
b.setContainerLayer();
}
-
mSurfaceControl = b.build();
+
+ if (useBLAST) {
+ mBLASTSurfaceControl = win.makeSurface()
+ .setParent(mSurfaceControl)
+ .setName("BLAST Adapter Layer")
+ .setBLASTLayer()
+ .build();
+ }
+
Trace.traceEnd(TRACE_TAG_WINDOW_MANAGER);
}
@@ -168,6 +182,9 @@ class WindowSurfaceController {
} finally {
setShown(false);
mSurfaceControl = null;
+ if (mBLASTSurfaceControl != null) {
+ mBLASTSurfaceControl.release();
+ }
}
}
@@ -474,6 +491,12 @@ class WindowSurfaceController {
outSurfaceControl.copyFrom(mSurfaceControl);
}
+ void getBLASTSurfaceControl(SurfaceControl outSurfaceControl) {
+ if (mBLASTSurfaceControl != null) {
+ outSurfaceControl.copyFrom(mBLASTSurfaceControl);
+ }
+ }
+
int getLayer() {
return mSurfaceLayer;
}
@@ -510,6 +533,13 @@ class WindowSurfaceController {
return mSurfaceH;
}
+ SurfaceControl getDeferTransactionBarrier() {
+ if (mBLASTSurfaceControl != null) {
+ return mBLASTSurfaceControl;
+ }
+ return mSurfaceControl;
+ }
+
void dumpDebug(ProtoOutputStream proto, long fieldId) {
final long token = proto.start(fieldId);
proto.write(SHOWN, mSurfaceShown);
diff --git a/services/core/jni/Android.bp b/services/core/jni/Android.bp
index 77d814e3076b..4a2636ee6ca4 100644
--- a/services/core/jni/Android.bp
+++ b/services/core/jni/Android.bp
@@ -14,6 +14,7 @@ cc_library_static {
srcs: [
":graphicsstats_proto",
+ ":lib_alarmManagerService_native",
"BroadcastRadio/JavaRef.cpp",
"BroadcastRadio/NativeCallbackThread.cpp",
"BroadcastRadio/BroadcastRadioService.cpp",
@@ -21,7 +22,6 @@ cc_library_static {
"BroadcastRadio/TunerCallback.cpp",
"BroadcastRadio/convert.cpp",
"BroadcastRadio/regions.cpp",
- "com_android_server_AlarmManagerService.cpp",
"com_android_server_am_BatteryStatsService.cpp",
"com_android_server_connectivity_Vpn.cpp",
"com_android_server_ConsumerIrService.cpp",
@@ -182,3 +182,10 @@ filegroup {
"com_android_server_net_NetworkStatsFactory.cpp",
],
}
+
+filegroup {
+ name: "lib_alarmManagerService_native",
+ srcs: [
+ "com_android_server_AlarmManagerService.cpp",
+ ],
+}
diff --git a/services/core/jni/com_android_server_VibratorService.cpp b/services/core/jni/com_android_server_VibratorService.cpp
index 6811e6d0e6f2..5a8e25e4cf1c 100644
--- a/services/core/jni/com_android_server_VibratorService.cpp
+++ b/services/core/jni/com_android_server_VibratorService.cpp
@@ -49,6 +49,12 @@ namespace android {
static jmethodID sMethodIdOnComplete;
+static struct {
+ jfieldID id;
+ jfieldID scale;
+ jfieldID delay;
+} gPrimitiveClassInfo;
+
static_assert(static_cast<uint8_t>(V1_0::EffectStrength::LIGHT) ==
static_cast<uint8_t>(aidl::EffectStrength::LIGHT));
static_assert(static_cast<uint8_t>(V1_0::EffectStrength::MEDIUM) ==
@@ -333,6 +339,21 @@ static void vibratorSetExternalControl(JNIEnv*, jclass, jboolean enabled) {
}
}
+static jintArray vibratorGetSupportedEffects(JNIEnv *env, jclass) {
+ if (auto hal = getHal<aidl::IVibrator>()) {
+ std::vector<aidl::Effect> supportedEffects;
+ if (!hal->call(&aidl::IVibrator::getSupportedEffects, &supportedEffects).isOk()) {
+ return nullptr;
+ }
+ jintArray arr = env->NewIntArray(supportedEffects.size());
+ env->SetIntArrayRegion(arr, 0, supportedEffects.size(),
+ reinterpret_cast<jint*>(supportedEffects.data()));
+ return arr;
+ } else {
+ return nullptr;
+ }
+}
+
static jlong vibratorPerformEffect(JNIEnv* env, jclass, jlong effect, jlong strength,
jobject vibration) {
if (auto hal = getHal<aidl::IVibrator>()) {
@@ -398,6 +419,37 @@ static jlong vibratorPerformEffect(JNIEnv* env, jclass, jlong effect, jlong stre
return -1;
}
+static aidl::CompositeEffect effectFromJavaPrimitive(JNIEnv* env, jobject primitive) {
+ aidl::CompositeEffect effect;
+ effect.primitive = static_cast<aidl::CompositePrimitive>(
+ env->GetIntField(primitive, gPrimitiveClassInfo.id));
+ effect.scale = static_cast<float>(env->GetFloatField(primitive, gPrimitiveClassInfo.scale));
+ effect.delayMs = static_cast<int>(env->GetIntField(primitive, gPrimitiveClassInfo.delay));
+ return effect;
+}
+
+static void vibratorPerformComposedEffect(JNIEnv* env, jclass, jobjectArray composition,
+ jobject vibration) {
+ auto hal = getHal<aidl::IVibrator>();
+ if (!hal) {
+ return;
+ }
+ size_t size = env->GetArrayLength(composition);
+ std::vector<aidl::CompositeEffect> effects;
+ for (size_t i = 0; i < size; i++) {
+ jobject element = env->GetObjectArrayElement(composition, i);
+ effects.push_back(effectFromJavaPrimitive(env, element));
+ }
+ sp<AidlVibratorCallback> effectCallback = new AidlVibratorCallback(env, vibration);
+
+ auto status = hal->call(&aidl::IVibrator::compose, effects, effectCallback);
+ if (!status.isOk()) {
+ if (status.exceptionCode() != binder::Status::EX_UNSUPPORTED_OPERATION) {
+ ALOGE("Failed to play haptic effect composition");
+ }
+ }
+}
+
static jlong vibratorGetCapabilities(JNIEnv*, jclass) {
if (auto hal = getHal<aidl::IVibrator>()) {
int32_t cap = 0;
@@ -433,7 +485,12 @@ static const JNINativeMethod method_table[] = {
{ "vibratorSupportsAmplitudeControl", "()Z", (void*)vibratorSupportsAmplitudeControl},
{ "vibratorSetAmplitude", "(I)V", (void*)vibratorSetAmplitude},
{ "vibratorPerformEffect", "(JJLcom/android/server/VibratorService$Vibration;)J",
- (void*)vibratorPerformEffect},
+ (void*)vibratorPerformEffect},
+ { "vibratorPerformComposedEffect",
+ "([Landroid/os/VibrationEffect$Composition$PrimitiveEffect;Lcom/android/server/VibratorService$Vibration;)V",
+ (void*)vibratorPerformComposedEffect},
+ { "vibratorGetSupportedEffects", "()[I",
+ (void*)vibratorGetSupportedEffects},
{ "vibratorSupportsExternalControl", "()Z", (void*)vibratorSupportsExternalControl},
{ "vibratorSetExternalControl", "(Z)V", (void*)vibratorSetExternalControl},
{ "vibratorGetCapabilities", "()J", (void*)vibratorGetCapabilities},
@@ -441,11 +498,15 @@ static const JNINativeMethod method_table[] = {
{ "vibratorAlwaysOnDisable", "(J)V", (void*)vibratorAlwaysOnDisable},
};
-int register_android_server_VibratorService(JNIEnv *env)
-{
+int register_android_server_VibratorService(JNIEnv *env) {
sMethodIdOnComplete = GetMethodIDOrDie(env,
FindClassOrDie(env, "com/android/server/VibratorService$Vibration"),
"onComplete", "()V");
+ jclass primitiveClass = FindClassOrDie(env,
+ "android/os/VibrationEffect$Composition$PrimitiveEffect");
+ gPrimitiveClassInfo.id = GetFieldIDOrDie(env, primitiveClass, "id", "I");
+ gPrimitiveClassInfo.scale = GetFieldIDOrDie(env, primitiveClass, "scale", "F");
+ gPrimitiveClassInfo.delay = GetFieldIDOrDie(env, primitiveClass, "delay", "I");
return jniRegisterNativeMethods(env, "com/android/server/VibratorService",
method_table, NELEM(method_table));
}
diff --git a/services/incremental/Android.bp b/services/incremental/Android.bp
index ddf4dd53d5d3..323e7f156941 100644
--- a/services/incremental/Android.bp
+++ b/services/incremental/Android.bp
@@ -47,6 +47,8 @@ cc_defaults {
shared_libs: [
"libandroidfw",
"libbinder",
+ "libcrypto",
+ "libcutils",
"libincfs",
"liblog",
"libz",
diff --git a/services/incremental/BinderIncrementalService.cpp b/services/incremental/BinderIncrementalService.cpp
index bb26c1f93159..f1b637f516ea 100644
--- a/services/incremental/BinderIncrementalService.cpp
+++ b/services/incremental/BinderIncrementalService.cpp
@@ -46,10 +46,10 @@ static bool incFsEnabled() {
return incfs::enabled();
}
-static bool incFsVersionValid(const sp<IVold>& vold) {
- int version = -1;
- auto status = vold->incFsVersion(&version);
- if (!status.isOk() || version <= 0) {
+static bool incFsValid(const sp<IVold>& vold) {
+ bool enabled = false;
+ auto status = vold->incFsEnabled(&enabled);
+ if (!status.isOk() || !enabled) {
return false;
}
return true;
@@ -74,7 +74,7 @@ BinderIncrementalService* BinderIncrementalService::start() {
return nullptr;
}
sp<IVold> vold = interface_cast<IVold>(voldBinder);
- if (!incFsVersionValid(vold)) {
+ if (!incFsValid(vold)) {
return nullptr;
}
@@ -86,6 +86,7 @@ BinderIncrementalService* BinderIncrementalService::start() {
sp<ProcessState> ps(ProcessState::self());
ps->startThreadPool();
ps->giveThreadPoolName();
+ // sm->addService increments the reference count, and now we're OK with returning the pointer.
return self.get();
}
@@ -107,9 +108,9 @@ binder::Status BinderIncrementalService::openStorage(const std::string& path,
return ok();
}
-binder::Status BinderIncrementalService::createStorage(
- const std::string& path, const DataLoaderParamsParcel& params,
- int32_t createMode, int32_t* _aidl_return) {
+binder::Status BinderIncrementalService::createStorage(const std::string& path,
+ const DataLoaderParamsParcel& params,
+ int32_t createMode, int32_t* _aidl_return) {
*_aidl_return =
mImpl.createStorage(path, const_cast<DataLoaderParamsParcel&&>(params),
android::incremental::IncrementalService::CreateOptions(
@@ -129,10 +130,10 @@ binder::Status BinderIncrementalService::createLinkedStorage(const std::string&
}
binder::Status BinderIncrementalService::makeBindMount(int32_t storageId,
- const std::string& pathUnderStorage,
+ const std::string& sourcePath,
const std::string& targetFullPath,
int32_t bindType, int32_t* _aidl_return) {
- *_aidl_return = mImpl.bind(storageId, pathUnderStorage, targetFullPath,
+ *_aidl_return = mImpl.bind(storageId, sourcePath, targetFullPath,
android::incremental::IncrementalService::BindKind(bindType));
return ok();
}
@@ -149,75 +150,127 @@ binder::Status BinderIncrementalService::deleteStorage(int32_t storageId) {
return ok();
}
-binder::Status BinderIncrementalService::makeDirectory(int32_t storageId,
- const std::string& pathUnderStorage,
+binder::Status BinderIncrementalService::makeDirectory(int32_t storageId, const std::string& path,
int32_t* _aidl_return) {
- auto inode = mImpl.makeDir(storageId, pathUnderStorage);
- *_aidl_return = inode < 0 ? inode : 0;
+ *_aidl_return = mImpl.makeDir(storageId, path);
return ok();
}
-binder::Status BinderIncrementalService::makeDirectories(int32_t storageId,
- const std::string& pathUnderStorage,
- int32_t* _aidl_return) {
- auto inode = mImpl.makeDirs(storageId, pathUnderStorage);
- *_aidl_return = inode < 0 ? inode : 0;
- return ok();
+static std::tuple<int, incfs::FileId, incfs::NewFileParams> toMakeFileParams(
+ const android::os::incremental::IncrementalNewFileParams& params) {
+ incfs::FileId id;
+ if (params.fileId.empty()) {
+ if (params.metadata.empty()) {
+ return {EINVAL, {}, {}};
+ }
+ id = IncrementalService::idFromMetadata(params.metadata);
+ } else if (params.fileId.size() != sizeof(id)) {
+ return {EINVAL, {}, {}};
+ } else {
+ memcpy(&id, params.fileId.data(), sizeof(id));
+ }
+ incfs::NewFileParams nfp;
+ nfp.size = params.size;
+ nfp.metadata = {(const char*)params.metadata.data(), (IncFsSize)params.metadata.size()};
+ if (!params.signature) {
+ nfp.verification = {};
+ } else {
+ nfp.verification.hashAlgorithm = IncFsHashAlgortithm(params.signature->hashAlgorithm);
+ nfp.verification.rootHash = {(const char*)params.signature->rootHash.data(),
+ (IncFsSize)params.signature->rootHash.size()};
+ nfp.verification.additionalData = {(const char*)params.signature->additionalData.data(),
+ (IncFsSize)params.signature->additionalData.size()};
+ nfp.verification.signature = {(const char*)params.signature->signature.data(),
+ (IncFsSize)params.signature->signature.size()};
+ }
+ return {0, id, nfp};
}
-binder::Status BinderIncrementalService::makeFile(int32_t storageId,
- const std::string& pathUnderStorage, int64_t size,
- const std::vector<uint8_t>& metadata,
- int32_t* _aidl_return) {
- auto inode = mImpl.makeFile(storageId, pathUnderStorage, size,
- {(const char*)metadata.data(), metadata.size()}, {});
- *_aidl_return = inode < 0 ? inode : 0;
+binder::Status BinderIncrementalService::makeFile(
+ int32_t storageId, const std::string& path,
+ const ::android::os::incremental::IncrementalNewFileParams& params, int32_t* _aidl_return) {
+ auto [err, fileId, nfp] = toMakeFileParams(params);
+ if (err) {
+ *_aidl_return = err;
+ return ok();
+ }
+
+ *_aidl_return = mImpl.makeFile(storageId, path, 0555, fileId, nfp);
return ok();
}
-binder::Status BinderIncrementalService::makeFileFromRange(
- int32_t storageId, const std::string& pathUnderStorage,
- const std::string& sourcePathUnderStorage, int64_t start, int64_t end,
- int32_t* _aidl_return) {
+binder::Status BinderIncrementalService::makeFileFromRange(int32_t storageId,
+ const std::string& targetPath,
+ const std::string& sourcePath,
+ int64_t start, int64_t end,
+ int32_t* _aidl_return) {
// TODO(b/136132412): implement this
- *_aidl_return = -1;
+ *_aidl_return = ENOSYS; // not implemented
return ok();
}
+
binder::Status BinderIncrementalService::makeLink(int32_t sourceStorageId,
- const std::string& relativeSourcePath,
+ const std::string& sourcePath,
int32_t destStorageId,
- const std::string& relativeDestPath,
+ const std::string& destPath,
int32_t* _aidl_return) {
- auto sourceInode = mImpl.nodeFor(sourceStorageId, relativeSourcePath);
- auto [targetParentInode, name] = mImpl.parentAndNameFor(destStorageId, relativeDestPath);
- *_aidl_return = mImpl.link(sourceStorageId, sourceInode, targetParentInode, name);
+ *_aidl_return = mImpl.link(sourceStorageId, sourcePath, destStorageId, destPath);
return ok();
}
-binder::Status BinderIncrementalService::unlink(int32_t storageId,
- const std::string& pathUnderStorage,
+
+binder::Status BinderIncrementalService::unlink(int32_t storageId, const std::string& path,
int32_t* _aidl_return) {
- auto [parentNode, name] = mImpl.parentAndNameFor(storageId, pathUnderStorage);
- *_aidl_return = mImpl.unlink(storageId, parentNode, name);
+ *_aidl_return = mImpl.unlink(storageId, path);
return ok();
}
+
binder::Status BinderIncrementalService::isFileRangeLoaded(int32_t storageId,
- const std::string& relativePath,
- int64_t start, int64_t end,
- bool* _aidl_return) {
+ const std::string& path, int64_t start,
+ int64_t end, bool* _aidl_return) {
+ // TODO: implement
*_aidl_return = false;
return ok();
}
-binder::Status BinderIncrementalService::getFileMetadata(int32_t storageId,
- const std::string& relativePath,
+
+binder::Status BinderIncrementalService::getMetadataByPath(int32_t storageId,
+ const std::string& path,
+ std::vector<uint8_t>* _aidl_return) {
+ auto fid = mImpl.nodeFor(storageId, path);
+ if (fid != kIncFsInvalidFileId) {
+ auto metadata = mImpl.getMetadata(storageId, fid);
+ _aidl_return->assign(metadata.begin(), metadata.end());
+ }
+ return ok();
+}
+
+static FileId toFileId(const std::vector<uint8_t>& id) {
+ FileId fid;
+ memcpy(&fid, id.data(), id.size());
+ return fid;
+}
+
+binder::Status BinderIncrementalService::getMetadataById(int32_t storageId,
+ const std::vector<uint8_t>& id,
std::vector<uint8_t>* _aidl_return) {
- auto inode = mImpl.nodeFor(storageId, relativePath);
- auto metadata = mImpl.getMetadata(storageId, inode);
+ if (id.size() != sizeof(incfs::FileId)) {
+ return ok();
+ }
+ auto fid = toFileId(id);
+ auto metadata = mImpl.getMetadata(storageId, fid);
_aidl_return->assign(metadata.begin(), metadata.end());
return ok();
}
+
+binder::Status BinderIncrementalService::makeDirectories(int32_t storageId, const std::string& path,
+ int32_t* _aidl_return) {
+ *_aidl_return = mImpl.makeDirs(storageId, path);
+ return ok();
+}
+
binder::Status BinderIncrementalService::startLoading(int32_t storageId, bool* _aidl_return) {
*_aidl_return = mImpl.startLoading(storageId);
return ok();
}
+
} // namespace android::os::incremental
jlong Incremental_IncrementalService_Start() {
diff --git a/services/incremental/BinderIncrementalService.h b/services/incremental/BinderIncrementalService.h
index 37c9661db28d..a94a75a26875 100644
--- a/services/incremental/BinderIncrementalService.h
+++ b/services/incremental/BinderIncrementalService.h
@@ -37,38 +37,39 @@ public:
void onSystemReady();
void onInvalidStorage(int mountId);
- binder::Status openStorage(const std::string &path, int32_t *_aidl_return) final;
- binder::Status createStorage(
- const std::string &path,
- const ::android::content::pm::DataLoaderParamsParcel &params,
- int32_t createMode, int32_t *_aidl_return) final;
- binder::Status createLinkedStorage(const std::string &path, int32_t otherStorageId,
- int32_t createMode, int32_t *_aidl_return) final;
- binder::Status makeBindMount(int32_t storageId, const std::string &pathUnderStorage,
- const std::string &targetFullPath, int32_t bindType,
- int32_t *_aidl_return) final;
- binder::Status deleteBindMount(int32_t storageId, const std::string &targetFullPath,
- int32_t *_aidl_return) final;
+ binder::Status openStorage(const std::string& path, int32_t* _aidl_return) final;
+ binder::Status createStorage(const std::string& path,
+ const ::android::content::pm::DataLoaderParamsParcel& params,
+ int32_t createMode, int32_t* _aidl_return) final;
+ binder::Status createLinkedStorage(const std::string& path, int32_t otherStorageId,
+ int32_t createMode, int32_t* _aidl_return) final;
+ binder::Status makeBindMount(int32_t storageId, const std::string& sourcePath,
+ const std::string& targetFullPath, int32_t bindType,
+ int32_t* _aidl_return) final;
+ binder::Status deleteBindMount(int32_t storageId, const std::string& targetFullPath,
+ int32_t* _aidl_return) final;
+ binder::Status makeDirectory(int32_t storageId, const std::string& path,
+ int32_t* _aidl_return) final;
+ 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 ::android::os::incremental::IncrementalNewFileParams& params,
+ 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;
+ binder::Status makeLink(int32_t sourceStorageId, const std::string& sourcePath,
+ int32_t destStorageId, const std::string& destPath,
+ int32_t* _aidl_return) final;
+ binder::Status unlink(int32_t storageId, const std::string& path, int32_t* _aidl_return) final;
+ binder::Status isFileRangeLoaded(int32_t storageId, const std::string& path, int64_t start,
+ int64_t end, bool* _aidl_return) final;
+ binder::Status getMetadataByPath(int32_t storageId, const std::string& path,
+ std::vector<uint8_t>* _aidl_return) final;
+ binder::Status getMetadataById(int32_t storageId, const std::vector<uint8_t>& id,
+ std::vector<uint8_t>* _aidl_return) final;
+ binder::Status startLoading(int32_t storageId, bool* _aidl_return) final;
binder::Status deleteStorage(int32_t storageId) final;
- binder::Status makeDirectory(int32_t storageId, const std::string &pathUnderStorage,
- int32_t *_aidl_return) final;
- binder::Status makeDirectories(int32_t storageId, const std::string &pathUnderStorage,
- int32_t *_aidl_return) final;
- binder::Status makeFile(int32_t storageId, const std::string &pathUnderStorage, int64_t size,
- const std::vector<uint8_t> &metadata, int32_t *_aidl_return) final;
- binder::Status makeFileFromRange(int32_t storageId, const std::string &pathUnderStorage,
- const std::string &sourcePathUnderStorage, int64_t start,
- int64_t end, int32_t *_aidl_return);
- binder::Status makeLink(int32_t sourceStorageId, const std::string &relativeSourcePath,
- int32_t destStorageId, const std::string &relativeDestPath,
- int32_t *_aidl_return) final;
- binder::Status unlink(int32_t storageId, const std::string &pathUnderStorage,
- int32_t *_aidl_return) final;
- binder::Status isFileRangeLoaded(int32_t storageId, const std::string &relativePath,
- int64_t start, int64_t end, bool *_aidl_return) final;
- binder::Status getFileMetadata(int32_t storageId, const std::string &relativePath,
- std::vector<uint8_t> *_aidl_return) final;
- binder::Status startLoading(int32_t storageId, bool *_aidl_return) final;
private:
android::incremental::IncrementalService mImpl;
diff --git a/services/incremental/IncrementalService.cpp b/services/incremental/IncrementalService.cpp
index afce260ed41e..414c66c866db 100644
--- a/services/incremental/IncrementalService.cpp
+++ b/services/incremental/IncrementalService.cpp
@@ -30,6 +30,8 @@
#include <binder/BinderService.h>
#include <binder/ParcelFileDescriptor.h>
#include <binder/Status.h>
+
+#include <openssl/sha.h>
#include <sys/stat.h>
#include <uuid/uuid.h>
#include <zlib.h>
@@ -55,7 +57,6 @@ using IncrementalFileSystemControlParcel =
struct Constants {
static constexpr auto backing = "backing_store"sv;
static constexpr auto mount = "mount"sv;
- static constexpr auto image = "incfs.img"sv;
static constexpr auto storagePrefix = "st"sv;
static constexpr auto mountpointMdPrefix = ".mountpoint."sv;
static constexpr auto infoMdName = ".info"sv;
@@ -70,7 +71,7 @@ template <base::LogSeverity level = base::ERROR>
bool mkdirOrLog(std::string_view name, int mode = 0770, bool allowExisting = true) {
auto cstr = path::c_str(name);
if (::mkdir(cstr, mode)) {
- if (errno != EEXIST) {
+ if (!allowExisting || errno != EEXIST) {
PLOG(level) << "Can't create directory '" << name << '\'';
return false;
}
@@ -80,6 +81,11 @@ bool mkdirOrLog(std::string_view name, int mode = 0770, bool allowExisting = tru
return false;
}
}
+ if (::chmod(cstr, mode)) {
+ PLOG(level) << "Changing permission failed for '" << name << '\'';
+ return false;
+ }
+
return true;
}
@@ -106,7 +112,7 @@ static std::pair<std::string, std::string> makeMountDir(std::string_view increme
for (int counter = 0; counter < 1000;
mountKey.resize(prefixSize), base::StringAppendF(&mountKey, "%d", counter++)) {
auto mountRoot = path::join(incrementalDir, mountKey);
- if (mkdirOrLog(mountRoot, 0770, false)) {
+ if (mkdirOrLog(mountRoot, 0777, false)) {
return {mountKey, mountRoot};
}
}
@@ -116,11 +122,7 @@ static std::pair<std::string, std::string> makeMountDir(std::string_view increme
template <class ProtoMessage, class Control>
static ProtoMessage parseFromIncfs(const IncFsWrapper* incfs, Control&& control,
std::string_view path) {
- struct stat st;
- if (::stat(path::c_str(path), &st)) {
- return {};
- }
- auto md = incfs->getMetadata(control, st.st_ino);
+ auto md = incfs->getMetadata(control, path);
ProtoMessage message;
return message.ParseFromArray(md.data(), md.size()) ? message : ProtoMessage{};
}
@@ -161,35 +163,66 @@ IncrementalService::IncFsMount::~IncFsMount() {
}
auto IncrementalService::IncFsMount::makeStorage(StorageId id) -> StorageMap::iterator {
- metadata::Storage st;
- st.set_id(id);
- auto metadata = st.SerializeAsString();
-
std::string name;
for (int no = nextStorageDirNo.fetch_add(1, std::memory_order_relaxed), i = 0;
i < 1024 && no >= 0; no = nextStorageDirNo.fetch_add(1, std::memory_order_relaxed), ++i) {
name.clear();
- base::StringAppendF(&name, "%.*s%d", int(constants().storagePrefix.size()),
- constants().storagePrefix.data(), no);
- if (auto node =
- incrementalService.mIncFs->makeDir(control, name, INCFS_ROOT_INODE, metadata);
- node >= 0) {
+ base::StringAppendF(&name, "%.*s_%d_%d", int(constants().storagePrefix.size()),
+ constants().storagePrefix.data(), id, no);
+ auto fullName = path::join(root, constants().mount, name);
+ if (auto err = incrementalService.mIncFs->makeDir(control, fullName); !err) {
std::lock_guard l(lock);
- return storages.insert_or_assign(id, Storage{std::move(name), node}).first;
+ return storages.insert_or_assign(id, Storage{std::move(fullName)}).first;
+ } else if (err != EEXIST) {
+ LOG(ERROR) << __func__ << "(): failed to create dir |" << fullName << "| " << err;
+ break;
}
}
nextStorageDirNo = 0;
return storages.end();
}
+static std::unique_ptr<DIR, decltype(&::closedir)> openDir(const char* path) {
+ return {::opendir(path), ::closedir};
+}
+
+static int rmDirContent(const char* path) {
+ auto dir = openDir(path);
+ if (!dir) {
+ return -EINVAL;
+ }
+ while (auto entry = ::readdir(dir.get())) {
+ if (entry->d_name == "."sv || entry->d_name == ".."sv) {
+ continue;
+ }
+ auto fullPath = android::base::StringPrintf("%s/%s", path, entry->d_name);
+ if (entry->d_type == DT_DIR) {
+ if (const auto err = rmDirContent(fullPath.c_str()); err != 0) {
+ PLOG(WARNING) << "Failed to delete " << fullPath << " content";
+ return err;
+ }
+ if (const auto err = ::rmdir(fullPath.c_str()); err != 0) {
+ PLOG(WARNING) << "Failed to rmdir " << fullPath;
+ return err;
+ }
+ } else {
+ if (const auto err = ::unlink(fullPath.c_str()); err != 0) {
+ PLOG(WARNING) << "Failed to delete " << fullPath;
+ return err;
+ }
+ }
+ }
+ return 0;
+}
+
void IncrementalService::IncFsMount::cleanupFilesystem(std::string_view root) {
- ::unlink(path::join(root, constants().backing, constants().image).c_str());
+ rmDirContent(path::join(root, constants().backing).c_str());
::rmdir(path::join(root, constants().backing).c_str());
::rmdir(path::join(root, constants().mount).c_str());
::rmdir(path::c_str(root));
}
-IncrementalService::IncrementalService(const ServiceManagerWrapper& sm, std::string_view rootDir)
+IncrementalService::IncrementalService(ServiceManagerWrapper&& sm, std::string_view rootDir)
: mVold(sm.getVoldService()),
mIncrementalManager(sm.getIncrementalManager()),
mIncFs(sm.getIncFs()),
@@ -205,6 +238,23 @@ IncrementalService::IncrementalService(const ServiceManagerWrapper& sm, std::str
// mountExistingImages();
}
+FileId IncrementalService::idFromMetadata(std::span<const uint8_t> metadata) {
+ incfs::FileId id = {};
+ if (size_t(metadata.size()) <= sizeof(id)) {
+ memcpy(&id, metadata.data(), metadata.size());
+ } else {
+ uint8_t buffer[SHA_DIGEST_LENGTH];
+ static_assert(sizeof(buffer) >= sizeof(id));
+
+ SHA_CTX ctx;
+ SHA1_Init(&ctx);
+ SHA1_Update(&ctx, metadata.data(), metadata.size());
+ SHA1_Final(buffer, &ctx);
+ memcpy(&id, buffer, sizeof(id));
+ }
+ return id;
+}
+
IncrementalService::~IncrementalService() = default;
std::optional<std::future<void>> IncrementalService::onSystemReady() {
@@ -300,26 +350,36 @@ StorageId IncrementalService::createStorage(std::string_view mountPoint,
std::unique_ptr<std::string, decltype(firstCleanup)>(&mountRoot, firstCleanup);
auto mountTarget = path::join(mountRoot, constants().mount);
- if (!mkdirOrLog(path::join(mountRoot, constants().backing)) || !mkdirOrLog(mountTarget)) {
+ const auto backing = path::join(mountRoot, constants().backing);
+ if (!mkdirOrLog(backing, 0777) || !mkdirOrLog(mountTarget)) {
return kInvalidStorageId;
}
- const auto image = path::join(mountRoot, constants().backing, constants().image);
IncFsMount::Control control;
{
std::lock_guard l(mMountOperationLock);
IncrementalFileSystemControlParcel controlParcel;
- auto status = mVold->mountIncFs(image, mountTarget, incfs::truncate, &controlParcel);
+
+ if (auto err = rmDirContent(backing.c_str())) {
+ LOG(ERROR) << "Coudn't clean the backing directory " << backing << ": " << err;
+ return kInvalidStorageId;
+ }
+ if (!mkdirOrLog(path::join(backing, ".index"), 0777)) {
+ return kInvalidStorageId;
+ }
+ auto status = mVold->mountIncFs(backing, mountTarget, 0, &controlParcel);
if (!status.isOk()) {
LOG(ERROR) << "Vold::mountIncFs() failed: " << status.toString8();
return kInvalidStorageId;
}
- if (!controlParcel.cmd || !controlParcel.log) {
+ if (controlParcel.cmd.get() < 0 || controlParcel.pendingReads.get() < 0 ||
+ controlParcel.log.get() < 0) {
LOG(ERROR) << "Vold::mountIncFs() returned invalid control parcel.";
return kInvalidStorageId;
}
- control.cmdFd = controlParcel.cmd->release();
- control.logFd = controlParcel.log->release();
+ control.cmd = controlParcel.cmd.release().release();
+ control.pendingReads = controlParcel.pendingReads.release().release();
+ control.logs = controlParcel.log.release().release();
}
std::unique_lock l(mLock);
@@ -344,7 +404,7 @@ StorageId IncrementalService::createStorage(std::string_view mountPoint,
const auto storageIt = ifs->makeStorage(ifs->mountId);
if (storageIt == ifs->storages.end()) {
- LOG(ERROR) << "Can't create default storage directory";
+ LOG(ERROR) << "Can't create a default storage directory";
return kInvalidStorageId;
}
@@ -359,9 +419,12 @@ StorageId IncrementalService::createStorage(std::string_view mountPoint,
m.mutable_loader()->release_arguments();
m.mutable_loader()->release_class_name();
m.mutable_loader()->release_package_name();
- if (auto err = mIncFs->makeFile(ifs->control, constants().infoMdName, INCFS_ROOT_INODE, 0,
- metadata);
- err < 0) {
+ if (auto err =
+ mIncFs->makeFile(ifs->control,
+ path::join(ifs->root, constants().mount,
+ constants().infoMdName),
+ 0777, idFromMetadata(metadata),
+ {.metadata = {metadata.data(), (IncFsSize)metadata.size()}})) {
LOG(ERROR) << "Saving mount metadata failed: " << -err;
return kInvalidStorageId;
}
@@ -369,8 +432,8 @@ StorageId IncrementalService::createStorage(std::string_view mountPoint,
const auto bk =
(options & CreateOptions::PermanentBind) ? BindKind::Permanent : BindKind::Temporary;
- if (auto err = addBindMount(*ifs, storageIt->first, std::string(storageIt->second.name),
- std::move(mountNorm), bk, l);
+ if (auto err = addBindMount(*ifs, storageIt->first, storageIt->second.name,
+ std::string(storageIt->second.name), std::move(mountNorm), bk, l);
err < 0) {
LOG(ERROR) << "adding bind mount failed: " << -err;
return kInvalidStorageId;
@@ -419,8 +482,9 @@ StorageId IncrementalService::createLinkedStorage(std::string_view mountPoint,
const auto bk =
(options & CreateOptions::PermanentBind) ? BindKind::Permanent : BindKind::Temporary;
- if (auto err = addBindMount(*ifs, storageIt->first, std::string(storageIt->second.name),
- path::normalize(mountPoint), bk, l);
+ if (auto err = addBindMount(*ifs, storageIt->first, storageIt->second.name,
+ std::string(storageIt->second.name), path::normalize(mountPoint),
+ bk, l);
err < 0) {
LOG(ERROR) << "bindMount failed with error: " << err;
return kInvalidStorageId;
@@ -492,40 +556,36 @@ StorageId IncrementalService::openStorage(std::string_view pathInMount) {
return findStorageId(path::normalize(pathInMount));
}
-Inode IncrementalService::nodeFor(StorageId storage, std::string_view subpath) const {
+FileId IncrementalService::nodeFor(StorageId storage, std::string_view subpath) const {
const auto ifs = getIfs(storage);
if (!ifs) {
- return -1;
+ return kIncFsInvalidFileId;
}
std::unique_lock l(ifs->lock);
auto storageIt = ifs->storages.find(storage);
if (storageIt == ifs->storages.end()) {
- return -1;
+ return kIncFsInvalidFileId;
}
if (subpath.empty() || subpath == "."sv) {
- return storageIt->second.node;
+ return kIncFsInvalidFileId;
}
auto path = path::join(ifs->root, constants().mount, storageIt->second.name, subpath);
l.unlock();
- struct stat st;
- if (::stat(path.c_str(), &st)) {
- return -1;
- }
- return st.st_ino;
+ return mIncFs->getFileId(ifs->control, path);
}
-std::pair<Inode, std::string_view> IncrementalService::parentAndNameFor(
+std::pair<FileId, std::string_view> IncrementalService::parentAndNameFor(
StorageId storage, std::string_view subpath) const {
auto name = path::basename(subpath);
if (name.empty()) {
- return {-1, {}};
+ return {kIncFsInvalidFileId, {}};
}
auto dir = path::dirname(subpath);
if (dir.empty() || dir == "/"sv) {
- return {-1, {}};
+ return {kIncFsInvalidFileId, {}};
}
- auto inode = nodeFor(storage, dir);
- return {inode, name};
+ auto id = nodeFor(storage, dir);
+ return {id, name};
}
IncrementalService::IfsMountPtr IncrementalService::getIfs(StorageId storage) const {
@@ -542,8 +602,8 @@ const IncrementalService::IfsMountPtr& IncrementalService::getIfsLocked(StorageI
return it->second;
}
-int IncrementalService::bind(StorageId storage, std::string_view sourceSubdir,
- std::string_view target, BindKind kind) {
+int IncrementalService::bind(StorageId storage, std::string_view source, std::string_view target,
+ BindKind kind) {
if (!isValidMountTarget(target)) {
return -EINVAL;
}
@@ -552,15 +612,20 @@ int IncrementalService::bind(StorageId storage, std::string_view sourceSubdir,
if (!ifs) {
return -EINVAL;
}
+ auto normSource = path::normalize(source);
+
std::unique_lock l(ifs->lock);
const auto storageInfo = ifs->storages.find(storage);
if (storageInfo == ifs->storages.end()) {
return -EINVAL;
}
- auto source = path::join(storageInfo->second.name, sourceSubdir);
+ if (!path::startsWith(normSource, storageInfo->second.name)) {
+ return -EINVAL;
+ }
l.unlock();
std::unique_lock l2(mLock, std::defer_lock);
- return addBindMount(*ifs, storage, std::move(source), path::normalize(target), kind, l2);
+ return addBindMount(*ifs, storage, storageInfo->second.name, std::move(normSource),
+ path::normalize(target), kind, l2);
}
int IncrementalService::unbind(StorageId storage, std::string_view target) {
@@ -599,90 +664,72 @@ int IncrementalService::unbind(StorageId storage, std::string_view target) {
ifs->bindPoints.erase(bindIt);
l2.unlock();
if (!savedFile.empty()) {
- mIncFs->unlink(ifs->control, INCFS_ROOT_INODE, savedFile);
+ mIncFs->unlink(ifs->control, path::join(ifs->root, constants().mount, savedFile));
}
}
return 0;
}
-Inode IncrementalService::makeFile(StorageId storageId, std::string_view pathUnderStorage,
- long size, std::string_view metadata,
- std::string_view signature) {
- (void)signature;
- auto [parentInode, name] = parentAndNameFor(storageId, pathUnderStorage);
- if (parentInode < 0) {
- return -EINVAL;
- }
- if (auto ifs = getIfs(storageId)) {
- auto inode = mIncFs->makeFile(ifs->control, name, parentInode, size, metadata);
- if (inode < 0) {
- return inode;
+int IncrementalService::makeFile(StorageId storage, std::string_view path, int mode, FileId id,
+ incfs::NewFileParams params) {
+ if (auto ifs = getIfs(storage)) {
+ auto err = mIncFs->makeFile(ifs->control, path, mode, id, params);
+ if (err) {
+ return err;
}
- auto metadataBytes = std::vector<uint8_t>();
- if (metadata.data() != nullptr && metadata.size() > 0) {
- metadataBytes.insert(metadataBytes.end(), &metadata.data()[0],
- &metadata.data()[metadata.size()]);
+ std::vector<uint8_t> metadataBytes;
+ if (params.metadata.data && params.metadata.size > 0) {
+ metadataBytes.assign(params.metadata.data, params.metadata.data + params.metadata.size);
}
- mIncrementalManager->newFileForDataLoader(ifs->mountId, inode, metadataBytes);
- return inode;
+ mIncrementalManager->newFileForDataLoader(ifs->mountId, id, metadataBytes);
+ return 0;
}
return -EINVAL;
}
-Inode IncrementalService::makeDir(StorageId storageId, std::string_view pathUnderStorage,
- std::string_view metadata) {
- auto [parentInode, name] = parentAndNameFor(storageId, pathUnderStorage);
- if (parentInode < 0) {
- return -EINVAL;
- }
+int IncrementalService::makeDir(StorageId storageId, std::string_view path, int mode) {
if (auto ifs = getIfs(storageId)) {
- return mIncFs->makeDir(ifs->control, name, parentInode, metadata);
+ return mIncFs->makeDir(ifs->control, path, mode);
}
return -EINVAL;
}
-Inode IncrementalService::makeDirs(StorageId storageId, std::string_view pathUnderStorage,
- std::string_view metadata) {
+int IncrementalService::makeDirs(StorageId storageId, std::string_view path, int mode) {
const auto ifs = getIfs(storageId);
if (!ifs) {
return -EINVAL;
}
- std::string_view parentDir(pathUnderStorage);
- auto p = parentAndNameFor(storageId, pathUnderStorage);
- std::stack<std::string> pathsToCreate;
- while (p.first < 0) {
- parentDir = path::dirname(parentDir);
- pathsToCreate.emplace(parentDir);
- p = parentAndNameFor(storageId, parentDir);
- }
- Inode inode;
- while (!pathsToCreate.empty()) {
- p = parentAndNameFor(storageId, pathsToCreate.top());
- pathsToCreate.pop();
- inode = mIncFs->makeDir(ifs->control, p.second, p.first, metadata);
- if (inode < 0) {
- return inode;
- }
+
+ auto err = mIncFs->makeDir(ifs->control, path, mode);
+ if (err == -EEXIST) {
+ return 0;
+ } else if (err != -ENOENT) {
+ return err;
+ }
+ if (auto err = makeDirs(storageId, path::dirname(path), mode)) {
+ return err;
}
- return mIncFs->makeDir(ifs->control, path::basename(pathUnderStorage), inode, metadata);
+ return mIncFs->makeDir(ifs->control, path, mode);
}
-int IncrementalService::link(StorageId storage, Inode item, Inode newParent,
- std::string_view newName) {
- if (auto ifs = getIfs(storage)) {
- return mIncFs->link(ifs->control, item, newParent, newName);
+int IncrementalService::link(StorageId sourceStorageId, std::string_view oldPath,
+ StorageId destStorageId, std::string_view newPath) {
+ if (auto ifsSrc = getIfs(sourceStorageId), ifsDest = getIfs(destStorageId);
+ ifsSrc && ifsSrc == ifsDest) {
+ return mIncFs->link(ifsSrc->control, oldPath, newPath);
}
return -EINVAL;
}
-int IncrementalService::unlink(StorageId storage, Inode parent, std::string_view name) {
+int IncrementalService::unlink(StorageId storage, std::string_view path) {
if (auto ifs = getIfs(storage)) {
- return mIncFs->unlink(ifs->control, parent, name);
+ return mIncFs->unlink(ifs->control, path);
}
return -EINVAL;
}
-int IncrementalService::addBindMount(IncFsMount& ifs, StorageId storage, std::string&& sourceSubdir,
+int IncrementalService::addBindMount(IncFsMount& ifs, StorageId storage,
+ std::string_view storageRoot, std::string&& source,
std::string&& target, BindKind kind,
std::unique_lock<std::mutex>& mainLock) {
if (!isValidMountTarget(target)) {
@@ -694,30 +741,30 @@ int IncrementalService::addBindMount(IncFsMount& ifs, StorageId storage, std::st
metadata::BindPoint bp;
bp.set_storage_id(storage);
bp.set_allocated_dest_path(&target);
- bp.set_allocated_source_subdir(&sourceSubdir);
+ bp.set_source_subdir(std::string(path::relativize(storageRoot, source)));
const auto metadata = bp.SerializeAsString();
- bp.release_source_subdir();
bp.release_dest_path();
mdFileName = makeBindMdName();
- auto node = mIncFs->makeFile(ifs.control, mdFileName, INCFS_ROOT_INODE, 0, metadata);
- if (node < 0) {
+ auto node =
+ mIncFs->makeFile(ifs.control, path::join(ifs.root, constants().mount, mdFileName),
+ 0444, idFromMetadata(metadata),
+ {.metadata = {metadata.data(), (IncFsSize)metadata.size()}});
+ if (node) {
return int(node);
}
}
- return addBindMountWithMd(ifs, storage, std::move(mdFileName), std::move(sourceSubdir),
+ return addBindMountWithMd(ifs, storage, std::move(mdFileName), std::move(source),
std::move(target), kind, mainLock);
}
int IncrementalService::addBindMountWithMd(IncrementalService::IncFsMount& ifs, StorageId storage,
- std::string&& metadataName, std::string&& sourceSubdir,
+ std::string&& metadataName, std::string&& source,
std::string&& target, BindKind kind,
std::unique_lock<std::mutex>& mainLock) {
- LOG(INFO) << "Adding bind mount: " << sourceSubdir << " -> " << target;
{
- auto path = path::join(ifs.root, constants().mount, sourceSubdir);
std::lock_guard l(mMountOperationLock);
- const auto status = mVold->bindMount(path, target);
+ const auto status = mVold->bindMount(source, target);
if (!status.isOk()) {
LOG(ERROR) << "Calling Vold::bindMount() failed: " << status.toString8();
return status.exceptionCode() == binder::Status::EX_SERVICE_SPECIFIC
@@ -736,12 +783,12 @@ int IncrementalService::addBindMountWithMd(IncrementalService::IncFsMount& ifs,
const auto [it, _] =
ifs.bindPoints.insert_or_assign(target,
IncFsMount::Bind{storage, std::move(metadataName),
- std::move(sourceSubdir), kind});
+ std::move(source), kind});
mBindsByPath[std::move(target)] = it;
return 0;
}
-RawMetadata IncrementalService::getMetadata(StorageId storage, Inode node) const {
+RawMetadata IncrementalService::getMetadata(StorageId storage, FileId node) const {
const auto ifs = getIfs(storage);
if (!ifs) {
return {};
@@ -831,21 +878,18 @@ bool IncrementalService::mountExistingImage(std::string_view root, std::string_v
LOG(INFO) << "Trying to mount: " << key;
auto mountTarget = path::join(root, constants().mount);
- const auto image = path::join(root, constants().backing, constants().image);
+ const auto backing = path::join(root, constants().backing);
IncFsMount::Control control;
IncrementalFileSystemControlParcel controlParcel;
- auto status = mVold->mountIncFs(image, mountTarget, 0, &controlParcel);
+ auto status = mVold->mountIncFs(backing, mountTarget, 0, &controlParcel);
if (!status.isOk()) {
LOG(ERROR) << "Vold::mountIncFs() failed: " << status.toString8();
return false;
}
- if (controlParcel.cmd) {
- control.cmdFd = controlParcel.cmd->release();
- }
- if (controlParcel.log) {
- control.logFd = controlParcel.log->release();
- }
+ control.cmd = controlParcel.cmd.release().release();
+ control.pendingReads = controlParcel.pendingReads.release().release();
+ control.logs = controlParcel.log.release().release();
auto ifs = std::make_shared<IncFsMount>(std::string(root), -1, std::move(control), *this);
@@ -860,8 +904,7 @@ bool IncrementalService::mountExistingImage(std::string_view root, std::string_v
mNextId = std::max(mNextId, ifs->mountId + 1);
std::vector<std::pair<std::string, metadata::BindPoint>> bindPoints;
- auto d = std::unique_ptr<DIR, decltype(&::closedir)>(::opendir(path::c_str(mountTarget)),
- ::closedir);
+ auto d = openDir(path::c_str(mountTarget));
while (auto e = ::readdir(d.get())) {
if (e->d_type == DT_REG) {
auto name = std::string_view(e->d_name);
@@ -874,7 +917,7 @@ bool IncrementalService::mountExistingImage(std::string_view root, std::string_v
if (bindPoints.back().second.dest_path().empty() ||
bindPoints.back().second.source_subdir().empty()) {
bindPoints.pop_back();
- mIncFs->unlink(ifs->control, INCFS_ROOT_INODE, name);
+ mIncFs->unlink(ifs->control, path::join(ifs->root, constants().mount, name));
}
}
} else if (e->d_type == DT_DIR) {
@@ -891,9 +934,7 @@ bool IncrementalService::mountExistingImage(std::string_view root, std::string_v
<< " for mount " << root;
continue;
}
- ifs->storages.insert_or_assign(md.id(),
- IncFsMount::Storage{std::string(name),
- Inode(e->d_ino)});
+ ifs->storages.insert_or_assign(md.id(), IncFsMount::Storage{std::string(name)});
mNextId = std::max(mNextId, md.id() + 1);
}
}
@@ -973,10 +1014,10 @@ bool IncrementalService::prepareDataLoader(IncrementalService::IncFsMount& ifs,
}
FileSystemControlParcel fsControlParcel;
fsControlParcel.incremental = std::make_unique<IncrementalFileSystemControlParcel>();
- fsControlParcel.incremental->cmd =
- std::make_unique<os::ParcelFileDescriptor>(base::unique_fd(::dup(ifs.control.cmdFd)));
- fsControlParcel.incremental->log =
- std::make_unique<os::ParcelFileDescriptor>(base::unique_fd(::dup(ifs.control.logFd)));
+ fsControlParcel.incremental->cmd.reset(base::unique_fd(::dup(ifs.control.cmd)));
+ fsControlParcel.incremental->pendingReads.reset(
+ base::unique_fd(::dup(ifs.control.pendingReads)));
+ fsControlParcel.incremental->log.reset(base::unique_fd(::dup(ifs.control.logs)));
sp<IncrementalDataLoaderListener> listener = new IncrementalDataLoaderListener(*this);
bool created = false;
auto status = mIncrementalManager->prepareDataLoader(ifs.mountId, fsControlParcel, *dlp,
diff --git a/services/incremental/IncrementalService.h b/services/incremental/IncrementalService.h
index a03ffa00d035..ca5e4dbd9231 100644
--- a/services/incremental/IncrementalService.h
+++ b/services/incremental/IncrementalService.h
@@ -52,16 +52,16 @@ namespace android::incremental {
using MountId = int;
using StorageId = int;
-using Inode = incfs::Inode;
+using FileId = incfs::FileId;
using BlockIndex = incfs::BlockIndex;
using RawMetadata = incfs::RawMetadata;
using Clock = std::chrono::steady_clock;
using TimePoint = std::chrono::time_point<Clock>;
using Seconds = std::chrono::seconds;
-class IncrementalService {
+class IncrementalService final {
public:
- explicit IncrementalService(const ServiceManagerWrapper& sm, std::string_view rootDir);
+ explicit IncrementalService(ServiceManagerWrapper&& sm, std::string_view rootDir);
#pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Wnon-virtual-dtor"
@@ -85,6 +85,11 @@ public:
Permanent = 1,
};
+ static FileId idFromMetadata(std::span<const uint8_t> metadata);
+ static inline FileId idFromMetadata(std::span<const char> metadata) {
+ return idFromMetadata({(const uint8_t*)metadata.data(), metadata.size()});
+ }
+
std::optional<std::future<void>> onSystemReady();
StorageId createStorage(std::string_view mountPoint,
@@ -92,30 +97,31 @@ public:
CreateOptions options = CreateOptions::Default);
StorageId createLinkedStorage(std::string_view mountPoint, StorageId linkedStorage,
CreateOptions options = CreateOptions::Default);
- StorageId openStorage(std::string_view pathInMount);
+ StorageId openStorage(std::string_view path);
- Inode nodeFor(StorageId storage, std::string_view subpath) const;
- std::pair<Inode, std::string_view> parentAndNameFor(StorageId storage,
- std::string_view subpath) const;
+ FileId nodeFor(StorageId storage, std::string_view path) const;
+ std::pair<FileId, std::string_view> parentAndNameFor(StorageId storage,
+ std::string_view path) const;
- int bind(StorageId storage, std::string_view subdir, std::string_view target, BindKind kind);
+ int bind(StorageId storage, std::string_view source, std::string_view target, BindKind kind);
int unbind(StorageId storage, std::string_view target);
void deleteStorage(StorageId storage);
- Inode makeFile(StorageId storage, std::string_view name, long size, std::string_view metadata,
- std::string_view signature);
- Inode makeDir(StorageId storage, std::string_view name, std::string_view metadata = {});
- Inode makeDirs(StorageId storage, std::string_view name, std::string_view metadata = {});
+ int makeFile(StorageId storage, std::string_view path, int mode, FileId id,
+ incfs::NewFileParams params);
+ int makeDir(StorageId storage, std::string_view path, int mode = 0555);
+ int makeDirs(StorageId storage, std::string_view path, int mode = 0555);
- int link(StorageId storage, Inode item, Inode newParent, std::string_view newName);
- int unlink(StorageId storage, Inode parent, std::string_view name);
+ int link(StorageId sourceStorageId, std::string_view oldPath, StorageId destStorageId,
+ std::string_view newPath);
+ int unlink(StorageId storage, std::string_view path);
- bool isRangeLoaded(StorageId storage, Inode file, std::pair<BlockIndex, BlockIndex> range) {
+ bool isRangeLoaded(StorageId storage, FileId file, std::pair<BlockIndex, BlockIndex> range) {
return false;
}
- RawMetadata getMetadata(StorageId storage, Inode node) const;
- std::string getSigngatureData(StorageId storage, Inode node) const { return {}; }
+ RawMetadata getMetadata(StorageId storage, FileId node) const;
+ std::string getSignatureData(StorageId storage, FileId node) const;
std::vector<std::string> listFiles(StorageId storage) const;
bool startLoading(StorageId storage) const;
@@ -142,19 +148,9 @@ private:
struct Storage {
std::string name;
- Inode node;
};
- struct Control {
- operator IncFsControl() const { return {cmdFd, logFd}; }
- void reset() {
- cmdFd.reset();
- logFd.reset();
- }
-
- base::unique_fd cmdFd;
- base::unique_fd logFd;
- };
+ using Control = incfs::UniqueControl;
using BindMap = std::map<std::string, Bind>;
using StorageMap = std::unordered_map<StorageId, Storage>;
@@ -196,11 +192,12 @@ private:
IfsMountPtr getIfs(StorageId storage) const;
const IfsMountPtr& getIfsLocked(StorageId storage) const;
- int addBindMount(IncFsMount& ifs, StorageId storage, std::string&& sourceSubdir,
- std::string&& target, BindKind kind, std::unique_lock<std::mutex>& mainLock);
+ int addBindMount(IncFsMount& ifs, StorageId storage, std::string_view storageRoot,
+ std::string&& source, std::string&& target, BindKind kind,
+ std::unique_lock<std::mutex>& mainLock);
int addBindMountWithMd(IncFsMount& ifs, StorageId storage, std::string&& metadataName,
- std::string&& sourceSubdir, std::string&& target, BindKind kind,
+ std::string&& source, std::string&& target, BindKind kind,
std::unique_lock<std::mutex>& mainLock);
bool prepareDataLoader(IncFsMount& ifs, DataLoaderParamsParcel* params);
@@ -212,10 +209,9 @@ private:
MountMap::iterator getStorageSlotLocked();
// Member variables
- // These are shared pointers for the sake of unit testing
- std::shared_ptr<VoldServiceWrapper> mVold;
- std::shared_ptr<IncrementalManagerWrapper> mIncrementalManager;
- std::shared_ptr<IncFsWrapper> mIncFs;
+ std::unique_ptr<VoldServiceWrapper> mVold;
+ std::unique_ptr<IncrementalManagerWrapper> mIncrementalManager;
+ std::unique_ptr<IncFsWrapper> mIncFs;
const std::string mIncrementalDir;
mutable std::mutex mLock;
diff --git a/services/incremental/ServiceWrappers.cpp b/services/incremental/ServiceWrappers.cpp
index a79b26ae4fb3..5d978a1cf741 100644
--- a/services/incremental/ServiceWrappers.cpp
+++ b/services/incremental/ServiceWrappers.cpp
@@ -16,14 +16,8 @@
#include "ServiceWrappers.h"
-#include <android-base/strings.h>
-#include <android-base/unique_fd.h>
-#include <binder/IServiceManager.h>
#include <utils/String16.h>
-#include <string>
-#include <string_view>
-
using namespace std::literals;
namespace android::os::incremental {
@@ -31,37 +25,38 @@ namespace android::os::incremental {
static constexpr auto kVoldServiceName = "vold"sv;
static constexpr auto kIncrementalManagerName = "incremental"sv;
-RealServiceManager::RealServiceManager(const sp<IServiceManager>& serviceManager)
- : mServiceManager(serviceManager) {}
+RealServiceManager::RealServiceManager(sp<IServiceManager> serviceManager)
+ : mServiceManager(std::move(serviceManager)) {}
template <class INTERFACE>
sp<INTERFACE> RealServiceManager::getRealService(std::string_view serviceName) const {
- sp<IBinder> binder = mServiceManager->getService(String16(serviceName.data()));
- if (binder == 0) {
- return 0;
+ sp<IBinder> binder =
+ mServiceManager->getService(String16(serviceName.data(), serviceName.size()));
+ if (!binder) {
+ return nullptr;
}
return interface_cast<INTERFACE>(binder);
}
-std::shared_ptr<VoldServiceWrapper> RealServiceManager::getVoldService() const {
+std::unique_ptr<VoldServiceWrapper> RealServiceManager::getVoldService() {
sp<os::IVold> vold = RealServiceManager::getRealService<os::IVold>(kVoldServiceName);
if (vold != 0) {
- return std::make_shared<RealVoldService>(vold);
+ return std::make_unique<RealVoldService>(vold);
}
return nullptr;
}
-std::shared_ptr<IncrementalManagerWrapper> RealServiceManager::getIncrementalManager() const {
+std::unique_ptr<IncrementalManagerWrapper> RealServiceManager::getIncrementalManager() {
sp<IIncrementalManager> manager =
RealServiceManager::getRealService<IIncrementalManager>(kIncrementalManagerName);
- if (manager != 0) {
- return std::make_shared<RealIncrementalManager>(manager);
+ if (manager) {
+ return std::make_unique<RealIncrementalManager>(manager);
}
return nullptr;
}
-std::shared_ptr<IncFsWrapper> RealServiceManager::getIncFs() const {
- return std::make_shared<RealIncFs>();
+std::unique_ptr<IncFsWrapper> RealServiceManager::getIncFs() {
+ return std::make_unique<RealIncFs>();
}
} // namespace android::os::incremental
diff --git a/services/incremental/ServiceWrappers.h b/services/incremental/ServiceWrappers.h
index 570458283ac9..ae3739dba2f0 100644
--- a/services/incremental/ServiceWrappers.h
+++ b/services/incremental/ServiceWrappers.h
@@ -26,6 +26,7 @@
#include <binder/IServiceManager.h>
#include <incfs.h>
+#include <memory>
#include <string>
#include <string_view>
@@ -36,10 +37,12 @@ namespace android::os::incremental {
// --- Wrapper interfaces ---
+using MountId = int32_t;
+
class VoldServiceWrapper {
public:
- virtual ~VoldServiceWrapper(){};
- virtual binder::Status mountIncFs(const std::string& imagePath, const std::string& targetDir,
+ virtual ~VoldServiceWrapper() = default;
+ virtual binder::Status mountIncFs(const std::string& backingPath, const std::string& targetDir,
int32_t flags,
IncrementalFileSystemControlParcel* _aidl_return) const = 0;
virtual binder::Status unmountIncFs(const std::string& dir) const = 0;
@@ -49,52 +52,52 @@ public:
class IncrementalManagerWrapper {
public:
- virtual ~IncrementalManagerWrapper() {}
- virtual binder::Status prepareDataLoader(
- int32_t mountId, const FileSystemControlParcel& control,
- const DataLoaderParamsParcel& params,
- const sp<IDataLoaderStatusListener>& listener,
- bool* _aidl_return) const = 0;
- virtual binder::Status startDataLoader(int32_t mountId, bool* _aidl_return) const = 0;
- virtual binder::Status destroyDataLoader(int32_t mountId) const = 0;
- virtual binder::Status newFileForDataLoader(int32_t mountId, int64_t inode,
- const ::std::vector<uint8_t>& metadata) const = 0;
- virtual binder::Status showHealthBlockedUI(int32_t mountId) const = 0;
+ virtual ~IncrementalManagerWrapper() = default;
+ virtual binder::Status prepareDataLoader(MountId mountId,
+ const FileSystemControlParcel& control,
+ const DataLoaderParamsParcel& params,
+ const sp<IDataLoaderStatusListener>& listener,
+ bool* _aidl_return) const = 0;
+ virtual binder::Status startDataLoader(MountId mountId, bool* _aidl_return) const = 0;
+ virtual binder::Status destroyDataLoader(MountId mountId) const = 0;
+ virtual binder::Status newFileForDataLoader(MountId mountId, FileId fileid,
+ const std::vector<uint8_t>& metadata) const = 0;
+ virtual binder::Status showHealthBlockedUI(MountId mountId) const = 0;
};
class IncFsWrapper {
public:
- virtual ~IncFsWrapper() {}
- virtual Inode makeFile(Control control, std::string_view name, Inode parent, Size size,
- std::string_view metadata) const = 0;
- virtual Inode makeDir(Control control, std::string_view name, Inode parent,
- std::string_view metadata, int mode = 0555) const = 0;
- virtual RawMetadata getMetadata(Control control, Inode inode) const = 0;
- virtual ErrorCode link(Control control, Inode item, Inode targetParent,
- std::string_view name) const = 0;
- virtual ErrorCode unlink(Control control, Inode parent, std::string_view name) const = 0;
- virtual ErrorCode writeBlocks(Control control, const incfs_new_data_block blocks[],
- int blocksCount) const = 0;
+ virtual ~IncFsWrapper() = default;
+ virtual ErrorCode makeFile(Control control, std::string_view path, int mode, FileId id,
+ NewFileParams params) const = 0;
+ virtual ErrorCode makeDir(Control control, std::string_view path, int mode = 0555) const = 0;
+ virtual RawMetadata getMetadata(Control control, FileId fileid) const = 0;
+ virtual RawMetadata getMetadata(Control control, std::string_view path) const = 0;
+ virtual FileId getFileId(Control control, std::string_view path) const = 0;
+ virtual ErrorCode link(Control control, std::string_view from, std::string_view to) const = 0;
+ virtual ErrorCode unlink(Control control, std::string_view path) const = 0;
+ virtual base::unique_fd openWrite(Control control, FileId id) const = 0;
+ virtual ErrorCode writeBlocks(std::span<const DataBlock> blocks) const = 0;
};
class ServiceManagerWrapper {
public:
- virtual ~ServiceManagerWrapper() {}
- virtual std::shared_ptr<VoldServiceWrapper> getVoldService() const = 0;
- virtual std::shared_ptr<IncrementalManagerWrapper> getIncrementalManager() const = 0;
- virtual std::shared_ptr<IncFsWrapper> getIncFs() const = 0;
+ virtual ~ServiceManagerWrapper() = default;
+ virtual std::unique_ptr<VoldServiceWrapper> getVoldService() = 0;
+ virtual std::unique_ptr<IncrementalManagerWrapper> getIncrementalManager() = 0;
+ virtual std::unique_ptr<IncFsWrapper> getIncFs() = 0;
};
// --- Real stuff ---
class RealVoldService : public VoldServiceWrapper {
public:
- RealVoldService(const sp<os::IVold> vold) : mInterface(vold) {}
+ RealVoldService(const sp<os::IVold> vold) : mInterface(std::move(vold)) {}
~RealVoldService() = default;
- binder::Status mountIncFs(const std::string& imagePath, const std::string& targetDir,
+ binder::Status mountIncFs(const std::string& backingPath, const std::string& targetDir,
int32_t flags,
IncrementalFileSystemControlParcel* _aidl_return) const override {
- return mInterface->mountIncFs(imagePath, targetDir, flags, _aidl_return);
+ return mInterface->mountIncFs(backingPath, targetDir, flags, _aidl_return);
}
binder::Status unmountIncFs(const std::string& dir) const override {
return mInterface->unmountIncFs(dir);
@@ -113,24 +116,26 @@ public:
RealIncrementalManager(const sp<os::incremental::IIncrementalManager> manager)
: mInterface(manager) {}
~RealIncrementalManager() = default;
- binder::Status prepareDataLoader(
- int32_t mountId, const FileSystemControlParcel& control,
- const DataLoaderParamsParcel& params,
- const sp<IDataLoaderStatusListener>& listener,
- bool* _aidl_return) const override {
+ binder::Status prepareDataLoader(MountId mountId, const FileSystemControlParcel& control,
+ const DataLoaderParamsParcel& params,
+ const sp<IDataLoaderStatusListener>& listener,
+ bool* _aidl_return) const override {
return mInterface->prepareDataLoader(mountId, control, params, listener, _aidl_return);
}
- binder::Status startDataLoader(int32_t mountId, bool* _aidl_return) const override {
+ binder::Status startDataLoader(MountId mountId, bool* _aidl_return) const override {
return mInterface->startDataLoader(mountId, _aidl_return);
}
- binder::Status destroyDataLoader(int32_t mountId) const override {
+ binder::Status destroyDataLoader(MountId mountId) const override {
return mInterface->destroyDataLoader(mountId);
}
- binder::Status newFileForDataLoader(int32_t mountId, int64_t inode,
- const ::std::vector<uint8_t>& metadata) const override {
- return mInterface->newFileForDataLoader(mountId, inode, metadata);
+ binder::Status newFileForDataLoader(MountId mountId, FileId fileid,
+ const std::vector<uint8_t>& metadata) const override {
+ return mInterface->newFileForDataLoader(mountId,
+ {(const uint8_t*)fileid.data,
+ (const uint8_t*)fileid.data + sizeof(fileid.data)},
+ metadata);
}
- binder::Status showHealthBlockedUI(int32_t mountId) const override {
+ binder::Status showHealthBlockedUI(MountId mountId) const override {
return mInterface->showHealthBlockedUI(mountId);
}
@@ -140,11 +145,11 @@ private:
class RealServiceManager : public ServiceManagerWrapper {
public:
- RealServiceManager(const sp<IServiceManager>& serviceManager);
+ RealServiceManager(sp<IServiceManager> serviceManager);
~RealServiceManager() = default;
- std::shared_ptr<VoldServiceWrapper> getVoldService() const override;
- std::shared_ptr<IncrementalManagerWrapper> getIncrementalManager() const override;
- std::shared_ptr<IncFsWrapper> getIncFs() const override;
+ std::unique_ptr<VoldServiceWrapper> getVoldService() override;
+ std::unique_ptr<IncrementalManagerWrapper> getIncrementalManager() override;
+ std::unique_ptr<IncFsWrapper> getIncFs() override;
private:
template <class INTERFACE>
@@ -156,27 +161,33 @@ class RealIncFs : public IncFsWrapper {
public:
RealIncFs() = default;
~RealIncFs() = default;
- Inode makeFile(Control control, std::string_view name, Inode parent, Size size,
- std::string_view metadata) const override {
- return incfs::makeFile(control, name, parent, size, metadata);
+ ErrorCode makeFile(Control control, std::string_view path, int mode, FileId id,
+ NewFileParams params) const override {
+ return incfs::makeFile(control, path, mode, id, params);
+ }
+ ErrorCode makeDir(Control control, std::string_view path, int mode) const override {
+ return incfs::makeDir(control, path, mode);
+ }
+ RawMetadata getMetadata(Control control, FileId fileid) const override {
+ return incfs::getMetadata(control, fileid);
+ }
+ RawMetadata getMetadata(Control control, std::string_view path) const override {
+ return incfs::getMetadata(control, path);
}
- Inode makeDir(Control control, std::string_view name, Inode parent, std::string_view metadata,
- int mode) const override {
- return incfs::makeDir(control, name, parent, metadata, mode);
+ FileId getFileId(Control control, std::string_view path) const override {
+ return incfs::getFileId(control, path);
}
- RawMetadata getMetadata(Control control, Inode inode) const override {
- return incfs::getMetadata(control, inode);
+ ErrorCode link(Control control, std::string_view from, std::string_view to) const override {
+ return incfs::link(control, from, to);
}
- ErrorCode link(Control control, Inode item, Inode targetParent,
- std::string_view name) const override {
- return incfs::link(control, item, targetParent, name);
+ ErrorCode unlink(Control control, std::string_view path) const override {
+ return incfs::unlink(control, path);
}
- ErrorCode unlink(Control control, Inode parent, std::string_view name) const override {
- return incfs::unlink(control, parent, name);
+ base::unique_fd openWrite(Control control, FileId id) const override {
+ return base::unique_fd{incfs::openWrite(control, id)};
}
- ErrorCode writeBlocks(Control control, const incfs_new_data_block blocks[],
- int blocksCount) const override {
- return incfs::writeBlocks(control, blocks, blocksCount);
+ ErrorCode writeBlocks(std::span<const DataBlock> blocks) const override {
+ return incfs::writeBlocks(blocks);
}
};
diff --git a/services/incremental/path.cpp b/services/incremental/path.cpp
index c529d61abd15..0d86f2a984d6 100644
--- a/services/incremental/path.cpp
+++ b/services/incremental/path.cpp
@@ -44,16 +44,45 @@ bool PathLess::operator()(std::string_view l, std::string_view r) const {
PathCharsLess());
}
+static void preparePathComponent(std::string_view path, bool trimFront) {
+ if (trimFront) {
+ while (!path.empty() && path.front() == '/') {
+ path.remove_prefix(1);
+ }
+ }
+ while (!path.empty() && path.back() == '/') {
+ path.remove_suffix(1);
+ }
+}
+
void details::append_next_path(std::string& target, std::string_view path) {
+ preparePathComponent(path, true);
if (path.empty()) {
return;
}
- if (!target.empty()) {
+ if (!target.empty() && !target.ends_with('/')) {
target.push_back('/');
}
target += path;
}
+std::string_view relativize(std::string_view parent, std::string_view nested) {
+ if (!nested.starts_with(parent)) {
+ return nested;
+ }
+ if (nested.size() == parent.size()) {
+ return {};
+ }
+ if (nested[parent.size()] != '/') {
+ return nested;
+ }
+ auto relative = nested.substr(parent.size());
+ while (relative.front() == '/') {
+ relative.remove_prefix(1);
+ }
+ return relative;
+}
+
bool isAbsolute(std::string_view path) {
return !path.empty() && path[0] == '/';
}
diff --git a/services/incremental/path.h b/services/incremental/path.h
index a1f4b8ec3093..3e5fd21b6535 100644
--- a/services/incremental/path.h
+++ b/services/incremental/path.h
@@ -67,6 +67,20 @@ inline details::CStrWrapper c_str(std::string_view sv) {
return {sv};
}
+std::string_view relativize(std::string_view parent, std::string_view nested);
+inline std::string_view relativize(const char* parent, const char* nested) {
+ return relativize(std::string_view(parent), std::string_view(nested));
+}
+inline std::string_view relativize(std::string_view parent, const char* nested) {
+ return relativize(parent, std::string_view(nested));
+}
+inline std::string_view relativize(const char* parent, std::string_view nested) {
+ return relativize(std::string_view(parent), nested);
+}
+
+std::string_view relativize(std::string&& parent, std::string_view nested) = delete;
+std::string_view relativize(std::string_view parent, std::string&& nested) = delete;
+
bool isAbsolute(std::string_view path);
std::string normalize(std::string_view path);
std::string_view dirname(std::string_view path);
diff --git a/services/incremental/test/IncrementalServiceTest.cpp b/services/incremental/test/IncrementalServiceTest.cpp
index ca1e1a972047..28268181f173 100644
--- a/services/incremental/test/IncrementalServiceTest.cpp
+++ b/services/incremental/test/IncrementalServiceTest.cpp
@@ -46,7 +46,7 @@ namespace android::os::incremental {
class MockVoldService : public VoldServiceWrapper {
public:
MOCK_CONST_METHOD4(mountIncFs,
- binder::Status(const std::string& imagePath, const std::string& targetDir,
+ binder::Status(const std::string& backingPath, const std::string& targetDir,
int32_t flags,
IncrementalFileSystemControlParcel* _aidl_return));
MOCK_CONST_METHOD1(unmountIncFs, binder::Status(const std::string& dir));
@@ -77,22 +77,20 @@ public:
binder::Status getInvalidControlParcel(const std::string& imagePath,
const std::string& targetDir, int32_t flags,
IncrementalFileSystemControlParcel* _aidl_return) {
- _aidl_return->cmd = nullptr;
- _aidl_return->log = nullptr;
+ _aidl_return = {};
return binder::Status::ok();
}
binder::Status incFsSuccess(const std::string& imagePath, const std::string& targetDir,
int32_t flags, IncrementalFileSystemControlParcel* _aidl_return) {
- _aidl_return->cmd = std::make_unique<os::ParcelFileDescriptor>(std::move(cmdFd));
- _aidl_return->log = std::make_unique<os::ParcelFileDescriptor>(std::move(logFd));
+ _aidl_return->pendingReads.reset(base::unique_fd(dup(STDIN_FILENO)));
+ _aidl_return->cmd.reset(base::unique_fd(dup(STDIN_FILENO)));
+ _aidl_return->log.reset(base::unique_fd(dup(STDIN_FILENO)));
return binder::Status::ok();
}
private:
TemporaryFile cmdFile;
TemporaryFile logFile;
- base::unique_fd cmdFd;
- base::unique_fd logFd;
};
class MockIncrementalManager : public IncrementalManagerWrapper {
@@ -105,7 +103,7 @@ public:
MOCK_CONST_METHOD2(startDataLoader, binder::Status(int32_t mountId, bool* _aidl_return));
MOCK_CONST_METHOD1(destroyDataLoader, binder::Status(int32_t mountId));
MOCK_CONST_METHOD3(newFileForDataLoader,
- binder::Status(int32_t mountId, int64_t inode,
+ binder::Status(int32_t mountId, FileId fileId,
const ::std::vector<uint8_t>& metadata));
MOCK_CONST_METHOD1(showHealthBlockedUI, binder::Status(int32_t mountId));
@@ -152,23 +150,21 @@ private:
class MockIncFs : public IncFsWrapper {
public:
MOCK_CONST_METHOD5(makeFile,
- Inode(Control control, std::string_view name, Inode parent, Size size,
- std::string_view metadata));
- MOCK_CONST_METHOD5(makeDir,
- Inode(Control control, std::string_view name, Inode parent,
- std::string_view metadata, int mode));
- MOCK_CONST_METHOD2(getMetadata, RawMetadata(Control control, Inode inode));
- MOCK_CONST_METHOD4(link,
- ErrorCode(Control control, Inode item, Inode targetParent,
- std::string_view name));
- MOCK_CONST_METHOD3(unlink, ErrorCode(Control control, Inode parent, std::string_view name));
- MOCK_CONST_METHOD3(writeBlocks,
- ErrorCode(Control control, const incfs_new_data_block blocks[],
- int blocksCount));
+ ErrorCode(Control control, std::string_view path, int mode, FileId id,
+ NewFileParams params));
+ MOCK_CONST_METHOD3(makeDir, ErrorCode(Control control, std::string_view path, int mode));
+ MOCK_CONST_METHOD2(getMetadata, RawMetadata(Control control, FileId fileid));
+ MOCK_CONST_METHOD2(getMetadata, RawMetadata(Control control, std::string_view path));
+ MOCK_CONST_METHOD2(getFileId, FileId(Control control, std::string_view path));
+ MOCK_CONST_METHOD3(link,
+ ErrorCode(Control control, std::string_view from, std::string_view to));
+ MOCK_CONST_METHOD2(unlink, ErrorCode(Control control, std::string_view path));
+ MOCK_CONST_METHOD2(openWrite, base::unique_fd(Control control, FileId id));
+ MOCK_CONST_METHOD1(writeBlocks, ErrorCode(std::span<const DataBlock> blocks));
void makeFileFails() { ON_CALL(*this, makeFile(_, _, _, _, _)).WillByDefault(Return(-1)); }
void makeFileSuccess() { ON_CALL(*this, makeFile(_, _, _, _, _)).WillByDefault(Return(0)); }
- RawMetadata getMountInfoMetadata(Control control, Inode inode) {
+ RawMetadata getMountInfoMetadata(Control control, std::string_view path) {
metadata::Mount m;
m.mutable_storage()->set_id(100);
m.mutable_loader()->set_package_name("com.test");
@@ -176,15 +172,15 @@ public:
const auto metadata = m.SerializeAsString();
m.mutable_loader()->release_arguments();
m.mutable_loader()->release_package_name();
- return std::vector<char>(metadata.begin(), metadata.end());
+ return {metadata.begin(), metadata.end()};
}
- RawMetadata getStorageMetadata(Control control, Inode inode) {
+ RawMetadata getStorageMetadata(Control control, std::string_view path) {
metadata::Storage st;
st.set_id(100);
auto metadata = st.SerializeAsString();
- return std::vector<char>(metadata.begin(), metadata.end());
+ return {metadata.begin(), metadata.end()};
}
- RawMetadata getBindPointMetadata(Control control, Inode inode) {
+ RawMetadata getBindPointMetadata(Control control, std::string_view path) {
metadata::BindPoint bp;
std::string destPath = "dest";
std::string srcPath = "src";
@@ -200,40 +196,41 @@ public:
class MockServiceManager : public ServiceManagerWrapper {
public:
- MockServiceManager(std::shared_ptr<MockVoldService> vold,
- std::shared_ptr<MockIncrementalManager> manager,
- std::shared_ptr<MockIncFs> incfs)
- : mVold(vold), mIncrementalManager(manager), mIncFs(incfs) {}
- std::shared_ptr<VoldServiceWrapper> getVoldService() const override { return mVold; }
- std::shared_ptr<IncrementalManagerWrapper> getIncrementalManager() const override {
- return mIncrementalManager;
+ MockServiceManager(std::unique_ptr<MockVoldService> vold,
+ std::unique_ptr<MockIncrementalManager> manager,
+ std::unique_ptr<MockIncFs> incfs)
+ : mVold(std::move(vold)),
+ mIncrementalManager(std::move(manager)),
+ mIncFs(std::move(incfs)) {}
+ std::unique_ptr<VoldServiceWrapper> getVoldService() final { return std::move(mVold); }
+ std::unique_ptr<IncrementalManagerWrapper> getIncrementalManager() final {
+ return std::move(mIncrementalManager);
}
- std::shared_ptr<IncFsWrapper> getIncFs() const override { return mIncFs; }
+ std::unique_ptr<IncFsWrapper> getIncFs() final { return std::move(mIncFs); }
private:
- std::shared_ptr<MockVoldService> mVold;
- std::shared_ptr<MockIncrementalManager> mIncrementalManager;
- std::shared_ptr<MockIncFs> mIncFs;
+ std::unique_ptr<MockVoldService> mVold;
+ std::unique_ptr<MockIncrementalManager> mIncrementalManager;
+ std::unique_ptr<MockIncFs> mIncFs;
};
// --- IncrementalServiceTest ---
-static Inode inode(std::string_view path) {
- struct stat st;
- if (::stat(path::c_str(path), &st)) {
- return -1;
- }
- return st.st_ino;
-}
-
class IncrementalServiceTest : public testing::Test {
public:
void SetUp() override {
- mVold = std::make_shared<NiceMock<MockVoldService>>();
- mIncrementalManager = std::make_shared<NiceMock<MockIncrementalManager>>();
- mIncFs = std::make_shared<NiceMock<MockIncFs>>();
- MockServiceManager serviceManager = MockServiceManager(mVold, mIncrementalManager, mIncFs);
- mIncrementalService = std::make_unique<IncrementalService>(serviceManager, mRootDir.path);
+ auto vold = std::make_unique<NiceMock<MockVoldService>>();
+ mVold = vold.get();
+ auto incrementalManager = std::make_unique<NiceMock<MockIncrementalManager>>();
+ mIncrementalManager = incrementalManager.get();
+ auto incFs = std::make_unique<NiceMock<MockIncFs>>();
+ mIncFs = incFs.get();
+ mIncrementalService =
+ std::make_unique<IncrementalService>(MockServiceManager(std::move(vold),
+ std::move(
+ incrementalManager),
+ std::move(incFs)),
+ mRootDir.path);
mDataLoaderParcel.packageName = "com.test";
mDataLoaderParcel.arguments = "uri";
mIncrementalService->onSystemReady();
@@ -252,20 +249,18 @@ public:
const auto mountPointsFile = rootDir + "/dir1/mount/.mountpoint.abcd";
ASSERT_TRUE(base::WriteStringToFile("info", mountInfoFile));
ASSERT_TRUE(base::WriteStringToFile("mounts", mountPointsFile));
- ASSERT_GE(inode(mountInfoFile), 0);
- ASSERT_GE(inode(mountPointsFile), 0);
- ON_CALL(*mIncFs, getMetadata(_, inode(mountInfoFile)))
- .WillByDefault(Invoke(mIncFs.get(), &MockIncFs::getMountInfoMetadata));
- ON_CALL(*mIncFs, getMetadata(_, inode(mountPointsFile)))
- .WillByDefault(Invoke(mIncFs.get(), &MockIncFs::getBindPointMetadata));
- ON_CALL(*mIncFs, getMetadata(_, inode(rootDir + "/dir1/mount/st0")))
- .WillByDefault(Invoke(mIncFs.get(), &MockIncFs::getStorageMetadata));
+ ON_CALL(*mIncFs, getMetadata(_, std::string_view(mountInfoFile)))
+ .WillByDefault(Invoke(mIncFs, &MockIncFs::getMountInfoMetadata));
+ ON_CALL(*mIncFs, getMetadata(_, std::string_view(mountPointsFile)))
+ .WillByDefault(Invoke(mIncFs, &MockIncFs::getBindPointMetadata));
+ ON_CALL(*mIncFs, getMetadata(_, std::string_view(rootDir + "/dir1/mount/st0")))
+ .WillByDefault(Invoke(mIncFs, &MockIncFs::getStorageMetadata));
}
protected:
- std::shared_ptr<NiceMock<MockVoldService>> mVold;
- std::shared_ptr<NiceMock<MockIncFs>> mIncFs;
- std::shared_ptr<NiceMock<MockIncrementalManager>> mIncrementalManager;
+ NiceMock<MockVoldService>* mVold;
+ NiceMock<MockIncFs>* mIncFs;
+ NiceMock<MockIncrementalManager>* mIncrementalManager;
std::unique_ptr<IncrementalService> mIncrementalService;
TemporaryDir mRootDir;
DataLoaderParamsParcel mDataLoaderParcel;
@@ -412,12 +407,12 @@ TEST_F(IncrementalServiceTest, testMakeDirectory) {
mIncrementalService->createStorage(tempDir.path, std::move(mDataLoaderParcel),
IncrementalService::CreateOptions::CreateNew);
std::string_view dir_path("test");
- EXPECT_CALL(*mIncFs, makeDir(_, dir_path, _, _, _));
- int fileIno = mIncrementalService->makeDir(storageId, dir_path, "");
- ASSERT_GE(fileIno, 0);
+ EXPECT_CALL(*mIncFs, makeDir(_, dir_path, _));
+ auto res = mIncrementalService->makeDir(storageId, dir_path, 0555);
+ ASSERT_EQ(res, 0);
}
-TEST_F(IncrementalServiceTest, testMakeDirectoryNoParent) {
+TEST_F(IncrementalServiceTest, testMakeDirectoryNested) {
mVold->mountIncFsSuccess();
mIncFs->makeFileSuccess();
mVold->bindMountSuccess();
@@ -427,13 +422,15 @@ TEST_F(IncrementalServiceTest, testMakeDirectoryNoParent) {
int storageId =
mIncrementalService->createStorage(tempDir.path, std::move(mDataLoaderParcel),
IncrementalService::CreateOptions::CreateNew);
- std::string_view first("first");
- std::string_view second("second");
+ auto first = "first"sv;
+ auto second = "second"sv;
std::string dir_path = std::string(first) + "/" + std::string(second);
- EXPECT_CALL(*mIncFs, makeDir(_, first, _, _, _)).Times(0);
- EXPECT_CALL(*mIncFs, makeDir(_, second, _, _, _)).Times(0);
- int fileIno = mIncrementalService->makeDir(storageId, dir_path, "");
- ASSERT_LT(fileIno, 0);
+ EXPECT_CALL(*mIncFs, makeDir(_, first, _)).Times(0);
+ EXPECT_CALL(*mIncFs, makeDir(_, second, _)).Times(0);
+ EXPECT_CALL(*mIncFs, makeDir(_, std::string_view(dir_path), _)).Times(1);
+
+ auto res = mIncrementalService->makeDir(storageId, dir_path, 0555);
+ ASSERT_EQ(res, 0);
}
TEST_F(IncrementalServiceTest, testMakeDirectories) {
@@ -446,16 +443,18 @@ TEST_F(IncrementalServiceTest, testMakeDirectories) {
int storageId =
mIncrementalService->createStorage(tempDir.path, std::move(mDataLoaderParcel),
IncrementalService::CreateOptions::CreateNew);
- std::string_view first("first");
- std::string_view second("second");
- std::string_view third("third");
+ auto first = "first"sv;
+ auto second = "second"sv;
+ auto third = "third"sv;
InSequence seq;
- EXPECT_CALL(*mIncFs, makeDir(_, first, _, _, _));
- EXPECT_CALL(*mIncFs, makeDir(_, second, _, _, _));
- EXPECT_CALL(*mIncFs, makeDir(_, third, _, _, _));
- std::string dir_path =
- std::string(first) + "/" + std::string(second) + "/" + std::string(third);
- int fileIno = mIncrementalService->makeDirs(storageId, dir_path, "");
- ASSERT_GE(fileIno, 0);
+ auto parent_path = std::string(first) + "/" + std::string(second);
+ auto dir_path = parent_path + "/" + std::string(third);
+ EXPECT_CALL(*mIncFs, makeDir(_, std::string_view(dir_path), _)).WillOnce(Return(-ENOENT));
+ EXPECT_CALL(*mIncFs, makeDir(_, std::string_view(parent_path), _)).WillOnce(Return(-ENOENT));
+ EXPECT_CALL(*mIncFs, makeDir(_, first, _)).WillOnce(Return(0));
+ EXPECT_CALL(*mIncFs, makeDir(_, std::string_view(parent_path), _)).WillOnce(Return(0));
+ EXPECT_CALL(*mIncFs, makeDir(_, std::string_view(dir_path), _)).WillOnce(Return(0));
+ auto res = mIncrementalService->makeDirs(storageId, dir_path, 0555);
+ ASSERT_EQ(res, 0);
}
} // namespace android::os::incremental
diff --git a/services/java/com/android/server/SystemServer.java b/services/java/com/android/server/SystemServer.java
index 258d7628e502..df2b9e932fbc 100644
--- a/services/java/com/android/server/SystemServer.java
+++ b/services/java/com/android/server/SystemServer.java
@@ -1724,9 +1724,11 @@ public final class SystemServer {
mSystemServiceManager.startService(SensorNotificationService.class);
t.traceEnd();
- t.traceBegin("StartContextHubSystemService");
- mSystemServiceManager.startService(ContextHubSystemService.class);
- t.traceEnd();
+ if (mPackageManager.hasSystemFeature(PackageManager.FEATURE_CONTEXTHUB)) {
+ t.traceBegin("StartContextHubSystemService");
+ mSystemServiceManager.startService(ContextHubSystemService.class);
+ t.traceEnd();
+ }
t.traceBegin("StartDiskStatsService");
try {
diff --git a/services/net/java/android/net/IpMemoryStore.java b/services/net/java/android/net/IpMemoryStore.java
index 6f91e006c853..dcefb537d0a0 100644
--- a/services/net/java/android/net/IpMemoryStore.java
+++ b/services/net/java/android/net/IpMemoryStore.java
@@ -52,6 +52,11 @@ public class IpMemoryStore extends IpMemoryStoreClient {
public int getInterfaceVersion() {
return this.VERSION;
}
+
+ @Override
+ public String getInterfaceHash() {
+ return this.HASH;
+ }
});
}
diff --git a/services/net/java/android/net/ip/IpClientUtil.java b/services/net/java/android/net/ip/IpClientUtil.java
index 7f723b1c232b..a3618b47171a 100644
--- a/services/net/java/android/net/ip/IpClientUtil.java
+++ b/services/net/java/android/net/ip/IpClientUtil.java
@@ -189,6 +189,11 @@ public class IpClientUtil {
public int getInterfaceVersion() {
return this.VERSION;
}
+
+ @Override
+ public String getInterfaceHash() {
+ return this.HASH;
+ }
}
/**
diff --git a/services/people/java/com/android/server/people/PeopleService.java b/services/people/java/com/android/server/people/PeopleService.java
index 9569c6e8be89..2d18a2994135 100644
--- a/services/people/java/com/android/server/people/PeopleService.java
+++ b/services/people/java/com/android/server/people/PeopleService.java
@@ -16,6 +16,7 @@
package com.android.server.people;
+import android.annotation.NonNull;
import android.app.prediction.AppPredictionContext;
import android.app.prediction.AppPredictionSessionId;
import android.app.prediction.AppTarget;
@@ -29,6 +30,7 @@ import android.util.Slog;
import com.android.internal.annotations.VisibleForTesting;
import com.android.server.SystemService;
+import com.android.server.people.data.DataManager;
import java.util.List;
import java.util.Map;
@@ -41,6 +43,8 @@ public class PeopleService extends SystemService {
private static final String TAG = "PeopleService";
+ private final DataManager mDataManager;
+
/**
* Initializes the system service.
*
@@ -48,6 +52,15 @@ public class PeopleService extends SystemService {
*/
public PeopleService(Context context) {
super(context);
+
+ mDataManager = new DataManager(context);
+ }
+
+ @Override
+ public void onBootPhase(int phase) {
+ if (phase == PHASE_SYSTEM_SERVICES_READY) {
+ mDataManager.initialize();
+ }
}
@Override
@@ -55,6 +68,16 @@ public class PeopleService extends SystemService {
publishLocalService(PeopleServiceInternal.class, new LocalService());
}
+ @Override
+ public void onUnlockUser(@NonNull TargetUser targetUser) {
+ mDataManager.onUserUnlocked(targetUser.getUserIdentifier());
+ }
+
+ @Override
+ public void onStopUser(@NonNull TargetUser targetUser) {
+ mDataManager.onUserStopped(targetUser.getUserIdentifier());
+ }
+
@VisibleForTesting
final class LocalService extends PeopleServiceInternal {
@@ -63,7 +86,7 @@ public class PeopleService extends SystemService {
@Override
public void onCreatePredictionSession(AppPredictionContext context,
AppPredictionSessionId sessionId) {
- mSessions.put(sessionId, new SessionInfo(context));
+ mSessions.put(sessionId, new SessionInfo(context, mDataManager));
}
@Override
diff --git a/services/people/java/com/android/server/people/SessionInfo.java b/services/people/java/com/android/server/people/SessionInfo.java
index df7cedf7626f..eb08e03c14de 100644
--- a/services/people/java/com/android/server/people/SessionInfo.java
+++ b/services/people/java/com/android/server/people/SessionInfo.java
@@ -24,6 +24,7 @@ import android.os.RemoteCallbackList;
import android.os.RemoteException;
import android.util.Slog;
+import com.android.server.people.data.DataManager;
import com.android.server.people.prediction.ConversationPredictor;
import java.util.List;
@@ -37,9 +38,9 @@ class SessionInfo {
private final RemoteCallbackList<IPredictionCallback> mCallbacks =
new RemoteCallbackList<>();
- SessionInfo(AppPredictionContext predictionContext) {
+ SessionInfo(AppPredictionContext predictionContext, DataManager dataManager) {
mConversationPredictor = new ConversationPredictor(predictionContext,
- this::updatePredictions);
+ this::updatePredictions, dataManager);
}
void addCallback(IPredictionCallback callback) {
diff --git a/services/people/java/com/android/server/people/data/AggregateEventHistoryImpl.java b/services/people/java/com/android/server/people/data/AggregateEventHistoryImpl.java
new file mode 100644
index 000000000000..4ac346b51b22
--- /dev/null
+++ b/services/people/java/com/android/server/people/data/AggregateEventHistoryImpl.java
@@ -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.server.people.data;
+
+import android.annotation.NonNull;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Set;
+
+/** An {@link EventHistory} that aggregates multiple {@link EventHistory}. */
+class AggregateEventHistoryImpl implements EventHistory {
+
+ private final List<EventHistory> mEventHistoryList = new ArrayList<>();
+
+ @NonNull
+ @Override
+ public EventIndex getEventIndex(int eventType) {
+ for (EventHistory eventHistory : mEventHistoryList) {
+ EventIndex eventIndex = eventHistory.getEventIndex(eventType);
+ if (!eventIndex.isEmpty()) {
+ return eventIndex;
+ }
+ }
+ return EventIndex.EMPTY;
+ }
+
+ @NonNull
+ @Override
+ public EventIndex getEventIndex(Set<Integer> eventTypes) {
+ EventIndex merged = new EventIndex();
+ for (EventHistory eventHistory : mEventHistoryList) {
+ EventIndex eventIndex = eventHistory.getEventIndex(eventTypes);
+ if (!eventIndex.isEmpty()) {
+ merged = EventIndex.combine(merged, eventIndex);
+ }
+ }
+ return merged;
+ }
+
+ @NonNull
+ @Override
+ public List<Event> queryEvents(Set<Integer> eventTypes, long startTime, long endTime) {
+ List<Event> results = new ArrayList<>();
+ for (EventHistory eventHistory : mEventHistoryList) {
+ EventIndex eventIndex = eventHistory.getEventIndex(eventTypes);
+ if (eventIndex.isEmpty()) {
+ continue;
+ }
+ List<Event> queryResults = eventHistory.queryEvents(eventTypes, startTime, endTime);
+ results = combineEventLists(results, queryResults);
+ }
+ return results;
+ }
+
+ void addEventHistory(EventHistory eventHistory) {
+ mEventHistoryList.add(eventHistory);
+ }
+
+ /**
+ * Combines the sorted events (in chronological order) from the given 2 lists {@code lhs}
+ * and {@code rhs} and preserves the order.
+ */
+ private List<Event> combineEventLists(List<Event> lhs, List<Event> rhs) {
+ List<Event> results = new ArrayList<>();
+ int i = 0, j = 0;
+ while (i < lhs.size() && j < rhs.size()) {
+ if (lhs.get(i).getTimestamp() < rhs.get(j).getTimestamp()) {
+ results.add(lhs.get(i++));
+ } else {
+ results.add(rhs.get(j++));
+ }
+ }
+ if (i < lhs.size()) {
+ results.addAll(lhs.subList(i, lhs.size()));
+ } else if (j < rhs.size()) {
+ results.addAll(rhs.subList(j, rhs.size()));
+ }
+ return results;
+ }
+}
diff --git a/services/people/java/com/android/server/people/data/ContactsQueryHelper.java b/services/people/java/com/android/server/people/data/ContactsQueryHelper.java
new file mode 100644
index 000000000000..8a3a44ae9f35
--- /dev/null
+++ b/services/people/java/com/android/server/people/data/ContactsQueryHelper.java
@@ -0,0 +1,182 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.people.data;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.annotation.WorkerThread;
+import android.content.Context;
+import android.database.Cursor;
+import android.net.Uri;
+import android.provider.ContactsContract;
+import android.provider.ContactsContract.Contacts;
+import android.text.TextUtils;
+import android.util.Slog;
+
+/** A helper class that queries the Contacts database. */
+class ContactsQueryHelper {
+
+ private static final String TAG = "ContactsQueryHelper";
+
+ private final Context mContext;
+ private Uri mContactUri;
+ private boolean mIsStarred;
+ private String mPhoneNumber;
+ private long mLastUpdatedTimestamp;
+
+ ContactsQueryHelper(Context context) {
+ mContext = context;
+ }
+
+ /**
+ * Queries the Contacts database with the given contact URI and returns whether the query runs
+ * successfully.
+ */
+ @WorkerThread
+ boolean query(@NonNull String contactUri) {
+ if (TextUtils.isEmpty(contactUri)) {
+ return false;
+ }
+ Uri uri = Uri.parse(contactUri);
+ if ("tel".equals(uri.getScheme())) {
+ return queryWithPhoneNumber(uri.getSchemeSpecificPart());
+ } else if ("mailto".equals(uri.getScheme())) {
+ return queryWithEmail(uri.getSchemeSpecificPart());
+ } else if (contactUri.startsWith(Contacts.CONTENT_LOOKUP_URI.toString())) {
+ return queryWithUri(uri);
+ }
+ return false;
+ }
+
+ /** Queries the Contacts database and read the most recently updated contact. */
+ @WorkerThread
+ boolean querySince(long sinceTime) {
+ final String[] projection = new String[] {
+ Contacts._ID, Contacts.LOOKUP_KEY, Contacts.STARRED, Contacts.HAS_PHONE_NUMBER,
+ Contacts.CONTACT_LAST_UPDATED_TIMESTAMP };
+ String selection = Contacts.CONTACT_LAST_UPDATED_TIMESTAMP + " > ?";
+ String[] selectionArgs = new String[] {Long.toString(sinceTime)};
+ return queryContact(Contacts.CONTENT_URI, projection, selection, selectionArgs);
+ }
+
+ @Nullable
+ Uri getContactUri() {
+ return mContactUri;
+ }
+
+ boolean isStarred() {
+ return mIsStarred;
+ }
+
+ @Nullable
+ String getPhoneNumber() {
+ return mPhoneNumber;
+ }
+
+ long getLastUpdatedTimestamp() {
+ return mLastUpdatedTimestamp;
+ }
+
+ private boolean queryWithPhoneNumber(String phoneNumber) {
+ Uri phoneUri = Uri.withAppendedPath(
+ ContactsContract.PhoneLookup.CONTENT_FILTER_URI, Uri.encode(phoneNumber));
+ return queryWithUri(phoneUri);
+ }
+
+ private boolean queryWithEmail(String email) {
+ Uri emailUri = Uri.withAppendedPath(
+ ContactsContract.CommonDataKinds.Email.CONTENT_LOOKUP_URI, Uri.encode(email));
+ return queryWithUri(emailUri);
+ }
+
+ private boolean queryWithUri(@NonNull Uri uri) {
+ final String[] projection = new String[] {
+ Contacts._ID, Contacts.LOOKUP_KEY, Contacts.STARRED, Contacts.HAS_PHONE_NUMBER };
+ return queryContact(uri, projection, /* selection= */ null, /* selectionArgs= */ null);
+ }
+
+ private boolean queryContact(@NonNull Uri uri, @NonNull String[] projection,
+ @Nullable String selection, @Nullable String[] selectionArgs) {
+ long contactId;
+ String lookupKey = null;
+ boolean hasPhoneNumber = false;
+ boolean found = false;
+ try (Cursor cursor = mContext.getContentResolver().query(
+ uri, projection, selection, selectionArgs, /* sortOrder= */ null)) {
+ if (cursor == null) {
+ Slog.w(TAG, "Cursor is null when querying contact.");
+ return false;
+ }
+ while (cursor.moveToNext()) {
+ // Contact ID
+ int idIndex = cursor.getColumnIndex(Contacts._ID);
+ contactId = cursor.getLong(idIndex);
+
+ // Lookup key
+ int lookupKeyIndex = cursor.getColumnIndex(Contacts.LOOKUP_KEY);
+ lookupKey = cursor.getString(lookupKeyIndex);
+
+ mContactUri = Contacts.getLookupUri(contactId, lookupKey);
+
+ // Starred
+ int starredIndex = cursor.getColumnIndex(Contacts.STARRED);
+ mIsStarred = cursor.getInt(starredIndex) != 0;
+
+ // Has phone number
+ int hasPhoneNumIndex = cursor.getColumnIndex(Contacts.HAS_PHONE_NUMBER);
+ hasPhoneNumber = cursor.getInt(hasPhoneNumIndex) != 0;
+
+ // Last updated timestamp
+ int lastUpdatedTimestampIndex = cursor.getColumnIndex(
+ Contacts.CONTACT_LAST_UPDATED_TIMESTAMP);
+ if (lastUpdatedTimestampIndex >= 0) {
+ mLastUpdatedTimestamp = cursor.getLong(lastUpdatedTimestampIndex);
+ }
+
+ found = true;
+ }
+ }
+ if (found && lookupKey != null && hasPhoneNumber) {
+ return queryPhoneNumber(lookupKey);
+ }
+ return found;
+ }
+
+ private boolean queryPhoneNumber(String lookupKey) {
+ String[] projection = new String[] {
+ ContactsContract.CommonDataKinds.Phone.NORMALIZED_NUMBER };
+ String selection = Contacts.LOOKUP_KEY + " = ?";
+ String[] selectionArgs = new String[] { lookupKey };
+ try (Cursor cursor = mContext.getContentResolver().query(
+ ContactsContract.CommonDataKinds.Phone.CONTENT_URI, projection, selection,
+ selectionArgs, /* sortOrder= */ null)) {
+ if (cursor == null) {
+ Slog.w(TAG, "Cursor is null when querying contact phone number.");
+ return false;
+ }
+ while (cursor.moveToNext()) {
+ // Phone number
+ int phoneNumIdx = cursor.getColumnIndex(
+ ContactsContract.CommonDataKinds.Phone.NORMALIZED_NUMBER);
+ if (phoneNumIdx >= 0) {
+ mPhoneNumber = cursor.getString(phoneNumIdx);
+ }
+ }
+ }
+ return true;
+ }
+}
diff --git a/services/people/java/com/android/server/people/data/ConversationInfo.java b/services/people/java/com/android/server/people/data/ConversationInfo.java
new file mode 100644
index 000000000000..bb97533b3222
--- /dev/null
+++ b/services/people/java/com/android/server/people/data/ConversationInfo.java
@@ -0,0 +1,372 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.people.data;
+
+import android.annotation.IntDef;
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.content.LocusId;
+import android.content.pm.ShortcutInfo;
+import android.content.pm.ShortcutInfo.ShortcutFlags;
+import android.net.Uri;
+
+import com.android.internal.util.Preconditions;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.util.Objects;
+
+/**
+ * Represents a conversation that is provided by the app based on {@link ShortcutInfo}.
+ */
+public class ConversationInfo {
+
+ private static final int FLAG_VIP = 1;
+
+ private static final int FLAG_NOTIFICATION_SILENCED = 1 << 1;
+
+ private static final int FLAG_BUBBLED = 1 << 2;
+
+ private static final int FLAG_PERSON_IMPORTANT = 1 << 3;
+
+ private static final int FLAG_PERSON_BOT = 1 << 4;
+
+ private static final int FLAG_CONTACT_STARRED = 1 << 5;
+
+ private static final int FLAG_DEMOTED = 1 << 6;
+
+ @IntDef(flag = true, prefix = {"FLAG_"}, value = {
+ FLAG_VIP,
+ FLAG_NOTIFICATION_SILENCED,
+ FLAG_BUBBLED,
+ FLAG_PERSON_IMPORTANT,
+ FLAG_PERSON_BOT,
+ FLAG_CONTACT_STARRED,
+ FLAG_DEMOTED,
+ })
+ @Retention(RetentionPolicy.SOURCE)
+ private @interface ConversationFlags {
+ }
+
+ @NonNull
+ private String mShortcutId;
+
+ @Nullable
+ private LocusId mLocusId;
+
+ @Nullable
+ private Uri mContactUri;
+
+ @Nullable
+ private String mContactPhoneNumber;
+
+ @Nullable
+ private String mNotificationChannelId;
+
+ @ShortcutFlags
+ private int mShortcutFlags;
+
+ @ConversationFlags
+ private int mConversationFlags;
+
+ private ConversationInfo(Builder builder) {
+ mShortcutId = builder.mShortcutId;
+ mLocusId = builder.mLocusId;
+ mContactUri = builder.mContactUri;
+ mContactPhoneNumber = builder.mContactPhoneNumber;
+ mNotificationChannelId = builder.mNotificationChannelId;
+ mShortcutFlags = builder.mShortcutFlags;
+ mConversationFlags = builder.mConversationFlags;
+ }
+
+ @NonNull
+ public String getShortcutId() {
+ return mShortcutId;
+ }
+
+ @Nullable
+ LocusId getLocusId() {
+ return mLocusId;
+ }
+
+ /** The URI to look up the entry in the contacts data provider. */
+ @Nullable
+ Uri getContactUri() {
+ return mContactUri;
+ }
+
+ /** The phone number of the associated contact. */
+ @Nullable
+ String getContactPhoneNumber() {
+ return mContactPhoneNumber;
+ }
+
+ /**
+ * ID of the {@link android.app.NotificationChannel} where the notifications for this
+ * conversation are posted.
+ */
+ @Nullable
+ String getNotificationChannelId() {
+ return mNotificationChannelId;
+ }
+
+ /** Whether the shortcut for this conversation is set long-lived by the app. */
+ public boolean isShortcutLongLived() {
+ return hasShortcutFlags(ShortcutInfo.FLAG_LONG_LIVED);
+ }
+
+ /** Whether this conversation is marked as VIP by the user. */
+ public boolean isVip() {
+ return hasConversationFlags(FLAG_VIP);
+ }
+
+ /** Whether the notifications for this conversation should be silenced. */
+ public boolean isNotificationSilenced() {
+ return hasConversationFlags(FLAG_NOTIFICATION_SILENCED);
+ }
+
+ /** Whether the notifications for this conversation should show in bubbles. */
+ public boolean isBubbled() {
+ return hasConversationFlags(FLAG_BUBBLED);
+ }
+
+ /**
+ * Whether this conversation is demoted by the user. New notifications for the demoted
+ * conversation will not show in the conversation space.
+ */
+ public boolean isDemoted() {
+ return hasConversationFlags(FLAG_DEMOTED);
+ }
+
+ /** Whether the associated person is marked as important by the app. */
+ public boolean isPersonImportant() {
+ return hasConversationFlags(FLAG_PERSON_IMPORTANT);
+ }
+
+ /** Whether the associated person is marked as a bot by the app. */
+ public boolean isPersonBot() {
+ return hasConversationFlags(FLAG_PERSON_BOT);
+ }
+
+ /** Whether the associated contact is marked as starred by the user. */
+ public boolean isContactStarred() {
+ return hasConversationFlags(FLAG_CONTACT_STARRED);
+ }
+
+ @Override
+ public boolean equals(Object obj) {
+ if (this == obj) {
+ return true;
+ }
+ if (!(obj instanceof ConversationInfo)) {
+ return false;
+ }
+ ConversationInfo other = (ConversationInfo) obj;
+ return Objects.equals(mShortcutId, other.mShortcutId)
+ && Objects.equals(mLocusId, other.mLocusId)
+ && Objects.equals(mContactUri, other.mContactUri)
+ && Objects.equals(mContactPhoneNumber, other.mContactPhoneNumber)
+ && Objects.equals(mNotificationChannelId, other.mNotificationChannelId)
+ && mShortcutFlags == other.mShortcutFlags
+ && mConversationFlags == other.mConversationFlags;
+ }
+
+ @Override
+ public int hashCode() {
+ return Objects.hash(mShortcutId, mLocusId, mContactUri, mContactPhoneNumber,
+ mNotificationChannelId, mShortcutFlags, mConversationFlags);
+ }
+
+ @Override
+ public String toString() {
+ StringBuilder sb = new StringBuilder();
+ sb.append("ConversationInfo {");
+ sb.append("shortcutId=").append(mShortcutId);
+ sb.append(", locusId=").append(mLocusId);
+ sb.append(", contactUri=").append(mContactUri);
+ sb.append(", phoneNumber=").append(mContactPhoneNumber);
+ sb.append(", notificationChannelId=").append(mNotificationChannelId);
+ sb.append(", shortcutFlags=0x").append(Integer.toHexString(mShortcutFlags));
+ sb.append(" [");
+ if (isShortcutLongLived()) {
+ sb.append("Liv");
+ }
+ sb.append("]");
+ sb.append(", conversationFlags=0x").append(Integer.toHexString(mConversationFlags));
+ sb.append(" [");
+ if (isVip()) {
+ sb.append("Vip");
+ }
+ if (isNotificationSilenced()) {
+ sb.append("Sil");
+ }
+ if (isBubbled()) {
+ sb.append("Bub");
+ }
+ if (isDemoted()) {
+ sb.append("Dem");
+ }
+ if (isPersonImportant()) {
+ sb.append("Imp");
+ }
+ if (isPersonBot()) {
+ sb.append("Bot");
+ }
+ if (isContactStarred()) {
+ sb.append("Sta");
+ }
+ sb.append("]}");
+ return sb.toString();
+ }
+
+ private boolean hasShortcutFlags(@ShortcutFlags int flags) {
+ return (mShortcutFlags & flags) == flags;
+ }
+
+ private boolean hasConversationFlags(@ConversationFlags int flags) {
+ return (mConversationFlags & flags) == flags;
+ }
+
+ /**
+ * Builder class for {@link ConversationInfo} objects.
+ */
+ static class Builder {
+
+ private String mShortcutId;
+
+ @Nullable
+ private LocusId mLocusId;
+
+ @Nullable
+ private Uri mContactUri;
+
+ @Nullable
+ private String mContactPhoneNumber;
+
+ @Nullable
+ private String mNotificationChannelId;
+
+ @ShortcutFlags
+ private int mShortcutFlags;
+
+ @ConversationFlags
+ private int mConversationFlags;
+
+ Builder() {
+ }
+
+ Builder(@NonNull ConversationInfo conversationInfo) {
+ if (mShortcutId == null) {
+ mShortcutId = conversationInfo.mShortcutId;
+ } else {
+ Preconditions.checkArgument(mShortcutId.equals(conversationInfo.mShortcutId));
+ }
+ mLocusId = conversationInfo.mLocusId;
+ mContactUri = conversationInfo.mContactUri;
+ mContactPhoneNumber = conversationInfo.mContactPhoneNumber;
+ mNotificationChannelId = conversationInfo.mNotificationChannelId;
+ mShortcutFlags = conversationInfo.mShortcutFlags;
+ mConversationFlags = conversationInfo.mConversationFlags;
+ }
+
+ Builder setShortcutId(@NonNull String shortcutId) {
+ mShortcutId = shortcutId;
+ return this;
+ }
+
+ Builder setLocusId(LocusId locusId) {
+ mLocusId = locusId;
+ return this;
+ }
+
+ Builder setContactUri(Uri contactUri) {
+ mContactUri = contactUri;
+ return this;
+ }
+
+ Builder setContactPhoneNumber(String phoneNumber) {
+ mContactPhoneNumber = phoneNumber;
+ return this;
+ }
+
+ Builder setNotificationChannelId(String notificationChannelId) {
+ mNotificationChannelId = notificationChannelId;
+ return this;
+ }
+
+ Builder setShortcutFlags(@ShortcutFlags int shortcutFlags) {
+ mShortcutFlags = shortcutFlags;
+ return this;
+ }
+
+ Builder setConversationFlags(@ConversationFlags int conversationFlags) {
+ mConversationFlags = conversationFlags;
+ return this;
+ }
+
+ Builder setVip(boolean value) {
+ return setConversationFlag(FLAG_VIP, value);
+ }
+
+ Builder setNotificationSilenced(boolean value) {
+ return setConversationFlag(FLAG_NOTIFICATION_SILENCED, value);
+ }
+
+ Builder setBubbled(boolean value) {
+ return setConversationFlag(FLAG_BUBBLED, value);
+ }
+
+ Builder setDemoted(boolean value) {
+ return setConversationFlag(FLAG_DEMOTED, value);
+ }
+
+ Builder setPersonImportant(boolean value) {
+ return setConversationFlag(FLAG_PERSON_IMPORTANT, value);
+ }
+
+ Builder setPersonBot(boolean value) {
+ return setConversationFlag(FLAG_PERSON_BOT, value);
+ }
+
+ Builder setContactStarred(boolean value) {
+ return setConversationFlag(FLAG_CONTACT_STARRED, value);
+ }
+
+ private Builder setConversationFlag(@ConversationFlags int flags, boolean value) {
+ if (value) {
+ return addConversationFlags(flags);
+ } else {
+ return removeConversationFlags(flags);
+ }
+ }
+
+ private Builder addConversationFlags(@ConversationFlags int flags) {
+ mConversationFlags |= flags;
+ return this;
+ }
+
+ private Builder removeConversationFlags(@ConversationFlags int flags) {
+ mConversationFlags &= ~flags;
+ return this;
+ }
+
+ ConversationInfo build() {
+ Objects.requireNonNull(mShortcutId);
+ return new ConversationInfo(this);
+ }
+ }
+}
diff --git a/services/people/java/com/android/server/people/data/ConversationStore.java b/services/people/java/com/android/server/people/data/ConversationStore.java
new file mode 100644
index 000000000000..f17e1b91cb5d
--- /dev/null
+++ b/services/people/java/com/android/server/people/data/ConversationStore.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.server.people.data;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.content.LocusId;
+import android.net.Uri;
+import android.util.ArrayMap;
+
+import java.util.Map;
+import java.util.function.Consumer;
+
+/** The store that stores and accesses the conversations data for a package. */
+class ConversationStore {
+
+ // Shortcut ID -> Conversation Info
+ private final Map<String, ConversationInfo> mConversationInfoMap = new ArrayMap<>();
+
+ // Locus ID -> Shortcut ID
+ private final Map<LocusId, String> mLocusIdToShortcutIdMap = new ArrayMap<>();
+
+ // Contact URI -> Shortcut ID
+ private final Map<Uri, String> mContactUriToShortcutIdMap = new ArrayMap<>();
+
+ // Phone Number -> Shortcut ID
+ private final Map<String, String> mPhoneNumberToShortcutIdMap = new ArrayMap<>();
+
+ void addOrUpdate(@NonNull ConversationInfo conversationInfo) {
+ mConversationInfoMap.put(conversationInfo.getShortcutId(), conversationInfo);
+
+ LocusId locusId = conversationInfo.getLocusId();
+ if (locusId != null) {
+ mLocusIdToShortcutIdMap.put(locusId, conversationInfo.getShortcutId());
+ }
+
+ Uri contactUri = conversationInfo.getContactUri();
+ if (contactUri != null) {
+ mContactUriToShortcutIdMap.put(contactUri, conversationInfo.getShortcutId());
+ }
+
+ String phoneNumber = conversationInfo.getContactPhoneNumber();
+ if (phoneNumber != null) {
+ mPhoneNumberToShortcutIdMap.put(phoneNumber, conversationInfo.getShortcutId());
+ }
+ }
+
+ void deleteConversation(@NonNull String shortcutId) {
+ ConversationInfo conversationInfo = mConversationInfoMap.remove(shortcutId);
+ if (conversationInfo == null) {
+ return;
+ }
+
+ LocusId locusId = conversationInfo.getLocusId();
+ if (locusId != null) {
+ mLocusIdToShortcutIdMap.remove(locusId);
+ }
+
+ Uri contactUri = conversationInfo.getContactUri();
+ if (contactUri != null) {
+ mContactUriToShortcutIdMap.remove(contactUri);
+ }
+
+ String phoneNumber = conversationInfo.getContactPhoneNumber();
+ if (phoneNumber != null) {
+ mPhoneNumberToShortcutIdMap.remove(phoneNumber);
+ }
+ }
+
+ void forAllConversations(@NonNull Consumer<ConversationInfo> consumer) {
+ for (ConversationInfo ci : mConversationInfoMap.values()) {
+ consumer.accept(ci);
+ }
+ }
+
+ @Nullable
+ ConversationInfo getConversation(@Nullable String shortcutId) {
+ return shortcutId != null ? mConversationInfoMap.get(shortcutId) : null;
+ }
+
+ @Nullable
+ ConversationInfo getConversationByLocusId(@NonNull LocusId locusId) {
+ return getConversation(mLocusIdToShortcutIdMap.get(locusId));
+ }
+
+ @Nullable
+ ConversationInfo getConversationByContactUri(@NonNull Uri contactUri) {
+ return getConversation(mContactUriToShortcutIdMap.get(contactUri));
+ }
+
+ @Nullable
+ ConversationInfo getConversationByPhoneNumber(@NonNull String phoneNumber) {
+ return getConversation(mPhoneNumberToShortcutIdMap.get(phoneNumber));
+ }
+}
diff --git a/services/people/java/com/android/server/people/data/DataManager.java b/services/people/java/com/android/server/people/data/DataManager.java
new file mode 100644
index 000000000000..13cce414ea7c
--- /dev/null
+++ b/services/people/java/com/android/server/people/data/DataManager.java
@@ -0,0 +1,559 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.people.data;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.annotation.UserIdInt;
+import android.annotation.WorkerThread;
+import android.app.Notification;
+import android.app.Person;
+import android.app.prediction.AppTarget;
+import android.app.prediction.AppTargetEvent;
+import android.app.usage.UsageEvents;
+import android.app.usage.UsageStatsManagerInternal;
+import android.content.BroadcastReceiver;
+import android.content.ComponentName;
+import android.content.Context;
+import android.content.Intent;
+import android.content.IntentFilter;
+import android.content.pm.LauncherApps.ShortcutQuery;
+import android.content.pm.ShortcutInfo;
+import android.content.pm.ShortcutManager;
+import android.content.pm.ShortcutManager.ShareShortcutInfo;
+import android.content.pm.ShortcutServiceInternal;
+import android.content.pm.UserInfo;
+import android.database.ContentObserver;
+import android.net.Uri;
+import android.os.Binder;
+import android.os.Handler;
+import android.os.Process;
+import android.os.RemoteException;
+import android.os.UserHandle;
+import android.os.UserManager;
+import android.provider.ContactsContract.Contacts;
+import android.service.notification.NotificationListenerService;
+import android.service.notification.StatusBarNotification;
+import android.telecom.TelecomManager;
+import android.text.TextUtils;
+import android.text.format.DateUtils;
+import android.util.SparseArray;
+
+import com.android.internal.annotations.VisibleForTesting;
+import com.android.internal.app.ChooserActivity;
+import com.android.internal.os.BackgroundThread;
+import com.android.internal.telephony.SmsApplication;
+import com.android.server.LocalServices;
+
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
+import java.util.concurrent.Executors;
+import java.util.concurrent.ScheduledExecutorService;
+import java.util.concurrent.ScheduledFuture;
+import java.util.concurrent.TimeUnit;
+import java.util.function.Consumer;
+
+/**
+ * A class manages the lifecycle of the conversations and associated data, and exposes the methods
+ * to access the data in People Service and other system services.
+ */
+public class DataManager {
+
+ private static final String PLATFORM_PACKAGE_NAME = "android";
+ private static final int MY_UID = Process.myUid();
+ private static final int MY_PID = Process.myPid();
+ private static final long USAGE_STATS_QUERY_MAX_EVENT_AGE_MS = DateUtils.DAY_IN_MILLIS;
+ private static final long USAGE_STATS_QUERY_INTERVAL_SEC = 120L;
+
+ private final Context mContext;
+ private final Injector mInjector;
+ private final ScheduledExecutorService mUsageStatsQueryExecutor;
+
+ private final SparseArray<UserData> mUserDataArray = new SparseArray<>();
+ private final SparseArray<BroadcastReceiver> mBroadcastReceivers = new SparseArray<>();
+ private final SparseArray<ContentObserver> mContactsContentObservers = new SparseArray<>();
+ private final SparseArray<ScheduledFuture<?>> mUsageStatsQueryFutures = new SparseArray<>();
+ private final SparseArray<NotificationListenerService> mNotificationListeners =
+ new SparseArray<>();
+
+ private ShortcutServiceInternal mShortcutServiceInternal;
+ private UsageStatsManagerInternal mUsageStatsManagerInternal;
+ private ShortcutManager mShortcutManager;
+ private UserManager mUserManager;
+
+ public DataManager(Context context) {
+ mContext = context;
+ mInjector = new Injector();
+ mUsageStatsQueryExecutor = mInjector.createScheduledExecutor();
+ }
+
+ @VisibleForTesting
+ DataManager(Context context, Injector injector) {
+ mContext = context;
+ mInjector = injector;
+ mUsageStatsQueryExecutor = mInjector.createScheduledExecutor();
+ }
+
+ /** Initialization. Called when the system services are up running. */
+ public void initialize() {
+ mShortcutServiceInternal = LocalServices.getService(ShortcutServiceInternal.class);
+ mUsageStatsManagerInternal = LocalServices.getService(UsageStatsManagerInternal.class);
+ mShortcutManager = mContext.getSystemService(ShortcutManager.class);
+ mUserManager = mContext.getSystemService(UserManager.class);
+
+ mShortcutServiceInternal.addListener(new ShortcutServiceListener());
+ }
+
+ /** This method is called when a user is unlocked. */
+ public void onUserUnlocked(int userId) {
+ UserData userData = mUserDataArray.get(userId);
+ if (userData == null) {
+ userData = new UserData(userId);
+ mUserDataArray.put(userId, userData);
+ }
+ userData.setUserUnlocked();
+ updateDefaultDialer(userData);
+ updateDefaultSmsApp(userData);
+
+ ScheduledFuture<?> scheduledFuture = mUsageStatsQueryExecutor.scheduleAtFixedRate(
+ new UsageStatsQueryRunnable(userId), 1L, USAGE_STATS_QUERY_INTERVAL_SEC,
+ TimeUnit.SECONDS);
+ mUsageStatsQueryFutures.put(userId, scheduledFuture);
+
+ IntentFilter intentFilter = new IntentFilter();
+ intentFilter.addAction(TelecomManager.ACTION_DEFAULT_DIALER_CHANGED);
+ intentFilter.addAction(SmsApplication.ACTION_DEFAULT_SMS_PACKAGE_CHANGED_INTERNAL);
+ BroadcastReceiver broadcastReceiver = new PerUserBroadcastReceiver(userId);
+ mBroadcastReceivers.put(userId, broadcastReceiver);
+ mContext.registerReceiverAsUser(
+ broadcastReceiver, UserHandle.of(userId), intentFilter, null, null);
+
+ ContentObserver contactsContentObserver = new ContactsContentObserver(
+ BackgroundThread.getHandler());
+ mContactsContentObservers.put(userId, contactsContentObserver);
+ mContext.getContentResolver().registerContentObserver(
+ Contacts.CONTENT_URI, /* notifyForDescendants= */ true,
+ contactsContentObserver, userId);
+
+ NotificationListener notificationListener = new NotificationListener();
+ mNotificationListeners.put(userId, notificationListener);
+ try {
+ notificationListener.registerAsSystemService(mContext,
+ new ComponentName(PLATFORM_PACKAGE_NAME, getClass().getSimpleName()),
+ UserHandle.myUserId());
+ } catch (RemoteException e) {
+ // Should never occur for local calls.
+ }
+ }
+
+ /** This method is called when a user is stopped. */
+ public void onUserStopped(int userId) {
+ if (mUserDataArray.indexOfKey(userId) >= 0) {
+ mUserDataArray.get(userId).setUserStopped();
+ }
+ if (mUsageStatsQueryFutures.indexOfKey(userId) >= 0) {
+ mUsageStatsQueryFutures.valueAt(userId).cancel(true);
+ }
+ if (mBroadcastReceivers.indexOfKey(userId) >= 0) {
+ mContext.unregisterReceiver(mBroadcastReceivers.get(userId));
+ }
+ if (mContactsContentObservers.indexOfKey(userId) >= 0) {
+ mContext.getContentResolver().unregisterContentObserver(
+ mContactsContentObservers.get(userId));
+ }
+ if (mNotificationListeners.indexOfKey(userId) >= 0) {
+ try {
+ mNotificationListeners.get(userId).unregisterAsSystemService();
+ } catch (RemoteException e) {
+ // Should never occur for local calls.
+ }
+ }
+ }
+
+ /**
+ * Iterates through all the {@link PackageData}s owned by the unlocked users who are in the
+ * same profile group as the calling user.
+ */
+ public void forAllPackages(Consumer<PackageData> consumer) {
+ List<UserInfo> users = mUserManager.getEnabledProfiles(mInjector.getCallingUserId());
+ for (UserInfo userInfo : users) {
+ UserData userData = getUnlockedUserData(userInfo.id);
+ if (userData != null) {
+ userData.forAllPackages(consumer);
+ }
+ }
+ }
+
+ /** Gets the {@link ShortcutInfo} for the given shortcut ID. */
+ @Nullable
+ public ShortcutInfo getShortcut(@NonNull String packageName, @UserIdInt int userId,
+ @NonNull String shortcutId) {
+ List<ShortcutInfo> shortcuts = getShortcuts(packageName, userId,
+ Collections.singletonList(shortcutId));
+ if (shortcuts != null && !shortcuts.isEmpty()) {
+ return shortcuts.get(0);
+ }
+ return null;
+ }
+
+ /**
+ * Gets the conversation {@link ShareShortcutInfo}s from all packages owned by the calling user
+ * that match the specified {@link IntentFilter}.
+ */
+ public List<ShareShortcutInfo> getConversationShareTargets(
+ @NonNull IntentFilter intentFilter) {
+ List<ShareShortcutInfo> shareShortcuts = mShortcutManager.getShareTargets(intentFilter);
+ List<ShareShortcutInfo> result = new ArrayList<>();
+ for (ShareShortcutInfo shareShortcut : shareShortcuts) {
+ ShortcutInfo si = shareShortcut.getShortcutInfo();
+ if (getConversationInfo(si.getPackage(), si.getUserId(), si.getId()) != null) {
+ result.add(shareShortcut);
+ }
+ }
+ return result;
+ }
+
+ /** Reports the {@link AppTargetEvent} from App Prediction Manager. */
+ public void reportAppTargetEvent(@NonNull AppTargetEvent event,
+ @Nullable IntentFilter intentFilter) {
+ AppTarget appTarget = event.getTarget();
+ ShortcutInfo shortcutInfo = appTarget != null ? appTarget.getShortcutInfo() : null;
+ if (shortcutInfo == null || event.getAction() != AppTargetEvent.ACTION_LAUNCH) {
+ return;
+ }
+ PackageData packageData = getPackageData(appTarget.getPackageName(),
+ appTarget.getUser().getIdentifier());
+ if (packageData == null) {
+ return;
+ }
+ if (ChooserActivity.LAUNCH_LOCATON_DIRECT_SHARE.equals(event.getLaunchLocation())) {
+ String mimeType = intentFilter != null ? intentFilter.getDataType(0) : null;
+ String shortcutId = shortcutInfo.getId();
+ if (packageData.getConversationStore().getConversation(shortcutId) == null
+ || TextUtils.isEmpty(mimeType)) {
+ return;
+ }
+ EventHistoryImpl eventHistory =
+ packageData.getEventStore().getOrCreateShortcutEventHistory(
+ shortcutInfo.getId());
+ @Event.EventType int eventType;
+ if (mimeType.startsWith("text/")) {
+ eventType = Event.TYPE_SHARE_TEXT;
+ } else if (mimeType.startsWith("image/")) {
+ eventType = Event.TYPE_SHARE_IMAGE;
+ } else if (mimeType.startsWith("video/")) {
+ eventType = Event.TYPE_SHARE_VIDEO;
+ } else {
+ eventType = Event.TYPE_SHARE_OTHER;
+ }
+ eventHistory.addEvent(new Event(System.currentTimeMillis(), eventType));
+ }
+ }
+
+ /** Gets a list of {@link ShortcutInfo}s with the given shortcut IDs. */
+ private List<ShortcutInfo> getShortcuts(
+ @NonNull String packageName, @UserIdInt int userId,
+ @Nullable List<String> shortcutIds) {
+ @ShortcutQuery.QueryFlags int queryFlags = ShortcutQuery.FLAG_MATCH_DYNAMIC
+ | ShortcutQuery.FLAG_MATCH_PINNED | ShortcutQuery.FLAG_MATCH_PINNED_BY_ANY_LAUNCHER;
+ return mShortcutServiceInternal.getShortcuts(
+ mInjector.getCallingUserId(), /*callingPackage=*/ PLATFORM_PACKAGE_NAME,
+ /*changedSince=*/ 0, packageName, shortcutIds, /*componentName=*/ null, queryFlags,
+ userId, MY_PID, MY_UID);
+ }
+
+ @Nullable
+ private UserData getUnlockedUserData(int userId) {
+ UserData userData = mUserDataArray.get(userId);
+ return userData != null && userData.isUnlocked() ? userData : null;
+ }
+
+ @Nullable
+ private PackageData getPackageData(@NonNull String packageName, int userId) {
+ UserData userData = getUnlockedUserData(userId);
+ return userData != null ? userData.getPackageData(packageName) : null;
+ }
+
+ @Nullable
+ private ConversationInfo getConversationInfo(@NonNull String packageName, @UserIdInt int userId,
+ @NonNull String shortcutId) {
+ PackageData packageData = getPackageData(packageName, userId);
+ return packageData != null ? packageData.getConversationStore().getConversation(shortcutId)
+ : null;
+ }
+
+ private void updateDefaultDialer(@NonNull UserData userData) {
+ TelecomManager telecomManager = mContext.getSystemService(TelecomManager.class);
+ String defaultDialer = telecomManager != null
+ ? telecomManager.getDefaultDialerPackage(userData.getUserId()) : null;
+ userData.setDefaultDialer(defaultDialer);
+ }
+
+ private void updateDefaultSmsApp(@NonNull UserData userData) {
+ ComponentName component = SmsApplication.getDefaultSmsApplicationAsUser(
+ mContext, /* updateIfNeeded= */ false, userData.getUserId());
+ String defaultSmsApp = component != null ? component.getPackageName() : null;
+ userData.setDefaultSmsApp(defaultSmsApp);
+ }
+
+ @Nullable
+ private EventHistoryImpl getEventHistoryIfEligible(StatusBarNotification sbn) {
+ Notification notification = sbn.getNotification();
+ String shortcutId = notification.getShortcutId();
+ if (shortcutId == null) {
+ return null;
+ }
+ PackageData packageData = getPackageData(sbn.getPackageName(),
+ sbn.getUser().getIdentifier());
+ if (packageData == null
+ || packageData.getConversationStore().getConversation(shortcutId) == null) {
+ return null;
+ }
+ return packageData.getEventStore().getOrCreateShortcutEventHistory(shortcutId);
+ }
+
+ @VisibleForTesting
+ @WorkerThread
+ void onShortcutAddedOrUpdated(@NonNull ShortcutInfo shortcutInfo) {
+ if (shortcutInfo.getPersons() == null || shortcutInfo.getPersons().length == 0) {
+ return;
+ }
+ UserData userData = getUnlockedUserData(shortcutInfo.getUserId());
+ if (userData == null) {
+ return;
+ }
+ PackageData packageData = userData.getOrCreatePackageData(shortcutInfo.getPackage());
+ ConversationStore conversationStore = packageData.getConversationStore();
+ ConversationInfo oldConversationInfo =
+ conversationStore.getConversation(shortcutInfo.getId());
+ ConversationInfo.Builder builder = oldConversationInfo != null
+ ? new ConversationInfo.Builder(oldConversationInfo)
+ : new ConversationInfo.Builder();
+
+ builder.setShortcutId(shortcutInfo.getId());
+ builder.setLocusId(shortcutInfo.getLocusId());
+ builder.setShortcutFlags(shortcutInfo.getFlags());
+
+ Person person = shortcutInfo.getPersons()[0];
+ builder.setPersonImportant(person.isImportant());
+ builder.setPersonBot(person.isBot());
+ String contactUri = person.getUri();
+ if (contactUri != null) {
+ ContactsQueryHelper helper = mInjector.createContactsQueryHelper(mContext);
+ if (helper.query(contactUri)) {
+ builder.setContactUri(helper.getContactUri());
+ builder.setContactStarred(helper.isStarred());
+ builder.setContactPhoneNumber(helper.getPhoneNumber());
+ }
+ } else {
+ builder.setContactUri(null);
+ builder.setContactPhoneNumber(null);
+ builder.setContactStarred(false);
+ }
+
+ conversationStore.addOrUpdate(builder.build());
+ }
+
+ @VisibleForTesting
+ @WorkerThread
+ void queryUsageStatsService(@UserIdInt int userId, long currentTime, long lastQueryTime) {
+ UsageEvents usageEvents = mUsageStatsManagerInternal.queryEventsForUser(
+ userId, lastQueryTime, currentTime, false);
+ if (usageEvents == null) {
+ return;
+ }
+ while (usageEvents.hasNextEvent()) {
+ UsageEvents.Event e = new UsageEvents.Event();
+ usageEvents.getNextEvent(e);
+
+ String packageName = e.getPackageName();
+ PackageData packageData = getPackageData(packageName, userId);
+ if (packageData == null) {
+ continue;
+ }
+ if (e.getEventType() == UsageEvents.Event.SHORTCUT_INVOCATION) {
+ String shortcutId = e.getShortcutId();
+ if (packageData.getConversationStore().getConversation(shortcutId) != null) {
+ EventHistoryImpl eventHistory =
+ packageData.getEventStore().getOrCreateShortcutEventHistory(
+ shortcutId);
+ eventHistory.addEvent(
+ new Event(e.getTimeStamp(), Event.TYPE_SHORTCUT_INVOCATION));
+ }
+ }
+ }
+ }
+
+ @VisibleForTesting
+ ContentObserver getContactsContentObserverForTesting(@UserIdInt int userId) {
+ return mContactsContentObservers.get(userId);
+ }
+
+ @VisibleForTesting
+ NotificationListenerService getNotificationListenerServiceForTesting(@UserIdInt int userId) {
+ return mNotificationListeners.get(userId);
+ }
+
+ /** Observer that observes the changes in the Contacts database. */
+ private class ContactsContentObserver extends ContentObserver {
+
+ private long mLastUpdatedTimestamp;
+
+ private ContactsContentObserver(Handler handler) {
+ super(handler);
+ mLastUpdatedTimestamp = System.currentTimeMillis();
+ }
+
+ @Override
+ public void onChange(boolean selfChange, Uri uri, @UserIdInt int userId) {
+ ContactsQueryHelper helper = mInjector.createContactsQueryHelper(mContext);
+ if (!helper.querySince(mLastUpdatedTimestamp)) {
+ return;
+ }
+ Uri contactUri = helper.getContactUri();
+
+ final ConversationSelector conversationSelector = new ConversationSelector();
+ UserData userData = getUnlockedUserData(userId);
+ if (userData == null) {
+ return;
+ }
+ userData.forAllPackages(packageData -> {
+ ConversationInfo ci =
+ packageData.getConversationStore().getConversationByContactUri(contactUri);
+ if (ci != null) {
+ conversationSelector.mConversationStore =
+ packageData.getConversationStore();
+ conversationSelector.mConversationInfo = ci;
+ }
+ });
+ if (conversationSelector.mConversationInfo == null) {
+ return;
+ }
+
+ ConversationInfo.Builder builder =
+ new ConversationInfo.Builder(conversationSelector.mConversationInfo);
+ builder.setContactStarred(helper.isStarred());
+ builder.setContactPhoneNumber(helper.getPhoneNumber());
+ conversationSelector.mConversationStore.addOrUpdate(builder.build());
+ mLastUpdatedTimestamp = helper.getLastUpdatedTimestamp();
+ }
+
+ private class ConversationSelector {
+ private ConversationStore mConversationStore = null;
+ private ConversationInfo mConversationInfo = null;
+ }
+ }
+
+ /** Listener for the shortcut data changes. */
+ private class ShortcutServiceListener implements
+ ShortcutServiceInternal.ShortcutChangeListener {
+
+ @Override
+ public void onShortcutChanged(@NonNull String packageName, int userId) {
+ BackgroundThread.getExecutor().execute(() -> {
+ List<ShortcutInfo> shortcuts = getShortcuts(packageName, userId,
+ /*shortcutIds=*/ null);
+ for (ShortcutInfo shortcut : shortcuts) {
+ onShortcutAddedOrUpdated(shortcut);
+ }
+ });
+ }
+ }
+
+ /** Listener for the notifications and their settings changes. */
+ private class NotificationListener extends NotificationListenerService {
+
+ @Override
+ public void onNotificationRemoved(StatusBarNotification sbn, RankingMap rankingMap,
+ int reason) {
+ if (reason != REASON_CLICK) {
+ return;
+ }
+ EventHistoryImpl eventHistory = getEventHistoryIfEligible(sbn);
+ if (eventHistory == null) {
+ return;
+ }
+ long currentTime = System.currentTimeMillis();
+ eventHistory.addEvent(new Event(currentTime, Event.TYPE_NOTIFICATION_OPENED));
+ }
+ }
+
+ /**
+ * A {@link Runnable} that queries the Usage Stats Service for recent events for a specified
+ * user.
+ */
+ private class UsageStatsQueryRunnable implements Runnable {
+
+ private final int mUserId;
+ private long mLastQueryTime;
+
+ private UsageStatsQueryRunnable(int userId) {
+ mUserId = userId;
+ mLastQueryTime = System.currentTimeMillis() - USAGE_STATS_QUERY_MAX_EVENT_AGE_MS;
+ }
+
+ @Override
+ public void run() {
+ long currentTime = System.currentTimeMillis();
+ queryUsageStatsService(mUserId, currentTime, mLastQueryTime);
+ mLastQueryTime = currentTime;
+ }
+ }
+
+ /** A {@link BroadcastReceiver} that receives the intents for a specified user. */
+ private class PerUserBroadcastReceiver extends BroadcastReceiver {
+
+ private final int mUserId;
+
+ private PerUserBroadcastReceiver(int userId) {
+ mUserId = userId;
+ }
+
+ @Override
+ public void onReceive(Context context, Intent intent) {
+ UserData userData = getUnlockedUserData(mUserId);
+ if (userData == null) {
+ return;
+ }
+ if (TelecomManager.ACTION_DEFAULT_DIALER_CHANGED.equals(intent.getAction())) {
+ String defaultDialer = intent.getStringExtra(
+ TelecomManager.EXTRA_CHANGE_DEFAULT_DIALER_PACKAGE_NAME);
+ userData.setDefaultDialer(defaultDialer);
+ } else if (SmsApplication.ACTION_DEFAULT_SMS_PACKAGE_CHANGED_INTERNAL.equals(
+ intent.getAction())) {
+ updateDefaultSmsApp(userData);
+ }
+ }
+ }
+
+ @VisibleForTesting
+ static class Injector {
+
+ ScheduledExecutorService createScheduledExecutor() {
+ return Executors.newSingleThreadScheduledExecutor();
+ }
+
+ ContactsQueryHelper createContactsQueryHelper(Context context) {
+ return new ContactsQueryHelper(context);
+ }
+
+ int getCallingUserId() {
+ return Binder.getCallingUserHandle().getIdentifier();
+ }
+ }
+}
diff --git a/services/people/java/com/android/server/people/data/Event.java b/services/people/java/com/android/server/people/data/Event.java
new file mode 100644
index 000000000000..c2364a295e30
--- /dev/null
+++ b/services/people/java/com/android/server/people/data/Event.java
@@ -0,0 +1,195 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.people.data;
+
+import android.annotation.IntDef;
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.text.format.DateFormat;
+import android.util.ArraySet;
+
+import com.android.internal.util.Preconditions;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.util.Set;
+
+/** An event representing the interaction with a specific conversation or app. */
+public class Event {
+
+ public static final int TYPE_SHORTCUT_INVOCATION = 1;
+
+ public static final int TYPE_NOTIFICATION_POSTED = 2;
+
+ public static final int TYPE_NOTIFICATION_OPENED = 3;
+
+ public static final int TYPE_SHARE_TEXT = 4;
+
+ public static final int TYPE_SHARE_IMAGE = 5;
+
+ public static final int TYPE_SHARE_VIDEO = 6;
+
+ public static final int TYPE_SHARE_OTHER = 7;
+
+ public static final int TYPE_SMS_OUTGOING = 8;
+
+ public static final int TYPE_SMS_INCOMING = 9;
+
+ public static final int TYPE_CALL_OUTGOING = 10;
+
+ public static final int TYPE_CALL_INCOMING = 11;
+
+ public static final int TYPE_CALL_MISSED = 12;
+
+ @IntDef(prefix = { "TYPE_" }, value = {
+ TYPE_SHORTCUT_INVOCATION,
+ TYPE_NOTIFICATION_POSTED,
+ TYPE_NOTIFICATION_OPENED,
+ TYPE_SHARE_TEXT,
+ TYPE_SHARE_IMAGE,
+ TYPE_SHARE_VIDEO,
+ TYPE_SHARE_OTHER,
+ TYPE_SMS_OUTGOING,
+ TYPE_SMS_INCOMING,
+ TYPE_CALL_OUTGOING,
+ TYPE_CALL_INCOMING,
+ TYPE_CALL_MISSED,
+ })
+ @Retention(RetentionPolicy.SOURCE)
+ public @interface EventType {}
+
+ public static final Set<Integer> NOTIFICATION_EVENT_TYPES = new ArraySet<>();
+ public static final Set<Integer> SHARE_EVENT_TYPES = new ArraySet<>();
+ public static final Set<Integer> SMS_EVENT_TYPES = new ArraySet<>();
+ public static final Set<Integer> CALL_EVENT_TYPES = new ArraySet<>();
+ public static final Set<Integer> ALL_EVENT_TYPES = new ArraySet<>();
+
+ static {
+ NOTIFICATION_EVENT_TYPES.add(TYPE_NOTIFICATION_POSTED);
+ NOTIFICATION_EVENT_TYPES.add(TYPE_NOTIFICATION_OPENED);
+
+ SHARE_EVENT_TYPES.add(TYPE_SHARE_TEXT);
+ SHARE_EVENT_TYPES.add(TYPE_SHARE_IMAGE);
+ SHARE_EVENT_TYPES.add(TYPE_SHARE_VIDEO);
+ SHARE_EVENT_TYPES.add(TYPE_SHARE_OTHER);
+
+ SMS_EVENT_TYPES.add(TYPE_SMS_INCOMING);
+ SMS_EVENT_TYPES.add(TYPE_SMS_OUTGOING);
+
+ CALL_EVENT_TYPES.add(TYPE_CALL_INCOMING);
+ CALL_EVENT_TYPES.add(TYPE_CALL_OUTGOING);
+ CALL_EVENT_TYPES.add(TYPE_CALL_MISSED);
+
+ ALL_EVENT_TYPES.add(TYPE_SHORTCUT_INVOCATION);
+ ALL_EVENT_TYPES.addAll(NOTIFICATION_EVENT_TYPES);
+ ALL_EVENT_TYPES.addAll(SHARE_EVENT_TYPES);
+ ALL_EVENT_TYPES.addAll(SMS_EVENT_TYPES);
+ ALL_EVENT_TYPES.addAll(CALL_EVENT_TYPES);
+ }
+
+ private final long mTimestamp;
+
+ private final int mType;
+
+ private final CallDetails mCallDetails;
+
+ Event(long timestamp, @EventType int type) {
+ mTimestamp = timestamp;
+ mType = type;
+ mCallDetails = null;
+ }
+
+ private Event(@NonNull Builder builder) {
+ mTimestamp = builder.mTimestamp;
+ mType = builder.mType;
+ mCallDetails = builder.mCallDetails;
+ }
+
+ public long getTimestamp() {
+ return mTimestamp;
+ }
+
+ public @EventType int getType() {
+ return mType;
+ }
+
+ /**
+ * Gets the {@link CallDetails} of the event. It is only available if the event type is one of
+ * {@code CALL_EVENT_TYPES}, otherwise, it's always {@code null}.
+ */
+ @Nullable
+ public CallDetails getCallDetails() {
+ return mCallDetails;
+ }
+
+ @Override
+ public String toString() {
+ StringBuilder sb = new StringBuilder();
+ sb.append("Event {");
+ sb.append("timestamp=").append(DateFormat.format("yyyy-MM-dd HH:mm:ss", mTimestamp));
+ sb.append(", type=").append(mType);
+ if (mCallDetails != null) {
+ sb.append(", callDetails=").append(mCallDetails);
+ }
+ sb.append("}");
+ return sb.toString();
+ }
+
+ /** Type-specific details of a call event. */
+ public static class CallDetails {
+
+ private final long mDurationSeconds;
+
+ CallDetails(long durationSeconds) {
+ mDurationSeconds = durationSeconds;
+ }
+
+ public long getDurationSeconds() {
+ return mDurationSeconds;
+ }
+
+ @Override
+ public String toString() {
+ return "CallDetails {durationSeconds=" + mDurationSeconds + "}";
+ }
+ }
+
+ /** Builder class for {@link Event} objects. */
+ static class Builder {
+
+ private final long mTimestamp;
+
+ private final int mType;
+
+ private CallDetails mCallDetails;
+
+ Builder(long timestamp, @EventType int type) {
+ mTimestamp = timestamp;
+ mType = type;
+ }
+
+ Builder setCallDetails(CallDetails callDetails) {
+ Preconditions.checkArgument(CALL_EVENT_TYPES.contains(mType));
+ mCallDetails = callDetails;
+ return this;
+ }
+
+ Event build() {
+ return new Event(this);
+ }
+ }
+}
diff --git a/services/people/java/com/android/server/people/data/EventHistory.java b/services/people/java/com/android/server/people/data/EventHistory.java
new file mode 100644
index 000000000000..5b11fd0caf05
--- /dev/null
+++ b/services/people/java/com/android/server/people/data/EventHistory.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.people.data;
+
+import android.annotation.NonNull;
+
+import java.util.List;
+import java.util.Set;
+
+/** The interface for querying the event time distribution and details. */
+public interface EventHistory {
+
+ /** Gets the {@link EventIndex} for the specified event type. */
+ @NonNull
+ EventIndex getEventIndex(@Event.EventType int eventType);
+
+ /** Gets the combined {@link EventIndex} for a set of event types. */
+ @NonNull
+ EventIndex getEventIndex(Set<Integer> eventTypes);
+
+ /**
+ * Returns a {@link List} of {@link Event}s those timestamps are between the specified {@code
+ * fromTimestamp}, inclusive, and {@code toTimestamp} exclusive, and match the specified event
+ * types.
+ *
+ * @return a list of matched events in chronological order.
+ */
+ @NonNull
+ List<Event> queryEvents(Set<Integer> eventTypes, long fromTimestamp, long toTimestamp);
+}
diff --git a/services/people/java/com/android/server/people/data/EventHistoryImpl.java b/services/people/java/com/android/server/people/data/EventHistoryImpl.java
new file mode 100644
index 000000000000..6b6bd7e3cfb0
--- /dev/null
+++ b/services/people/java/com/android/server/people/data/EventHistoryImpl.java
@@ -0,0 +1,88 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.people.data;
+
+import android.annotation.NonNull;
+import android.util.SparseArray;
+
+import com.android.internal.annotations.VisibleForTesting;
+
+import java.util.List;
+import java.util.Set;
+
+class EventHistoryImpl implements EventHistory {
+
+ private final Injector mInjector;
+
+ // Event Type -> Event Index
+ private final SparseArray<EventIndex> mEventIndexArray = new SparseArray<>();
+
+ private final EventList mRecentEvents = new EventList();
+
+ EventHistoryImpl() {
+ mInjector = new Injector();
+ }
+
+ @VisibleForTesting
+ EventHistoryImpl(Injector injector) {
+ mInjector = injector;
+ }
+
+ @Override
+ @NonNull
+ public EventIndex getEventIndex(@Event.EventType int eventType) {
+ EventIndex eventIndex = mEventIndexArray.get(eventType);
+ return eventIndex != null ? new EventIndex(eventIndex) : mInjector.createEventIndex();
+ }
+
+ @Override
+ @NonNull
+ public EventIndex getEventIndex(Set<Integer> eventTypes) {
+ EventIndex combined = mInjector.createEventIndex();
+ for (@Event.EventType int eventType : eventTypes) {
+ EventIndex eventIndex = mEventIndexArray.get(eventType);
+ if (eventIndex != null) {
+ combined = EventIndex.combine(combined, eventIndex);
+ }
+ }
+ return combined;
+ }
+
+ @Override
+ @NonNull
+ public List<Event> queryEvents(Set<Integer> eventTypes, long startTime, long endTime) {
+ return mRecentEvents.queryEvents(eventTypes, startTime, endTime);
+ }
+
+ void addEvent(Event event) {
+ EventIndex eventIndex = mEventIndexArray.get(event.getType());
+ if (eventIndex == null) {
+ eventIndex = mInjector.createEventIndex();
+ mEventIndexArray.put(event.getType(), eventIndex);
+ }
+ eventIndex.addEvent(event.getTimestamp());
+ mRecentEvents.add(event);
+ }
+
+ @VisibleForTesting
+ static class Injector {
+
+ EventIndex createEventIndex() {
+ return new EventIndex();
+ }
+ }
+}
diff --git a/services/people/java/com/android/server/people/data/EventIndex.java b/services/people/java/com/android/server/people/data/EventIndex.java
new file mode 100644
index 000000000000..b74a3fae98a5
--- /dev/null
+++ b/services/people/java/com/android/server/people/data/EventIndex.java
@@ -0,0 +1,377 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.people.data;
+
+import android.annotation.IntDef;
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.text.format.DateFormat;
+import android.util.Range;
+
+import com.android.internal.annotations.VisibleForTesting;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.time.Instant;
+import java.time.LocalDateTime;
+import java.time.ZoneId;
+import java.time.temporal.ChronoUnit;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.List;
+import java.util.TimeZone;
+import java.util.function.Function;
+
+/**
+ * The index of {@link Event}s. It is used for quickly looking up the time distribution of
+ * {@link Event}s based on {@code Event#getTimestamp()}.
+ *
+ * <p>The 64-bits {code long} is used as the bitmap index. Each bit is to denote whether there are
+ * any events in a specified time slot. The least significant bit is for the most recent time slot.
+ * And the most significant bit is for the oldest time slot.
+ *
+ * <p>Multiple {code long}s are used to index the events in different time grains. For the recent
+ * events, the fine-grained bitmap index can provide the narrower time range. For the older events,
+ * the coarse-grained bitmap index can cover longer period but can only provide wider time range.
+ *
+ * <p>E.g. the below chart shows how the bitmap indexes index the events in the past 24 hours:
+ * <pre>
+ * 2020/1/3 2020/1/4
+ * 0:00 4:00 8:00 12:00 16:00 20:00 0:00
+ * --+-----------------------------------------------------------------------+- 1 day per bit
+ * --+-----------+-----------+-----------+-----------+-----------+-----------+- 4 hours per bit
+ * --+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+- 1 hour per bit
+ * +++++++++ 2 minutes per bit
+ * </pre>
+ */
+public class EventIndex {
+
+ private static final int LONG_SIZE_BITS = 64;
+
+ private static final int TIME_SLOT_ONE_DAY = 0;
+
+ private static final int TIME_SLOT_FOUR_HOURS = 1;
+
+ private static final int TIME_SLOT_ONE_HOUR = 2;
+
+ private static final int TIME_SLOT_TWO_MINUTES = 3;
+
+ @IntDef(prefix = {"TIME_SLOT_"}, value = {
+ TIME_SLOT_ONE_DAY,
+ TIME_SLOT_FOUR_HOURS,
+ TIME_SLOT_ONE_HOUR,
+ TIME_SLOT_TWO_MINUTES,
+ })
+ @Retention(RetentionPolicy.SOURCE)
+ private @interface TimeSlotType {
+ }
+
+ private static final int TIME_SLOT_TYPES_COUNT = 4;
+
+ static final EventIndex EMPTY = new EventIndex();
+
+ private static final List<Function<Long, Range<Long>>> TIME_SLOT_FACTORIES =
+ Collections.unmodifiableList(
+ Arrays.asList(
+ EventIndex::createOneDayLongTimeSlot,
+ EventIndex::createFourHoursLongTimeSlot,
+ EventIndex::createOneHourLongTimeSlot,
+ EventIndex::createTwoMinutesLongTimeSlot
+ )
+ );
+
+ /** Combines the two {@link EventIndex} objects and returns the combined result. */
+ static EventIndex combine(EventIndex lhs, EventIndex rhs) {
+ EventIndex older = lhs.mLastUpdatedTime < rhs.mLastUpdatedTime ? lhs : rhs;
+ EventIndex younger = lhs.mLastUpdatedTime >= rhs.mLastUpdatedTime ? lhs : rhs;
+
+ EventIndex combined = new EventIndex(older);
+ combined.updateEventBitmaps(younger.mLastUpdatedTime);
+
+ for (int slotType = 0; slotType < TIME_SLOT_TYPES_COUNT; slotType++) {
+ combined.mEventBitmaps[slotType] |= younger.mEventBitmaps[slotType];
+ }
+ return combined;
+ }
+
+ private final long[] mEventBitmaps;
+
+ private long mLastUpdatedTime;
+
+ private final Object mLock = new Object();
+
+ private final Injector mInjector;
+
+ EventIndex() {
+ mInjector = new Injector();
+ mEventBitmaps = new long[]{0L, 0L, 0L, 0L};
+ mLastUpdatedTime = mInjector.currentTimeMillis();
+ }
+
+ EventIndex(EventIndex from) {
+ mInjector = new Injector();
+ mEventBitmaps = Arrays.copyOf(from.mEventBitmaps, TIME_SLOT_TYPES_COUNT);
+ mLastUpdatedTime = from.mLastUpdatedTime;
+ }
+
+ @VisibleForTesting
+ EventIndex(Injector injector) {
+ mInjector = injector;
+ mEventBitmaps = new long[]{0L, 0L, 0L, 0L};
+ mLastUpdatedTime = mInjector.currentTimeMillis();
+ }
+
+ /**
+ * Gets the most recent active time slot. A time slot is active if there is at least one event
+ * occurred in that time slot.
+ */
+ @Nullable
+ public Range<Long> getMostRecentActiveTimeSlot() {
+ synchronized (mLock) {
+ for (int slotType = TIME_SLOT_TYPES_COUNT - 1; slotType >= 0; slotType--) {
+ if (mEventBitmaps[slotType] == 0L) {
+ continue;
+ }
+ Range<Long> lastTimeSlot =
+ TIME_SLOT_FACTORIES.get(slotType).apply(mLastUpdatedTime);
+ int numberOfTrailingZeros = Long.numberOfTrailingZeros(mEventBitmaps[slotType]);
+ long offset = getDuration(lastTimeSlot) * numberOfTrailingZeros;
+ return Range.create(lastTimeSlot.getLower() - offset,
+ lastTimeSlot.getUpper() - offset);
+ }
+ }
+ return null;
+ }
+
+ /**
+ * Gets the active time slots. A time slot is active if there is at least one event occurred
+ * in that time slot.
+ *
+ * @return active time slots in chronological order.
+ */
+ @NonNull
+ public List<Range<Long>> getActiveTimeSlots() {
+ List<Range<Long>> activeTimeSlots = new ArrayList<>();
+ synchronized (mLock) {
+ for (int slotType = 0; slotType < TIME_SLOT_TYPES_COUNT; slotType++) {
+ activeTimeSlots = combineTimeSlotLists(activeTimeSlots,
+ getActiveTimeSlotsForType(slotType));
+ }
+ }
+ Collections.reverse(activeTimeSlots);
+ return activeTimeSlots;
+ }
+
+ /** Returns whether this {@link EventIndex} instance is empty. */
+ public boolean isEmpty() {
+ synchronized (mLock) {
+ for (int slotType = 0; slotType < TIME_SLOT_TYPES_COUNT; slotType++) {
+ if (mEventBitmaps[slotType] != 0L) {
+ return false;
+ }
+ }
+ }
+ return true;
+ }
+
+ /**
+ * Adds an event to this index with the given event time. Before the new event is recorded, the
+ * index is updated first with the current timestamp.
+ */
+ void addEvent(long eventTime) {
+ if (EMPTY == this) {
+ throw new IllegalStateException("EMPTY instance is immutable");
+ }
+ synchronized (mLock) {
+ long currentTime = mInjector.currentTimeMillis();
+ updateEventBitmaps(currentTime);
+ for (int slotType = 0; slotType < TIME_SLOT_TYPES_COUNT; slotType++) {
+ int offset = diffTimeSlots(slotType, eventTime, currentTime);
+ if (offset < LONG_SIZE_BITS) {
+ mEventBitmaps[slotType] |= (1L << offset);
+ }
+ }
+ }
+ }
+
+ /** Updates to make all bitmaps up to date. */
+ void update() {
+ updateEventBitmaps(mInjector.currentTimeMillis());
+ }
+
+ @Override
+ public String toString() {
+ StringBuilder sb = new StringBuilder();
+ sb.append("EventIndex {");
+ sb.append("perDayEventBitmap=0b");
+ sb.append(Long.toBinaryString(mEventBitmaps[TIME_SLOT_ONE_DAY]));
+ sb.append(", perFourHoursEventBitmap=0b");
+ sb.append(Long.toBinaryString(mEventBitmaps[TIME_SLOT_FOUR_HOURS]));
+ sb.append(", perHourEventBitmap=0b");
+ sb.append(Long.toBinaryString(mEventBitmaps[TIME_SLOT_ONE_HOUR]));
+ sb.append(", perTwoMinutesEventBitmap=0b");
+ sb.append(Long.toBinaryString(mEventBitmaps[TIME_SLOT_TWO_MINUTES]));
+ sb.append(", lastUpdatedTime=");
+ sb.append(DateFormat.format("yyyy-MM-dd HH:mm:ss", mLastUpdatedTime));
+ sb.append("}");
+ return sb.toString();
+ }
+
+ /** Shifts the event bitmaps to make them up-to-date. */
+ private void updateEventBitmaps(long currentTimeMillis) {
+ for (int slotType = 0; slotType < TIME_SLOT_TYPES_COUNT; slotType++) {
+ int offset = diffTimeSlots(slotType, mLastUpdatedTime, currentTimeMillis);
+ if (offset < LONG_SIZE_BITS) {
+ mEventBitmaps[slotType] <<= offset;
+ } else {
+ mEventBitmaps[slotType] = 0L;
+ }
+ }
+ mLastUpdatedTime = currentTimeMillis;
+ }
+
+ private static LocalDateTime toLocalDateTime(long epochMilli) {
+ return LocalDateTime.ofInstant(
+ Instant.ofEpochMilli(epochMilli), TimeZone.getDefault().toZoneId());
+ }
+
+ private static long toEpochMilli(LocalDateTime localDateTime) {
+ return localDateTime.atZone(ZoneId.systemDefault()).toInstant().toEpochMilli();
+ }
+
+ private static long getDuration(Range<Long> timeSlot) {
+ return timeSlot.getUpper() - timeSlot.getLower();
+ }
+
+ /**
+ * Finds the time slots for the given two timestamps and returns the distance (in the number
+ * of time slots) between these two time slots.
+ */
+ private static int diffTimeSlots(@TimeSlotType int timeSlotType, long fromTime, long toTime) {
+ Function<Long, Range<Long>> timeSlotFactory = TIME_SLOT_FACTORIES.get(timeSlotType);
+ Range<Long> fromSlot = timeSlotFactory.apply(fromTime);
+ Range<Long> toSlot = timeSlotFactory.apply(toTime);
+ return (int) ((toSlot.getLower() - fromSlot.getLower()) / getDuration(fromSlot));
+ }
+
+ /**
+ * Returns the active time slots for a specified type. The returned time slots are in
+ * reverse-chronological order.
+ */
+ private List<Range<Long>> getActiveTimeSlotsForType(@TimeSlotType int timeSlotType) {
+ long eventBitmap = mEventBitmaps[timeSlotType];
+ Range<Long> latestTimeSlot = TIME_SLOT_FACTORIES.get(timeSlotType).apply(mLastUpdatedTime);
+ long startTime = latestTimeSlot.getLower();
+ final long duration = getDuration(latestTimeSlot);
+ List<Range<Long>> timeSlots = new ArrayList<>();
+ while (eventBitmap != 0) {
+ int trailingZeros = Long.numberOfTrailingZeros(eventBitmap);
+ if (trailingZeros > 0) {
+ startTime -= duration * trailingZeros;
+ eventBitmap >>>= trailingZeros;
+ }
+ if (eventBitmap != 0) {
+ timeSlots.add(Range.create(startTime, startTime + duration));
+ startTime -= duration;
+ eventBitmap >>>= 1;
+ }
+ }
+ return timeSlots;
+ }
+
+ /**
+ * Combines two lists of time slots into one. If one longer time slot covers one or multiple
+ * shorter time slots, the smaller time slot(s) will be added to the result and the longer one
+ * will be dropped. This ensures the returned list does not contain any overlapping time slots.
+ */
+ private static List<Range<Long>> combineTimeSlotLists(List<Range<Long>> longerSlots,
+ List<Range<Long>> shorterSlots) {
+ List<Range<Long>> result = new ArrayList<>();
+ int i = 0;
+ int j = 0;
+ while (i < longerSlots.size() && j < shorterSlots.size()) {
+ Range<Long> longerSlot = longerSlots.get(i);
+ Range<Long> shorterSlot = shorterSlots.get(j);
+ if (longerSlot.contains(shorterSlot)) {
+ result.add(shorterSlot);
+ i++;
+ j++;
+ } else if (longerSlot.getLower() < shorterSlot.getLower()) {
+ result.add(shorterSlot);
+ j++;
+ } else {
+ result.add(longerSlot);
+ i++;
+ }
+ }
+ if (i < longerSlots.size()) {
+ result.addAll(longerSlots.subList(i, longerSlots.size()));
+ } else if (j < shorterSlots.size()) {
+ result.addAll(shorterSlots.subList(j, shorterSlots.size()));
+ }
+ return result;
+ }
+
+ /**
+ * Finds and creates the time slot (duration = 1 day) that the given time falls into.
+ */
+ @NonNull
+ private static Range<Long> createOneDayLongTimeSlot(long time) {
+ LocalDateTime beginTime = toLocalDateTime(time).truncatedTo(ChronoUnit.DAYS);
+ return Range.create(toEpochMilli(beginTime), toEpochMilli(beginTime.plusDays(1)));
+ }
+
+ /**
+ * Finds and creates the time slot (duration = 4 hours) that the given time falls into.
+ */
+ @NonNull
+ private static Range<Long> createFourHoursLongTimeSlot(long time) {
+ int hourOfDay = toLocalDateTime(time).getHour();
+ LocalDateTime beginTime =
+ toLocalDateTime(time).truncatedTo(ChronoUnit.HOURS).minusHours(hourOfDay % 4);
+ return Range.create(toEpochMilli(beginTime), toEpochMilli(beginTime.plusHours(4)));
+ }
+
+ /**
+ * Finds and creates the time slot (duration = 1 hour) that the given time falls into.
+ */
+ @NonNull
+ private static Range<Long> createOneHourLongTimeSlot(long time) {
+ LocalDateTime beginTime = toLocalDateTime(time).truncatedTo(ChronoUnit.HOURS);
+ return Range.create(toEpochMilli(beginTime), toEpochMilli(beginTime.plusHours(1)));
+ }
+
+ /**
+ * Finds and creates the time slot (duration = 2 minutes) that the given time falls into.
+ */
+ @NonNull
+ private static Range<Long> createTwoMinutesLongTimeSlot(long time) {
+ int minuteOfHour = toLocalDateTime(time).getMinute();
+ LocalDateTime beginTime = toLocalDateTime(time).truncatedTo(
+ ChronoUnit.MINUTES).minusMinutes(minuteOfHour % 2);
+ return Range.create(toEpochMilli(beginTime), toEpochMilli(beginTime.plusMinutes(2)));
+ }
+
+ @VisibleForTesting
+ static class Injector {
+ /** This should be the only way to get the current timestamp in {@code EventIndex}. */
+ long currentTimeMillis() {
+ return System.currentTimeMillis();
+ }
+ }
+}
diff --git a/services/people/java/com/android/server/people/data/EventList.java b/services/people/java/com/android/server/people/data/EventList.java
new file mode 100644
index 000000000000..b267d667b422
--- /dev/null
+++ b/services/people/java/com/android/server/people/data/EventList.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.people.data;
+
+import android.annotation.NonNull;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Set;
+
+/** A container that holds a list of {@link Event}s in chronological order. */
+class EventList {
+
+ private final List<Event> mEvents = new ArrayList<>();
+
+ /**
+ * Adds an event to the list unless there is an existing event with the same timestamp and
+ * type.
+ */
+ void add(@NonNull Event event) {
+ int index = firstIndexOnOrAfter(event.getTimestamp());
+ if (index < mEvents.size()
+ && mEvents.get(index).getTimestamp() == event.getTimestamp()
+ && isDuplicate(event, index)) {
+ return;
+ }
+ mEvents.add(index, event);
+ }
+
+ /**
+ * Returns a {@link List} of {@link Event}s whose timestamps are between the specified {@code
+ * fromTimestamp}, inclusive, and {@code toTimestamp} exclusive, and match the specified event
+ * types.
+ *
+ * @return a {@link List} of matched {@link Event}s in chronological order.
+ */
+ @NonNull
+ List<Event> queryEvents(@NonNull Set<Integer> eventTypes, long fromTimestamp,
+ long toTimestamp) {
+ int fromIndex = firstIndexOnOrAfter(fromTimestamp);
+ if (fromIndex == mEvents.size()) {
+ return new ArrayList<>();
+ }
+ int toIndex = firstIndexOnOrAfter(toTimestamp);
+ if (toIndex < fromIndex) {
+ return new ArrayList<>();
+ }
+ List<Event> result = new ArrayList<>();
+ for (int i = fromIndex; i < toIndex; i++) {
+ Event e = mEvents.get(i);
+ if (eventTypes.contains(e.getType())) {
+ result.add(e);
+ }
+ }
+ return result;
+ }
+
+ /** Returns the first index whose timestamp is greater or equal to the provided timestamp. */
+ private int firstIndexOnOrAfter(long timestamp) {
+ int result = mEvents.size();
+ int low = 0;
+ int high = mEvents.size() - 1;
+ while (low <= high) {
+ int mid = (low + high) >>> 1;
+ if (mEvents.get(mid).getTimestamp() >= timestamp) {
+ high = mid - 1;
+ result = mid;
+ } else {
+ low = mid + 1;
+ }
+ }
+ return result;
+ }
+
+ /**
+ * Checks whether the {@link Event} is duplicate with one of the existing events. The checking
+ * starts from the {@code startIndex}.
+ */
+ private boolean isDuplicate(Event event, int startIndex) {
+ int size = mEvents.size();
+ int index = startIndex;
+ while (index < size && mEvents.get(index).getTimestamp() <= event.getTimestamp()) {
+ if (mEvents.get(index++).getType() == event.getType()) {
+ return true;
+ }
+ }
+ return false;
+ }
+}
diff --git a/services/people/java/com/android/server/people/data/EventStore.java b/services/people/java/com/android/server/people/data/EventStore.java
new file mode 100644
index 000000000000..d6b7a863ca2d
--- /dev/null
+++ b/services/people/java/com/android/server/people/data/EventStore.java
@@ -0,0 +1,114 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.people.data;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.content.LocusId;
+import android.util.ArrayMap;
+
+import java.util.Map;
+
+/** The store that stores and accesses the events data for a package. */
+class EventStore {
+
+ private final EventHistoryImpl mPackageEventHistory = new EventHistoryImpl();
+
+ // Shortcut ID -> Event History
+ private final Map<String, EventHistoryImpl> mShortcutEventHistoryMap = new ArrayMap<>();
+
+ // Locus ID -> Event History
+ private final Map<LocusId, EventHistoryImpl> mLocusEventHistoryMap = new ArrayMap<>();
+
+ // Phone Number -> Event History
+ private final Map<String, EventHistoryImpl> mCallEventHistoryMap = new ArrayMap<>();
+
+ // Phone Number -> Event History
+ private final Map<String, EventHistoryImpl> mSmsEventHistoryMap = new ArrayMap<>();
+
+ /** Gets the package level {@link EventHistory}. */
+ @NonNull
+ EventHistory getPackageEventHistory() {
+ return mPackageEventHistory;
+ }
+
+ /** Gets the {@link EventHistory} for the specified {@code shortcutId} if exists. */
+ @Nullable
+ EventHistory getShortcutEventHistory(String shortcutId) {
+ return mShortcutEventHistoryMap.get(shortcutId);
+ }
+
+ /** Gets the {@link EventHistory} for the specified {@code locusId} if exists. */
+ @Nullable
+ EventHistory getLocusEventHistory(LocusId locusId) {
+ return mLocusEventHistoryMap.get(locusId);
+ }
+
+ /** Gets the phone call {@link EventHistory} for the specified {@code phoneNumber} if exists. */
+ @Nullable
+ EventHistory getCallEventHistory(String phoneNumber) {
+ return mCallEventHistoryMap.get(phoneNumber);
+ }
+
+ /** Gets the SMS {@link EventHistory} for the specified {@code phoneNumber} if exists. */
+ @Nullable
+ EventHistory getSmsEventHistory(String phoneNumber) {
+ return mSmsEventHistoryMap.get(phoneNumber);
+ }
+
+ /**
+ * Gets the {@link EventHistoryImpl} for the specified {@code shortcutId} or creates a new
+ * instance and put it into the store if not exists. The caller needs to verify if a
+ * conversation with this shortcut ID exists before calling this method.
+ */
+ @NonNull
+ EventHistoryImpl getOrCreateShortcutEventHistory(String shortcutId) {
+ return mShortcutEventHistoryMap.computeIfAbsent(shortcutId, key -> new EventHistoryImpl());
+ }
+
+ /**
+ * Gets the {@link EventHistoryImpl} for the specified {@code locusId} or creates a new
+ * instance and put it into the store if not exists. The caller needs to ensure a conversation
+ * with this locus ID exists before calling this method.
+ */
+ @NonNull
+ EventHistoryImpl getOrCreateLocusEventHistory(LocusId locusId) {
+ return mLocusEventHistoryMap.computeIfAbsent(locusId, key -> new EventHistoryImpl());
+ }
+
+ /**
+ * Gets the {@link EventHistoryImpl} for the specified {@code phoneNumber} for call events
+ * or creates a new instance and put it into the store if not exists. The caller needs to ensure
+ * a conversation with this phone number exists and this package is the default dialer
+ * before calling this method.
+ */
+ @NonNull
+ EventHistoryImpl getOrCreateCallEventHistory(String phoneNumber) {
+ return mCallEventHistoryMap.computeIfAbsent(phoneNumber, key -> new EventHistoryImpl());
+ }
+
+ /**
+ * Gets the {@link EventHistoryImpl} for the specified {@code phoneNumber} for SMS events
+ * or creates a new instance and put it into the store if not exists. The caller needs to ensure
+ * a conversation with this phone number exists and this package is the default SMS app
+ * before calling this method.
+ */
+ @NonNull
+ EventHistoryImpl getOrCreateSmsEventHistory(String phoneNumber) {
+ return mSmsEventHistoryMap.computeIfAbsent(phoneNumber, key -> new EventHistoryImpl());
+ }
+}
diff --git a/services/people/java/com/android/server/people/data/PackageData.java b/services/people/java/com/android/server/people/data/PackageData.java
new file mode 100644
index 000000000000..9c22a7f1c484
--- /dev/null
+++ b/services/people/java/com/android/server/people/data/PackageData.java
@@ -0,0 +1,145 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.people.data;
+
+import android.annotation.NonNull;
+import android.annotation.UserIdInt;
+import android.content.LocusId;
+import android.text.TextUtils;
+
+import java.util.function.Consumer;
+
+/** The data associated with a package. */
+public class PackageData {
+
+ @NonNull
+ private final String mPackageName;
+
+ private final @UserIdInt int mUserId;
+
+ @NonNull
+ private final ConversationStore mConversationStore;
+
+ @NonNull
+ private final EventStore mEventStore;
+
+ private boolean mIsDefaultDialer;
+
+ private boolean mIsDefaultSmsApp;
+
+ PackageData(@NonNull String packageName, @UserIdInt int userId) {
+ mPackageName = packageName;
+ mUserId = userId;
+ mConversationStore = new ConversationStore();
+ mEventStore = new EventStore();
+ }
+
+ @NonNull
+ public String getPackageName() {
+ return mPackageName;
+ }
+
+ public @UserIdInt int getUserId() {
+ return mUserId;
+ }
+
+ /** Iterates over all the conversations in this package. */
+ public void forAllConversations(@NonNull Consumer<ConversationInfo> consumer) {
+ mConversationStore.forAllConversations(consumer);
+ }
+
+ @NonNull
+ public EventHistory getPackageLevelEventHistory() {
+ return getEventStore().getPackageEventHistory();
+ }
+
+ /**
+ * Gets the combined {@link EventHistory} for a given shortcut ID. This returned {@link
+ * EventHistory} has events of all types, no matter whether they're annotated with shortcut ID,
+ * Locus ID, or phone number etc.
+ */
+ @NonNull
+ public EventHistory getEventHistory(@NonNull String shortcutId) {
+ AggregateEventHistoryImpl result = new AggregateEventHistoryImpl();
+
+ ConversationInfo conversationInfo = mConversationStore.getConversation(shortcutId);
+ if (conversationInfo == null) {
+ return result;
+ }
+
+ EventHistory shortcutEventHistory = getEventStore().getShortcutEventHistory(shortcutId);
+ if (shortcutEventHistory != null) {
+ result.addEventHistory(shortcutEventHistory);
+ }
+
+ LocusId locusId = conversationInfo.getLocusId();
+ if (locusId != null) {
+ EventHistory locusEventHistory = getEventStore().getLocusEventHistory(locusId);
+ if (locusEventHistory != null) {
+ result.addEventHistory(locusEventHistory);
+ }
+ }
+
+ String phoneNumber = conversationInfo.getContactPhoneNumber();
+ if (TextUtils.isEmpty(phoneNumber)) {
+ return result;
+ }
+ if (isDefaultDialer()) {
+ EventHistory callEventHistory = getEventStore().getCallEventHistory(phoneNumber);
+ if (callEventHistory != null) {
+ result.addEventHistory(callEventHistory);
+ }
+ }
+ if (isDefaultSmsApp()) {
+ EventHistory smsEventHistory = getEventStore().getSmsEventHistory(phoneNumber);
+ if (smsEventHistory != null) {
+ result.addEventHistory(smsEventHistory);
+ }
+ }
+ return result;
+ }
+
+ public boolean isDefaultDialer() {
+ return mIsDefaultDialer;
+ }
+
+ public boolean isDefaultSmsApp() {
+ return mIsDefaultSmsApp;
+ }
+
+ @NonNull
+ ConversationStore getConversationStore() {
+ return mConversationStore;
+ }
+
+ @NonNull
+ EventStore getEventStore() {
+ return mEventStore;
+ }
+
+ void setIsDefaultDialer(boolean value) {
+ mIsDefaultDialer = value;
+ }
+
+ void setIsDefaultSmsApp(boolean value) {
+ mIsDefaultSmsApp = value;
+ }
+
+ void onDestroy() {
+ // TODO: STOPSHIP: Implements this method for the case of package being uninstalled.
+ }
+}
diff --git a/services/people/java/com/android/server/people/data/UserData.java b/services/people/java/com/android/server/people/data/UserData.java
new file mode 100644
index 000000000000..2c16059e89ba
--- /dev/null
+++ b/services/people/java/com/android/server/people/data/UserData.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.people.data;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.annotation.UserIdInt;
+import android.text.TextUtils;
+import android.util.ArrayMap;
+
+import java.util.Map;
+import java.util.function.Consumer;
+
+/** The data associated with a user profile. */
+class UserData {
+
+ private final @UserIdInt int mUserId;
+
+ private boolean mIsUnlocked;
+
+ private Map<String, PackageData> mPackageDataMap = new ArrayMap<>();
+
+ UserData(@UserIdInt int userId) {
+ mUserId = userId;
+ }
+
+ @UserIdInt int getUserId() {
+ return mUserId;
+ }
+
+ void forAllPackages(@NonNull Consumer<PackageData> consumer) {
+ for (PackageData packageData : mPackageDataMap.values()) {
+ consumer.accept(packageData);
+ }
+ }
+
+ void setUserUnlocked() {
+ mIsUnlocked = true;
+ }
+
+ void setUserStopped() {
+ mIsUnlocked = false;
+ }
+
+ boolean isUnlocked() {
+ return mIsUnlocked;
+ }
+
+ /**
+ * Gets the {@link PackageData} for the specified {@code packageName} if exists; otherwise
+ * creates a new instance and returns it.
+ */
+ @NonNull
+ PackageData getOrCreatePackageData(String packageName) {
+ return mPackageDataMap.computeIfAbsent(
+ packageName, key -> new PackageData(packageName, mUserId));
+ }
+
+ /**
+ * Gets the {@link PackageData} for the specified {@code packageName} if exists; otherwise
+ * returns {@code null}.
+ */
+ @Nullable
+ PackageData getPackageData(@NonNull String packageName) {
+ return mPackageDataMap.get(packageName);
+ }
+
+ void setDefaultDialer(@Nullable String packageName) {
+ for (PackageData packageData : mPackageDataMap.values()) {
+ if (packageData.isDefaultDialer()) {
+ packageData.setIsDefaultDialer(false);
+ }
+ if (TextUtils.equals(packageName, packageData.getPackageName())) {
+ packageData.setIsDefaultDialer(true);
+ }
+ }
+ }
+
+ void setDefaultSmsApp(@Nullable String packageName) {
+ for (PackageData packageData : mPackageDataMap.values()) {
+ if (packageData.isDefaultSmsApp()) {
+ packageData.setIsDefaultSmsApp(false);
+ }
+ if (TextUtils.equals(packageName, packageData.getPackageName())) {
+ packageData.setIsDefaultSmsApp(true);
+ }
+ }
+ }
+}
diff --git a/services/people/java/com/android/server/people/prediction/ConversationData.java b/services/people/java/com/android/server/people/prediction/ConversationData.java
new file mode 100644
index 000000000000..0cc763329764
--- /dev/null
+++ b/services/people/java/com/android/server/people/prediction/ConversationData.java
@@ -0,0 +1,55 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.people.prediction;
+
+import android.annotation.NonNull;
+
+import com.android.server.people.data.ConversationInfo;
+import com.android.server.people.data.EventHistory;
+
+/** The conversation data which is used for scoring and then ranking the conversations. */
+class ConversationData {
+
+ private final String mPackageName;
+ private final int mUserId;
+ private final ConversationInfo mConversationInfo;
+ private final EventHistory mEventHistory;
+
+ ConversationData(@NonNull String packageName, int userId,
+ @NonNull ConversationInfo conversationInfo, @NonNull EventHistory eventHistory) {
+ mPackageName = packageName;
+ mUserId = userId;
+ mConversationInfo = conversationInfo;
+ mEventHistory = eventHistory;
+ }
+
+ String getPackageName() {
+ return mPackageName;
+ }
+
+ int getUserId() {
+ return mUserId;
+ }
+
+ ConversationInfo getConversationInfo() {
+ return mConversationInfo;
+ }
+
+ EventHistory getEventHistory() {
+ return mEventHistory;
+ }
+}
diff --git a/services/people/java/com/android/server/people/prediction/ConversationPredictor.java b/services/people/java/com/android/server/people/prediction/ConversationPredictor.java
index de71d292ff7d..ed8a56bb6435 100644
--- a/services/people/java/com/android/server/people/prediction/ConversationPredictor.java
+++ b/services/people/java/com/android/server/people/prediction/ConversationPredictor.java
@@ -17,12 +17,20 @@
package com.android.server.people.prediction;
import android.annotation.MainThread;
+import android.annotation.NonNull;
+import android.annotation.Nullable;
import android.app.prediction.AppPredictionContext;
import android.app.prediction.AppTarget;
import android.app.prediction.AppTargetEvent;
import android.app.prediction.AppTargetId;
+import android.content.IntentFilter;
+import android.content.pm.ShortcutInfo;
+import android.content.pm.ShortcutManager.ShareShortcutInfo;
import com.android.internal.annotations.VisibleForTesting;
+import com.android.internal.app.ChooserActivity;
+import com.android.server.people.data.DataManager;
+import com.android.server.people.data.EventHistory;
import java.util.ArrayList;
import java.util.List;
@@ -35,15 +43,28 @@ import java.util.function.Consumer;
*/
public class ConversationPredictor {
+ private static final String UI_SURFACE_SHARE = "share";
+
private final AppPredictionContext mPredictionContext;
private final Consumer<List<AppTarget>> mUpdatePredictionsMethod;
+ private final DataManager mDataManager;
private final ExecutorService mCallbackExecutor;
+ @Nullable
+ private final IntentFilter mIntentFilter;
- public ConversationPredictor(AppPredictionContext predictionContext,
- Consumer<List<AppTarget>> updatePredictionsMethod) {
+ public ConversationPredictor(@NonNull AppPredictionContext predictionContext,
+ @NonNull Consumer<List<AppTarget>> updatePredictionsMethod,
+ @NonNull DataManager dataManager) {
mPredictionContext = predictionContext;
mUpdatePredictionsMethod = updatePredictionsMethod;
+ mDataManager = dataManager;
mCallbackExecutor = Executors.newSingleThreadExecutor();
+ if (UI_SURFACE_SHARE.equals(mPredictionContext.getUiSurface())) {
+ mIntentFilter = mPredictionContext.getExtras().getParcelable(
+ ChooserActivity.APP_PREDICTION_INTENT_FILTER_KEY);
+ } else {
+ mIntentFilter = null;
+ }
}
/**
@@ -51,14 +72,14 @@ public class ConversationPredictor {
*/
@MainThread
public void onAppTargetEvent(AppTargetEvent event) {
+ mDataManager.reportAppTargetEvent(event, mIntentFilter);
}
/**
* Called by the client app to indicate a particular location has been shown to the user.
*/
@MainThread
- public void onLaunchLocationShown(String launchLocation, List<AppTargetId> targetIds) {
- }
+ public void onLaunchLocationShown(String launchLocation, List<AppTargetId> targetIds) {}
/**
* Called by the client app to request sorting of the provided targets based on the prediction
@@ -74,8 +95,44 @@ public class ConversationPredictor {
*/
@MainThread
public void onRequestPredictionUpdate() {
- List<AppTarget> targets = new ArrayList<>();
- mCallbackExecutor.execute(() -> mUpdatePredictionsMethod.accept(targets));
+ // TODO: Re-route the call to different ranking classes for different surfaces.
+ mCallbackExecutor.execute(() -> {
+ List<AppTarget> targets = new ArrayList<>();
+ if (mIntentFilter != null) {
+ List<ShareShortcutInfo> shareShortcuts =
+ mDataManager.getConversationShareTargets(mIntentFilter);
+ for (ShareShortcutInfo shareShortcut : shareShortcuts) {
+ ShortcutInfo shortcutInfo = shareShortcut.getShortcutInfo();
+ AppTargetId appTargetId = new AppTargetId(shortcutInfo.getId());
+ String shareTargetClass = shareShortcut.getTargetComponent().getClassName();
+ targets.add(new AppTarget.Builder(appTargetId, shortcutInfo)
+ .setClassName(shareTargetClass)
+ .build());
+ }
+ } else {
+ List<ConversationData> conversationDataList = new ArrayList<>();
+ mDataManager.forAllPackages(packageData ->
+ packageData.forAllConversations(conversationInfo -> {
+ EventHistory eventHistory = packageData.getEventHistory(
+ conversationInfo.getShortcutId());
+ ConversationData conversationData = new ConversationData(
+ packageData.getPackageName(), packageData.getUserId(),
+ conversationInfo, eventHistory);
+ conversationDataList.add(conversationData);
+ }));
+ for (ConversationData conversationData : conversationDataList) {
+ String shortcutId = conversationData.getConversationInfo().getShortcutId();
+ ShortcutInfo shortcut = mDataManager.getShortcut(
+ conversationData.getPackageName(), conversationData.getUserId(),
+ shortcutId);
+ if (shortcut != null) {
+ AppTargetId appTargetId = new AppTargetId(shortcut.getId());
+ targets.add(new AppTarget.Builder(appTargetId, shortcut).build());
+ }
+ }
+ }
+ mUpdatePredictionsMethod.accept(targets);
+ });
}
@VisibleForTesting
diff --git a/services/tests/mockingservicestests/src/com/android/server/job/controllers/ConnectivityControllerTest.java b/services/tests/mockingservicestests/src/com/android/server/job/controllers/ConnectivityControllerTest.java
index 1c88c40c3e2b..e724e60b3ae5 100644
--- a/services/tests/mockingservicestests/src/com/android/server/job/controllers/ConnectivityControllerTest.java
+++ b/services/tests/mockingservicestests/src/com/android/server/job/controllers/ConnectivityControllerTest.java
@@ -20,6 +20,7 @@ import static android.net.NetworkCapabilities.NET_CAPABILITY_INTERNET;
import static android.net.NetworkCapabilities.NET_CAPABILITY_NOT_CONGESTED;
import static android.net.NetworkCapabilities.NET_CAPABILITY_NOT_METERED;
import static android.net.NetworkCapabilities.NET_CAPABILITY_VALIDATED;
+import static android.net.NetworkCapabilities.TRANSPORT_CELLULAR;
import static com.android.dx.mockito.inline.extended.ExtendedMockito.doNothing;
import static com.android.dx.mockito.inline.extended.ExtendedMockito.doReturn;
@@ -27,6 +28,9 @@ import static com.android.dx.mockito.inline.extended.ExtendedMockito.inOrder;
import static com.android.dx.mockito.inline.extended.ExtendedMockito.mock;
import static com.android.dx.mockito.inline.extended.ExtendedMockito.spy;
import static com.android.dx.mockito.inline.extended.ExtendedMockito.when;
+import static com.android.server.job.JobSchedulerService.FREQUENT_INDEX;
+import static com.android.server.job.JobSchedulerService.RARE_INDEX;
+import static com.android.server.job.JobSchedulerService.RESTRICTED_INDEX;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertTrue;
@@ -568,6 +572,48 @@ public class ConnectivityControllerTest {
assertFalse(controller.isStandbyExceptionRequestedLocked(UID_RED));
}
+ @Test
+ public void testRestrictedJobTracking() {
+ final JobStatus networked = createJobStatus(createJob()
+ .setEstimatedNetworkBytes(DataUnit.MEBIBYTES.toBytes(1), 0)
+ .setRequiredNetworkType(JobInfo.NETWORK_TYPE_CELLULAR), UID_RED);
+ final JobStatus unnetworked = createJobStatus(createJob(), UID_BLUE);
+ networked.setStandbyBucket(FREQUENT_INDEX);
+ unnetworked.setStandbyBucket(FREQUENT_INDEX);
+
+ final Network cellularNet = new Network(101);
+ final NetworkCapabilities cellularCaps =
+ createCapabilities().addTransportType(TRANSPORT_CELLULAR);
+ reset(mConnManager);
+ answerNetwork(UID_RED, cellularNet, cellularCaps);
+ answerNetwork(UID_BLUE, cellularNet, cellularCaps);
+
+ final ConnectivityController controller = new ConnectivityController(mService);
+ controller.maybeStartTrackingJobLocked(networked, null);
+ controller.maybeStartTrackingJobLocked(unnetworked, null);
+
+ assertTrue(networked.isConstraintSatisfied(JobStatus.CONSTRAINT_CONNECTIVITY));
+ assertFalse(unnetworked.isConstraintSatisfied(JobStatus.CONSTRAINT_CONNECTIVITY));
+
+ networked.setStandbyBucket(RESTRICTED_INDEX);
+ unnetworked.setStandbyBucket(RESTRICTED_INDEX);
+ controller.startTrackingRestrictedJobLocked(networked);
+ controller.startTrackingRestrictedJobLocked(unnetworked);
+ assertFalse(networked.isConstraintSatisfied(JobStatus.CONSTRAINT_CONNECTIVITY));
+ // Unnetworked shouldn't be affected by ConnectivityController since it doesn't have a
+ // connectivity constraint.
+ assertFalse(unnetworked.isConstraintSatisfied(JobStatus.CONSTRAINT_CONNECTIVITY));
+
+ networked.setStandbyBucket(RARE_INDEX);
+ unnetworked.setStandbyBucket(RARE_INDEX);
+ controller.stopTrackingRestrictedJobLocked(networked);
+ controller.stopTrackingRestrictedJobLocked(unnetworked);
+ assertTrue(networked.isConstraintSatisfied(JobStatus.CONSTRAINT_CONNECTIVITY));
+ // Unnetworked shouldn't be affected by ConnectivityController since it doesn't have a
+ // connectivity constraint.
+ assertFalse(unnetworked.isConstraintSatisfied(JobStatus.CONSTRAINT_CONNECTIVITY));
+ }
+
private void answerNetwork(int uid, Network net, NetworkCapabilities caps) {
when(mConnManager.getActiveNetworkForUid(eq(uid))).thenReturn(net);
when(mConnManager.getNetworkCapabilities(eq(net))).thenReturn(caps);
diff --git a/services/tests/servicestests/AndroidManifest.xml b/services/tests/servicestests/AndroidManifest.xml
index d2ddff3627b9..b7c900130e39 100644
--- a/services/tests/servicestests/AndroidManifest.xml
+++ b/services/tests/servicestests/AndroidManifest.xml
@@ -66,6 +66,8 @@
<uses-permission android:name="android.permission.FOREGROUND_SERVICE"/>
<uses-permission android:name="android.permission.SUSPEND_APPS"/>
<uses-permission android:name="android.permission.CONTROL_KEYGUARD"/>
+ <uses-permission android:name="android.permission.READ_COMPAT_CHANGE_CONFIG"/>
+ <uses-permission android:name="android.permission.LOG_COMPAT_CHANGE"/>
<uses-permission android:name="android.permission.MANAGE_BIND_INSTANT_SERVICE"/>
<uses-permission android:name="android.permission.CONTROL_DISPLAY_COLOR_TRANSFORMS" />
<uses-permission android:name="android.permission.READ_DEVICE_CONFIG" />
diff --git a/services/tests/servicestests/src/com/android/server/WatchdogDiagnosticsTest.java b/services/tests/servicestests/src/com/android/server/WatchdogDiagnosticsTest.java
index 4d229efc8328..625766a97f13 100644
--- a/services/tests/servicestests/src/com/android/server/WatchdogDiagnosticsTest.java
+++ b/services/tests/servicestests/src/com/android/server/WatchdogDiagnosticsTest.java
@@ -157,6 +157,8 @@ public class WatchdogDiagnosticsTest {
String expected =
"TestThread2 annotated stack trace:\n" +
" at java.lang.Object.wait(Native Method)\n" +
+ " at java.lang.Object.wait(Object.java:442)\n" +
+ " at java.lang.Object.wait(Object.java:568)\n" +
" at com.android.server.WatchdogDiagnosticsTest$TestThread2.y(" +
"WatchdogDiagnosticsTest.java:91)\n" +
" - locked <HASH> (a java.lang.String)\n" +
diff --git a/services/tests/servicestests/src/com/android/server/backup/restore/PerformUnifiedRestoreTaskTest.java b/services/tests/servicestests/src/com/android/server/backup/restore/PerformUnifiedRestoreTaskTest.java
index 6359edf190b0..3d220432cc8e 100644
--- a/services/tests/servicestests/src/com/android/server/backup/restore/PerformUnifiedRestoreTaskTest.java
+++ b/services/tests/servicestests/src/com/android/server/backup/restore/PerformUnifiedRestoreTaskTest.java
@@ -20,7 +20,6 @@ import static junit.framework.Assert.assertEquals;
import static junit.framework.Assert.assertFalse;
import static junit.framework.Assert.assertTrue;
-import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.anyInt;
import static org.mockito.Mockito.when;
@@ -55,6 +54,8 @@ public class PerformUnifiedRestoreTaskTest {
private static final String INCLUDED_KEY = "included_key";
private static final String EXCLUDED_KEY_1 = "excluded_key_1";
private static final String EXCLUDED_KEY_2 = "excluded_key_2";
+ private static final String SYSTEM_PACKAGE_NAME = "android";
+ private static final String NON_SYSTEM_PACKAGE_NAME = "package";
@Mock private BackupDataInput mBackupDataInput;
@Mock private BackupDataOutput mBackupDataOutput;
@@ -98,9 +99,6 @@ public class PerformUnifiedRestoreTaskTest {
return null;
}
});
-
- mRestoreTask = new PerformUnifiedRestoreTask(Collections.singletonMap(
- PACKAGE_NAME, mExcludedkeys));
}
private void populateTestData() {
@@ -116,6 +114,8 @@ public class PerformUnifiedRestoreTaskTest {
@Test
public void testFilterExcludedKeys() throws Exception {
+ mRestoreTask = new PerformUnifiedRestoreTask(Collections.singletonMap(
+ PACKAGE_NAME, mExcludedkeys));
mRestoreTask.filterExcludedKeys(PACKAGE_NAME, mBackupDataInput, mBackupDataOutput);
// Verify only the correct were written into BackupDataOutput object.
@@ -123,4 +123,34 @@ public class PerformUnifiedRestoreTaskTest {
allowedBackupKeys.removeAll(mExcludedkeys);
assertEquals(allowedBackupKeys, mBackupDataDump);
}
+
+ @Test
+ public void testStageBackupData_stageForNonSystemPackageWithKeysToExclude() {
+ mRestoreTask = new PerformUnifiedRestoreTask(Collections.singletonMap(
+ PACKAGE_NAME, mExcludedkeys));
+
+ assertTrue(mRestoreTask.shouldStageBackupData(NON_SYSTEM_PACKAGE_NAME));
+ }
+
+ @Test
+ public void testStageBackupData_stageForNonSystemPackageWithNoKeysToExclude() {
+ mRestoreTask = new PerformUnifiedRestoreTask(Collections.emptyMap());
+
+ assertTrue(mRestoreTask.shouldStageBackupData(NON_SYSTEM_PACKAGE_NAME));
+ }
+
+ @Test
+ public void testStageBackupData_doNotStageForSystemPackageWithNoKeysToExclude() {
+ mRestoreTask = new PerformUnifiedRestoreTask(Collections.emptyMap());
+
+ assertFalse(mRestoreTask.shouldStageBackupData(SYSTEM_PACKAGE_NAME));
+ }
+
+ @Test
+ public void testStageBackupData_stageForSystemPackageWithKeysToExclude() {
+ mRestoreTask = new PerformUnifiedRestoreTask(Collections.singletonMap(
+ PACKAGE_NAME, mExcludedkeys));
+
+ assertTrue(mRestoreTask.shouldStageBackupData(NON_SYSTEM_PACKAGE_NAME));
+ }
}
diff --git a/services/tests/servicestests/src/com/android/server/integrity/AppIntegrityManagerServiceImplTest.java b/services/tests/servicestests/src/com/android/server/integrity/AppIntegrityManagerServiceImplTest.java
index 9a633931017e..49ad866a56a4 100644
--- a/services/tests/servicestests/src/com/android/server/integrity/AppIntegrityManagerServiceImplTest.java
+++ b/services/tests/servicestests/src/com/android/server/integrity/AppIntegrityManagerServiceImplTest.java
@@ -32,11 +32,9 @@ import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.doReturn;
import static org.mockito.Mockito.doThrow;
import static org.mockito.Mockito.mock;
-import static org.mockito.Mockito.never;
import static org.mockito.Mockito.spy;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
-import static org.mockito.internal.verification.VerificationModeFactory.times;
import android.content.BroadcastReceiver;
import android.content.Context;
@@ -59,7 +57,6 @@ import android.os.Message;
import androidx.test.InstrumentationRegistry;
import com.android.internal.R;
-import com.android.server.LocalServices;
import com.android.server.integrity.engine.RuleEvaluationEngine;
import com.android.server.integrity.model.IntegrityCheckResult;
import com.android.server.testutils.TestUtils;
@@ -147,29 +144,8 @@ public class AppIntegrityManagerServiceImplTest {
when(mIntegrityFileManager.initialized()).thenReturn(true);
}
- // This is not a test of the class, but more of a safeguard that we don't block any install in
- // the default case. This is needed because we don't have any emergency kill switch to disable
- // this component.
- @Test
- public void default_allow() throws Exception {
- LocalServices.removeServiceForTest(PackageManagerInternal.class);
- LocalServices.addService(PackageManagerInternal.class, mPackageManagerInternal);
- mService = AppIntegrityManagerServiceImpl.create(mMockContext);
- ArgumentCaptor<BroadcastReceiver> broadcastReceiverCaptor =
- ArgumentCaptor.forClass(BroadcastReceiver.class);
- verify(mMockContext, times(2))
- .registerReceiver(broadcastReceiverCaptor.capture(), any(), any(), any());
- Intent intent = makeVerificationIntent();
-
- broadcastReceiverCaptor.getValue().onReceive(mMockContext, intent);
-
- // Since we are not mocking handler in this case, we must wait.
- // 2 seconds should be a sensible timeout.
- Thread.sleep(2000);
- verify(mPackageManagerInternal)
- .setIntegrityVerificationResult(
- 1, PackageManagerInternal.INTEGRITY_VERIFICATION_ALLOW);
- }
+ // TODO(b/148370598): Implement a test to validate that allow response is retuned when the test
+ // request times out.
@Test
public void updateRuleSet_notAuthorized() throws Exception {
@@ -352,10 +328,11 @@ public class AppIntegrityManagerServiceImplTest {
when(mRuleEvaluationEngine.evaluate(any(), any()))
.thenReturn(
IntegrityCheckResult.deny(
- new Rule(
- new AtomicFormula.BooleanAtomicFormula(
- AtomicFormula.PRE_INSTALLED, false),
- Rule.DENY)));
+ Arrays.asList(
+ new Rule(
+ new AtomicFormula.BooleanAtomicFormula(
+ AtomicFormula.PRE_INSTALLED, false),
+ Rule.DENY))));
Intent intent = makeVerificationIntent();
broadcastReceiverCaptor.getValue().onReceive(mMockContext, intent);
@@ -381,10 +358,10 @@ public class AppIntegrityManagerServiceImplTest {
broadcastReceiverCaptor.getValue().onReceive(mMockContext, intent);
runJobInHandler();
+ // The evaluation will still run since we still evaluate manifest based rules.
verify(mPackageManagerInternal)
.setIntegrityVerificationResult(
1, PackageManagerInternal.INTEGRITY_VERIFICATION_ALLOW);
- verify(mSpyPackageManager, never()).getPackageArchiveInfo(any(), anyInt());
}
@Test
@@ -432,8 +409,8 @@ public class AppIntegrityManagerServiceImplTest {
private Intent makeVerificationIntent() throws Exception {
PackageInfo packageInfo =
- mRealContext.getPackageManager().getPackageInfo(TEST_FRAMEWORK_PACKAGE,
- PackageManager.GET_SIGNATURES);
+ mRealContext.getPackageManager()
+ .getPackageInfo(TEST_FRAMEWORK_PACKAGE, PackageManager.GET_SIGNATURES);
doReturn(packageInfo)
.when(mSpyPackageManager)
.getPackageInfo(eq(INSTALLER), anyInt());
diff --git a/services/tests/servicestests/src/com/android/server/integrity/engine/RuleEvaluationEngineTest.java b/services/tests/servicestests/src/com/android/server/integrity/engine/RuleEvaluationEngineTest.java
index 99157024bb66..26b20965fbf5 100644
--- a/services/tests/servicestests/src/com/android/server/integrity/engine/RuleEvaluationEngineTest.java
+++ b/services/tests/servicestests/src/com/android/server/integrity/engine/RuleEvaluationEngineTest.java
@@ -16,12 +16,12 @@
package com.android.server.integrity.engine;
-import static org.junit.Assert.assertEquals;
+import static com.google.common.truth.Truth.assertThat;
+
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.Mockito.when;
import android.content.integrity.AppInstallMetadata;
-import android.content.integrity.Rule;
import com.android.server.integrity.IntegrityFileManager;
import com.android.server.integrity.model.IntegrityCheckResult;
@@ -36,7 +36,6 @@ import org.mockito.MockitoAnnotations;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
-import java.util.List;
import java.util.Map;
@RunWith(JUnit4.class)
@@ -68,33 +67,28 @@ public class RuleEvaluationEngineTest {
public void testAllowedInstallers_empty() {
Map<String, String> allowedInstallers = Collections.emptyMap();
- assertEquals(
- IntegrityCheckResult.Effect.ALLOW,
- mEngine.evaluate(
- getAppInstallMetadataBuilder()
- .setInstallerName(INSTALLER_1)
- .setInstallerCertificate(INSTALLER_1_CERT)
- .build(),
- allowedInstallers)
- .getEffect());
- assertEquals(
- IntegrityCheckResult.Effect.ALLOW,
- mEngine.evaluate(
- getAppInstallMetadataBuilder()
- .setInstallerName(INSTALLER_2)
- .setInstallerCertificate(INSTALLER_2_CERT)
- .build(),
- allowedInstallers)
- .getEffect());
- assertEquals(
- IntegrityCheckResult.Effect.ALLOW,
- mEngine.evaluate(
- getAppInstallMetadataBuilder()
- .setInstallerName(RANDOM_INSTALLER)
- .setInstallerCertificate(RANDOM_INSTALLER_CERT)
- .build(),
- allowedInstallers)
- .getEffect());
+ AppInstallMetadata appInstallMetadata1 =
+ getAppInstallMetadataBuilder()
+ .setInstallerName(INSTALLER_1)
+ .setInstallerCertificate(INSTALLER_1_CERT)
+ .build();
+ AppInstallMetadata appInstallMetadata2 =
+ getAppInstallMetadataBuilder()
+ .setInstallerName(INSTALLER_2)
+ .setInstallerCertificate(INSTALLER_2_CERT)
+ .build();
+ AppInstallMetadata appInstallMetadata3 =
+ getAppInstallMetadataBuilder()
+ .setInstallerName(RANDOM_INSTALLER)
+ .setInstallerCertificate(RANDOM_INSTALLER_CERT)
+ .build();
+
+ assertThat(mEngine.evaluate(appInstallMetadata1, allowedInstallers).getEffect())
+ .isEqualTo(IntegrityCheckResult.Effect.ALLOW);
+ assertThat(mEngine.evaluate(appInstallMetadata2, allowedInstallers).getEffect())
+ .isEqualTo(IntegrityCheckResult.Effect.ALLOW);
+ assertThat(mEngine.evaluate(appInstallMetadata3, allowedInstallers).getEffect())
+ .isEqualTo(IntegrityCheckResult.Effect.ALLOW);
}
@Test
@@ -102,87 +96,100 @@ public class RuleEvaluationEngineTest {
Map<String, String> allowedInstallers =
Collections.singletonMap(INSTALLER_1, INSTALLER_1_CERT);
- assertEquals(
- IntegrityCheckResult.Effect.ALLOW,
- mEngine.evaluate(
- getAppInstallMetadataBuilder()
- .setInstallerName(INSTALLER_1)
- .setInstallerCertificate(INSTALLER_1_CERT)
- .build(),
- allowedInstallers)
- .getEffect());
- assertEquals(
- IntegrityCheckResult.Effect.DENY,
- mEngine.evaluate(
- getAppInstallMetadataBuilder()
- .setInstallerName(RANDOM_INSTALLER)
- .setInstallerCertificate(INSTALLER_1_CERT)
- .build(),
- allowedInstallers)
- .getEffect());
- assertEquals(
- IntegrityCheckResult.Effect.DENY,
- mEngine.evaluate(
- getAppInstallMetadataBuilder()
- .setInstallerName(INSTALLER_1)
- .setInstallerCertificate(RANDOM_INSTALLER_CERT)
- .build(),
- allowedInstallers)
- .getEffect());
- assertEquals(
- IntegrityCheckResult.Effect.DENY,
- mEngine.evaluate(
- getAppInstallMetadataBuilder()
- .setInstallerName(RANDOM_INSTALLER)
- .setInstallerCertificate(RANDOM_INSTALLER_CERT)
- .build(),
- allowedInstallers)
- .getEffect());
+ AppInstallMetadata appInstallMetadata1 =
+ getAppInstallMetadataBuilder()
+ .setInstallerName(INSTALLER_1)
+ .setInstallerCertificate(INSTALLER_1_CERT)
+ .build();
+ assertThat(mEngine.evaluate(appInstallMetadata1, allowedInstallers).getEffect())
+ .isEqualTo(IntegrityCheckResult.Effect.ALLOW);
+
+ AppInstallMetadata appInstallMetadata2 =
+ getAppInstallMetadataBuilder()
+ .setInstallerName(RANDOM_INSTALLER)
+ .setInstallerCertificate(INSTALLER_1_CERT)
+ .build();
+ assertThat(mEngine.evaluate(appInstallMetadata2, allowedInstallers).getEffect())
+ .isEqualTo(IntegrityCheckResult.Effect.DENY);
+
+ AppInstallMetadata appInstallMetadata3 =
+ getAppInstallMetadataBuilder()
+ .setInstallerName(INSTALLER_1)
+ .setInstallerCertificate(RANDOM_INSTALLER_CERT)
+ .build();
+ assertThat(mEngine.evaluate(appInstallMetadata3, allowedInstallers).getEffect())
+ .isEqualTo(IntegrityCheckResult.Effect.DENY);
+
+ AppInstallMetadata appInstallMetadata4 =
+ getAppInstallMetadataBuilder()
+ .setInstallerName(INSTALLER_1)
+ .setInstallerCertificate(RANDOM_INSTALLER_CERT)
+ .build();
+ assertThat(mEngine.evaluate(appInstallMetadata4, allowedInstallers).getEffect())
+ .isEqualTo(IntegrityCheckResult.Effect.DENY);
}
@Test
public void testAllowedInstallers_multipleElement() {
- List<Rule> rules = new ArrayList<>();
Map<String, String> allowedInstallers = new HashMap<>(2);
allowedInstallers.put(INSTALLER_1, INSTALLER_1_CERT);
allowedInstallers.put(INSTALLER_2, INSTALLER_2_CERT);
- assertEquals(
- IntegrityCheckResult.Effect.ALLOW,
- mEngine.evaluate(
- getAppInstallMetadataBuilder()
- .setInstallerName(INSTALLER_1)
- .setInstallerCertificate(INSTALLER_1_CERT)
- .build(),
- allowedInstallers)
- .getEffect());
- assertEquals(
- IntegrityCheckResult.Effect.ALLOW,
- mEngine.evaluate(
- getAppInstallMetadataBuilder()
- .setInstallerName(INSTALLER_2)
- .setInstallerCertificate(INSTALLER_2_CERT)
- .build(),
- allowedInstallers)
- .getEffect());
- assertEquals(
- IntegrityCheckResult.Effect.DENY,
- mEngine.evaluate(
- getAppInstallMetadataBuilder()
- .setInstallerName(INSTALLER_1)
- .setInstallerCertificate(INSTALLER_2_CERT)
- .build(),
- allowedInstallers)
- .getEffect());
- assertEquals(
- IntegrityCheckResult.Effect.DENY,
- mEngine.evaluate(
- getAppInstallMetadataBuilder()
- .setInstallerName(INSTALLER_2)
- .setInstallerCertificate(INSTALLER_1_CERT)
- .build(),
- allowedInstallers)
- .getEffect());
+ AppInstallMetadata appInstallMetadata1 =
+ getAppInstallMetadataBuilder()
+ .setInstallerName(INSTALLER_1)
+ .setInstallerCertificate(INSTALLER_1_CERT)
+ .build();
+ assertThat(mEngine.evaluate(appInstallMetadata1, allowedInstallers).getEffect())
+ .isEqualTo(IntegrityCheckResult.Effect.ALLOW);
+
+ AppInstallMetadata appInstallMetadata2 =
+ getAppInstallMetadataBuilder()
+ .setInstallerName(INSTALLER_2)
+ .setInstallerCertificate(INSTALLER_2_CERT)
+ .build();
+ assertThat(mEngine.evaluate(appInstallMetadata2, allowedInstallers).getEffect())
+ .isEqualTo(IntegrityCheckResult.Effect.ALLOW);
+
+ AppInstallMetadata appInstallMetadata3 =
+ getAppInstallMetadataBuilder()
+ .setInstallerName(INSTALLER_1)
+ .setInstallerCertificate(INSTALLER_2_CERT)
+ .build();
+ assertThat(mEngine.evaluate(appInstallMetadata3, allowedInstallers).getEffect())
+ .isEqualTo(IntegrityCheckResult.Effect.DENY);
+
+ AppInstallMetadata appInstallMetadata4 =
+ getAppInstallMetadataBuilder()
+ .setInstallerName(INSTALLER_2)
+ .setInstallerCertificate(INSTALLER_1_CERT)
+ .build();
+ assertThat(mEngine.evaluate(appInstallMetadata4, allowedInstallers).getEffect())
+ .isEqualTo(IntegrityCheckResult.Effect.DENY);
+ }
+
+ @Test
+ public void manifestBasedRuleEvaluationWorksEvenWhenIntegrityFilesAreUnavailable() {
+ when(mIntegrityFileManager.initialized()).thenReturn(false);
+
+ Map<String, String> allowedInstallers =
+ Collections.singletonMap(INSTALLER_1, INSTALLER_1_CERT);
+
+ AppInstallMetadata appInstallMetadata1 =
+ getAppInstallMetadataBuilder()
+ .setInstallerName(INSTALLER_1)
+ .setInstallerCertificate(INSTALLER_1_CERT)
+ .build();
+ assertThat(mEngine.evaluate(appInstallMetadata1, allowedInstallers).getEffect())
+ .isEqualTo(IntegrityCheckResult.Effect.ALLOW);
+
+ AppInstallMetadata appInstallMetadata2 =
+ getAppInstallMetadataBuilder()
+ .setInstallerName(RANDOM_INSTALLER)
+ .setInstallerCertificate(INSTALLER_1_CERT)
+ .build();
+ assertThat(mEngine.evaluate(appInstallMetadata2, allowedInstallers).getEffect())
+ .isEqualTo(IntegrityCheckResult.Effect.DENY);
}
/** Returns a builder with all fields filled with some dummy data. */
diff --git a/services/tests/servicestests/src/com/android/server/integrity/engine/RuleEvaluatorTest.java b/services/tests/servicestests/src/com/android/server/integrity/engine/RuleEvaluatorTest.java
index 629fd14befcc..7b53e5e7c762 100644
--- a/services/tests/servicestests/src/com/android/server/integrity/engine/RuleEvaluatorTest.java
+++ b/services/tests/servicestests/src/com/android/server/integrity/engine/RuleEvaluatorTest.java
@@ -63,7 +63,7 @@ public class RuleEvaluatorTest {
@Test
public void testEvaluateRules_noMatchedRules_allow() {
- Rule rule1 =
+ Rule rule =
new Rule(
new StringAtomicFormula(
AtomicFormula.PACKAGE_NAME,
@@ -72,7 +72,7 @@ public class RuleEvaluatorTest {
Rule.DENY);
IntegrityCheckResult result =
- RuleEvaluator.evaluateRules(Collections.singletonList(rule1), APP_INSTALL_METADATA);
+ RuleEvaluator.evaluateRules(Collections.singletonList(rule), APP_INSTALL_METADATA);
assertThat(result.getEffect()).isEqualTo(ALLOW);
}
@@ -98,7 +98,7 @@ public class RuleEvaluatorTest {
RuleEvaluator.evaluateRules(Arrays.asList(rule1, rule2), APP_INSTALL_METADATA);
assertThat(result.getEffect()).isEqualTo(DENY);
- assertThat(result.getRule()).isEqualTo(rule1);
+ assertThat(result.getMatchedRules()).containsExactly(rule1);
}
@Test
@@ -110,7 +110,7 @@ public class RuleEvaluatorTest {
PACKAGE_NAME_1,
/* isHashedValue= */ false),
Rule.DENY);
- CompoundFormula compoundFormula2 =
+ Rule rule2 = new Rule(
new CompoundFormula(
CompoundFormula.AND,
Arrays.asList(
@@ -121,33 +121,33 @@ public class RuleEvaluatorTest {
new StringAtomicFormula(
AtomicFormula.APP_CERTIFICATE,
APP_CERTIFICATE,
- /* isHashedValue= */ false)));
- Rule rule2 = new Rule(compoundFormula2, Rule.DENY);
+ /* isHashedValue= */ false))),
+ Rule.DENY);
IntegrityCheckResult result =
RuleEvaluator.evaluateRules(Arrays.asList(rule1, rule2), APP_INSTALL_METADATA);
assertThat(result.getEffect()).isEqualTo(DENY);
- assertThat(result.getRule()).isEqualTo(rule1);
+ assertThat(result.getMatchedRules()).containsExactly(rule1, rule2);
}
@Test
public void testEvaluateRules_ruleWithNot_deny() {
- CompoundFormula compoundFormula =
+ Rule rule = new Rule(
new CompoundFormula(
CompoundFormula.NOT,
Collections.singletonList(
new StringAtomicFormula(
AtomicFormula.PACKAGE_NAME,
PACKAGE_NAME_2,
- /* isHashedValue= */ false)));
- Rule rule = new Rule(compoundFormula, Rule.DENY);
+ /* isHashedValue= */ false))),
+ Rule.DENY);
IntegrityCheckResult result =
RuleEvaluator.evaluateRules(Collections.singletonList(rule), APP_INSTALL_METADATA);
assertThat(result.getEffect()).isEqualTo(DENY);
- assertThat(result.getRule()).isEqualTo(rule);
+ assertThat(result.getMatchedRules()).containsExactly(rule);
}
@Test
@@ -162,12 +162,12 @@ public class RuleEvaluatorTest {
RuleEvaluator.evaluateRules(Collections.singletonList(rule), APP_INSTALL_METADATA);
assertThat(result.getEffect()).isEqualTo(DENY);
- assertThat(result.getRule()).isEqualTo(rule);
+ assertThat(result.getMatchedRules()).containsExactly(rule);
}
@Test
public void testEvaluateRules_validForm_deny() {
- CompoundFormula compoundFormula =
+ Rule rule = new Rule(
new CompoundFormula(
CompoundFormula.AND,
Arrays.asList(
@@ -178,19 +178,19 @@ public class RuleEvaluatorTest {
new StringAtomicFormula(
AtomicFormula.APP_CERTIFICATE,
APP_CERTIFICATE,
- /* isHashedValue= */ false)));
- Rule rule = new Rule(compoundFormula, Rule.DENY);
+ /* isHashedValue= */ false))),
+ Rule.DENY);
IntegrityCheckResult result =
RuleEvaluator.evaluateRules(Collections.singletonList(rule), APP_INSTALL_METADATA);
assertThat(result.getEffect()).isEqualTo(DENY);
- assertThat(result.getRule()).isEqualTo(rule);
+ assertThat(result.getMatchedRules()).containsExactly(rule);
}
@Test
public void testEvaluateRules_orRules() {
- CompoundFormula compoundFormula =
+ Rule rule = new Rule(
new CompoundFormula(
CompoundFormula.OR,
Arrays.asList(
@@ -201,13 +201,14 @@ public class RuleEvaluatorTest {
new StringAtomicFormula(
AtomicFormula.APP_CERTIFICATE,
APP_CERTIFICATE,
- /* isHashedValue= */ false)));
- Rule rule = new Rule(compoundFormula, Rule.DENY);
+ /* isHashedValue= */ false))),
+ Rule.DENY);
IntegrityCheckResult result =
RuleEvaluator.evaluateRules(Collections.singletonList(rule), APP_INSTALL_METADATA);
assertThat(result.getEffect()).isEqualTo(DENY);
+ assertThat(result.getMatchedRules()).containsExactly(rule);
}
@Test
@@ -232,6 +233,7 @@ public class RuleEvaluatorTest {
RuleEvaluator.evaluateRules(Collections.singletonList(rule), APP_INSTALL_METADATA);
assertThat(result.getEffect()).isEqualTo(DENY);
+ assertThat(result.getMatchedRules()).containsExactly(rule);
}
@Test
@@ -243,7 +245,7 @@ public class RuleEvaluatorTest {
PACKAGE_NAME_1,
/* isHashedValue= */ false),
Rule.FORCE_ALLOW);
- CompoundFormula compoundFormula2 =
+ Rule rule2 = new Rule(
new CompoundFormula(
CompoundFormula.AND,
Arrays.asList(
@@ -254,13 +256,43 @@ public class RuleEvaluatorTest {
new StringAtomicFormula(
AtomicFormula.APP_CERTIFICATE,
APP_CERTIFICATE,
- /* isHashedValue= */ false)));
- Rule rule2 = new Rule(compoundFormula2, Rule.DENY);
+ /* isHashedValue= */ false))),
+ Rule.DENY);
+
+ IntegrityCheckResult result =
+ RuleEvaluator.evaluateRules(Arrays.asList(rule1, rule2), APP_INSTALL_METADATA);
+
+ assertThat(result.getEffect()).isEqualTo(ALLOW);
+ assertThat(result.getMatchedRules()).containsExactly(rule1);
+ }
+
+ @Test
+ public void testEvaluateRules_multipleMatches_forceAllow() {
+ Rule rule1 =
+ new Rule(
+ new StringAtomicFormula(
+ AtomicFormula.PACKAGE_NAME,
+ PACKAGE_NAME_1,
+ /* isHashedValue= */ false),
+ Rule.FORCE_ALLOW);
+ Rule rule2 = new Rule(
+ new CompoundFormula(
+ CompoundFormula.AND,
+ Arrays.asList(
+ new StringAtomicFormula(
+ AtomicFormula.PACKAGE_NAME,
+ PACKAGE_NAME_1,
+ /* isHashedValue= */ false),
+ new StringAtomicFormula(
+ AtomicFormula.APP_CERTIFICATE,
+ APP_CERTIFICATE,
+ /* isHashedValue= */ false))),
+ Rule.FORCE_ALLOW);
IntegrityCheckResult result =
RuleEvaluator.evaluateRules(Arrays.asList(rule1, rule2), APP_INSTALL_METADATA);
assertThat(result.getEffect()).isEqualTo(ALLOW);
- assertThat(result.getRule()).isEqualTo(rule1);
+ assertThat(result.getMatchedRules()).containsExactly(rule1, rule2);
}
} \ No newline at end of file
diff --git a/services/tests/servicestests/src/com/android/server/integrity/model/IntegrityCheckResultTest.java b/services/tests/servicestests/src/com/android/server/integrity/model/IntegrityCheckResultTest.java
new file mode 100644
index 000000000000..9cc9f10e2c82
--- /dev/null
+++ b/services/tests/servicestests/src/com/android/server/integrity/model/IntegrityCheckResultTest.java
@@ -0,0 +1,79 @@
+/*
+ * 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.integrity.model;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import android.content.integrity.AtomicFormula;
+import android.content.integrity.Rule;
+import android.util.StatsLog;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.JUnit4;
+
+import java.util.Collections;
+
+@RunWith(JUnit4.class)
+public class IntegrityCheckResultTest {
+
+ @Test
+ public void createAllowResult() {
+ IntegrityCheckResult allowResult = IntegrityCheckResult.allow();
+
+ assertThat(allowResult.getEffect()).isEqualTo(IntegrityCheckResult.Effect.ALLOW);
+ assertThat(allowResult.getMatchedRules()).isEmpty();
+ assertThat(allowResult.getLoggingResponse())
+ .isEqualTo(StatsLog.INTEGRITY_CHECK_RESULT_REPORTED__RESPONSE__ALLOWED);
+ }
+
+ @Test
+ public void createAllowResultWithRule() {
+ String packageName = "com.test.deny";
+ Rule forceAllowRule =
+ new Rule(
+ new AtomicFormula.StringAtomicFormula(AtomicFormula.PACKAGE_NAME,
+ packageName),
+ Rule.FORCE_ALLOW);
+
+ IntegrityCheckResult allowResult =
+ IntegrityCheckResult.allow(Collections.singletonList(forceAllowRule));
+
+ assertThat(allowResult.getEffect()).isEqualTo(IntegrityCheckResult.Effect.ALLOW);
+ assertThat(allowResult.getMatchedRules()).containsExactly(forceAllowRule);
+ assertThat(allowResult.getLoggingResponse())
+ .isEqualTo(StatsLog.INTEGRITY_CHECK_RESULT_REPORTED__RESPONSE__FORCE_ALLOWED);
+ }
+
+ @Test
+ public void createDenyResultWithRule() {
+ String packageName = "com.test.deny";
+ Rule failedRule =
+ new Rule(
+ new AtomicFormula.StringAtomicFormula(AtomicFormula.PACKAGE_NAME,
+ packageName),
+ Rule.DENY);
+
+ IntegrityCheckResult denyResult =
+ IntegrityCheckResult.deny(Collections.singletonList(failedRule));
+
+ assertThat(denyResult.getEffect()).isEqualTo(IntegrityCheckResult.Effect.DENY);
+ assertThat(denyResult.getMatchedRules()).containsExactly(failedRule);
+ assertThat(denyResult.getLoggingResponse())
+ .isEqualTo(StatsLog.INTEGRITY_CHECK_RESULT_REPORTED__RESPONSE__REJECTED);
+ }
+}
diff --git a/services/tests/servicestests/src/com/android/server/GnssManagerServiceTest.java b/services/tests/servicestests/src/com/android/server/location/gnss/GnssManagerServiceTest.java
index 8b5444c2c55c..f262733bb882 100644
--- a/services/tests/servicestests/src/com/android/server/GnssManagerServiceTest.java
+++ b/services/tests/servicestests/src/com/android/server/location/gnss/GnssManagerServiceTest.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,7 +14,7 @@
* limitations under the License.
*/
-package com.android.server;
+package com.android.server.location.gnss;
import static com.google.common.truth.Truth.assertThat;
@@ -52,6 +52,7 @@ import android.os.IInterface;
import android.os.Message;
import android.os.RemoteException;
+import com.android.server.LocationManagerService;
import com.android.server.location.GnssBatchingProvider;
import com.android.server.location.GnssCapabilitiesProvider;
import com.android.server.location.GnssLocationProvider;
@@ -71,11 +72,12 @@ import org.mockito.Mockito;
import org.mockito.MockitoAnnotations;
import org.mockito.invocation.InvocationOnMock;
+import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
/**
- * Unit tests for {@link com.android.server.GnssManagerService}.
+ * Unit tests for {@link com.android.server.location.gnss.GnssManagerService}.
*/
public class GnssManagerServiceTest {
@@ -111,7 +113,7 @@ public class GnssManagerServiceTest {
private Context mMockContext;
// Class under test
- private GnssManagerService mGnssManagerService;
+ private com.android.server.location.gnss.GnssManagerService mGnssManagerService;
@Before
public void setUp() {
@@ -371,7 +373,7 @@ public class GnssManagerServiceTest {
@Test
public void addGnssBatchCallbackWithoutPermissionsTest() throws RemoteException {
IBatchedLocationCallback mockBatchedLocationCallback = createMockBatchedLocationCallback();
- List<Location> mockLocationList = (List<Location>) mock(List.class);
+ List<Location> mockLocationList = new ArrayList<>();
disableLocationPermissions();
@@ -387,7 +389,7 @@ public class GnssManagerServiceTest {
@Test
public void addGnssBatchCallbackWithPermissionsTest() throws RemoteException {
IBatchedLocationCallback mockBatchedLocationCallback = createMockBatchedLocationCallback();
- List<Location> mockLocationList = (List<Location>) mock(List.class);
+ List<Location> mockLocationList = new ArrayList<>();
enableLocationPermissions();
@@ -404,7 +406,7 @@ public class GnssManagerServiceTest {
public void replaceGnssBatchCallbackTest() throws RemoteException {
IBatchedLocationCallback mockBatchedLocationCallback1 = createMockBatchedLocationCallback();
IBatchedLocationCallback mockBatchedLocationCallback2 = createMockBatchedLocationCallback();
- List<Location> mockLocationList = (List<Location>) mock(List.class);
+ List<Location> mockLocationList = new ArrayList<>();
enableLocationPermissions();
@@ -441,7 +443,7 @@ public class GnssManagerServiceTest {
@Test
public void removeGnssBatchingCallbackWithoutPermissionsTest() throws RemoteException {
IBatchedLocationCallback mockBatchedLocationCallback = createMockBatchedLocationCallback();
- List<Location> mockLocationList = (List<Location>) mock(List.class);
+ List<Location> mockLocationList = new ArrayList<>();
enableLocationPermissions();
@@ -461,7 +463,7 @@ public class GnssManagerServiceTest {
@Test
public void removeGnssBatchingCallbackWithPermissionsTest() throws RemoteException {
IBatchedLocationCallback mockBatchedLocationCallback = createMockBatchedLocationCallback();
- List<Location> mockLocationList = (List<Location>) mock(List.class);
+ List<Location> mockLocationList = new ArrayList<>();
enableLocationPermissions();
diff --git a/services/tests/servicestests/src/com/android/server/people/data/AggregateEventHistoryImplTest.java b/services/tests/servicestests/src/com/android/server/people/data/AggregateEventHistoryImplTest.java
new file mode 100644
index 000000000000..b614a4f91d28
--- /dev/null
+++ b/services/tests/servicestests/src/com/android/server/people/data/AggregateEventHistoryImplTest.java
@@ -0,0 +1,131 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.people.data;
+
+import static com.android.server.people.data.TestUtils.timestamp;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertTrue;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.JUnit4;
+
+import java.util.List;
+
+@RunWith(JUnit4.class)
+public final class AggregateEventHistoryImplTest {
+
+ private static final long CURRENT_TIMESTAMP = timestamp("01-30 18:50");
+
+ private static final Event E1 = new Event(timestamp("01-06 05:26"),
+ Event.TYPE_NOTIFICATION_OPENED);
+ private static final Event E2 = new Event(timestamp("01-27 18:41"),
+ Event.TYPE_NOTIFICATION_OPENED);
+ private static final Event E3 = new Event(timestamp("01-30 03:06"),
+ Event.TYPE_SMS_OUTGOING);
+ private static final Event E4 = new Event(timestamp("01-30 18:14"),
+ Event.TYPE_SMS_INCOMING);
+
+ private EventHistoryImpl mEventHistory1;
+ private EventHistoryImpl mEventHistory2;
+
+ private AggregateEventHistoryImpl mAggEventHistory;
+
+ private EventIndex.Injector mInjector = new EventIndex.Injector() {
+ @Override
+ long currentTimeMillis() {
+ return CURRENT_TIMESTAMP;
+ }
+ };
+
+ @Before
+ public void setUp() {
+ mAggEventHistory = new AggregateEventHistoryImpl();
+
+ EventHistoryImpl.Injector injector = new EventHistoryImplInjector();
+
+ mEventHistory1 = new EventHistoryImpl(injector);
+ mEventHistory1.addEvent(E1);
+ mEventHistory1.addEvent(E2);
+
+ mEventHistory2 = new EventHistoryImpl(injector);
+ mEventHistory2.addEvent(E3);
+ mEventHistory2.addEvent(E4);
+ }
+
+ @Test
+ public void testEmptyAggregateEventHistory() {
+ assertTrue(mAggEventHistory.getEventIndex(Event.TYPE_SHORTCUT_INVOCATION).isEmpty());
+ assertTrue(mAggEventHistory.getEventIndex(Event.ALL_EVENT_TYPES).isEmpty());
+ assertTrue(mAggEventHistory.queryEvents(
+ Event.ALL_EVENT_TYPES, 0L, Long.MAX_VALUE).isEmpty());
+ }
+
+ @Test
+ public void testQueryEventIndexForSingleEventType() {
+ mAggEventHistory.addEventHistory(mEventHistory1);
+ mAggEventHistory.addEventHistory(mEventHistory2);
+
+ EventIndex eventIndex;
+
+ eventIndex = mAggEventHistory.getEventIndex(Event.TYPE_NOTIFICATION_OPENED);
+ assertEquals(2, eventIndex.getActiveTimeSlots().size());
+
+ eventIndex = mAggEventHistory.getEventIndex(Event.TYPE_SMS_OUTGOING);
+ assertEquals(1, eventIndex.getActiveTimeSlots().size());
+
+ eventIndex = mAggEventHistory.getEventIndex(Event.TYPE_SHORTCUT_INVOCATION);
+ assertTrue(eventIndex.isEmpty());
+ }
+
+ @Test
+ public void testQueryEventIndexForMultipleEventTypes() {
+ mAggEventHistory.addEventHistory(mEventHistory1);
+ mAggEventHistory.addEventHistory(mEventHistory2);
+
+ EventIndex eventIndex;
+
+ eventIndex = mAggEventHistory.getEventIndex(Event.SMS_EVENT_TYPES);
+ assertEquals(2, eventIndex.getActiveTimeSlots().size());
+
+ eventIndex = mAggEventHistory.getEventIndex(Event.ALL_EVENT_TYPES);
+ assertEquals(4, eventIndex.getActiveTimeSlots().size());
+ }
+
+ @Test
+ public void testQueryEvents() {
+ mAggEventHistory.addEventHistory(mEventHistory1);
+ mAggEventHistory.addEventHistory(mEventHistory2);
+
+ List<Event> events;
+
+ events = mAggEventHistory.queryEvents(Event.NOTIFICATION_EVENT_TYPES, 0L, Long.MAX_VALUE);
+ assertEquals(2, events.size());
+
+ events = mAggEventHistory.queryEvents(Event.ALL_EVENT_TYPES, 0L, Long.MAX_VALUE);
+ assertEquals(4, events.size());
+ }
+
+ private class EventHistoryImplInjector extends EventHistoryImpl.Injector {
+
+ EventIndex createEventIndex() {
+ return new EventIndex(mInjector);
+ }
+ }
+}
diff --git a/services/tests/servicestests/src/com/android/server/people/data/ContactsQueryHelperTest.java b/services/tests/servicestests/src/com/android/server/people/data/ContactsQueryHelperTest.java
new file mode 100644
index 000000000000..96302b954e75
--- /dev/null
+++ b/services/tests/servicestests/src/com/android/server/people/data/ContactsQueryHelperTest.java
@@ -0,0 +1,187 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.people.data;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertNull;
+import static org.junit.Assert.assertTrue;
+import static org.mockito.Mockito.when;
+
+import android.database.Cursor;
+import android.database.MatrixCursor;
+import android.net.Uri;
+import android.provider.ContactsContract;
+import android.provider.ContactsContract.Contacts;
+import android.test.mock.MockContentProvider;
+import android.test.mock.MockContentResolver;
+import android.test.mock.MockContext;
+import android.util.ArrayMap;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.JUnit4;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+
+import java.util.Map;
+
+@RunWith(JUnit4.class)
+public final class ContactsQueryHelperTest {
+
+ private static final String CONTACT_LOOKUP_KEY = "123";
+ private static final String PHONE_NUMBER = "+1234567890";
+
+ private static final String[] CONTACTS_COLUMNS = new String[] {
+ Contacts._ID, Contacts.LOOKUP_KEY, Contacts.STARRED, Contacts.HAS_PHONE_NUMBER,
+ Contacts.CONTACT_LAST_UPDATED_TIMESTAMP };
+ private static final String[] CONTACTS_LOOKUP_COLUMNS = new String[] {
+ Contacts._ID, Contacts.LOOKUP_KEY, Contacts.STARRED, Contacts.HAS_PHONE_NUMBER };
+ private static final String[] PHONE_COLUMNS = new String[] {
+ ContactsContract.CommonDataKinds.Phone.NORMALIZED_NUMBER };
+
+ @Mock
+ private MockContext mContext;
+
+ private MatrixCursor mContactsCursor;
+ private MatrixCursor mContactsLookupCursor;
+ private MatrixCursor mPhoneCursor;
+ private ContactsQueryHelper mHelper;
+
+ @Before
+ public void setUp() {
+ MockitoAnnotations.initMocks(this);
+
+ mContactsCursor = new MatrixCursor(CONTACTS_COLUMNS);
+ mContactsLookupCursor = new MatrixCursor(CONTACTS_LOOKUP_COLUMNS);
+ mPhoneCursor = new MatrixCursor(PHONE_COLUMNS);
+
+ MockContentResolver contentResolver = new MockContentResolver();
+ ContactsContentProvider contentProvider = new ContactsContentProvider();
+ contentProvider.registerCursor(Contacts.CONTENT_URI, mContactsCursor);
+ contentProvider.registerCursor(
+ ContactsContract.PhoneLookup.CONTENT_FILTER_URI, mContactsLookupCursor);
+ contentProvider.registerCursor(
+ ContactsContract.CommonDataKinds.Email.CONTENT_LOOKUP_URI, mContactsLookupCursor);
+ contentProvider.registerCursor(
+ ContactsContract.CommonDataKinds.Phone.CONTENT_URI, mPhoneCursor);
+
+ contentResolver.addProvider(ContactsContract.AUTHORITY, contentProvider);
+ when(mContext.getContentResolver()).thenReturn(contentResolver);
+
+ mHelper = new ContactsQueryHelper(mContext);
+ }
+
+ @Test
+ public void testQueryWithUri() {
+ mContactsCursor.addRow(new Object[] {
+ /* id= */ 11, CONTACT_LOOKUP_KEY, /* starred= */ 1, /* hasPhoneNumber= */ 1,
+ /* lastUpdatedTimestamp= */ 100L });
+ mPhoneCursor.addRow(new String[] { PHONE_NUMBER });
+ Uri contactUri = Uri.withAppendedPath(Contacts.CONTENT_LOOKUP_URI, CONTACT_LOOKUP_KEY);
+ assertTrue(mHelper.query(contactUri.toString()));
+ assertNotNull(mHelper.getContactUri());
+ assertEquals(PHONE_NUMBER, mHelper.getPhoneNumber());
+ assertEquals(100L, mHelper.getLastUpdatedTimestamp());
+ assertTrue(mHelper.isStarred());
+ }
+
+ @Test
+ public void testQueryWithUriNotStarredNoPhoneNumber() {
+ mContactsCursor.addRow(new Object[] {
+ /* id= */ 11, CONTACT_LOOKUP_KEY, /* starred= */ 0, /* hasPhoneNumber= */ 0,
+ /* lastUpdatedTimestamp= */ 100L });
+ Uri contactUri = Uri.withAppendedPath(Contacts.CONTENT_LOOKUP_URI, CONTACT_LOOKUP_KEY);
+ assertTrue(mHelper.query(contactUri.toString()));
+ assertNotNull(mHelper.getContactUri());
+ assertNull(mHelper.getPhoneNumber());
+ assertFalse(mHelper.isStarred());
+ assertEquals(100L, mHelper.getLastUpdatedTimestamp());
+ }
+
+ @Test
+ public void testQueryWithUriNotFound() {
+ Uri contactUri = Uri.withAppendedPath(Contacts.CONTENT_LOOKUP_URI, CONTACT_LOOKUP_KEY);
+ assertFalse(mHelper.query(contactUri.toString()));
+ }
+
+ @Test
+ public void testQueryWithPhoneNumber() {
+ mContactsLookupCursor.addRow(new Object[] {
+ /* id= */ 11, CONTACT_LOOKUP_KEY, /* starred= */ 1, /* hasPhoneNumber= */ 1 });
+ mPhoneCursor.addRow(new String[] { PHONE_NUMBER });
+ String contactUri = "tel:" + PHONE_NUMBER;
+ assertTrue(mHelper.query(contactUri));
+ assertNotNull(mHelper.getContactUri());
+ assertEquals(PHONE_NUMBER, mHelper.getPhoneNumber());
+ assertTrue(mHelper.isStarred());
+ }
+
+ @Test
+ public void testQueryWithEmail() {
+ mContactsLookupCursor.addRow(new Object[] {
+ /* id= */ 11, CONTACT_LOOKUP_KEY, /* starred= */ 1, /* hasPhoneNumber= */ 0 });
+ String contactUri = "mailto:test@gmail.com";
+ assertTrue(mHelper.query(contactUri));
+ assertNotNull(mHelper.getContactUri());
+ assertNull(mHelper.getPhoneNumber());
+ assertTrue(mHelper.isStarred());
+ }
+
+ @Test
+ public void testQueryUpdatedContactSinceTime() {
+ mContactsCursor.addRow(new Object[] {
+ /* id= */ 11, CONTACT_LOOKUP_KEY, /* starred= */ 1, /* hasPhoneNumber= */ 0,
+ /* lastUpdatedTimestamp= */ 100L });
+ assertTrue(mHelper.querySince(50L));
+ assertNotNull(mHelper.getContactUri());
+ assertNull(mHelper.getPhoneNumber());
+ assertTrue(mHelper.isStarred());
+ assertEquals(100L, mHelper.getLastUpdatedTimestamp());
+ }
+
+ @Test
+ public void testQueryWithUnsupportedScheme() {
+ mContactsLookupCursor.addRow(new Object[] {
+ /* id= */ 11, CONTACT_LOOKUP_KEY, /* starred= */ 1, /* hasPhoneNumber= */ 1 });
+ mPhoneCursor.addRow(new String[] { PHONE_NUMBER });
+ String contactUri = "unknown:test";
+ assertFalse(mHelper.query(contactUri));
+ }
+
+ private class ContactsContentProvider extends MockContentProvider {
+
+ private Map<Uri, Cursor> mUriPrefixToCursorMap = new ArrayMap<>();
+
+ @Override
+ public Cursor query(Uri uri, String[] projection, String selection, String[] selectionArgs,
+ String sortOrder) {
+ for (Uri prefixUri : mUriPrefixToCursorMap.keySet()) {
+ if (uri.isPathPrefixMatch(prefixUri)) {
+ return mUriPrefixToCursorMap.get(prefixUri);
+ }
+ }
+ return mUriPrefixToCursorMap.get(uri);
+ }
+
+ private void registerCursor(Uri uriPrefix, Cursor cursor) {
+ mUriPrefixToCursorMap.put(uriPrefix, cursor);
+ }
+ }
+}
diff --git a/services/tests/servicestests/src/com/android/server/people/data/ConversationInfoTest.java b/services/tests/servicestests/src/com/android/server/people/data/ConversationInfoTest.java
new file mode 100644
index 000000000000..05a9a80e262c
--- /dev/null
+++ b/services/tests/servicestests/src/com/android/server/people/data/ConversationInfoTest.java
@@ -0,0 +1,130 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.people.data;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertNull;
+import static org.junit.Assert.assertTrue;
+
+import android.content.LocusId;
+import android.content.pm.ShortcutInfo;
+import android.net.Uri;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.JUnit4;
+
+@RunWith(JUnit4.class)
+public final class ConversationInfoTest {
+
+ private static final String SHORTCUT_ID = "abc";
+ private static final LocusId LOCUS_ID = new LocusId("def");
+ private static final Uri CONTACT_URI = Uri.parse("tel:+1234567890");
+ private static final String PHONE_NUMBER = "+1234567890";
+ private static final String NOTIFICATION_CHANNEL_ID = "test : abc";
+
+ @Test
+ public void testBuild() {
+ ConversationInfo conversationInfo = new ConversationInfo.Builder()
+ .setShortcutId(SHORTCUT_ID)
+ .setLocusId(LOCUS_ID)
+ .setContactUri(CONTACT_URI)
+ .setContactPhoneNumber(PHONE_NUMBER)
+ .setNotificationChannelId(NOTIFICATION_CHANNEL_ID)
+ .setShortcutFlags(ShortcutInfo.FLAG_LONG_LIVED)
+ .setVip(true)
+ .setNotificationSilenced(true)
+ .setBubbled(true)
+ .setDemoted(true)
+ .setPersonImportant(true)
+ .setPersonBot(true)
+ .setContactStarred(true)
+ .build();
+
+ assertEquals(SHORTCUT_ID, conversationInfo.getShortcutId());
+ assertEquals(LOCUS_ID, conversationInfo.getLocusId());
+ assertEquals(CONTACT_URI, conversationInfo.getContactUri());
+ assertEquals(PHONE_NUMBER, conversationInfo.getContactPhoneNumber());
+ assertEquals(NOTIFICATION_CHANNEL_ID, conversationInfo.getNotificationChannelId());
+ assertTrue(conversationInfo.isShortcutLongLived());
+ assertTrue(conversationInfo.isVip());
+ assertTrue(conversationInfo.isNotificationSilenced());
+ assertTrue(conversationInfo.isBubbled());
+ assertTrue(conversationInfo.isDemoted());
+ assertTrue(conversationInfo.isPersonImportant());
+ assertTrue(conversationInfo.isPersonBot());
+ assertTrue(conversationInfo.isContactStarred());
+ }
+
+ @Test
+ public void testBuildEmpty() {
+ ConversationInfo conversationInfo = new ConversationInfo.Builder()
+ .setShortcutId(SHORTCUT_ID)
+ .build();
+
+ assertEquals(SHORTCUT_ID, conversationInfo.getShortcutId());
+ assertNull(conversationInfo.getLocusId());
+ assertNull(conversationInfo.getContactUri());
+ assertNull(conversationInfo.getContactPhoneNumber());
+ assertNull(conversationInfo.getNotificationChannelId());
+ assertFalse(conversationInfo.isShortcutLongLived());
+ assertFalse(conversationInfo.isVip());
+ assertFalse(conversationInfo.isNotificationSilenced());
+ assertFalse(conversationInfo.isBubbled());
+ assertFalse(conversationInfo.isDemoted());
+ assertFalse(conversationInfo.isPersonImportant());
+ assertFalse(conversationInfo.isPersonBot());
+ assertFalse(conversationInfo.isContactStarred());
+ }
+
+ @Test
+ public void testBuildFromAnotherConversationInfo() {
+ ConversationInfo source = new ConversationInfo.Builder()
+ .setShortcutId(SHORTCUT_ID)
+ .setLocusId(LOCUS_ID)
+ .setContactUri(CONTACT_URI)
+ .setContactPhoneNumber(PHONE_NUMBER)
+ .setNotificationChannelId(NOTIFICATION_CHANNEL_ID)
+ .setShortcutFlags(ShortcutInfo.FLAG_LONG_LIVED)
+ .setVip(true)
+ .setNotificationSilenced(true)
+ .setBubbled(true)
+ .setPersonImportant(true)
+ .setPersonBot(true)
+ .setContactStarred(true)
+ .build();
+
+ ConversationInfo destination = new ConversationInfo.Builder(source)
+ .setVip(false)
+ .setContactStarred(false)
+ .build();
+
+ assertEquals(SHORTCUT_ID, destination.getShortcutId());
+ assertEquals(LOCUS_ID, destination.getLocusId());
+ assertEquals(CONTACT_URI, destination.getContactUri());
+ assertEquals(PHONE_NUMBER, destination.getContactPhoneNumber());
+ assertEquals(NOTIFICATION_CHANNEL_ID, destination.getNotificationChannelId());
+ assertTrue(destination.isShortcutLongLived());
+ assertFalse(destination.isVip());
+ assertTrue(destination.isNotificationSilenced());
+ assertTrue(destination.isBubbled());
+ assertTrue(destination.isPersonImportant());
+ assertTrue(destination.isPersonBot());
+ assertFalse(destination.isContactStarred());
+ }
+}
diff --git a/services/tests/servicestests/src/com/android/server/people/data/ConversationStoreTest.java b/services/tests/servicestests/src/com/android/server/people/data/ConversationStoreTest.java
new file mode 100644
index 000000000000..a40c6ab90197
--- /dev/null
+++ b/services/tests/servicestests/src/com/android/server/people/data/ConversationStoreTest.java
@@ -0,0 +1,153 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.people.data;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertNull;
+import static org.junit.Assert.assertTrue;
+
+import android.content.LocusId;
+import android.content.pm.ShortcutInfo;
+import android.net.Uri;
+import android.util.ArraySet;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.JUnit4;
+
+import java.util.Set;
+
+@RunWith(JUnit4.class)
+public final class ConversationStoreTest {
+
+ private static final String SHORTCUT_ID = "abc";
+ private static final LocusId LOCUS_ID = new LocusId("def");
+ private static final Uri CONTACT_URI = Uri.parse("tel:+1234567890");
+ private static final String PHONE_NUMBER = "+1234567890";
+
+ private ConversationStore mConversationStore;
+
+ @Before
+ public void setUp() {
+ mConversationStore = new ConversationStore();
+ }
+
+ @Test
+ public void testAddConversation() {
+ mConversationStore.addOrUpdate(buildConversationInfo(SHORTCUT_ID));
+
+ ConversationInfo out = mConversationStore.getConversation(SHORTCUT_ID);
+ assertNotNull(out);
+ assertEquals(SHORTCUT_ID, out.getShortcutId());
+ }
+
+ @Test
+ public void testUpdateConversation() {
+ ConversationInfo original =
+ buildConversationInfo(SHORTCUT_ID, LOCUS_ID, CONTACT_URI, PHONE_NUMBER);
+ mConversationStore.addOrUpdate(original);
+ assertEquals(LOCUS_ID, mConversationStore.getConversation(SHORTCUT_ID).getLocusId());
+
+ LocusId newLocusId = new LocusId("ghi");
+ ConversationInfo update = buildConversationInfo(
+ SHORTCUT_ID, newLocusId, CONTACT_URI, PHONE_NUMBER);
+ mConversationStore.addOrUpdate(update);
+ assertEquals(newLocusId, mConversationStore.getConversation(SHORTCUT_ID).getLocusId());
+ }
+
+ @Test
+ public void testDeleteConversation() {
+ mConversationStore.addOrUpdate(buildConversationInfo(SHORTCUT_ID));
+ assertNotNull(mConversationStore.getConversation(SHORTCUT_ID));
+
+ mConversationStore.deleteConversation(SHORTCUT_ID);
+ assertNull(mConversationStore.getConversation(SHORTCUT_ID));
+ }
+
+ @Test
+ public void testForAllConversations() {
+ mConversationStore.addOrUpdate(buildConversationInfo("a"));
+ mConversationStore.addOrUpdate(buildConversationInfo("b"));
+ mConversationStore.addOrUpdate(buildConversationInfo("c"));
+
+ Set<String> shortcutIds = new ArraySet<>();
+
+ mConversationStore.forAllConversations(
+ conversationInfo -> shortcutIds.add(conversationInfo.getShortcutId()));
+ assertTrue(shortcutIds.contains("a"));
+ assertTrue(shortcutIds.contains("b"));
+ assertTrue(shortcutIds.contains("c"));
+ }
+
+ @Test
+ public void testGetConversationByLocusId() {
+ ConversationInfo in =
+ buildConversationInfo(SHORTCUT_ID, LOCUS_ID, CONTACT_URI, PHONE_NUMBER);
+ mConversationStore.addOrUpdate(in);
+ ConversationInfo out = mConversationStore.getConversationByLocusId(LOCUS_ID);
+ assertNotNull(out);
+ assertEquals(SHORTCUT_ID, out.getShortcutId());
+
+ mConversationStore.deleteConversation(SHORTCUT_ID);
+ assertNull(mConversationStore.getConversationByLocusId(LOCUS_ID));
+ }
+
+ @Test
+ public void testGetConversationByContactUri() {
+ ConversationInfo in =
+ buildConversationInfo(SHORTCUT_ID, LOCUS_ID, CONTACT_URI, PHONE_NUMBER);
+ mConversationStore.addOrUpdate(in);
+ ConversationInfo out = mConversationStore.getConversationByContactUri(CONTACT_URI);
+ assertNotNull(out);
+ assertEquals(SHORTCUT_ID, out.getShortcutId());
+
+ mConversationStore.deleteConversation(SHORTCUT_ID);
+ assertNull(mConversationStore.getConversationByContactUri(CONTACT_URI));
+ }
+
+ @Test
+ public void testGetConversationByPhoneNumber() {
+ ConversationInfo in =
+ buildConversationInfo(SHORTCUT_ID, LOCUS_ID, CONTACT_URI, PHONE_NUMBER);
+ mConversationStore.addOrUpdate(in);
+ ConversationInfo out = mConversationStore.getConversationByPhoneNumber(PHONE_NUMBER);
+ assertNotNull(out);
+ assertEquals(SHORTCUT_ID, out.getShortcutId());
+
+ mConversationStore.deleteConversation(SHORTCUT_ID);
+ assertNull(mConversationStore.getConversationByPhoneNumber(PHONE_NUMBER));
+ }
+
+ private static ConversationInfo buildConversationInfo(String shortcutId) {
+ return buildConversationInfo(shortcutId, null, null, null);
+ }
+
+ private static ConversationInfo buildConversationInfo(
+ String shortcutId, LocusId locusId, Uri contactUri, String phoneNumber) {
+ return new ConversationInfo.Builder()
+ .setShortcutId(shortcutId)
+ .setLocusId(locusId)
+ .setContactUri(contactUri)
+ .setContactPhoneNumber(phoneNumber)
+ .setShortcutFlags(ShortcutInfo.FLAG_LONG_LIVED)
+ .setVip(true)
+ .setBubbled(true)
+ .build();
+ }
+}
diff --git a/services/tests/servicestests/src/com/android/server/people/data/DataManagerTest.java b/services/tests/servicestests/src/com/android/server/people/data/DataManagerTest.java
new file mode 100644
index 000000000000..9f3d656188e1
--- /dev/null
+++ b/services/tests/servicestests/src/com/android/server/people/data/DataManagerTest.java
@@ -0,0 +1,451 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.people.data;
+
+import static android.app.usage.UsageEvents.Event.SHORTCUT_INVOCATION;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.anyBoolean;
+import static org.mockito.ArgumentMatchers.anyInt;
+import static org.mockito.ArgumentMatchers.anyLong;
+import static org.mockito.ArgumentMatchers.anyString;
+import static org.mockito.Mockito.eq;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.app.Notification;
+import android.app.Person;
+import android.app.prediction.AppTarget;
+import android.app.prediction.AppTargetEvent;
+import android.app.prediction.AppTargetId;
+import android.app.usage.UsageEvents;
+import android.app.usage.UsageStatsManagerInternal;
+import android.content.ComponentName;
+import android.content.ContentResolver;
+import android.content.Context;
+import android.content.Intent;
+import android.content.IntentFilter;
+import android.content.pm.ShortcutInfo;
+import android.content.pm.ShortcutManager;
+import android.content.pm.ShortcutManager.ShareShortcutInfo;
+import android.content.pm.ShortcutServiceInternal;
+import android.content.pm.UserInfo;
+import android.database.ContentObserver;
+import android.net.Uri;
+import android.os.Looper;
+import android.os.UserHandle;
+import android.os.UserManager;
+import android.provider.ContactsContract;
+import android.service.notification.NotificationListenerService;
+import android.service.notification.StatusBarNotification;
+import android.telephony.TelephonyManager;
+import android.util.Range;
+
+import com.android.internal.app.ChooserActivity;
+import com.android.server.LocalServices;
+
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.JUnit4;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.List;
+import java.util.concurrent.ScheduledExecutorService;
+import java.util.concurrent.ScheduledFuture;
+import java.util.concurrent.TimeUnit;
+
+@RunWith(JUnit4.class)
+public final class DataManagerTest {
+
+ private static final int USER_ID_PRIMARY = 0;
+ private static final int USER_ID_PRIMARY_MANAGED = 10;
+ private static final int USER_ID_SECONDARY = 11;
+ private static final String TEST_PKG_NAME = "pkg";
+ private static final String TEST_SHORTCUT_ID = "sc";
+ private static final String CONTACT_URI = "content://com.android.contacts/contacts/lookup/123";
+ private static final String PHONE_NUMBER = "+1234567890";
+
+ @Mock private Context mContext;
+ @Mock private ShortcutServiceInternal mShortcutServiceInternal;
+ @Mock private UsageStatsManagerInternal mUsageStatsManagerInternal;
+ @Mock private ShortcutManager mShortcutManager;
+ @Mock private UserManager mUserManager;
+ @Mock private TelephonyManager mTelephonyManager;
+ @Mock private ContentResolver mContentResolver;
+ @Mock private ScheduledExecutorService mExecutorService;
+ @Mock private ScheduledFuture mScheduledFuture;
+ @Mock private StatusBarNotification mStatusBarNotification;
+ @Mock private Notification mNotification;
+
+ private DataManager mDataManager;
+ private int mCallingUserId;
+ private TestInjector mInjector;
+
+ @Before
+ public void setUp() {
+ MockitoAnnotations.initMocks(this);
+
+ addLocalServiceMock(ShortcutServiceInternal.class, mShortcutServiceInternal);
+
+ addLocalServiceMock(UsageStatsManagerInternal.class, mUsageStatsManagerInternal);
+
+ when(mContext.getMainLooper()).thenReturn(Looper.getMainLooper());
+
+ when(mContext.getSystemService(Context.SHORTCUT_SERVICE)).thenReturn(mShortcutManager);
+ when(mContext.getSystemServiceName(ShortcutManager.class)).thenReturn(
+ Context.SHORTCUT_SERVICE);
+
+ when(mContext.getSystemService(Context.USER_SERVICE)).thenReturn(mUserManager);
+ when(mContext.getSystemServiceName(UserManager.class)).thenReturn(
+ Context.USER_SERVICE);
+
+ when(mContext.getSystemService(Context.TELEPHONY_SERVICE)).thenReturn(mTelephonyManager);
+
+ when(mExecutorService.scheduleAtFixedRate(any(Runnable.class), anyLong(), anyLong(), any(
+ TimeUnit.class))).thenReturn(mScheduledFuture);
+
+ when(mUserManager.getEnabledProfiles(USER_ID_PRIMARY))
+ .thenReturn(Arrays.asList(
+ buildUserInfo(USER_ID_PRIMARY),
+ buildUserInfo(USER_ID_PRIMARY_MANAGED)));
+ when(mUserManager.getEnabledProfiles(USER_ID_SECONDARY))
+ .thenReturn(Collections.singletonList(buildUserInfo(USER_ID_SECONDARY)));
+
+ when(mContext.getContentResolver()).thenReturn(mContentResolver);
+
+ when(mStatusBarNotification.getNotification()).thenReturn(mNotification);
+ when(mStatusBarNotification.getPackageName()).thenReturn(TEST_PKG_NAME);
+ when(mStatusBarNotification.getUser()).thenReturn(UserHandle.of(USER_ID_PRIMARY));
+ when(mNotification.getShortcutId()).thenReturn(TEST_SHORTCUT_ID);
+
+ mCallingUserId = USER_ID_PRIMARY;
+
+ mInjector = new TestInjector();
+ mDataManager = new DataManager(mContext, mInjector);
+ mDataManager.initialize();
+ }
+
+ @After
+ public void tearDown() {
+ LocalServices.removeServiceForTest(ShortcutServiceInternal.class);
+ LocalServices.removeServiceForTest(UsageStatsManagerInternal.class);
+ }
+
+ @Test
+ public void testAccessConversationFromTheSameProfileGroup() {
+ mDataManager.onUserUnlocked(USER_ID_PRIMARY);
+ mDataManager.onUserUnlocked(USER_ID_PRIMARY_MANAGED);
+ mDataManager.onUserUnlocked(USER_ID_SECONDARY);
+
+ mDataManager.onShortcutAddedOrUpdated(
+ buildShortcutInfo("pkg_1", USER_ID_PRIMARY, "sc_1",
+ buildPerson(true, false)));
+ mDataManager.onShortcutAddedOrUpdated(
+ buildShortcutInfo("pkg_2", USER_ID_PRIMARY_MANAGED, "sc_2",
+ buildPerson(false, true)));
+ mDataManager.onShortcutAddedOrUpdated(
+ buildShortcutInfo("pkg_3", USER_ID_SECONDARY, "sc_3", buildPerson()));
+
+ List<ConversationInfo> conversations = new ArrayList<>();
+ mDataManager.forAllPackages(
+ packageData -> packageData.forAllConversations(conversations::add));
+
+ // USER_ID_SECONDARY is not in the same profile group as USER_ID_PRIMARY.
+ assertEquals(2, conversations.size());
+
+ assertEquals("sc_1", conversations.get(0).getShortcutId());
+ assertTrue(conversations.get(0).isPersonImportant());
+ assertFalse(conversations.get(0).isPersonBot());
+ assertFalse(conversations.get(0).isContactStarred());
+ assertEquals(PHONE_NUMBER, conversations.get(0).getContactPhoneNumber());
+
+ assertEquals("sc_2", conversations.get(1).getShortcutId());
+ assertFalse(conversations.get(1).isPersonImportant());
+ assertTrue(conversations.get(1).isPersonBot());
+ assertFalse(conversations.get(0).isContactStarred());
+ assertEquals(PHONE_NUMBER, conversations.get(0).getContactPhoneNumber());
+ }
+
+ @Test
+ public void testAccessConversationForUnlockedUsersOnly() {
+ mDataManager.onUserUnlocked(USER_ID_PRIMARY);
+ mDataManager.onShortcutAddedOrUpdated(
+ buildShortcutInfo("pkg_1", USER_ID_PRIMARY, "sc_1", buildPerson()));
+ mDataManager.onShortcutAddedOrUpdated(
+ buildShortcutInfo("pkg_2", USER_ID_PRIMARY_MANAGED, "sc_2", buildPerson()));
+
+ List<ConversationInfo> conversations = new ArrayList<>();
+ mDataManager.forAllPackages(
+ packageData -> packageData.forAllConversations(conversations::add));
+
+ // USER_ID_PRIMARY_MANAGED is not locked, so only USER_ID_PRIMARY's conversation is stored.
+ assertEquals(1, conversations.size());
+ assertEquals("sc_1", conversations.get(0).getShortcutId());
+
+ mDataManager.onUserStopped(USER_ID_PRIMARY);
+ conversations.clear();
+ mDataManager.forAllPackages(
+ packageData -> packageData.forAllConversations(conversations::add));
+ assertTrue(conversations.isEmpty());
+ }
+
+ @Test
+ public void testGetShortcut() {
+ mDataManager.getShortcut(TEST_PKG_NAME, USER_ID_PRIMARY, TEST_SHORTCUT_ID);
+ verify(mShortcutServiceInternal).getShortcuts(anyInt(), anyString(), anyLong(),
+ eq(TEST_PKG_NAME), eq(Collections.singletonList(TEST_SHORTCUT_ID)),
+ eq(null), anyInt(), eq(USER_ID_PRIMARY), anyInt(), anyInt());
+ }
+
+ @Test
+ public void testGetShareTargets() {
+ mDataManager.onUserUnlocked(USER_ID_PRIMARY);
+
+ ShortcutInfo shortcut1 =
+ buildShortcutInfo("pkg_1", USER_ID_PRIMARY, "sc_1", buildPerson());
+ ShareShortcutInfo shareShortcut1 =
+ new ShareShortcutInfo(shortcut1, new ComponentName("pkg_1", "activity"));
+
+ ShortcutInfo shortcut2 =
+ buildShortcutInfo("pkg_2", USER_ID_PRIMARY, "sc_2", buildPerson());
+ ShareShortcutInfo shareShortcut2 =
+ new ShareShortcutInfo(shortcut2, new ComponentName("pkg_2", "activity"));
+ mDataManager.onShortcutAddedOrUpdated(shortcut2);
+
+ when(mShortcutManager.getShareTargets(any(IntentFilter.class)))
+ .thenReturn(Arrays.asList(shareShortcut1, shareShortcut2));
+
+ List<ShareShortcutInfo> shareShortcuts =
+ mDataManager.getConversationShareTargets(new IntentFilter());
+ // Only "sc_2" is stored as a conversation.
+ assertEquals(1, shareShortcuts.size());
+ assertEquals("sc_2", shareShortcuts.get(0).getShortcutInfo().getId());
+ }
+
+ @Test
+ public void testReportAppTargetEvent() throws IntentFilter.MalformedMimeTypeException {
+ mDataManager.onUserUnlocked(USER_ID_PRIMARY);
+ ShortcutInfo shortcut = buildShortcutInfo(TEST_PKG_NAME, USER_ID_PRIMARY, TEST_SHORTCUT_ID,
+ buildPerson());
+ mDataManager.onShortcutAddedOrUpdated(shortcut);
+
+ AppTarget appTarget = new AppTarget.Builder(new AppTargetId(TEST_SHORTCUT_ID), shortcut)
+ .build();
+ AppTargetEvent appTargetEvent =
+ new AppTargetEvent.Builder(appTarget, AppTargetEvent.ACTION_LAUNCH)
+ .setLaunchLocation(ChooserActivity.LAUNCH_LOCATON_DIRECT_SHARE)
+ .build();
+ IntentFilter intentFilter = new IntentFilter(Intent.ACTION_SEND, "image/jpg");
+ mDataManager.reportAppTargetEvent(appTargetEvent, intentFilter);
+
+ List<Range<Long>> activeShareTimeSlots = new ArrayList<>();
+ mDataManager.forAllPackages(packageData ->
+ activeShareTimeSlots.addAll(
+ packageData.getEventHistory(TEST_SHORTCUT_ID)
+ .getEventIndex(Event.TYPE_SHARE_IMAGE)
+ .getActiveTimeSlots()));
+ assertEquals(1, activeShareTimeSlots.size());
+ }
+
+ @Test
+ public void testContactsChanged() {
+ mDataManager.onUserUnlocked(USER_ID_PRIMARY);
+
+ ShortcutInfo shortcut = buildShortcutInfo(TEST_PKG_NAME, USER_ID_PRIMARY, TEST_SHORTCUT_ID,
+ buildPerson());
+ mDataManager.onShortcutAddedOrUpdated(shortcut);
+
+ final String newPhoneNumber = "+1000000000";
+ mInjector.mContactsQueryHelper.mIsStarred = true;
+ mInjector.mContactsQueryHelper.mPhoneNumber = newPhoneNumber;
+
+ ContentObserver contentObserver = mDataManager.getContactsContentObserverForTesting(
+ USER_ID_PRIMARY);
+ contentObserver.onChange(false, ContactsContract.Contacts.CONTENT_URI, USER_ID_PRIMARY);
+
+ List<ConversationInfo> conversations = new ArrayList<>();
+ mDataManager.forAllPackages(
+ packageData -> packageData.forAllConversations(conversations::add));
+ assertEquals(1, conversations.size());
+
+ assertEquals(TEST_SHORTCUT_ID, conversations.get(0).getShortcutId());
+ assertTrue(conversations.get(0).isContactStarred());
+ assertEquals(newPhoneNumber, conversations.get(0).getContactPhoneNumber());
+ }
+
+ @Test
+ public void testNotificationListener() {
+ mDataManager.onUserUnlocked(USER_ID_PRIMARY);
+
+ ShortcutInfo shortcut = buildShortcutInfo(TEST_PKG_NAME, USER_ID_PRIMARY, TEST_SHORTCUT_ID,
+ buildPerson());
+ mDataManager.onShortcutAddedOrUpdated(shortcut);
+
+ NotificationListenerService listenerService =
+ mDataManager.getNotificationListenerServiceForTesting(USER_ID_PRIMARY);
+
+ listenerService.onNotificationRemoved(mStatusBarNotification, null,
+ NotificationListenerService.REASON_CLICK);
+
+ List<Range<Long>> activeNotificationOpenTimeSlots = new ArrayList<>();
+ mDataManager.forAllPackages(packageData ->
+ activeNotificationOpenTimeSlots.addAll(
+ packageData.getEventHistory(TEST_SHORTCUT_ID)
+ .getEventIndex(Event.TYPE_NOTIFICATION_OPENED)
+ .getActiveTimeSlots()));
+ assertEquals(1, activeNotificationOpenTimeSlots.size());
+ }
+
+ @Test
+ public void testQueryUsageStatsService() {
+ UsageEvents.Event e = new UsageEvents.Event(SHORTCUT_INVOCATION,
+ System.currentTimeMillis());
+ e.mPackage = TEST_PKG_NAME;
+ e.mShortcutId = TEST_SHORTCUT_ID;
+ List<UsageEvents.Event> events = new ArrayList<>();
+ events.add(e);
+ UsageEvents usageEvents = new UsageEvents(events, new String[]{});
+ when(mUsageStatsManagerInternal.queryEventsForUser(anyInt(), anyLong(), anyLong(),
+ anyBoolean())).thenReturn(usageEvents);
+
+ mDataManager.onUserUnlocked(USER_ID_PRIMARY);
+
+ ShortcutInfo shortcut = buildShortcutInfo(TEST_PKG_NAME, USER_ID_PRIMARY, TEST_SHORTCUT_ID,
+ buildPerson());
+ mDataManager.onShortcutAddedOrUpdated(shortcut);
+
+ mDataManager.queryUsageStatsService(USER_ID_PRIMARY, 0L, Long.MAX_VALUE);
+
+ List<Range<Long>> activeShortcutInvocationTimeSlots = new ArrayList<>();
+ mDataManager.forAllPackages(packageData ->
+ activeShortcutInvocationTimeSlots.addAll(
+ packageData.getEventHistory(TEST_SHORTCUT_ID)
+ .getEventIndex(Event.TYPE_SHORTCUT_INVOCATION)
+ .getActiveTimeSlots()));
+ assertEquals(1, activeShortcutInvocationTimeSlots.size());
+ }
+
+ private static <T> void addLocalServiceMock(Class<T> clazz, T mock) {
+ LocalServices.removeServiceForTest(clazz);
+ LocalServices.addService(clazz, mock);
+ }
+
+ private ShortcutInfo buildShortcutInfo(String packageName, int userId, String id,
+ @Nullable Person person) {
+ Context mockContext = mock(Context.class);
+ when(mockContext.getPackageName()).thenReturn(packageName);
+ when(mockContext.getUserId()).thenReturn(userId);
+ when(mockContext.getUser()).thenReturn(UserHandle.of(userId));
+ ShortcutInfo.Builder builder = new ShortcutInfo.Builder(mockContext, id)
+ .setShortLabel(id)
+ .setIntent(new Intent("TestIntent"));
+ if (person != null) {
+ builder.setPersons(new Person[] {person});
+ }
+ return builder.build();
+ }
+
+ private Person buildPerson() {
+ return buildPerson(true, false);
+ }
+
+ private Person buildPerson(boolean isImportant, boolean isBot) {
+ return new Person.Builder()
+ .setImportant(isImportant)
+ .setBot(isBot)
+ .setUri(CONTACT_URI)
+ .build();
+ }
+
+ private UserInfo buildUserInfo(int userId) {
+ return new UserInfo(userId, "", 0);
+ }
+
+ private class TestContactsQueryHelper extends ContactsQueryHelper {
+
+ private Uri mContactUri;
+ private boolean mIsStarred;
+ private String mPhoneNumber;
+
+ TestContactsQueryHelper(Context context) {
+ super(context);
+ mContactUri = Uri.parse(CONTACT_URI);
+ mIsStarred = false;
+ mPhoneNumber = PHONE_NUMBER;
+ }
+
+ @Override
+ boolean query(@NonNull String contactUri) {
+ return true;
+ }
+
+ @Override
+ boolean querySince(long sinceTime) {
+ return true;
+ }
+
+ @Override
+ @Nullable
+ Uri getContactUri() {
+ return mContactUri;
+ }
+
+ @Override
+ boolean isStarred() {
+ return mIsStarred;
+ }
+
+ @Override
+ @Nullable
+ String getPhoneNumber() {
+ return mPhoneNumber;
+ }
+ }
+
+ private class TestInjector extends DataManager.Injector {
+
+ private final TestContactsQueryHelper mContactsQueryHelper =
+ new TestContactsQueryHelper(mContext);
+
+ @Override
+ ScheduledExecutorService createScheduledExecutor() {
+ return mExecutorService;
+ }
+
+ @Override
+ ContactsQueryHelper createContactsQueryHelper(Context context) {
+ return mContactsQueryHelper;
+ }
+
+ @Override
+ int getCallingUserId() {
+ return mCallingUserId;
+ }
+ }
+}
diff --git a/services/tests/servicestests/src/com/android/server/people/data/EventHistoryImplTest.java b/services/tests/servicestests/src/com/android/server/people/data/EventHistoryImplTest.java
new file mode 100644
index 000000000000..43e1001f2aee
--- /dev/null
+++ b/services/tests/servicestests/src/com/android/server/people/data/EventHistoryImplTest.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.people.data;
+
+import static com.android.server.people.data.TestUtils.timestamp;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertTrue;
+
+import com.google.android.collect.Sets;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.JUnit4;
+
+import java.util.List;
+
+@RunWith(JUnit4.class)
+public final class EventHistoryImplTest {
+
+ private static final long CURRENT_TIMESTAMP = timestamp("01-30 18:50");
+
+ private static final Event E1 = new Event(timestamp("01-06 05:26"),
+ Event.TYPE_NOTIFICATION_OPENED);
+ private static final Event E2 = new Event(timestamp("01-27 18:41"),
+ Event.TYPE_NOTIFICATION_OPENED);
+ private static final Event E3 = new Event(timestamp("01-30 03:06"),
+ Event.TYPE_SHARE_IMAGE);
+ private static final Event E4 = new Event(timestamp("01-30 18:14"),
+ Event.TYPE_SMS_INCOMING);
+
+ private EventHistoryImpl mEventHistory;
+
+ @Before
+ public void setUp() {
+ EventIndex.Injector eventIndexInjector = new EventIndex.Injector() {
+ @Override
+ long currentTimeMillis() {
+ return CURRENT_TIMESTAMP;
+ }
+ };
+ EventHistoryImpl.Injector eventHistoryInjector = new EventHistoryImpl.Injector() {
+ @Override
+ EventIndex createEventIndex() {
+ return new EventIndex(eventIndexInjector);
+ }
+ };
+ mEventHistory = new EventHistoryImpl(eventHistoryInjector);
+ }
+
+ @Test
+ public void testNoEvents() {
+ EventIndex eventIndex = mEventHistory.getEventIndex(Event.ALL_EVENT_TYPES);
+ assertTrue(eventIndex.isEmpty());
+
+ List<Event> events = mEventHistory.queryEvents(Event.ALL_EVENT_TYPES, 0L, 999L);
+ assertTrue(events.isEmpty());
+ }
+
+ @Test
+ public void testMultipleEvents() {
+ mEventHistory.addEvent(E1);
+ mEventHistory.addEvent(E2);
+ mEventHistory.addEvent(E3);
+ mEventHistory.addEvent(E4);
+
+ EventIndex eventIndex = mEventHistory.getEventIndex(Event.ALL_EVENT_TYPES);
+ assertEquals(4, eventIndex.getActiveTimeSlots().size());
+
+ List<Event> events = mEventHistory.queryEvents(Event.ALL_EVENT_TYPES, 0L, Long.MAX_VALUE);
+ assertEquals(4, events.size());
+ }
+
+ @Test
+ public void testQuerySomeEventTypes() {
+ mEventHistory.addEvent(E1);
+ mEventHistory.addEvent(E2);
+ mEventHistory.addEvent(E3);
+ mEventHistory.addEvent(E4);
+
+ EventIndex eventIndex = mEventHistory.getEventIndex(Event.NOTIFICATION_EVENT_TYPES);
+ assertEquals(2, eventIndex.getActiveTimeSlots().size());
+
+ List<Event> events = mEventHistory.queryEvents(
+ Event.NOTIFICATION_EVENT_TYPES, 0L, Long.MAX_VALUE);
+ assertEquals(2, events.size());
+ }
+
+ @Test
+ public void testQuerySingleEventType() {
+ mEventHistory.addEvent(E1);
+ mEventHistory.addEvent(E2);
+ mEventHistory.addEvent(E3);
+ mEventHistory.addEvent(E4);
+
+ EventIndex eventIndex = mEventHistory.getEventIndex(Event.TYPE_SHARE_IMAGE);
+ assertEquals(1, eventIndex.getActiveTimeSlots().size());
+
+ List<Event> events = mEventHistory.queryEvents(
+ Sets.newArraySet(Event.TYPE_SHARE_IMAGE), 0L, Long.MAX_VALUE);
+ assertEquals(1, events.size());
+ }
+}
diff --git a/services/tests/servicestests/src/com/android/server/people/data/EventIndexTest.java b/services/tests/servicestests/src/com/android/server/people/data/EventIndexTest.java
new file mode 100644
index 000000000000..e87f428d09bb
--- /dev/null
+++ b/services/tests/servicestests/src/com/android/server/people/data/EventIndexTest.java
@@ -0,0 +1,176 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.people.data;
+
+import static com.android.server.people.data.TestUtils.timestamp;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertNull;
+import static org.junit.Assert.assertTrue;
+
+import android.util.Range;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.JUnit4;
+
+import java.util.List;
+
+@RunWith(JUnit4.class)
+public final class EventIndexTest {
+
+ private static final long CURRENT_TIMESTAMP = timestamp("01-30 18:50");
+ private static final long SECONDS_PER_HOUR = 60L * 60L;
+ private static final long SECONDS_PER_DAY = SECONDS_PER_HOUR * 24L;
+
+ private TestInjector mInjector;
+ private EventIndex mEventIndex;
+
+ @Before
+ public void setUp() {
+ mInjector = new TestInjector(CURRENT_TIMESTAMP);
+ mEventIndex = new EventIndex(mInjector);
+ }
+
+ @Test
+ public void testNoEvents() {
+ assertTrue(mEventIndex.isEmpty());
+ assertNull(mEventIndex.getMostRecentActiveTimeSlot());
+ assertTrue(mEventIndex.getActiveTimeSlots().isEmpty());
+ }
+
+ @Test
+ public void testMultipleEvents() {
+ mEventIndex.addEvent(timestamp("01-06 05:26"));
+ mEventIndex.addEvent(timestamp("01-27 18:41"));
+ mEventIndex.addEvent(timestamp("01-30 03:06"));
+ mEventIndex.addEvent(timestamp("01-30 18:14"));
+
+ assertFalse(mEventIndex.isEmpty());
+ Range<Long> mostRecentSlot = mEventIndex.getMostRecentActiveTimeSlot();
+ assertNotNull(mostRecentSlot);
+ assertTimeSlot(timestamp("01-30 18:14"), timestamp("01-30 18:16"), mostRecentSlot);
+
+ List<Range<Long>> slots = mEventIndex.getActiveTimeSlots();
+ assertEquals(4, slots.size());
+ assertTimeSlot(timestamp("01-06 00:00"), timestamp("01-07 00:00"), slots.get(0));
+ assertTimeSlot(timestamp("01-27 16:00"), timestamp("01-27 20:00"), slots.get(1));
+ assertTimeSlot(timestamp("01-30 03:00"), timestamp("01-30 04:00"), slots.get(2));
+ assertTimeSlot(timestamp("01-30 18:14"), timestamp("01-30 18:16"), slots.get(3));
+ }
+
+ @Test
+ public void testBitmapShift() {
+ mEventIndex.addEvent(CURRENT_TIMESTAMP);
+ List<Range<Long>> slots;
+
+ slots = mEventIndex.getActiveTimeSlots();
+ assertEquals(1, slots.size());
+ assertTimeSlot(timestamp("01-30 18:50"), timestamp("01-30 18:52"), slots.get(0));
+
+ mInjector.moveTimeForwardSeconds(SECONDS_PER_HOUR * 3L);
+ mEventIndex.update();
+ slots = mEventIndex.getActiveTimeSlots();
+ assertEquals(1, slots.size());
+ assertTimeSlot(timestamp("01-30 18:00"), timestamp("01-30 19:00"), slots.get(0));
+
+ mInjector.moveTimeForwardSeconds(SECONDS_PER_DAY * 6L);
+ mEventIndex.update();
+ slots = mEventIndex.getActiveTimeSlots();
+ assertEquals(1, slots.size());
+ assertTimeSlot(timestamp("01-30 16:00"), timestamp("01-30 20:00"), slots.get(0));
+
+ mInjector.moveTimeForwardSeconds(SECONDS_PER_DAY * 30L);
+ mEventIndex.update();
+ slots = mEventIndex.getActiveTimeSlots();
+ assertEquals(1, slots.size());
+ assertTimeSlot(timestamp("01-30 00:00"), timestamp("01-31 00:00"), slots.get(0));
+
+ mInjector.moveTimeForwardSeconds(SECONDS_PER_DAY * 80L);
+ mEventIndex.update();
+ slots = mEventIndex.getActiveTimeSlots();
+ // The event has been shifted off the left end.
+ assertTrue(slots.isEmpty());
+ }
+
+ @Test
+ public void testCopyConstructor() {
+ mEventIndex.addEvent(timestamp("01-06 05:26"));
+ mEventIndex.addEvent(timestamp("01-27 18:41"));
+ mEventIndex.addEvent(timestamp("01-30 03:06"));
+ mEventIndex.addEvent(timestamp("01-30 18:14"));
+
+ List<Range<Long>> slots = mEventIndex.getActiveTimeSlots();
+
+ EventIndex newIndex = new EventIndex(mEventIndex);
+ List<Range<Long>> newSlots = newIndex.getActiveTimeSlots();
+
+ assertEquals(slots.size(), newSlots.size());
+ for (int i = 0; i < slots.size(); i++) {
+ assertEquals(slots.get(i), newSlots.get(i));
+ }
+ }
+
+ @Test
+ public void combineEventIndexes() {
+ EventIndex a = new EventIndex(mInjector);
+ mInjector.mCurrentTimeMillis = timestamp("01-27 18:41");
+ a.addEvent(mInjector.mCurrentTimeMillis);
+ mInjector.mCurrentTimeMillis = timestamp("01-30 03:06");
+ a.addEvent(mInjector.mCurrentTimeMillis);
+
+ mInjector.mCurrentTimeMillis = CURRENT_TIMESTAMP;
+ EventIndex b = new EventIndex(mInjector);
+ b.addEvent(timestamp("01-06 05:26"));
+ b.addEvent(timestamp("01-30 18:14"));
+
+ EventIndex combined = EventIndex.combine(a, b);
+ List<Range<Long>> slots = combined.getActiveTimeSlots();
+ assertEquals(4, slots.size());
+ assertTimeSlot(timestamp("01-06 00:00"), timestamp("01-07 00:00"), slots.get(0));
+ assertTimeSlot(timestamp("01-27 16:00"), timestamp("01-27 20:00"), slots.get(1));
+ assertTimeSlot(timestamp("01-30 03:00"), timestamp("01-30 04:00"), slots.get(2));
+ assertTimeSlot(timestamp("01-30 18:14"), timestamp("01-30 18:16"), slots.get(3));
+ }
+
+ private static void assertTimeSlot(
+ long expectedLower, long expectedUpper, Range<Long> actualSlot) {
+ assertEquals(expectedLower, actualSlot.getLower().longValue());
+ assertEquals(expectedUpper, actualSlot.getUpper().longValue());
+ }
+
+ private class TestInjector extends EventIndex.Injector {
+
+ private long mCurrentTimeMillis;
+
+ TestInjector(long currentTimeMillis) {
+ mCurrentTimeMillis = currentTimeMillis;
+ }
+
+ private void moveTimeForwardSeconds(long seconds) {
+ mCurrentTimeMillis += (seconds * 1000L);
+ }
+
+ @Override
+ long currentTimeMillis() {
+ return mCurrentTimeMillis;
+ }
+ }
+}
diff --git a/services/tests/servicestests/src/com/android/server/people/data/EventListTest.java b/services/tests/servicestests/src/com/android/server/people/data/EventListTest.java
new file mode 100644
index 000000000000..f2f372c1b8c3
--- /dev/null
+++ b/services/tests/servicestests/src/com/android/server/people/data/EventListTest.java
@@ -0,0 +1,137 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.people.data;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertTrue;
+
+import com.google.android.collect.Lists;
+import com.google.android.collect.Sets;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.JUnit4;
+
+import java.util.List;
+
+@RunWith(JUnit4.class)
+public final class EventListTest {
+
+ private static final Event E1 = new Event(101L, Event.TYPE_NOTIFICATION_OPENED);
+ private static final Event E2 = new Event(103L, Event.TYPE_NOTIFICATION_OPENED);
+ private static final Event E3 = new Event(107L, Event.TYPE_SHARE_IMAGE);
+ private static final Event E4 = new Event(109L, Event.TYPE_SMS_INCOMING);
+
+ private EventList mEventList;
+
+ @Before
+ public void setUp() {
+ mEventList = new EventList();
+ }
+
+ @Test
+ public void testQueryEmptyEventList() {
+ List<Event> events = mEventList.queryEvents(Event.ALL_EVENT_TYPES, 0L, 999L);
+ assertTrue(events.isEmpty());
+ }
+
+ @Test
+ public void testAddAndQueryEvents() {
+ List<Event> in = Lists.newArrayList(E1, E2, E3, E4);
+ for (Event e : in) {
+ mEventList.add(e);
+ }
+
+ List<Event> out = mEventList.queryEvents(Event.ALL_EVENT_TYPES, 0L, 999L);
+ assertEventListEquals(in, out);
+ }
+
+ @Test
+ public void testAddEventsNotInOrder() {
+ mEventList.add(E3);
+ mEventList.add(E1);
+ mEventList.add(E4);
+ mEventList.add(E2);
+
+ List<Event> out = mEventList.queryEvents(Event.ALL_EVENT_TYPES, 0L, 999L);
+ List<Event> expected = Lists.newArrayList(E1, E2, E3, E4);
+ assertEventListEquals(expected, out);
+ }
+
+ @Test
+ public void testQueryEventsByType() {
+ mEventList.add(E1);
+ mEventList.add(E2);
+ mEventList.add(E3);
+ mEventList.add(E4);
+
+ List<Event> out = mEventList.queryEvents(
+ Sets.newArraySet(Event.TYPE_NOTIFICATION_OPENED), 0L, 999L);
+ assertEventListEquals(Lists.newArrayList(E1, E2), out);
+ }
+
+ @Test
+ public void testQueryEventsByTimeRange() {
+ mEventList.add(E1);
+ mEventList.add(E2);
+ mEventList.add(E3);
+ mEventList.add(E4);
+
+ List<Event> out = mEventList.queryEvents(Event.ALL_EVENT_TYPES, 103L, 109L);
+ // Only E2 and E3 are in the time range [103L, 109L).
+ assertEventListEquals(Lists.newArrayList(E2, E3), out);
+ }
+
+ @Test
+ public void testQueryEventsOutOfRange() {
+ mEventList.add(E1);
+ mEventList.add(E2);
+ mEventList.add(E3);
+ mEventList.add(E4);
+
+ List<Event> out = mEventList.queryEvents(Event.ALL_EVENT_TYPES, 900L, 900L);
+ assertTrue(out.isEmpty());
+ }
+
+ @Test
+ public void testAddDuplicateEvents() {
+ mEventList.add(E1);
+ mEventList.add(E2);
+ mEventList.add(E2);
+ mEventList.add(E3);
+ mEventList.add(E2);
+ mEventList.add(E3);
+ mEventList.add(E3);
+ mEventList.add(E4);
+ mEventList.add(E1);
+ mEventList.add(E3);
+ mEventList.add(E2);
+
+ List<Event> out = mEventList.queryEvents(Event.ALL_EVENT_TYPES, 0L, 999L);
+ List<Event> expected = Lists.newArrayList(E1, E2, E3, E4);
+ assertEventListEquals(expected, out);
+ }
+
+ private static void assertEventListEquals(List<Event> expected, List<Event> actual) {
+ assertEquals(expected.size(), actual.size());
+ for (int i = 0; i < expected.size(); i++) {
+ assertEquals(expected.get(i).getTimestamp(), actual.get(i).getTimestamp());
+ assertEquals(expected.get(i).getType(), actual.get(i).getType());
+ }
+ }
+}
diff --git a/services/tests/servicestests/src/com/android/server/people/data/PackageDataTest.java b/services/tests/servicestests/src/com/android/server/people/data/PackageDataTest.java
new file mode 100644
index 000000000000..1b80d6fc3a2d
--- /dev/null
+++ b/services/tests/servicestests/src/com/android/server/people/data/PackageDataTest.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.people.data;
+
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
+
+import android.content.LocusId;
+import android.content.pm.ShortcutInfo;
+import android.net.Uri;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.JUnit4;
+
+import java.util.List;
+
+@RunWith(JUnit4.class)
+public final class PackageDataTest {
+
+ private static final String PACKAGE_NAME = "com.google.test";
+ private static final int USER_ID = 0;
+ private static final String SHORTCUT_ID = "abc";
+ private static final LocusId LOCUS_ID = new LocusId("def");
+ private static final Uri CONTACT_URI = Uri.parse("tel:+1234567890");
+ private static final String PHONE_NUMBER = "+1234567890";
+
+ private Event mE1;
+ private Event mE2;
+ private Event mE3;
+ private Event mE4;
+
+ private PackageData mPackageData;
+
+ @Before
+ public void setUp() {
+ mPackageData = new PackageData(PACKAGE_NAME, USER_ID);
+ ConversationInfo conversationInfo = new ConversationInfo.Builder()
+ .setShortcutId(SHORTCUT_ID)
+ .setLocusId(LOCUS_ID)
+ .setContactUri(CONTACT_URI)
+ .setContactPhoneNumber(PHONE_NUMBER)
+ .setShortcutFlags(ShortcutInfo.FLAG_LONG_LIVED)
+ .build();
+ mPackageData.getConversationStore().addOrUpdate(conversationInfo);
+
+ long currentTimestamp = System.currentTimeMillis();
+ mE1 = new Event(currentTimestamp - 800L, Event.TYPE_SHORTCUT_INVOCATION);
+ mE2 = new Event(currentTimestamp - 700L, Event.TYPE_NOTIFICATION_OPENED);
+ mE3 = new Event(currentTimestamp - 600L, Event.TYPE_CALL_INCOMING);
+ mE4 = new Event(currentTimestamp - 500L, Event.TYPE_SMS_OUTGOING);
+ }
+
+ @Test
+ public void testGetEventHistory() {
+ EventStore eventStore = mPackageData.getEventStore();
+ eventStore.getOrCreateShortcutEventHistory(SHORTCUT_ID).addEvent(mE1);
+ eventStore.getOrCreateLocusEventHistory(LOCUS_ID).addEvent(mE2);
+
+ EventHistory eventHistory = mPackageData.getEventHistory(SHORTCUT_ID);
+ List<Event> events = eventHistory.queryEvents(Event.ALL_EVENT_TYPES, 0L, Long.MAX_VALUE);
+ assertEquals(2, events.size());
+ assertEventEquals(mE1, events.get(0));
+ assertEventEquals(mE2, events.get(1));
+ }
+
+ @Test
+ public void testGetEventHistoryDefaultDialerAndSmsApp() {
+ mPackageData.setIsDefaultDialer(true);
+ mPackageData.setIsDefaultSmsApp(true);
+ EventStore eventStore = mPackageData.getEventStore();
+ eventStore.getOrCreateShortcutEventHistory(SHORTCUT_ID).addEvent(mE1);
+ eventStore.getOrCreateCallEventHistory(PHONE_NUMBER).addEvent(mE3);
+ eventStore.getOrCreateSmsEventHistory(PHONE_NUMBER).addEvent(mE4);
+
+ assertTrue(mPackageData.isDefaultDialer());
+ assertTrue(mPackageData.isDefaultSmsApp());
+ EventHistory eventHistory = mPackageData.getEventHistory(SHORTCUT_ID);
+ List<Event> events = eventHistory.queryEvents(Event.ALL_EVENT_TYPES, 0L, Long.MAX_VALUE);
+ assertEquals(3, events.size());
+ assertEventEquals(mE1, events.get(0));
+ assertEventEquals(mE3, events.get(1));
+ assertEventEquals(mE4, events.get(2));
+ }
+
+ @Test
+ public void testGetEventHistoryNotDefaultDialerOrSmsApp() {
+ EventStore eventStore = mPackageData.getEventStore();
+ eventStore.getOrCreateShortcutEventHistory(SHORTCUT_ID).addEvent(mE1);
+ eventStore.getOrCreateCallEventHistory(PHONE_NUMBER).addEvent(mE3);
+ eventStore.getOrCreateSmsEventHistory(PHONE_NUMBER).addEvent(mE4);
+
+ assertFalse(mPackageData.isDefaultDialer());
+ assertFalse(mPackageData.isDefaultSmsApp());
+ EventHistory eventHistory = mPackageData.getEventHistory(SHORTCUT_ID);
+ List<Event> events = eventHistory.queryEvents(Event.ALL_EVENT_TYPES, 0L, Long.MAX_VALUE);
+ assertEquals(1, events.size());
+ assertEventEquals(mE1, events.get(0));
+ }
+
+ private void assertEventEquals(Event expected, Event actual) {
+ assertEquals(expected.getTimestamp(), actual.getTimestamp());
+ assertEquals(expected.getType(), actual.getType());
+ }
+}
diff --git a/services/tests/servicestests/src/com/android/server/people/data/TestUtils.java b/services/tests/servicestests/src/com/android/server/people/data/TestUtils.java
new file mode 100644
index 000000000000..41889aa5b224
--- /dev/null
+++ b/services/tests/servicestests/src/com/android/server/people/data/TestUtils.java
@@ -0,0 +1,42 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.people.data;
+
+import java.time.LocalDateTime;
+import java.time.ZoneId;
+import java.time.format.DateTimeFormatter;
+
+final class TestUtils {
+
+ /**
+ * Gets the epoch time in millis for the specified time string.
+ * @param timeString e.g. "01-02 15:20"
+ * @return epoch time in millis
+ */
+ static long timestamp(String timeString) {
+ String str = String.format("2020-%s", timeString);
+ DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm");
+ return toEpochMilli(LocalDateTime.parse(str, formatter));
+ }
+
+ private static long toEpochMilli(LocalDateTime localDateTime) {
+ return localDateTime.atZone(ZoneId.systemDefault()).toInstant().toEpochMilli();
+ }
+
+ private TestUtils() {
+ }
+}
diff --git a/services/tests/servicestests/src/com/android/server/pm/AppsFilterTest.java b/services/tests/servicestests/src/com/android/server/pm/AppsFilterTest.java
index 7f66f3c49185..3e3f40d31d0e 100644
--- a/services/tests/servicestests/src/com/android/server/pm/AppsFilterTest.java
+++ b/services/tests/servicestests/src/com/android/server/pm/AppsFilterTest.java
@@ -27,6 +27,8 @@ import android.annotation.Nullable;
import android.content.Intent;
import android.content.IntentFilter;
import android.content.pm.ApplicationInfo;
+import android.content.pm.PackageParser;
+import android.content.pm.Signature;
import android.content.pm.parsing.AndroidPackage;
import android.content.pm.parsing.ComponentParseUtils;
import android.content.pm.parsing.ComponentParseUtils.ParsedActivity;
@@ -48,8 +50,10 @@ import org.junit.Test;
import org.junit.runner.RunWith;
import org.junit.runners.JUnit4;
import org.mockito.Mock;
+import org.mockito.Mockito;
import org.mockito.MockitoAnnotations;
+import java.security.cert.CertificateException;
import java.util.Collections;
import java.util.Map;
import java.util.Set;
@@ -284,6 +288,33 @@ public class AppsFilterTest {
assertFalse(appsFilter.shouldFilterApplication(DUMMY_CALLING_UID, calling, target, 0));
}
+
+ @Test
+ public void testSystemSignedTarget_DoesntFilter() throws CertificateException {
+ final AppsFilter appsFilter =
+ new AppsFilter(mFeatureConfigMock, new String[]{}, false, null);
+ appsFilter.onSystemReady();
+
+ final Signature frameworkSignature = Mockito.mock(Signature.class);
+ final PackageParser.SigningDetails frameworkSigningDetails =
+ new PackageParser.SigningDetails(new Signature[]{frameworkSignature}, 1);
+
+ final Signature otherSignature = Mockito.mock(Signature.class);
+ final PackageParser.SigningDetails otherSigningDetails =
+ new PackageParser.SigningDetails(new Signature[]{otherSignature}, 1);
+
+ simulateAddPackage(appsFilter, pkg("android"), 1000,
+ b -> b.setSigningDetails(frameworkSigningDetails));
+ PackageSetting target = simulateAddPackage(appsFilter, pkg("com.some.package"),
+ DUMMY_TARGET_UID,
+ b -> b.setSigningDetails(frameworkSigningDetails));
+ PackageSetting calling = simulateAddPackage(appsFilter,
+ pkg("com.some.other.package"), DUMMY_CALLING_UID,
+ b -> b.setSigningDetails(otherSigningDetails));
+
+ assertFalse(appsFilter.shouldFilterApplication(DUMMY_CALLING_UID, calling, target, 0));
+ }
+
@Test
public void testForceQueryableByDevice_NonSystemCaller_Filters() {
final AppsFilter appsFilter =
diff --git a/services/tests/servicestests/src/com/android/server/pm/PackageInstallerSessionTest.java b/services/tests/servicestests/src/com/android/server/pm/PackageInstallerSessionTest.java
index a8674a8f8be4..bfe0c15ef6e8 100644
--- a/services/tests/servicestests/src/com/android/server/pm/PackageInstallerSessionTest.java
+++ b/services/tests/servicestests/src/com/android/server/pm/PackageInstallerSessionTest.java
@@ -156,7 +156,8 @@ public class PackageInstallerSessionTest {
if (isMultiPackage) {
params.isMultiPackage = true;
}
- InstallSource installSource = InstallSource.create("testInstaller", null, "testInstaller");
+ InstallSource installSource = InstallSource.create("testInstallInitiator",
+ "testInstallOriginator", "testInstaller");
return new PackageInstallerSession(
/* callback */ null,
/* context */null,
@@ -297,6 +298,8 @@ public class PackageInstallerSessionTest {
assertEquals(expected.userId, actual.userId);
assertSessionParamsEquivalent(expected.params, actual.params);
assertEquals(expected.getInstallerUid(), actual.getInstallerUid());
+ assertEquals(expected.getInstallerPackageName(), actual.getInstallerPackageName());
+ assertInstallSourcesEquivalent(expected.getInstallSource(), actual.getInstallSource());
assertEquals(expected.stageDir.getAbsolutePath(), actual.stageDir.getAbsolutePath());
assertEquals(expected.stageCid, actual.stageCid);
assertEquals(expected.isPrepared(), actual.isPrepared());
@@ -316,4 +319,10 @@ public class PackageInstallerSessionTest {
assertEquals(expected.getParentSessionId(), actual.getParentSessionId());
assertArrayEquals(expected.getChildSessionIds(), actual.getChildSessionIds());
}
+
+ private void assertInstallSourcesEquivalent(InstallSource expected, InstallSource actual) {
+ assertEquals(expected.installerPackageName, actual.installerPackageName);
+ assertEquals(expected.initiatingPackageName, actual.initiatingPackageName);
+ assertEquals(expected.originatingPackageName, actual.originatingPackageName);
+ }
}
diff --git a/services/tests/servicestests/src/com/android/server/pm/PackageSettingBuilder.java b/services/tests/servicestests/src/com/android/server/pm/PackageSettingBuilder.java
index 84414947056f..338d5fa347a6 100644
--- a/services/tests/servicestests/src/com/android/server/pm/PackageSettingBuilder.java
+++ b/services/tests/servicestests/src/com/android/server/pm/PackageSettingBuilder.java
@@ -16,6 +16,7 @@
package com.android.server.pm;
+import android.content.pm.PackageParser;
import android.content.pm.PackageUserState;
import android.content.pm.parsing.AndroidPackage;
import android.util.SparseArray;
@@ -42,6 +43,7 @@ public class PackageSettingBuilder {
private AndroidPackage mPkg;
private int mAppId;
private InstallSource mInstallSource;
+ private PackageParser.SigningDetails mSigningDetails;
public PackageSettingBuilder setPackage(AndroidPackage pkg) {
this.mPkg = pkg;
@@ -143,12 +145,21 @@ public class PackageSettingBuilder {
return this;
}
+ public PackageSettingBuilder setSigningDetails(
+ PackageParser.SigningDetails signingDetails) {
+ mSigningDetails = signingDetails;
+ return this;
+ }
+
public PackageSetting build() {
final PackageSetting packageSetting = new PackageSetting(mName, mRealName,
new File(mCodePath), new File(mResourcePath),
mLegacyNativeLibraryPathString, mPrimaryCpuAbiString, mSecondaryCpuAbiString,
mCpuAbiOverrideString, mPVersionCode, mPkgFlags, mPrivateFlags, mSharedUserId,
mUsesStaticLibraries, mUsesStaticLibrariesVersions);
+ packageSetting.signatures = mSigningDetails != null
+ ? new PackageSignatures(mSigningDetails)
+ : new PackageSignatures();
packageSetting.pkg = mPkg;
packageSetting.appId = mAppId;
packageSetting.volumeUuid = this.mVolumeUuid;
diff --git a/services/tests/servicestests/src/com/android/server/pm/UserManagerServiceUserInfoTest.java b/services/tests/servicestests/src/com/android/server/pm/UserManagerServiceUserInfoTest.java
index ac27a087ba8e..5df856865ff9 100644
--- a/services/tests/servicestests/src/com/android/server/pm/UserManagerServiceUserInfoTest.java
+++ b/services/tests/servicestests/src/com/android/server/pm/UserManagerServiceUserInfoTest.java
@@ -153,6 +153,25 @@ public class UserManagerServiceUserInfoTest {
assertTrue(mUserManagerService.isUserOfType(testId, typeName));
}
+ /** Test UserInfo.supportsSwitchTo() for precreated users. */
+ @Test
+ public void testSupportSwitchTo_preCreated() throws Exception {
+ UserInfo userInfo = createUser(100, FLAG_FULL, null);
+ userInfo.preCreated = true;
+ assertFalse("Switching to a precreated user should be disabled",
+ userInfo.supportsSwitchTo());
+
+ userInfo.preCreated = false;
+ assertTrue("Switching to a full, real user should be allowed", userInfo.supportsSwitchTo());
+ }
+
+ /** Test UserInfo.supportsSwitchTo() for profiles. */
+ @Test
+ public void testSupportSwitchTo_profile() throws Exception {
+ UserInfo userInfo = createUser(100, FLAG_PROFILE, null);
+ assertFalse("Switching to a profiles should be disabled", userInfo.supportsSwitchTo());
+ }
+
/** Tests upgradeIfNecessaryLP (but without locking) for upgrading from version 8 to 9+. */
@Test
public void testUpgradeIfNecessaryLP_9() {
diff --git a/services/tests/servicestests/src/com/android/server/power/WakeLockLogTest.java b/services/tests/servicestests/src/com/android/server/power/WakeLockLogTest.java
new file mode 100644
index 000000000000..a03ba9c5e87e
--- /dev/null
+++ b/services/tests/servicestests/src/com/android/server/power/WakeLockLogTest.java
@@ -0,0 +1,268 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.power;
+
+import static org.junit.Assert.assertEquals;
+import static org.mockito.Mockito.spy;
+import static org.mockito.Mockito.when;
+
+import android.os.Looper;
+import android.os.PowerManager;
+import android.os.test.TestLooper;
+
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+
+import java.io.PrintWriter;
+import java.io.StringWriter;
+import java.text.SimpleDateFormat;
+import java.util.TimeZone;
+
+/**
+ * Tests for {@link WakeLockLog}.
+ */
+public class WakeLockLogTest {
+
+ private TestLooper mTestLooper;
+
+ @Before
+ public void setUp() throws Exception {
+ mTestLooper = new TestLooper();
+ }
+
+ @After
+ public void tearDown() throws Exception {
+ }
+
+ @Test
+ public void testAddTwoItems() {
+ final int tagDatabaseSize = 128;
+ final int logSize = 20;
+ TestInjector injectorSpy = spy(new TestInjector(tagDatabaseSize, logSize));
+ WakeLockLog log = new WakeLockLog(injectorSpy);
+
+ when(injectorSpy.currentTimeMillis()).thenReturn(1000L);
+ log.onWakeLockAcquired("TagPartial", 101,
+ PowerManager.PARTIAL_WAKE_LOCK | PowerManager.ON_AFTER_RELEASE);
+
+ when(injectorSpy.currentTimeMillis()).thenReturn(1150L);
+ log.onWakeLockAcquired("TagFull", 102,
+ PowerManager.FULL_WAKE_LOCK | PowerManager.ACQUIRE_CAUSES_WAKEUP);
+
+ assertEquals("Wake Lock Log\n"
+ + " 01-01 00:00:01.000 - 101 - ACQ TagPartial (partial,on-after-release)\n"
+ + " 01-01 00:00:01.150 - 102 - ACQ TagFull (full,acq-causes-wake)\n"
+ + " -\n"
+ + " Events: 2, Time-Resets: 0\n"
+ + " Buffer, Bytes used: 6\n",
+ dispatchAndDump(log, false));
+ }
+
+ @Test
+ public void testAddTwoItemsWithTimeReset() {
+ final int tagDatabaseSize = 128;
+ final int logSize = 20;
+ TestInjector injectorSpy = spy(new TestInjector(tagDatabaseSize, logSize));
+ WakeLockLog log = new WakeLockLog(injectorSpy);
+
+ when(injectorSpy.currentTimeMillis()).thenReturn(1000L);
+ log.onWakeLockAcquired("TagPartial", 101, PowerManager.PARTIAL_WAKE_LOCK);
+
+ when(injectorSpy.currentTimeMillis()).thenReturn(1350L);
+ log.onWakeLockAcquired("TagFull", 102, PowerManager.FULL_WAKE_LOCK);
+
+ assertEquals("Wake Lock Log\n"
+ + " 01-01 00:00:01.000 - 101 - ACQ TagPartial (partial)\n"
+ + " 01-01 00:00:01.350 - 102 - ACQ TagFull (full)\n"
+ + " -\n"
+ + " Events: 2, Time-Resets: 1\n"
+ + " Buffer, Bytes used: 15\n",
+ dispatchAndDump(log, false));
+ }
+
+ @Test
+ public void testAddTwoItemsWithTagOverwrite() {
+ final int tagDatabaseSize = 2;
+ final int logSize = 20;
+ TestInjector injectorSpy = spy(new TestInjector(tagDatabaseSize, logSize));
+ WakeLockLog log = new WakeLockLog(injectorSpy);
+
+ when(injectorSpy.currentTimeMillis()).thenReturn(1000L);
+ log.onWakeLockAcquired("TagPartial", 101, PowerManager.PARTIAL_WAKE_LOCK);
+
+ when(injectorSpy.currentTimeMillis()).thenReturn(1150L);
+ log.onWakeLockAcquired("TagFull", 102, PowerManager.FULL_WAKE_LOCK);
+
+ assertEquals("Wake Lock Log\n"
+ + " 01-01 00:00:01.000 - --- - ACQ UNKNOWN (partial)\n"
+ + " 01-01 00:00:01.150 - 102 - ACQ TagFull (full)\n"
+ + " -\n"
+ + " Events: 2, Time-Resets: 0\n"
+ + " Buffer, Bytes used: 6\n",
+ dispatchAndDump(log, false));
+ }
+
+ @Test
+ public void testAddFourItemsWithRingBufferOverflow() {
+ final int tagDatabaseSize = 6;
+ final int logSize = 10;
+ TestInjector injectorSpy = spy(new TestInjector(tagDatabaseSize, logSize));
+ WakeLockLog log = new WakeLockLog(injectorSpy);
+
+ // This first item will get deleted when ring buffer loops around
+ when(injectorSpy.currentTimeMillis()).thenReturn(1000L);
+ log.onWakeLockAcquired("TagPartial", 101, PowerManager.PARTIAL_WAKE_LOCK);
+
+ when(injectorSpy.currentTimeMillis()).thenReturn(1150L);
+ log.onWakeLockAcquired("TagFull", 102, PowerManager.FULL_WAKE_LOCK);
+ when(injectorSpy.currentTimeMillis()).thenReturn(1151L);
+ log.onWakeLockAcquired("TagThree", 101, PowerManager.PARTIAL_WAKE_LOCK);
+ when(injectorSpy.currentTimeMillis()).thenReturn(1152L);
+ log.onWakeLockAcquired("TagFour", 101, PowerManager.PARTIAL_WAKE_LOCK);
+
+ assertEquals("Wake Lock Log\n"
+ + " 01-01 00:00:01.150 - 102 - ACQ TagFull (full)\n"
+ + " 01-01 00:00:01.151 - 101 - ACQ TagThree (partial)\n"
+ + " 01-01 00:00:01.152 - 101 - ACQ TagFour (partial)\n"
+ + " -\n"
+ + " Events: 3, Time-Resets: 0\n"
+ + " Buffer, Bytes used: 9\n",
+ dispatchAndDump(log, false));
+ }
+
+ @Test
+ public void testAddItemWithBadTag() {
+ final int tagDatabaseSize = 6;
+ final int logSize = 10;
+ TestInjector injectorSpy = spy(new TestInjector(tagDatabaseSize, logSize));
+ WakeLockLog log = new WakeLockLog(injectorSpy);
+
+ // Bad tag means it wont get written
+ when(injectorSpy.currentTimeMillis()).thenReturn(1000L);
+ log.onWakeLockAcquired(null /* tag */, 0 /* ownerUid */, PowerManager.PARTIAL_WAKE_LOCK);
+
+ assertEquals("Wake Lock Log\n"
+ + " -\n"
+ + " Events: 0, Time-Resets: 0\n"
+ + " Buffer, Bytes used: 0\n",
+ dispatchAndDump(log, false));
+ }
+
+ @Test
+ public void testAddItemWithReducedTagName() {
+ final int tagDatabaseSize = 6;
+ final int logSize = 10;
+ TestInjector injectorSpy = spy(new TestInjector(tagDatabaseSize, logSize));
+ WakeLockLog log = new WakeLockLog(injectorSpy);
+
+ when(injectorSpy.currentTimeMillis()).thenReturn(1000L);
+ log.onWakeLockAcquired("*job*/com.one.two.3hree/.one..Last", 101,
+ PowerManager.PARTIAL_WAKE_LOCK);
+
+ assertEquals("Wake Lock Log\n"
+ + " 01-01 00:00:01.000 - 101 - ACQ *job*/c.o.t.3/.o..Last (partial)\n"
+ + " -\n"
+ + " Events: 1, Time-Resets: 0\n"
+ + " Buffer, Bytes used: 3\n",
+ dispatchAndDump(log, false));
+ }
+
+ @Test
+ public void testAddAcquireAndReleaseWithRepeatTagName() {
+ final int tagDatabaseSize = 6;
+ final int logSize = 10;
+ TestInjector injectorSpy = spy(new TestInjector(tagDatabaseSize, logSize));
+ WakeLockLog log = new WakeLockLog(injectorSpy);
+
+ when(injectorSpy.currentTimeMillis()).thenReturn(1000L);
+ log.onWakeLockAcquired("HowdyTag", 101, PowerManager.PARTIAL_WAKE_LOCK);
+ when(injectorSpy.currentTimeMillis()).thenReturn(1001L);
+ log.onWakeLockReleased("HowdyTag", 101);
+
+ assertEquals("Wake Lock Log\n"
+ + " 01-01 00:00:01.000 - 101 - ACQ HowdyTag (partial)\n"
+ + " 01-01 00:00:01.001 - 101 - REL HowdyTag\n"
+ + " -\n"
+ + " Events: 2, Time-Resets: 0\n"
+ + " Buffer, Bytes used: 5\n"
+ + " Tag Database: size(5), entries: 1, Bytes used: 80\n",
+ dispatchAndDump(log, true));
+ }
+
+ @Test
+ public void testAddAcquireAndReleaseWithTimeTravel() {
+ final int tagDatabaseSize = 6;
+ final int logSize = 10;
+ TestInjector injectorSpy = spy(new TestInjector(tagDatabaseSize, logSize));
+ WakeLockLog log = new WakeLockLog(injectorSpy);
+
+ when(injectorSpy.currentTimeMillis()).thenReturn(1100L);
+ log.onWakeLockAcquired("HowdyTag", 101, PowerManager.PARTIAL_WAKE_LOCK);
+
+ // New element goes back in time...should not be written to log.
+ when(injectorSpy.currentTimeMillis()).thenReturn(1000L);
+ log.onWakeLockReleased("HowdyTag", 101);
+
+ assertEquals("Wake Lock Log\n"
+ + " 01-01 00:00:01.100 - 101 - ACQ HowdyTag (partial)\n"
+ + " -\n"
+ + " Events: 1, Time-Resets: 0\n"
+ + " Buffer, Bytes used: 3\n",
+ dispatchAndDump(log, false));
+ }
+
+ private String dispatchAndDump(WakeLockLog log, boolean includeTagDb) {
+ mTestLooper.dispatchAll();
+ StringWriter sw = new StringWriter();
+ PrintWriter pw = new PrintWriter(sw);
+ log.dump(pw, includeTagDb);
+ return sw.toString();
+ }
+
+ public class TestInjector extends WakeLockLog.Injector {
+ private final int mTagDatabaseSize;
+ private final int mLogSize;
+
+ public TestInjector(int tagDatabaseSize, int logSize) {
+ mTagDatabaseSize = tagDatabaseSize;
+ mLogSize = logSize;
+ }
+
+ @Override
+ public Looper getLooper() {
+ return mTestLooper.getLooper();
+ }
+
+ @Override
+ public int getTagDatabaseSize() {
+ return mTagDatabaseSize;
+ }
+
+ @Override
+ public int getLogSize() {
+ return mLogSize;
+ }
+
+ @Override
+ public SimpleDateFormat getDateFormat() {
+ SimpleDateFormat format = new SimpleDateFormat(super.getDateFormat().toPattern());
+ format.setTimeZone(TimeZone.getTimeZone("UTC"));
+ return format;
+ }
+ }
+}
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 1b92abef7c94..768b4721a1ee 100755
--- a/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java
+++ b/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java
@@ -5438,6 +5438,46 @@ public class NotificationManagerServiceTest extends UiServiceTestCase {
}
@Test
+ public void testOnBubbleNotificationSuppressionChanged() throws Exception {
+ // Bubble notification
+ NotificationRecord nr = generateMessageBubbleNotifRecord(mTestNotificationChannel, "tag");
+
+ // Bubbles are allowed!
+ setUpPrefsForBubbles(PKG, nr.sbn.getUserId(), true /* global */,
+ true /* app */, true /* channel */);
+
+ mBinderService.enqueueNotificationWithTag(PKG, PKG, nr.sbn.getTag(),
+ nr.sbn.getId(), nr.sbn.getNotification(), nr.sbn.getUserId());
+ waitForIdle();
+
+ // NOT suppressed
+ Notification n = mBinderService.getActiveNotifications(PKG)[0].getNotification();
+ assertFalse(n.getBubbleMetadata().isNotificationSuppressed());
+
+ // Reset as this is called when the notif is first sent
+ reset(mListeners);
+
+ // Test: update suppression to true
+ mService.mNotificationDelegate.onBubbleNotificationSuppressionChanged(nr.getKey(), true);
+ waitForIdle();
+
+ // Check
+ n = mBinderService.getActiveNotifications(PKG)[0].getNotification();
+ assertTrue(n.getBubbleMetadata().isNotificationSuppressed());
+
+ // Reset to check again
+ reset(mListeners);
+
+ // Test: update suppression to false
+ mService.mNotificationDelegate.onBubbleNotificationSuppressionChanged(nr.getKey(), false);
+ waitForIdle();
+
+ // Check
+ n = mBinderService.getActiveNotifications(PKG)[0].getNotification();
+ assertFalse(n.getBubbleMetadata().isNotificationSuppressed());
+ }
+
+ @Test
public void testGrantInlineReplyUriPermission_recordExists() throws Exception {
NotificationRecord nr = generateNotificationRecord(mTestNotificationChannel, 0);
mBinderService.enqueueNotificationWithTag(PKG, PKG, "tag",
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 0fc2bc510d05..bab877e6c26f 100644
--- a/services/tests/wmtests/src/com/android/server/wm/ActivityStarterTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/ActivityStarterTests.java
@@ -1008,4 +1008,18 @@ public class ActivityStarterTests extends ActivityTestsBase {
.setOutActivity(outActivity).execute();
assertThat(outActivity[0].inSplitScreenSecondaryWindowingMode()).isTrue();
}
+
+ @Test
+ public void testActivityStart_expectAddedToRecentTask() {
+ RecentTasks recentTasks = mock(RecentTasks.class);
+ mService.mStackSupervisor.setRecentTasks(recentTasks);
+ doReturn(true).when(recentTasks).isCallerRecents(anyInt());
+
+ final ActivityStarter starter = prepareStarter(0 /* flags */);
+
+ starter.setReason("testAddToTaskListOnActivityStart")
+ .execute();
+
+ verify(recentTasks, times(1)).add(any());
+ }
}
diff --git a/services/tests/wmtests/src/com/android/server/wm/RecentTasksTest.java b/services/tests/wmtests/src/com/android/server/wm/RecentTasksTest.java
index b5e7dd58f7cc..a672a95dcc12 100644
--- a/services/tests/wmtests/src/com/android/server/wm/RecentTasksTest.java
+++ b/services/tests/wmtests/src/com/android/server/wm/RecentTasksTest.java
@@ -39,6 +39,8 @@ import static com.google.common.truth.Truth.assertWithMessage;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertNull;
import static org.junit.Assert.assertTrue;
import static org.junit.Assert.fail;
import static org.mockito.ArgumentMatchers.anyBoolean;
@@ -83,6 +85,7 @@ import java.util.HashSet;
import java.util.List;
import java.util.Random;
import java.util.Set;
+import java.util.function.Function;
/**
* Build/Install/Run:
@@ -419,6 +422,36 @@ public class RecentTasksTest extends ActivityTestsBase {
}
@Test
+ public void testAddTasksHomeClearUntrackedTasks_expectFinish() {
+ // There may be multiple tasks with the same base intent by flags (FLAG_ACTIVITY_NEW_TASK |
+ // FLAG_ACTIVITY_MULTIPLE_TASK). If the previous task is still active, it should be removed
+ // because user may not be able to return to the task.
+ final String className = ".PermissionsReview";
+ final Function<Boolean, Task> taskBuilder = visible -> {
+ final Task task = createTaskBuilder(className).build();
+ // Make the task non-empty.
+ final ActivityRecord r = new ActivityBuilder(mService).setTask(task).build();
+ r.setVisibility(visible);
+ return task;
+ };
+
+ final Task task1 = taskBuilder.apply(false /* visible */);
+ mRecentTasks.add(task1);
+ final Task task2 = taskBuilder.apply(true /* visible */);
+ mRecentTasks.add(task2);
+ // Only the last task is kept in recents and the previous 2 tasks will becomes untracked
+ // tasks because their intents are identical.
+ mRecentTasks.add(createTaskBuilder(className).build());
+ // Go home to trigger the removal of untracked tasks.
+ mRecentTasks.add(createTaskBuilder(".Home").setStack(mDisplay.getRootHomeTask()).build());
+
+ // All activities in the invisible task should be finishing or removed.
+ assertNull(task1.getTopNonFinishingActivity());
+ // The visible task should not be affected.
+ assertNotNull(task2.getTopNonFinishingActivity());
+ }
+
+ @Test
public void testUsersTasks() {
mRecentTasks.setOnlyTestVisibleRange();
mRecentTasks.unloadUserDataFromMemoryLocked(TEST_USER_0_ID);
diff --git a/services/tests/wmtests/src/com/android/server/wm/TaskOrganizerTests.java b/services/tests/wmtests/src/com/android/server/wm/TaskOrganizerTests.java
index 8d2da1e6cb5b..c9fd79fb4e39 100644
--- a/services/tests/wmtests/src/com/android/server/wm/TaskOrganizerTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/TaskOrganizerTests.java
@@ -80,7 +80,6 @@ public class TaskOrganizerTests extends WindowTestsBase {
task.setTaskOrganizer(organizer);
verify(organizer).taskAppeared(any(), any());
- assertTrue(task.isControlledByTaskOrganizer());
task.removeImmediately();
verify(organizer).taskVanished(any());
@@ -106,48 +105,13 @@ public class TaskOrganizerTests extends WindowTestsBase {
final Task task = createTaskInStack(stack, 0 /* userId */);
final ITaskOrganizer organizer = makeAndRegisterMockOrganizer();
- task.setTaskOrganizer(organizer);
- verify(organizer).taskAppeared(any(), any());
- assertTrue(task.isControlledByTaskOrganizer());
-
- task.setTaskOrganizer(null);
- verify(organizer).taskVanished(any());
- assertFalse(task.isControlledByTaskOrganizer());
- }
-
- @Test
- public void testTransferStackToOrganizer() throws RemoteException {
- final ActivityStack stack = createTaskStackOnDisplay(mDisplayContent);
- final Task task = createTaskInStack(stack, 0 /* userId */);
- final Task task2 = createTaskInStack(stack, 0 /* userId */);
- final ITaskOrganizer organizer = makeAndRegisterMockOrganizer();
-
- stack.transferToTaskOrganizer(organizer);
-
- verify(organizer, times(2)).taskAppeared(any(), any());
- assertTrue(task.isControlledByTaskOrganizer());
- assertTrue(task2.isControlledByTaskOrganizer());
-
- stack.transferToTaskOrganizer(null);
-
- verify(organizer, times(2)).taskVanished(any());
- assertFalse(task.isControlledByTaskOrganizer());
- assertFalse(task2.isControlledByTaskOrganizer());
- }
-
- @Test
- public void testRegisterTaskOrganizerTaskWindowingModeChanges() throws RemoteException {
- final ITaskOrganizer organizer = makeAndRegisterMockOrganizer();
-
- final ActivityStack stack = createTaskStackOnDisplay(mDisplayContent);
- final Task task = createTaskInStack(stack, 0 /* userId */);
- task.setWindowingMode(WINDOWING_MODE_PINNED);
+ stack.setTaskOrganizer(organizer);
verify(organizer).taskAppeared(any(), any());
- assertTrue(task.isControlledByTaskOrganizer());
+ assertTrue(stack.isControlledByTaskOrganizer());
- task.setWindowingMode(WINDOWING_MODE_FULLSCREEN);
+ stack.setTaskOrganizer(null);
verify(organizer).taskVanished(any());
- assertFalse(task.isControlledByTaskOrganizer());
+ assertFalse(stack.isControlledByTaskOrganizer());
}
@Test
@@ -158,13 +122,9 @@ public class TaskOrganizerTests extends WindowTestsBase {
final Task task = createTaskInStack(stack, 0 /* userId */);
final Task task2 = createTaskInStack(stack, 0 /* userId */);
stack.setWindowingMode(WINDOWING_MODE_PINNED);
- verify(organizer, times(2)).taskAppeared(any(), any());
- assertTrue(task.isControlledByTaskOrganizer());
- assertTrue(task2.isControlledByTaskOrganizer());
+ verify(organizer, times(1)).taskAppeared(any(), any());
stack.setWindowingMode(WINDOWING_MODE_FULLSCREEN);
- verify(organizer, times(2)).taskVanished(any());
- assertFalse(task.isControlledByTaskOrganizer());
- assertFalse(task2.isControlledByTaskOrganizer());
+ verify(organizer, times(1)).taskVanished(any());
}
}
diff --git a/startop/scripts/app_startup/metrics/com.google.android.GoogleCamera b/startop/scripts/app_startup/metrics/com.google.android.GoogleCamera
deleted file mode 100755
index d95fa32dce3f..000000000000
--- a/startop/scripts/app_startup/metrics/com.google.android.GoogleCamera
+++ /dev/null
@@ -1,25 +0,0 @@
-#!/bin/bash
-#
-# Copyright 2019, The Android Open Source Project
-#
-# Licensed under the Apache License, Version 2.0 (the "License");
-# you may not use this file except in compliance with the License.
-# You may obtain a copy of the License at
-#
-# http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS,
-# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-# See the License for the specific language governing permissions and
-# limitations under the License.
-
-# 05-06 14:51:17.688 29691 29897 I CAM_Timing: CameraActivity: START -> ACTIVITY_FIRST_PREVIEW_FRAME_RECEIVED: 385ms
-pattern="CAM_Timing: CameraActivity: START -> ACTIVITY_FIRST_PREVIEW_FRAME_RECEIVED:"
-re_pattern='.*ACTIVITY_FIRST_PREVIEW_FRAME_RECEIVED: \([[:digit:]]\+\)ms'
-parse_metric_from_logcat "ACTIVITY_FIRST_PREVIEW_FRAME_RECEIVED_ms" "$pattern" "$re_pattern"
-
-# 05-06 14:51:17.724 29691 29691 I CAM_Timing: CameraActivity: START -> ACTIVITY_FIRST_PREVIEW_FRAME_RENDERED: 421ms
-pattern="CAM_Timing: CameraActivity: START -> ACTIVITY_FIRST_PREVIEW_FRAME_RENDERED:"
-re_pattern='.*ACTIVITY_FIRST_PREVIEW_FRAME_RENDERED: \([[:digit:]]\+\)ms'
-parse_metric_from_logcat "ACTIVITY_FIRST_PREVIEW_FRAME_RENDERED_ms" "$pattern" "$re_pattern"
diff --git a/startop/scripts/app_startup/parse_metrics b/startop/scripts/app_startup/parse_metrics
index c6bf08ee9dfd..036609ff02be 100755
--- a/startop/scripts/app_startup/parse_metrics
+++ b/startop/scripts/app_startup/parse_metrics
@@ -50,7 +50,7 @@ source "$DIR/lib/common"
package=""
activity=""
-timeout=10
+timeout=5
simulate="n"
parse_arguments() {
while [[ $# -gt 0 ]]; do
@@ -123,6 +123,9 @@ parse_metric_from_logcat() {
local pattern="$2"
local re_pattern="$3"
local retcode
+ local result
+ local sec
+ local ms
# parse logcat for 'Displayed...' and that other one...
@@ -137,14 +140,28 @@ parse_metric_from_logcat() {
return 0
fi
- logcat_extract_pattern "$timeout" "$timestamp" "$pattern" "$re_pattern"
+ result="$(logcat_extract_pattern "$timeout" "$timestamp" "$pattern" "$re_pattern")"
retcode=$?
if [[ $retcode -ne 0 ]]; then
# Timed out before finding the pattern. Could also mean the pattern is wrong.
+ echo "Parse $re_pattern from logcat TIMED OUT after $timeout seconds." >&2
echo "-$?"
+ return $retcode
fi
+ # "10s123ms" -> "10s123"
+ result=${result/ms/}
+ if [[ $result =~ s ]]; then
+ ms=${result/*s/}
+ sec=${result/s*/}
+ else
+ sec=0
+ ms=$result
+ fi
+ ((result=sec*1000+ms))
+
+ echo "$result"
return $retcode
}
@@ -169,10 +186,17 @@ echo "TotalTime_ms=$total_time"
# 05-06 14:34:08.854 29460 29481 I ActivityTaskManager: Displayed com.google.android.dialer/.extensions.GoogleDialtactsActivity: +361ms
pattern="ActivityTaskManager: Displayed ${package}"
-re_pattern='.*Displayed[[:blank:]]\+'"${package}"'[/][^[:blank:]]\+[[:blank:]]+\([[:digit:]]\+\).*'
+re_pattern='.*Displayed[[:blank:]]\+'"${package}"'[/][^[:blank:]]\+[[:blank:]]+\([[:digit:]]\+ms\|[[:digit:]]\+s[[:digit:]]\+ms\).*'
parse_metric_from_logcat "Displayed_ms" "$pattern" "$re_pattern"
+# 01-16 17:31:44.550 11172 11204 I ActivityTaskManager: Fully drawn com.google.android.GoogleCamera/com.android.camera.CameraLauncher: +10s897ms
+pattern="ActivityTaskManager: Fully drawn ${package}"
+#re_pattern='.*Fully drawn[[:blank:]]\+'"${package}"'[/][^[:blank:]]\+[[:blank:]]+\([[:digit:]]\+\).*'
+re_pattern='.*Fully drawn[[:blank:]]\+'"${package}"'[/][^[:blank:]]\+[[:blank:]]+\([[:digit:]]\+ms\|[[:digit:]]\+s[[:digit:]]\+ms\).*'
+
+parse_metric_from_logcat "Fully_drawn_ms" "$pattern" "$re_pattern"
+
# also call into package-specific scripts if there are additional metrics
if [[ -x "$DIR/metrics/$package" ]]; then
source "$DIR/metrics/$package" "$timestamp"
diff --git a/telecomm/java/android/telecom/Call.java b/telecomm/java/android/telecom/Call.java
index acf51f3856d3..f54f8d1f5832 100644
--- a/telecomm/java/android/telecom/Call.java
+++ b/telecomm/java/android/telecom/Call.java
@@ -265,6 +265,29 @@ public final class Call {
public static final String EVENT_HANDOVER_FAILED =
"android.telecom.event.HANDOVER_FAILED";
+
+ /**
+ * Reject reason used with {@link #reject(int)} to indicate that the user is rejecting this
+ * call because they have declined to answer it. This typically means that they are unable
+ * to answer the call at this time and would prefer it be sent to voicemail.
+ */
+ public static final int REJECT_REASON_DECLINED = 1;
+
+ /**
+ * Reject reason used with {@link #reject(int)} to indicate that the user is rejecting this
+ * call because it is an unwanted call. This allows the user to indicate that they are
+ * rejecting a call because it is likely a nuisance call.
+ */
+ public static final int REJECT_REASON_UNWANTED = 2;
+
+ /**
+ * @hide
+ */
+ @IntDef(prefix = { "REJECT_REASON_" },
+ value = {REJECT_REASON_DECLINED, REJECT_REASON_UNWANTED})
+ @Retention(RetentionPolicy.SOURCE)
+ public @interface RejectReason {};
+
public static class Details {
/** @hide */
@Retention(RetentionPolicy.SOURCE)
@@ -1520,6 +1543,16 @@ public final class Call {
}
/**
+ * Instructs the {@link ConnectionService} providing this {@link #STATE_RINGING} call that the
+ * user has chosen to reject the call and has indicated a reason why the call is being rejected.
+ *
+ * @param rejectReason the reason the call is being rejected.
+ */
+ public void reject(@RejectReason int rejectReason) {
+ mInCallAdapter.rejectCall(mTelecomCallId, rejectReason);
+ }
+
+ /**
* Instructs this {@code Call} to disconnect.
*/
public void disconnect() {
diff --git a/telecomm/java/android/telecom/Connection.java b/telecomm/java/android/telecom/Connection.java
index c934625f588b..72c66d20548a 100644
--- a/telecomm/java/android/telecom/Connection.java
+++ b/telecomm/java/android/telecom/Connection.java
@@ -3037,6 +3037,17 @@ public abstract class Connection extends Conferenceable {
public void onReject() {}
/**
+ * Notifies this Connection, which is in {@link #STATE_RINGING}, of a request to reject.
+ * <p>
+ * For managed {@link ConnectionService}s, this will be called when the user rejects a call via
+ * the default dialer's {@link InCallService} using {@link Call#reject(int)}.
+ * @param rejectReason the reason the user provided for rejecting the call.
+ */
+ public void onReject(@android.telecom.Call.RejectReason int rejectReason) {
+ // to be implemented by ConnectionService.
+ }
+
+ /**
* Notifies this Connection, which is in {@link #STATE_RINGING}, of
* a request to reject with a message.
*/
diff --git a/telecomm/java/android/telecom/ConnectionService.java b/telecomm/java/android/telecom/ConnectionService.java
index 440f044fdcf7..00c2918837ac 100644
--- a/telecomm/java/android/telecom/ConnectionService.java
+++ b/telecomm/java/android/telecom/ConnectionService.java
@@ -194,6 +194,7 @@ public abstract class ConnectionService extends Service {
private static final int MSG_CREATE_CONFERENCE = 35;
private static final int MSG_CREATE_CONFERENCE_COMPLETE = 36;
private static final int MSG_CREATE_CONFERENCE_FAILED = 37;
+ private static final int MSG_REJECT_WITH_REASON = 38;
private static Connection sNullConnection;
@@ -450,6 +451,21 @@ public abstract class ConnectionService extends Service {
}
@Override
+ public void rejectWithReason(String callId,
+ @android.telecom.Call.RejectReason int rejectReason, Session.Info sessionInfo) {
+ Log.startSession(sessionInfo, SESSION_REJECT);
+ try {
+ SomeArgs args = SomeArgs.obtain();
+ args.arg1 = callId;
+ args.argi1 = rejectReason;
+ args.arg2 = Log.createSubsession();
+ mHandler.obtainMessage(MSG_REJECT_WITH_REASON, args).sendToTarget();
+ } finally {
+ Log.endSession();
+ }
+ }
+
+ @Override
public void rejectWithMessage(String callId, String message, Session.Info sessionInfo) {
Log.startSession(sessionInfo, SESSION_REJECT_MESSAGE);
try {
@@ -1053,6 +1069,17 @@ public abstract class ConnectionService extends Service {
}
break;
}
+ case MSG_REJECT_WITH_REASON: {
+ SomeArgs args = (SomeArgs) msg.obj;
+ Log.continueSession((Session) args.arg2, SESSION_HANDLER + SESSION_REJECT);
+ try {
+ reject((String) args.arg1, args.argi1);
+ } finally {
+ args.recycle();
+ Log.endSession();
+ }
+ break;
+ }
case MSG_REJECT_WITH_MESSAGE: {
SomeArgs args = (SomeArgs) msg.obj;
Log.continueSession((Session) args.arg3,
@@ -1981,6 +2008,11 @@ public abstract class ConnectionService extends Service {
findConnectionForAction(callId, "reject").onReject(rejectWithMessage);
}
+ private void reject(String callId, @android.telecom.Call.RejectReason int rejectReason) {
+ Log.d(this, "reject %s with reason %d", callId, rejectReason);
+ findConnectionForAction(callId, "reject").onReject(rejectReason);
+ }
+
private void silence(String callId) {
Log.d(this, "silence %s", callId);
findConnectionForAction(callId, "silence").onSilence();
diff --git a/telecomm/java/android/telecom/InCallAdapter.java b/telecomm/java/android/telecom/InCallAdapter.java
index 261246818f1d..594c1eb392b3 100644
--- a/telecomm/java/android/telecom/InCallAdapter.java
+++ b/telecomm/java/android/telecom/InCallAdapter.java
@@ -89,6 +89,19 @@ public final class InCallAdapter {
}
/**
+ * Instructs Telecom to reject the specified call.
+ *
+ * @param callId The identifier of the call to reject.
+ * @param rejectReason The reason the call was rejected.
+ */
+ public void rejectCall(String callId, @Call.RejectReason int rejectReason) {
+ try {
+ mAdapter.rejectCallWithReason(callId, rejectReason);
+ } catch (RemoteException e) {
+ }
+ }
+
+ /**
* Instructs Telecom to disconnect the specified call.
*
* @param callId The identifier of the call to disconnect.
diff --git a/telecomm/java/com/android/internal/telecom/IConnectionService.aidl b/telecomm/java/com/android/internal/telecom/IConnectionService.aidl
index 96f2483f32f9..4249dff151c7 100644
--- a/telecomm/java/com/android/internal/telecom/IConnectionService.aidl
+++ b/telecomm/java/com/android/internal/telecom/IConnectionService.aidl
@@ -77,6 +77,8 @@ oneway interface IConnectionService {
void reject(String callId, in Session.Info sessionInfo);
+ void rejectWithReason(String callId, int rejectReason, in Session.Info sessionInfo);
+
void rejectWithMessage(String callId, String message, in Session.Info sessionInfo);
void disconnect(String callId, in Session.Info sessionInfo);
diff --git a/telecomm/java/com/android/internal/telecom/IInCallAdapter.aidl b/telecomm/java/com/android/internal/telecom/IInCallAdapter.aidl
index 60745e40aa77..eb2d714fe3f4 100644
--- a/telecomm/java/com/android/internal/telecom/IInCallAdapter.aidl
+++ b/telecomm/java/com/android/internal/telecom/IInCallAdapter.aidl
@@ -34,6 +34,8 @@ oneway interface IInCallAdapter {
void rejectCall(String callId, boolean rejectWithMessage, String textMessage);
+ void rejectCallWithReason(String callId, int rejectReason);
+
void disconnectCall(String callId);
void holdCall(String callId);
diff --git a/telephony/common/com/android/internal/telephony/SmsApplication.java b/telephony/common/com/android/internal/telephony/SmsApplication.java
index 32f9d53e59f8..bb6f154335a9 100644
--- a/telephony/common/com/android/internal/telephony/SmsApplication.java
+++ b/telephony/common/com/android/internal/telephony/SmsApplication.java
@@ -35,7 +35,6 @@ import android.content.pm.ServiceInfo;
import android.net.Uri;
import android.os.AsyncTask;
import android.os.Binder;
-import android.os.Debug;
import android.os.Process;
import android.os.UserHandle;
import android.provider.Telephony;
@@ -45,8 +44,6 @@ import android.telephony.TelephonyManager;
import android.util.Log;
import com.android.internal.annotations.VisibleForTesting;
-import com.android.internal.logging.MetricsLogger;
-import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
import java.util.Collection;
import java.util.HashMap;
@@ -197,7 +194,7 @@ public final class SmsApplication {
final int callingUid = Binder.getCallingUid();
if (DEBUG_MULTIUSER) {
Log.i(LOG_TAG, "getIncomingUserHandle caller=" + callingUid + ", myuid="
- + android.os.Process.myUid() + "\n\t" + Debug.getCallers(4));
+ + android.os.Process.myUid());
}
if (UserHandle.getAppId(callingUid)
< android.os.Process.FIRST_APPLICATION_UID) {
@@ -669,9 +666,21 @@ public final class SmsApplication {
}
/**
+ * Broadcast action:
+ * Same as {@link Intent#ACTION_DEFAULT_SMS_PACKAGE_CHANGED} but it's implicit (e.g. sent to
+ * all apps) and requires
+ * {@link #PERMISSION_MONITOR_DEFAULT_SMS_PACKAGE} to receive.
+ */
+ public static final String ACTION_DEFAULT_SMS_PACKAGE_CHANGED_INTERNAL =
+ "android.provider.action.DEFAULT_SMS_PACKAGE_CHANGED_INTERNAL";
+
+ public static final String PERMISSION_MONITOR_DEFAULT_SMS_PACKAGE =
+ "android.permission.MONITOR_DEFAULT_SMS_PACKAGE";
+
+ /**
* Sends broadcasts on sms app change:
* {@link Intent#ACTION_DEFAULT_SMS_PACKAGE_CHANGED}
- * {@link Intents.ACTION_DEFAULT_SMS_PACKAGE_CHANGED_INTERNAL}
+ * {@link #ACTION_DEFAULT_SMS_PACKAGE_CHANGED_INTERNAL}
*/
public static void broadcastSmsAppChange(Context context,
UserHandle userHandle, @Nullable String oldPackage, @Nullable String newPackage) {
@@ -721,16 +730,11 @@ public final class SmsApplication {
}
// Send an implicit broadcast for the system server.
- // (or anyone with MONITOR_DEFAULT_SMS_PACKAGE, really.)
+ // (or anyone with PERMISSION_MONITOR_DEFAULT_SMS_PACKAGE, really.)
final Intent intent =
- new Intent(Intents.ACTION_DEFAULT_SMS_PACKAGE_CHANGED_INTERNAL);
+ new Intent(ACTION_DEFAULT_SMS_PACKAGE_CHANGED_INTERNAL);
context.sendBroadcastAsUser(intent, userHandle,
- permission.MONITOR_DEFAULT_SMS_PACKAGE);
-
- if (applicationData != null) {
- MetricsLogger.action(context, MetricsEvent.ACTION_DEFAULT_SMS_APP_CHANGED,
- applicationData.mPackageName);
- }
+ PERMISSION_MONITOR_DEFAULT_SMS_PACKAGE);
}
/**
diff --git a/telephony/common/com/android/internal/telephony/SmsNumberUtils.java b/telephony/common/com/android/internal/telephony/SmsNumberUtils.java
index cd365a113189..95098e89539d 100644
--- a/telephony/common/com/android/internal/telephony/SmsNumberUtils.java
+++ b/telephony/common/com/android/internal/telephony/SmsNumberUtils.java
@@ -31,6 +31,7 @@ import android.util.Log;
import com.android.internal.telephony.HbpcdLookup.MccIdd;
import com.android.internal.telephony.HbpcdLookup.MccLookup;
+import com.android.internal.telephony.util.TelephonyUtils;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
@@ -677,7 +678,7 @@ public class SmsNumberUtils {
*/
private static String secureHash(byte[] input) {
// Refrain from logging user personal information in user build.
- if (android.os.Build.IS_USER) {
+ if (TelephonyUtils.IS_USER) {
return "****";
}
diff --git a/telephony/common/com/android/internal/telephony/TelephonyPermissions.java b/telephony/common/com/android/internal/telephony/TelephonyPermissions.java
index 89cd4615637b..7b3aace798f7 100644
--- a/telephony/common/com/android/internal/telephony/TelephonyPermissions.java
+++ b/telephony/common/com/android/internal/telephony/TelephonyPermissions.java
@@ -31,7 +31,6 @@ import android.os.UserHandle;
import android.telephony.SubscriptionManager;
import android.telephony.TelephonyManager;
import android.util.Log;
-import android.util.StatsLog;
import com.android.internal.annotations.VisibleForTesting;
@@ -393,8 +392,8 @@ public final class TelephonyPermissions {
invokedMethods = sReportedDeviceIDPackages.get(callingPackage);
}
invokedMethods.add(message);
- StatsLog.write(StatsLog.DEVICE_IDENTIFIER_ACCESS_DENIED, callingPackage, message,
- isPreinstalled, false);
+ TelephonyCommonStatsLog.write(TelephonyCommonStatsLog.DEVICE_IDENTIFIER_ACCESS_DENIED,
+ callingPackage, message, isPreinstalled, false);
}
Log.w(LOG_TAG, "reportAccessDeniedToReadIdentifiers:" + callingPackage + ":" + message
+ ":isPreinstalled=" + isPreinstalled);
diff --git a/telephony/common/com/android/internal/telephony/util/TelephonyUtils.java b/telephony/common/com/android/internal/telephony/util/TelephonyUtils.java
index a7ad884ca107..682697469af9 100644
--- a/telephony/common/com/android/internal/telephony/util/TelephonyUtils.java
+++ b/telephony/common/com/android/internal/telephony/util/TelephonyUtils.java
@@ -28,6 +28,8 @@ import android.os.RemoteException;
import android.os.SystemProperties;
import java.io.PrintWriter;
+import java.util.Collections;
+import java.util.List;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.TimeUnit;
import java.util.function.Supplier;
@@ -61,6 +63,11 @@ public final class TelephonyUtils {
return str == null ? "" : str;
}
+ /** Returns an empty list if the input is {@code null}. */
+ public static @NonNull <T> List<T> emptyIfNull(@Nullable List<T> cur) {
+ return cur == null ? Collections.emptyList() : cur;
+ }
+
/** Throws a {@link RuntimeException} that wrapps the {@link RemoteException}. */
public static RuntimeException rethrowAsRuntimeException(RemoteException remoteException) {
throw new RuntimeException(remoteException);
diff --git a/telephony/common/com/google/android/mms/util/DrmConvertSession.java b/telephony/common/com/google/android/mms/util/DrmConvertSession.java
index 156c7ad8baac..17ab15470670 100644
--- a/telephony/common/com/google/android/mms/util/DrmConvertSession.java
+++ b/telephony/common/com/google/android/mms/util/DrmConvertSession.java
@@ -20,7 +20,6 @@ import android.compat.annotation.UnsupportedAppUsage;
import android.content.Context;
import android.drm.DrmConvertedStatus;
import android.drm.DrmManagerClient;
-import android.provider.Downloads;
import android.util.Log;
import java.io.FileNotFoundException;
@@ -33,6 +32,13 @@ public class DrmConvertSession {
private int mConvertSessionId;
private static final String TAG = "DrmConvertSession";
+ // These values are copied from Downloads.Impl.* for backward compatibility since
+ // {@link #close()} that uses it is marked @UnsupportedAppUsage.
+ public static final int STATUS_SUCCESS = 200;
+ public static final int STATUS_NOT_ACCEPTABLE = 406;
+ public static final int STATUS_UNKNOWN_ERROR = 491;
+ public static final int STATUS_FILE_ERROR = 492;
+
private DrmConvertSession(DrmManagerClient drmClient, int convertSessionId) {
mDrmClient = drmClient;
mConvertSessionId = convertSessionId;
@@ -118,38 +124,38 @@ public class DrmConvertSession {
* Ends a conversion session of a file.
*
* @param fileName The filename of the converted file.
- * @return Downloads.Impl.STATUS_SUCCESS if execution is ok.
- * Downloads.Impl.STATUS_FILE_ERROR in case converted file can not
- * be accessed. Downloads.Impl.STATUS_NOT_ACCEPTABLE if a problem
+ * @return STATUS_SUCCESS if execution is ok.
+ * STATUS_FILE_ERROR in case converted file can not
+ * be accessed. STATUS_NOT_ACCEPTABLE if a problem
* occurs when accessing drm framework.
- * Downloads.Impl.STATUS_UNKNOWN_ERROR if a general error occurred.
+ * STATUS_UNKNOWN_ERROR if a general error occurred.
*/
@UnsupportedAppUsage
public int close(String filename) {
DrmConvertedStatus convertedStatus = null;
- int result = Downloads.Impl.STATUS_UNKNOWN_ERROR;
+ int result = STATUS_UNKNOWN_ERROR;
if (mDrmClient != null && mConvertSessionId >= 0) {
try {
convertedStatus = mDrmClient.closeConvertSession(mConvertSessionId);
if (convertedStatus == null ||
convertedStatus.statusCode != DrmConvertedStatus.STATUS_OK ||
convertedStatus.convertedData == null) {
- result = Downloads.Impl.STATUS_NOT_ACCEPTABLE;
+ result = STATUS_NOT_ACCEPTABLE;
} else {
RandomAccessFile rndAccessFile = null;
try {
rndAccessFile = new RandomAccessFile(filename, "rw");
rndAccessFile.seek(convertedStatus.offset);
rndAccessFile.write(convertedStatus.convertedData);
- result = Downloads.Impl.STATUS_SUCCESS;
+ result = STATUS_SUCCESS;
} catch (FileNotFoundException e) {
- result = Downloads.Impl.STATUS_FILE_ERROR;
+ result = STATUS_FILE_ERROR;
Log.w(TAG, "File: " + filename + " could not be found.", e);
} catch (IOException e) {
- result = Downloads.Impl.STATUS_FILE_ERROR;
+ result = STATUS_FILE_ERROR;
Log.w(TAG, "Could not access File: " + filename + " .", e);
} catch (IllegalArgumentException e) {
- result = Downloads.Impl.STATUS_FILE_ERROR;
+ result = STATUS_FILE_ERROR;
Log.w(TAG, "Could not open file in mode: rw", e);
} catch (SecurityException e) {
Log.w(TAG, "Access to File: " + filename +
@@ -159,7 +165,7 @@ public class DrmConvertSession {
try {
rndAccessFile.close();
} catch (IOException e) {
- result = Downloads.Impl.STATUS_FILE_ERROR;
+ result = STATUS_FILE_ERROR;
Log.w(TAG, "Failed to close File:" + filename
+ ".", e);
}
diff --git a/telephony/java/android/telephony/Annotation.java b/telephony/java/android/telephony/Annotation.java
index 9e6dfef0608b..d2a5905f7a99 100644
--- a/telephony/java/android/telephony/Annotation.java
+++ b/telephony/java/android/telephony/Annotation.java
@@ -1,6 +1,7 @@
package android.telephony;
import android.annotation.IntDef;
+import android.provider.Telephony;
import android.telecom.Connection;
import android.telephony.data.ApnSetting;
@@ -598,6 +599,48 @@ public class Annotation {
}
/**
+ * Call forwarding function status
+ */
+ @IntDef(prefix = { "STATUS_" }, value = {
+ CallForwardingInfo.STATUS_ACTIVE,
+ CallForwardingInfo.STATUS_INACTIVE,
+ CallForwardingInfo.STATUS_UNKNOWN_ERROR,
+ CallForwardingInfo.STATUS_NOT_SUPPORTED,
+ CallForwardingInfo.STATUS_FDN_CHECK_FAILURE
+ })
+ @Retention(RetentionPolicy.SOURCE)
+ public @interface CallForwardingStatus {
+ }
+
+ /**
+ * Call forwarding reason types
+ */
+ @IntDef(flag = true, prefix = { "REASON_" }, value = {
+ CallForwardingInfo.REASON_UNCONDITIONAL,
+ CallForwardingInfo.REASON_BUSY,
+ CallForwardingInfo.REASON_NO_REPLY,
+ CallForwardingInfo.REASON_NOT_REACHABLE,
+ CallForwardingInfo.REASON_ALL,
+ CallForwardingInfo.REASON_ALL_CONDITIONAL
+ })
+ @Retention(RetentionPolicy.SOURCE)
+ public @interface CallForwardingReason {
+ }
+
+ /**
+ * Call waiting function status
+ */
+ @IntDef(prefix = { "CALL_WAITING_STATUS_" }, value = {
+ TelephonyManager.CALL_WAITING_STATUS_ACTIVE,
+ TelephonyManager.CALL_WAITING_STATUS_INACTIVE,
+ TelephonyManager.CALL_WAITING_STATUS_NOT_SUPPORTED,
+ TelephonyManager.CALL_WAITING_STATUS_UNKNOWN_ERROR
+ })
+ @Retention(RetentionPolicy.SOURCE)
+ public @interface CallWaitingStatus {
+ }
+
+ /**
* UICC SIM Application Types
*/
@IntDef(prefix = { "APPTYPE_" }, value = {
@@ -609,4 +652,13 @@ public class Annotation {
})
@Retention(RetentionPolicy.SOURCE)
public @interface UiccAppType{}
+
+ /** @hide */
+ @IntDef({
+ Telephony.Carriers.SKIP_464XLAT_DEFAULT,
+ Telephony.Carriers.SKIP_464XLAT_DISABLE,
+ Telephony.Carriers.SKIP_464XLAT_ENABLE,
+ })
+ @Retention(RetentionPolicy.SOURCE)
+ public @interface Skip464XlatStatus {}
}
diff --git a/telephony/java/android/telephony/CallAttributes.java b/telephony/java/android/telephony/CallAttributes.java
index cd830adf23b0..0c258f4b6435 100644
--- a/telephony/java/android/telephony/CallAttributes.java
+++ b/telephony/java/android/telephony/CallAttributes.java
@@ -21,8 +21,8 @@ import android.annotation.Nullable;
import android.annotation.SystemApi;
import android.os.Parcel;
import android.os.Parcelable;
-
import android.telephony.Annotation.NetworkType;
+
import java.util.Objects;
/**
@@ -130,14 +130,14 @@ public final class CallAttributes implements Parcelable {
/**
* {@link Parcelable#describeContents}
*/
- public @Parcelable.ContentsFlags int describeContents() {
+ public int describeContents() {
return 0;
}
/**
* {@link Parcelable#writeToParcel}
*/
- public void writeToParcel(Parcel dest, @Parcelable.WriteFlags int flags) {
+ public void writeToParcel(Parcel dest, int flags) {
dest.writeParcelable(mPreciseCallState, flags);
dest.writeInt(mNetworkType);
dest.writeParcelable(mCallQuality, flags);
diff --git a/telephony/java/android/telephony/CallForwardingInfo.aidl b/telephony/java/android/telephony/CallForwardingInfo.aidl
new file mode 100644
index 000000000000..2019e07d4bda
--- /dev/null
+++ b/telephony/java/android/telephony/CallForwardingInfo.aidl
@@ -0,0 +1,19 @@
+/*
+ * 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.telephony;
+
+parcelable CallForwardingInfo; \ No newline at end of file
diff --git a/telephony/java/android/telephony/CallForwardingInfo.java b/telephony/java/android/telephony/CallForwardingInfo.java
new file mode 100644
index 000000000000..33ad5e8beea3
--- /dev/null
+++ b/telephony/java/android/telephony/CallForwardingInfo.java
@@ -0,0 +1,307 @@
+/*
+ * 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.telephony;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.annotation.SuppressLint;
+import android.annotation.SystemApi;
+import android.os.Parcel;
+import android.os.Parcelable;
+import android.telephony.Annotation.CallForwardingReason;
+import android.telephony.Annotation.CallForwardingStatus;
+
+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
+ */
+ @SystemApi
+ public static final int STATUS_INACTIVE = 0;
+
+ /**
+ * Indicates the call forwarding status is active.
+ *
+ * @hide
+ */
+ @SystemApi
+ public static final int STATUS_ACTIVE = 1;
+
+ /**
+ * Indicates the call forwarding could not be enabled because the recipient is not on
+ * Fixed Dialing Number (FDN) list.
+ *
+ * @hide
+ */
+ @SystemApi
+ public static final int STATUS_FDN_CHECK_FAILURE = 2;
+
+ /**
+ * Indicates the call forwarding status is with an unknown error.
+ *
+ * @hide
+ */
+ @SystemApi
+ public static final int STATUS_UNKNOWN_ERROR = 3;
+
+ /**
+ * Indicates the call forwarding is not supported (e.g. called via CDMA).
+ *
+ * @hide
+ */
+ @SystemApi
+ public static final int STATUS_NOT_SUPPORTED = 4;
+
+ /**
+ * Indicates the 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
+ */
+ @SystemApi
+ public static final int REASON_UNCONDITIONAL = 0;
+
+ /**
+ * 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
+ */
+ @SystemApi
+ public static final int REASON_BUSY = 1;
+
+ /**
+ * 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
+ */
+ @SystemApi
+ public static final int REASON_NO_REPLY = 2;
+
+ /**
+ * 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
+ */
+ @SystemApi
+ public static final int REASON_NOT_REACHABLE = 3;
+
+ /**
+ * Indicates the call forwarding reason is "all", for setting all call forwarding reasons
+ * 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
+ */
+ @SystemApi
+ public static final int REASON_ALL = 4;
+
+ /**
+ * Indicates the call forwarding reason is "all_conditional", for setting all conditional call
+ * 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
+ */
+ @SystemApi
+ public static final int REASON_ALL_CONDITIONAL = 5;
+
+ /**
+ * The call forwarding status.
+ */
+ private @CallForwardingStatus int mStatus;
+
+ /**
+ * The call forwarding reason indicates the condition under which calls will be forwarded.
+ * Reference: 3GPP TS 27.007 version 10.3.0 Release 10 - 7.11 Call forwarding number
+ * and conditions +CCFC
+ */
+ private @CallForwardingReason int mReason;
+
+ /**
+ * The phone number to which calls will be forwarded.
+ * Reference: 3GPP TS 27.007 version 10.3.0 Release 10 - 7.11 Call forwarding number
+ * and conditions +CCFC
+ */
+ private String mNumber;
+
+ /**
+ * Gets the timeout (in seconds) before the forwarding is attempted.
+ */
+ private int mTimeSeconds;
+
+ /**
+ * Construct a CallForwardingInfo.
+ *
+ * @param status the call forwarding status
+ * @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
+ */
+ @SystemApi
+ public CallForwardingInfo(@CallForwardingStatus int status, @CallForwardingReason int reason,
+ @Nullable String number, int timeSeconds) {
+ mStatus = status;
+ mReason = reason;
+ mNumber = number;
+ mTimeSeconds = timeSeconds;
+ }
+
+ /**
+ * Returns the call forwarding status.
+ *
+ * @return the call forwarding status.
+ *
+ * @hide
+ */
+ @SystemApi
+ public @CallForwardingStatus int getStatus() {
+ return mStatus;
+ }
+
+ /**
+ * 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.
+ *
+ * @return the call forwarding reason.
+ *
+ * @hide
+ */
+ @SystemApi
+ public @CallForwardingReason int getReason() {
+ return mReason;
+ }
+
+ /**
+ * 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
+ */
+ @SystemApi
+ @Nullable
+ public String getNumber() {
+ return mNumber;
+ }
+
+ /**
+ * Gets the timeout (in seconds) before the 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()}.
+ *
+ * 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
+ */
+ @SystemApi
+ @SuppressLint("MethodNameUnits")
+ public int getTimeoutSeconds() {
+ return mTimeSeconds;
+ }
+
+ @Override
+ public int describeContents() {
+ return 0;
+ }
+
+ /**
+ * @hide
+ */
+ @Override
+ public void writeToParcel(Parcel out, int flags) {
+ out.writeString(mNumber);
+ out.writeInt(mStatus);
+ out.writeInt(mReason);
+ out.writeInt(mTimeSeconds);
+ }
+
+ private CallForwardingInfo(Parcel in) {
+ mNumber = in.readString();
+ mStatus = in.readInt();
+ mReason = in.readInt();
+ mTimeSeconds = in.readInt();
+ }
+
+ /**
+ * @hide
+ */
+ @Override
+ public boolean equals(Object o) {
+ if (this == o) return true;
+
+ if (!(o instanceof CallForwardingInfo)) {
+ return false;
+ }
+
+ CallForwardingInfo other = (CallForwardingInfo) o;
+ return mStatus == other.mStatus
+ && mNumber == other.mNumber
+ && mReason == other.mReason
+ && mTimeSeconds == other.mTimeSeconds;
+ }
+
+ /**
+ * @hide
+ */
+ @Override
+ public int hashCode() {
+ return Objects.hash(mStatus, mNumber, mReason, mTimeSeconds);
+ }
+
+ public static final @NonNull Parcelable.Creator<CallForwardingInfo> CREATOR =
+ new Parcelable.Creator<CallForwardingInfo>() {
+ @Override
+ public CallForwardingInfo createFromParcel(Parcel in) {
+ return new CallForwardingInfo(in);
+ }
+
+ @Override
+ public CallForwardingInfo[] newArray(int size) {
+ return new CallForwardingInfo[size];
+ }
+ };
+
+ /**
+ * @hide
+ */
+ @Override
+ public String toString() {
+ return "[CallForwardingInfo: status=" + mStatus
+ + ", reason= " + mReason
+ + ", timeSec= " + mTimeSeconds + " seconds"
+ + ", number=" + Rlog.pii(TAG, mNumber) + "]";
+ }
+}
diff --git a/telephony/java/android/telephony/CallQuality.java b/telephony/java/android/telephony/CallQuality.java
index 1e1cdba70ad0..428a515844e6 100644
--- a/telephony/java/android/telephony/CallQuality.java
+++ b/telephony/java/android/telephony/CallQuality.java
@@ -400,14 +400,14 @@ public final class CallQuality implements Parcelable {
/**
* {@link Parcelable#describeContents}
*/
- public @Parcelable.ContentsFlags int describeContents() {
+ public int describeContents() {
return 0;
}
/**
* {@link Parcelable#writeToParcel}
*/
- public void writeToParcel(Parcel dest, @Parcelable.WriteFlags int flags) {
+ public void writeToParcel(Parcel dest, int flags) {
dest.writeInt(mDownlinkCallQualityLevel);
dest.writeInt(mUplinkCallQualityLevel);
dest.writeInt(mCallDuration);
diff --git a/telephony/java/android/telephony/ImsManager.java b/telephony/java/android/telephony/ImsManager.java
index 9b4292f42172..704e5aa78188 100644
--- a/telephony/java/android/telephony/ImsManager.java
+++ b/telephony/java/android/telephony/ImsManager.java
@@ -27,11 +27,7 @@ import android.telephony.SubscriptionManager;
/**
* Provides access to information about Telephony IMS services on the device.
- *
- * @hide
*/
-@SystemApi
-@TestApi
@SystemService(Context.TELEPHONY_IMS_SERVICE)
public class ImsManager {
@@ -45,7 +41,10 @@ public class ImsManager {
* <p class="note">
* Carrier applications may listen to this broadcast to be notified of possible IMS provisioning
* issues.
+ * @hide
*/
+ @SystemApi
+ @TestApi
// Moved from TelephonyIntents, need to keep backwards compatibility with OEM apps that have
// this value hard-coded in BroadcastReceiver.
@SuppressLint("ActionValue")
@@ -104,7 +103,10 @@ public class ImsManager {
* @param subscriptionId The ID of the subscription that this ImsRcsManager will use.
* @throws IllegalArgumentException if the subscription is invalid.
* @return a ImsRcsManager instance with the specific subscription ID.
+ * @hide
*/
+ @SystemApi
+ @TestApi
@NonNull
public ImsRcsManager getImsRcsManager(int subscriptionId) {
if (!SubscriptionManager.isValidSubscriptionId(subscriptionId)) {
diff --git a/telephony/java/android/telephony/PhoneCapability.java b/telephony/java/android/telephony/PhoneCapability.java
index 90244b3df350..a53792802d92 100644
--- a/telephony/java/android/telephony/PhoneCapability.java
+++ b/telephony/java/android/telephony/PhoneCapability.java
@@ -25,7 +25,7 @@ import android.telephony.AccessNetworkConstants.AccessNetworkType;
import android.telephony.AccessNetworkConstants.RadioAccessNetworkType;
import android.telephony.TelephonyManager.NetworkTypeBitMask;
-import com.android.internal.util.CollectionUtils;
+import com.android.internal.telephony.util.TelephonyUtils;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
@@ -181,13 +181,13 @@ public final class PhoneCapability implements Parcelable {
this.mEutranUeCategoryUl = eutranUeCategoryUl;
this.mPsDataConnectionLingerTimeMillis = psDataConnectionLingerTimeMillis;
this.mSupportedRats = supportedRats;
- this.mGeranBands = CollectionUtils.emptyIfNull(geranBands);
- this.mUtranBands = CollectionUtils.emptyIfNull(utranBands);
- this.mEutranBands = CollectionUtils.emptyIfNull(eutranBands);
- this.mNgranBands = CollectionUtils.emptyIfNull(ngranBands);
- this.mLogicalModemUuids = CollectionUtils.emptyIfNull(logicalModemUuids);
- this.mSimSlotCapabilities = CollectionUtils.emptyIfNull(simSlotCapabilities);
- this.mConcurrentFeaturesSupport = CollectionUtils.emptyIfNull(concurrentFeaturesSupport);
+ this.mGeranBands = TelephonyUtils.emptyIfNull(geranBands);
+ this.mUtranBands = TelephonyUtils.emptyIfNull(utranBands);
+ this.mEutranBands = TelephonyUtils.emptyIfNull(eutranBands);
+ this.mNgranBands = TelephonyUtils.emptyIfNull(ngranBands);
+ this.mLogicalModemUuids = TelephonyUtils.emptyIfNull(logicalModemUuids);
+ this.mSimSlotCapabilities = TelephonyUtils.emptyIfNull(simSlotCapabilities);
+ this.mConcurrentFeaturesSupport = TelephonyUtils.emptyIfNull(concurrentFeaturesSupport);
}
private PhoneCapability(Parcel in) {
@@ -429,14 +429,14 @@ public final class PhoneCapability implements Parcelable {
/**
* {@link Parcelable#describeContents}
*/
- public @Parcelable.ContentsFlags int describeContents() {
+ public int describeContents() {
return 0;
}
/**
* {@link Parcelable#writeToParcel}
*/
- public void writeToParcel(@NonNull Parcel dest, @Parcelable.WriteFlags int flags) {
+ public void writeToParcel(@NonNull Parcel dest, int flags) {
dest.writeInt(mUtranUeCategoryDl);
dest.writeInt(mUtranUeCategoryUl);
dest.writeInt(mEutranUeCategoryDl);
diff --git a/telephony/java/android/telephony/SimSlotCapability.java b/telephony/java/android/telephony/SimSlotCapability.java
index 3d38d0429908..b4fef46725a4 100644
--- a/telephony/java/android/telephony/SimSlotCapability.java
+++ b/telephony/java/android/telephony/SimSlotCapability.java
@@ -105,14 +105,14 @@ public final class SimSlotCapability implements Parcelable {
/**
* {@link Parcelable#describeContents}
*/
- public @ContentsFlags int describeContents() {
+ public int describeContents() {
return 0;
}
/**
* {@link Parcelable#writeToParcel}
*/
- public void writeToParcel(@NonNull Parcel dest, @WriteFlags int flags) {
+ public void writeToParcel(@NonNull Parcel dest, int flags) {
dest.writeInt(mPhysicalSlotIndex);
dest.writeInt(mSlotType);
}
diff --git a/telephony/java/android/telephony/SmsCbMessage.java b/telephony/java/android/telephony/SmsCbMessage.java
index 3c6709415281..c0dfec9a1172 100644
--- a/telephony/java/android/telephony/SmsCbMessage.java
+++ b/telephony/java/android/telephony/SmsCbMessage.java
@@ -177,6 +177,9 @@ public final class SmsCbMessage implements Parcelable {
@Nullable
private final String mLanguage;
+ /** The 8-bit data coding scheme defined in 3GPP TS 23.038 section 4. */
+ private final int mDataCodingScheme;
+
/** Message body, as a String. */
@Nullable
private final String mBody;
@@ -220,7 +223,7 @@ public final class SmsCbMessage implements Parcelable {
@Nullable SmsCbCmasInfo cmasWarningInfo, int slotIndex, int subId) {
this(messageFormat, geographicalScope, serialNumber, location, serviceCategory, language,
- body, priority, etwsWarningInfo, cmasWarningInfo, 0 /* maximumWaitingTime */,
+ 0, body, priority, etwsWarningInfo, cmasWarningInfo, 0 /* maximumWaitingTime */,
null /* geometries */, System.currentTimeMillis(), slotIndex, subId);
}
@@ -230,8 +233,8 @@ public final class SmsCbMessage implements Parcelable {
*/
public SmsCbMessage(int messageFormat, int geographicalScope, int serialNumber,
@NonNull SmsCbLocation location, int serviceCategory,
- @Nullable String language, @Nullable String body, int priority,
- @Nullable SmsCbEtwsInfo etwsWarningInfo,
+ @Nullable String language, int dataCodingScheme, @Nullable String body,
+ int priority, @Nullable SmsCbEtwsInfo etwsWarningInfo,
@Nullable SmsCbCmasInfo cmasWarningInfo, int maximumWaitTimeSec,
@Nullable List<Geometry> geometries, long receivedTimeMillis, int slotIndex,
int subId) {
@@ -241,6 +244,7 @@ public final class SmsCbMessage implements Parcelable {
mLocation = location;
mServiceCategory = serviceCategory;
mLanguage = language;
+ mDataCodingScheme = dataCodingScheme;
mBody = body;
mPriority = priority;
mEtwsWarningInfo = etwsWarningInfo;
@@ -263,6 +267,7 @@ public final class SmsCbMessage implements Parcelable {
mLocation = new SmsCbLocation(in);
mServiceCategory = in.readInt();
mLanguage = in.readString();
+ mDataCodingScheme = in.readInt();
mBody = in.readString();
mPriority = in.readInt();
int type = in.readInt();
@@ -305,6 +310,7 @@ public final class SmsCbMessage implements Parcelable {
mLocation.writeToParcel(dest, flags);
dest.writeInt(mServiceCategory);
dest.writeString(mLanguage);
+ dest.writeInt(mDataCodingScheme);
dest.writeString(mBody);
dest.writeInt(mPriority);
if (mEtwsWarningInfo != null) {
@@ -398,6 +404,15 @@ public final class SmsCbMessage implements Parcelable {
}
/**
+ * Get data coding scheme of the message
+ *
+ * @return The 8-bit data coding scheme defined in 3GPP TS 23.038 section 4.
+ */
+ public int getDataCodingScheme() {
+ return mDataCodingScheme;
+ }
+
+ /**
* Get the body of this message, or null if no body available
*
* @return Body, or null
@@ -718,7 +733,7 @@ public final class SmsCbMessage implements Parcelable {
cursor.getColumnIndexOrThrow(CellBroadcasts.MAXIMUM_WAIT_TIME));
return new SmsCbMessage(format, geoScope, serialNum, location, category,
- language, body, priority, etwsInfo, cmasInfo, maximumWaitTimeSec, geometries,
+ language, 0, body, priority, etwsInfo, cmasInfo, maximumWaitTimeSec, geometries,
receivedTimeMillis, slotIndex, subId);
}
diff --git a/telephony/java/android/telephony/TelephonyManager.java b/telephony/java/android/telephony/TelephonyManager.java
index 86913a636913..26dc5f01e67b 100644
--- a/telephony/java/android/telephony/TelephonyManager.java
+++ b/telephony/java/android/telephony/TelephonyManager.java
@@ -69,7 +69,9 @@ import android.telecom.PhoneAccount;
import android.telecom.PhoneAccountHandle;
import android.telecom.TelecomManager;
import android.telephony.Annotation.ApnType;
+import android.telephony.Annotation.CallForwardingReason;
import android.telephony.Annotation.CallState;
+import android.telephony.Annotation.CallWaitingStatus;
import android.telephony.Annotation.NetworkType;
import android.telephony.Annotation.RadioPowerState;
import android.telephony.Annotation.SimActivationState;
@@ -5638,13 +5640,6 @@ public class TelephonyManager {
//
/**
- * To check the SDK version for {@link TelephonyManager#listen}.
- */
- @ChangeId
- @EnabledAfter(targetSdkVersion = Build.VERSION_CODES.P)
- private static final long LISTEN_CODE_CHANGE = 147600208L;
-
- /**
* Registers a listener object to receive notification of changes
* in specified telephony states.
* <p>
@@ -5681,19 +5676,7 @@ public class TelephonyManager {
(TelephonyRegistryManager)
mContext.getSystemService(Context.TELEPHONY_REGISTRY_SERVICE);
if (telephonyRegistry != null) {
- // subId from PhoneStateListener is deprecated Q on forward, use the subId from
- // TelephonyManager instance. keep using subId from PhoneStateListener for pre-Q.
- int subId = mSubId;
- if (Compatibility.isChangeEnabled(LISTEN_CODE_CHANGE)) {
- // since mSubId in PhoneStateListener is deprecated from Q on forward, this is
- // the only place to set mSubId and its for "informational" only.
- // TODO: remove this once we completely get rid of mSubId in PhoneStateListener
- listener.mSubId = (events == PhoneStateListener.LISTEN_NONE)
- ? SubscriptionManager.INVALID_SUBSCRIPTION_ID : subId;
- } else if (listener.mSubId != null) {
- subId = listener.mSubId;
- }
- telephonyRegistry.listenForSubscriber(subId, getOpPackageName(), getFeatureId(),
+ telephonyRegistry.listenForSubscriber(mSubId, getOpPackageName(), getFeatureId(),
listener, events, notifyNow);
} else {
Rlog.w(TAG, "telephony registry not ready.");
@@ -5712,7 +5695,7 @@ public class TelephonyManager {
@NonNull
public CdmaEriInformation getCdmaEriInformation() {
return new CdmaEriInformation(
- getCdmaEriIconMode(getSubId()), getCdmaEriIconIndex(getSubId()));
+ getCdmaEriIconIndex(getSubId()), getCdmaEriIconMode(getSubId()));
}
/**
@@ -12765,6 +12748,191 @@ public class TelephonyManager {
}
/**
+ * Gets the voice call forwarding info {@link CallForwardingInfo}, given the call forward
+ * reason.
+ *
+ * @param callForwardingReason the call forwarding reasons
+ *
+ * @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}
+ *
+ * @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.
+ *
+ * @hide
+ */
+ @SystemApi
+ @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE)
+ @NonNull
+ public CallForwardingInfo getCallForwarding(@CallForwardingReason int callForwardingReason) {
+ if (callForwardingReason < CallForwardingInfo.REASON_UNCONDITIONAL
+ || callForwardingReason > CallForwardingInfo.REASON_ALL_CONDITIONAL) {
+ throw new IllegalArgumentException("callForwardingReason is out of range");
+ }
+ try {
+ ITelephony telephony = getITelephony();
+ if (telephony != null) {
+ return telephony.getCallForwarding(getSubId(), callForwardingReason);
+ }
+ } catch (RemoteException ex) {
+ Rlog.e(TAG, "getCallForwarding RemoteException", ex);
+ } catch (NullPointerException ex) {
+ Rlog.e(TAG, "getCallForwarding NPE", ex);
+ }
+ 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.
+ *
+ * @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 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.
+ *
+ * @return {@code true} to indicate it was set successfully; {@code false} otherwise.
+ *
+ * @hide
+ */
+ @SystemApi
+ @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE)
+ public boolean setCallForwarding(@NonNull CallForwardingInfo callForwardingInfo) {
+ 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.getTimeoutSeconds() <= 0) {
+ throw new IllegalArgumentException("callForwarding timeout isn't positive");
+ }
+ try {
+ ITelephony telephony = getITelephony();
+ if (telephony != null) {
+ return telephony.setCallForwarding(getSubId(), callForwardingInfo);
+ }
+ } catch (RemoteException ex) {
+ Rlog.e(TAG, "setCallForwarding RemoteException", ex);
+ } catch (NullPointerException ex) {
+ Rlog.e(TAG, "setCallForwarding NPE", ex);
+ }
+ return false;
+ }
+
+ /**
+ * Indicates the call waiting status is active.
+ *
+ * @hide
+ */
+ @SystemApi
+ public static final int CALL_WAITING_STATUS_ACTIVE = 1;
+
+ /**
+ * Indicates the call waiting status is inactive.
+ *
+ * @hide
+ */
+ @SystemApi
+ public static final int CALL_WAITING_STATUS_INACTIVE = 2;
+
+ /**
+ * Indicates the call waiting status is with an unknown error.
+ *
+ * @hide
+ */
+ @SystemApi
+ public static final int CALL_WAITING_STATUS_UNKNOWN_ERROR = 3;
+
+ /**
+ * Indicates the call waiting is not supported (e.g. called via CDMA).
+ *
+ * @hide
+ */
+ @SystemApi
+ public static final int CALL_WAITING_STATUS_NOT_SUPPORTED = 4;
+
+ /**
+ * 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
+ */
+ @SystemApi
+ @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE)
+ public @CallWaitingStatus int getCallWaitingStatus() {
+ try {
+ ITelephony telephony = getITelephony();
+ if (telephony != null) {
+ return telephony.getCallWaitingStatus(getSubId());
+ }
+ } catch (RemoteException ex) {
+ Rlog.e(TAG, "getCallWaitingStatus RemoteException", ex);
+ } catch (NullPointerException ex) {
+ Rlog.e(TAG, "getCallWaitingStatus NPE", 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.
+ *
+ * @param isEnable {@code true} to enable; {@code false} to disable.
+ * @return {@code true} to indicate it was set successfully; {@code false} otherwise.
+ *
+ * @hide
+ */
+ @SystemApi
+ @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE)
+ public boolean setCallWaitingStatus(boolean isEnable) {
+ try {
+ ITelephony telephony = getITelephony();
+ if (telephony != null) {
+ return telephony.setCallWaitingStatus(getSubId(), isEnable);
+ }
+ } catch (RemoteException ex) {
+ Rlog.e(TAG, "setCallWaitingStatus RemoteException", ex);
+ } catch (NullPointerException ex) {
+ Rlog.e(TAG, "setCallWaitingStatus NPE", ex);
+ }
+ return false;
+ }
+
+ /**
* Set allowing mobile data during voice call. This is used for allowing data on the non-default
* data SIM. When a voice call is placed on the non-default data SIM on DSDS devices, users will
* not be able to use mobile data. By calling this API, data will be temporarily enabled on the
diff --git a/telephony/java/android/telephony/data/ApnSetting.java b/telephony/java/android/telephony/data/ApnSetting.java
index 789632082758..f5dfacc6a0be 100644
--- a/telephony/java/android/telephony/data/ApnSetting.java
+++ b/telephony/java/android/telephony/data/ApnSetting.java
@@ -27,6 +27,7 @@ import android.os.Parcel;
import android.os.Parcelable;
import android.provider.Telephony;
import android.provider.Telephony.Carriers;
+import android.telephony.Annotation;
import android.telephony.Annotation.ApnType;
import android.telephony.Annotation.NetworkType;
import android.telephony.ServiceState;
@@ -744,7 +745,7 @@ public class ApnSetting implements Parcelable {
* @return SKIP_464XLAT_DEFAULT, SKIP_464XLAT_DISABLE or SKIP_464XLAT_ENABLE
* @hide
*/
- @Carriers.Skip464XlatStatus
+ @Annotation.Skip464XlatStatus
public int getSkip464Xlat() {
return mSkip464Xlat;
}
@@ -2061,10 +2062,10 @@ public class ApnSetting implements Parcelable {
/**
* Sets skip464xlat flag for this APN.
*
- * @param skip464xlat skip464xlat for this APN
+ * @param skip464xlat skip464xlat for this APN.
* @hide
*/
- public Builder setSkip464Xlat(@Carriers.Skip464XlatStatus int skip464xlat) {
+ public Builder setSkip464Xlat(@Annotation.Skip464XlatStatus int skip464xlat) {
this.mSkip464Xlat = skip464xlat;
return this;
}
diff --git a/telephony/java/android/telephony/ims/ImsException.java b/telephony/java/android/telephony/ims/ImsException.java
index cb3f0f92625e..643f452d2e75 100644
--- a/telephony/java/android/telephony/ims/ImsException.java
+++ b/telephony/java/android/telephony/ims/ImsException.java
@@ -61,7 +61,6 @@ public final class ImsException extends Exception {
* This is a configuration error and there should be no retry. The subscription used for this
* operation is either invalid or has become inactive. The active subscriptions can be queried
* with {@link SubscriptionManager#getActiveSubscriptionInfoList()}.
- * @hide
*/
public static final int CODE_ERROR_INVALID_SUBSCRIPTION = 3;
diff --git a/telephony/java/android/telephony/ims/ImsMmTelManager.java b/telephony/java/android/telephony/ims/ImsMmTelManager.java
index 5fdef8307d42..3341fa74d672 100644
--- a/telephony/java/android/telephony/ims/ImsMmTelManager.java
+++ b/telephony/java/android/telephony/ims/ImsMmTelManager.java
@@ -56,7 +56,8 @@ import java.util.function.Consumer;
* registration and MmTel capability status callbacks, as well as query/modify user settings for the
* associated subscription.
*
- * @see #createForSubscriptionId(int)
+ * Use {@link android.telephony.ims.ImsManager#getImsMmTelManager(int)} to get an instance of this
+ * manager.
*/
public class ImsMmTelManager implements RegistrationManager {
@@ -228,8 +229,13 @@ public class ImsMmTelManager implements RegistrationManager {
* (see {@link android.telephony.TelephonyManager#hasCarrierPrivileges}).
*
* @throws IllegalArgumentException if the subscription is invalid.
- *
+ * @deprecated Use {@link android.telephony.ims.ImsManager#getImsMmTelManager(int)} to get an
+ * instance of this class.
+ * @hide
*/
+ @SystemApi
+ @TestApi
+ @Deprecated
@SuppressAutoDoc // No support for device / profile owner or carrier privileges (b/72967236).
@RequiresPermission(anyOf = {
android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE,
@@ -245,7 +251,7 @@ public class ImsMmTelManager implements RegistrationManager {
}
/**
- * Only visible for testing, use {@link #createForSubscriptionId(int)} instead.
+ * Only visible for testing, use {@link ImsManager#getImsMmTelManager(int)} instead.
* @hide
*/
@VisibleForTesting
@@ -255,7 +261,7 @@ public class ImsMmTelManager implements RegistrationManager {
/**
* Registers a {@link RegistrationCallback} with the system, which will provide registration
- * updates for the subscription specified in {@link #createForSubscriptionId(int)}. Use
+ * updates for the subscription specified in {@link ImsManager#getImsMmTelManager(int)}. Use
* {@link SubscriptionManager.OnSubscriptionsChangedListener} to listen to Subscription changed
* events and call {@link #unregisterImsRegistrationCallback(RegistrationCallback)} to clean up.
*
@@ -453,7 +459,7 @@ public class ImsMmTelManager implements RegistrationManager {
/**
* Registers a {@link CapabilityCallback} with the system, which will provide MmTel service
* availability updates for the subscription specified in
- * {@link #createForSubscriptionId(int)}. The method {@see #isAvailable(int, int)}
+ * {@link ImsManager#getImsMmTelManager(int)}. The method {@see #isAvailable(int, int)}
* can also be used to query this information at any time.
*
* Use {@link SubscriptionManager.OnSubscriptionsChangedListener} to listen to
diff --git a/telephony/java/android/telephony/ims/ImsService.java b/telephony/java/android/telephony/ims/ImsService.java
index 62bc2ae44573..2b3072eefe2e 100644
--- a/telephony/java/android/telephony/ims/ImsService.java
+++ b/telephony/java/android/telephony/ims/ImsService.java
@@ -60,9 +60,10 @@ import com.android.internal.annotations.VisibleForTesting;
* The telephony framework will then bind to the ImsService you have defined in your manifest
* if you are either:
* 1) Defined as the default ImsService for the device in the device overlay using
- * "config_ims_package".
+ * "config_ims_mmtel_package" or "config_ims_rcs_package".
* 2) Defined as a Carrier Provided ImsService in the Carrier Configuration using
- * {@link CarrierConfigManager#KEY_CONFIG_IMS_PACKAGE_OVERRIDE_STRING}.
+ * {@link CarrierConfigManager#KEY_CONFIG_IMS_MMTEL_PACKAGE_OVERRIDE_STRING} or
+ * {@link CarrierConfigManager#KEY_CONFIG_IMS_RCS_PACKAGE_OVERRIDE_STRING}.
*
* There are two ways to define to the platform which {@link ImsFeature}s this {@link ImsService}
* supports, dynamic or static definitions.
diff --git a/telephony/java/android/telephony/ims/RcsContactUceCapability.java b/telephony/java/android/telephony/ims/RcsContactUceCapability.java
index 3e2903fa6f47..57b9b7a30f8c 100644
--- a/telephony/java/android/telephony/ims/RcsContactUceCapability.java
+++ b/telephony/java/android/telephony/ims/RcsContactUceCapability.java
@@ -16,10 +16,11 @@
package android.telephony.ims;
-import android.annotation.IntDef;
+import android.annotation.LongDef;
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.annotation.SystemApi;
+import android.annotation.TestApi;
import android.net.Uri;
import android.os.Parcel;
import android.os.Parcelable;
@@ -37,6 +38,7 @@ import java.util.Map;
* @hide
*/
@SystemApi
+@TestApi
public final class RcsContactUceCapability implements Parcelable {
/** Supports 1-to-1 chat */
@@ -99,11 +101,16 @@ public final class RcsContactUceCapability implements Parcelable {
public static final int CAPABILITY_CHAT_BOT_ROLE = (1 << 27);
/** Supports the unidirectional plug-ins framework. */
public static final int CAPABILITY_PLUG_IN = (1 << 28);
+ /** Supports standalone Chatbot communication. */
+ public static final int CAPABILITY_STANDALONE_CHAT_BOT = (1 << 29);
+ /** Supports MMTEL based call composer. */
+ public static final int CAPABILITY_MMTEL_CALL_COMPOSER = (1 << 30);
+
/** @hide*/
@Retention(RetentionPolicy.SOURCE)
- @IntDef(prefix = "CAPABILITY_", flag = true, value = {
+ @LongDef(prefix = "CAPABILITY_", flag = true, value = {
CAPABILITY_CHAT_STANDALONE,
CAPABILITY_CHAT_SESSION,
CAPABILITY_CHAT_SESSION_STORE_FORWARD,
@@ -132,7 +139,9 @@ public final class RcsContactUceCapability implements Parcelable {
CAPABILITY_SHARED_SKETCH,
CAPABILITY_CHAT_BOT,
CAPABILITY_CHAT_BOT_ROLE,
- CAPABILITY_PLUG_IN
+ CAPABILITY_PLUG_IN,
+ CAPABILITY_STANDALONE_CHAT_BOT,
+ CAPABILITY_MMTEL_CALL_COMPOSER
})
public @interface CapabilityFlag {}
@@ -159,11 +168,11 @@ public final class RcsContactUceCapability implements Parcelable {
* @param type The capability to map to a service URI that is different from the contact's
* URI.
*/
- public @NonNull Builder add(@CapabilityFlag int type, @NonNull Uri serviceUri) {
+ public @NonNull Builder add(@CapabilityFlag long type, @NonNull Uri serviceUri) {
mCapabilities.mCapabilities |= type;
// Put each of these capabilities into the map separately.
- for (int shift = 0; shift < Integer.SIZE; shift++) {
- int cap = type & (1 << shift);
+ for (long shift = 0; shift < Integer.SIZE; shift++) {
+ long cap = type & (1 << shift);
if (cap != 0) {
mCapabilities.mServiceMap.put(cap, serviceUri);
// remove that capability from the field.
@@ -181,7 +190,7 @@ public final class RcsContactUceCapability implements Parcelable {
* Add a UCE capability flag that this contact supports.
* @param type the capability that the contact supports.
*/
- public @NonNull Builder add(@CapabilityFlag int type) {
+ public @NonNull Builder add(@CapabilityFlag long type) {
mCapabilities.mCapabilities |= type;
return this;
}
@@ -207,7 +216,7 @@ public final class RcsContactUceCapability implements Parcelable {
private final Uri mContactUri;
private long mCapabilities;
private List<String> mExtensionTags = new ArrayList<>();
- private Map<Integer, Uri> mServiceMap = new HashMap<>();
+ private Map<Long, Uri> mServiceMap = new HashMap<>();
/**
* Use {@link Builder} to build an instance of this interface.
@@ -225,7 +234,7 @@ public final class RcsContactUceCapability implements Parcelable {
// read mServiceMap as key,value pair
int mapSize = in.readInt();
for (int i = 0; i < mapSize; i++) {
- mServiceMap.put(in.readInt(), in.readParcelable(Uri.class.getClassLoader()));
+ mServiceMap.put(in.readLong(), in.readParcelable(Uri.class.getClassLoader()));
}
}
@@ -250,8 +259,8 @@ public final class RcsContactUceCapability implements Parcelable {
// write mServiceMap as key,value pairs
int mapSize = mServiceMap.keySet().size();
out.writeInt(mapSize);
- for (int key : mServiceMap.keySet()) {
- out.writeInt(key);
+ for (long key : mServiceMap.keySet()) {
+ out.writeLong(key);
out.writeParcelable(mServiceMap.get(key), 0);
}
}
@@ -266,7 +275,7 @@ public final class RcsContactUceCapability implements Parcelable {
* @param type The capability flag to query.
* @return true if the capability flag specified is set, false otherwise.
*/
- public boolean isCapable(@CapabilityFlag int type) {
+ public boolean isCapable(@CapabilityFlag long type) {
return (mCapabilities & type) > 0;
}
@@ -290,13 +299,13 @@ public final class RcsContactUceCapability implements Parcelable {
* <p>
* This will typically be the contact {@link Uri} available via {@link #getContactUri()} unless
* a different service {@link Uri} was associated with this capability using
- * {@link Builder#add(int, Uri)}.
+ * {@link Builder#add(long, Uri)}.
*
* @return a String containing the {@link Uri} associated with the service tag or
* {@code null} if this capability is not set as capable.
- * @see #isCapable(int)
+ * @see #isCapable(long)
*/
- public @Nullable Uri getServiceUri(@CapabilityFlag int type) {
+ public @Nullable Uri getServiceUri(@CapabilityFlag long type) {
Uri result = mServiceMap.getOrDefault(type, null);
// If the capability is capable, but does not have a service URI associated, use the default
// contact URI.
diff --git a/telephony/java/android/telephony/ims/RcsUceAdapter.java b/telephony/java/android/telephony/ims/RcsUceAdapter.java
index 72167761c88d..72a00ce052da 100644
--- a/telephony/java/android/telephony/ims/RcsUceAdapter.java
+++ b/telephony/java/android/telephony/ims/RcsUceAdapter.java
@@ -174,7 +174,6 @@ public class RcsUceAdapter {
* Provides a one-time callback for the response to a UCE request. After this callback is called
* by the framework, the reference to this callback will be discarded on the service side.
* @see #requestCapabilities(Executor, List, CapabilitiesCallback)
- * @hide
*/
public static class CapabilitiesCallback {
@@ -226,10 +225,9 @@ public class RcsUceAdapter {
* {@link RcsUceAdapter} is valid, but the ImsService associated with the subscription is not
* available. This can happen if the ImsService has crashed, for example, or if the subscription
* becomes inactive. See {@link ImsException#getCode()} for more information on the error codes.
- * @hide
*/
@RequiresPermission(Manifest.permission.READ_PRIVILEGED_PHONE_STATE)
- public void requestCapabilities(@CallbackExecutor Executor executor,
+ public void requestCapabilities(@NonNull @CallbackExecutor Executor executor,
@NonNull List<Uri> contactNumbers,
@NonNull CapabilitiesCallback c) throws ImsException {
if (c == null) {
@@ -289,7 +287,6 @@ public class RcsUceAdapter {
* {@link RcsUceAdapter} is valid, but the ImsService associated with the subscription is not
* available. This can happen if the ImsService has crashed, for example, or if the subscription
* becomes inactive. See {@link ImsException#getCode()} for more information on the error codes.
- * @hide
*/
@RequiresPermission(Manifest.permission.READ_PRIVILEGED_PHONE_STATE)
public @PublishState int getUcePublishState() throws ImsException {
diff --git a/telephony/java/android/telephony/ims/feature/MmTelFeature.java b/telephony/java/android/telephony/ims/feature/MmTelFeature.java
index f3aea4978971..7ff8735d7b73 100644
--- a/telephony/java/android/telephony/ims/feature/MmTelFeature.java
+++ b/telephony/java/android/telephony/ims/feature/MmTelFeature.java
@@ -259,10 +259,7 @@ public class MmTelFeature extends ImsFeature {
super(capabilities);
}
- /**
- * @hide
- */
- @SystemApi @TestApi
+ /** @hide */
@IntDef(flag = true,
value = {
CAPABILITY_TYPE_VOICE,
@@ -396,10 +393,7 @@ public class MmTelFeature extends ImsFeature {
@SystemApi @TestApi
public static final int PROCESS_CALL_CSFB = 1;
- /**
- * @hide
- */
- @SystemApi @TestApi
+ /** @hide */
@IntDef(flag = true,
value = {
PROCESS_CALL_IMS,
@@ -513,7 +507,7 @@ public class MmTelFeature extends ImsFeature {
* @param callProfile The {@link ImsCallProfile} IMS call profile with details.
* This can be null if no call information is available for the rejected call.
* @param reason The {@link ImsReasonInfo} call rejection reason.
- * * @hide
+ * @hide
*/
@SystemApi @TestApi
public final void notifyRejectedCall(@NonNull ImsCallProfile callProfile,
diff --git a/telephony/java/android/telephony/ims/feature/RcsFeature.java b/telephony/java/android/telephony/ims/feature/RcsFeature.java
index 884a0bc7c06e..8e67621b2ea3 100644
--- a/telephony/java/android/telephony/ims/feature/RcsFeature.java
+++ b/telephony/java/android/telephony/ims/feature/RcsFeature.java
@@ -349,9 +349,8 @@ public class RcsFeature extends ImsFeature {
*
* @return An instance of {@link RcsSipOptionsImplBase} that implements SIP options exchange if
* it is supported by the device.
- * @hide
*/
- public RcsSipOptionsImplBase getOptionsExchangeImpl() {
+ public @NonNull RcsSipOptionsImplBase getOptionsExchangeImpl() {
// Base Implementation, override to implement functionality
return new RcsSipOptionsImplBase();
}
@@ -365,9 +364,8 @@ public class RcsFeature extends ImsFeature {
*
* @return An instance of {@link RcsPresenceExchangeImplBase} that implements presence
* exchange if it is supported by the device.
- * @hide
*/
- public RcsPresenceExchangeImplBase getPresenceExchangeImpl() {
+ public @NonNull RcsPresenceExchangeImplBase getPresenceExchangeImpl() {
// Base Implementation, override to implement functionality.
return new RcsPresenceExchangeImplBase();
}
diff --git a/telephony/java/android/telephony/ims/stub/ImsCallSessionImplBase.java b/telephony/java/android/telephony/ims/stub/ImsCallSessionImplBase.java
index f4367da4a4dc..e8f69ea64a22 100644
--- a/telephony/java/android/telephony/ims/stub/ImsCallSessionImplBase.java
+++ b/telephony/java/android/telephony/ims/stub/ImsCallSessionImplBase.java
@@ -410,6 +410,13 @@ public class ImsCallSessionImplBase implements AutoCloseable {
* Rejects an incoming call or session update.
*
* @param reason reason code to reject an incoming call, defined in {@link ImsReasonInfo}.
+ * The {@link android.telecom.InCallService} (dialer app) can use the
+ * {@link android.telecom.Call#reject(int)} API to reject a call while specifying
+ * a user-indicated reason for rejecting the call.
+ * Normal call declines ({@link android.telecom.Call#REJECT_REASON_DECLINED}) will
+ * map to {@link ImsReasonInfo#CODE_USER_DECLINE}.
+ * Unwanted calls ({@link android.telecom.Call#REJECT_REASON_UNWANTED}) will map
+ * to {@link ImsReasonInfo#CODE_SIP_USER_MARKED_UNWANTED}.
* {@link ImsCallSession.Listener#callSessionStartFailed}
*/
public void reject(int reason) {
diff --git a/telephony/java/android/telephony/ims/stub/RcsCapabilityExchange.java b/telephony/java/android/telephony/ims/stub/RcsCapabilityExchange.java
index fda295a27111..a24af2f74e27 100644
--- a/telephony/java/android/telephony/ims/stub/RcsCapabilityExchange.java
+++ b/telephony/java/android/telephony/ims/stub/RcsCapabilityExchange.java
@@ -17,6 +17,8 @@
package android.telephony.ims.stub;
import android.annotation.IntDef;
+import android.annotation.SystemApi;
+import android.annotation.TestApi;
import android.os.RemoteException;
import android.telephony.ims.ImsException;
import android.telephony.ims.aidl.IRcsFeatureListener;
@@ -32,6 +34,8 @@ import java.lang.annotation.RetentionPolicy;
*
* @hide
*/
+@SystemApi
+@TestApi
public class RcsCapabilityExchange {
/** Service is unknown. */
diff --git a/telephony/java/android/telephony/ims/stub/RcsPresenceExchangeImplBase.java b/telephony/java/android/telephony/ims/stub/RcsPresenceExchangeImplBase.java
index bb034489a296..f200ea2af2bc 100644
--- a/telephony/java/android/telephony/ims/stub/RcsPresenceExchangeImplBase.java
+++ b/telephony/java/android/telephony/ims/stub/RcsPresenceExchangeImplBase.java
@@ -18,6 +18,8 @@ package android.telephony.ims.stub;
import android.annotation.IntDef;
import android.annotation.NonNull;
+import android.annotation.SystemApi;
+import android.annotation.TestApi;
import android.net.Uri;
import android.os.RemoteException;
import android.telephony.ims.ImsException;
@@ -37,6 +39,8 @@ import java.util.List;
*
* @hide
*/
+@SystemApi
+@TestApi
public class RcsPresenceExchangeImplBase extends RcsCapabilityExchange {
private static final String LOG_TAG = "RcsPresenceExchangeIB";
diff --git a/telephony/java/android/telephony/ims/stub/RcsSipOptionsImplBase.java b/telephony/java/android/telephony/ims/stub/RcsSipOptionsImplBase.java
index 1c68fc08529e..355c4dde75d8 100644
--- a/telephony/java/android/telephony/ims/stub/RcsSipOptionsImplBase.java
+++ b/telephony/java/android/telephony/ims/stub/RcsSipOptionsImplBase.java
@@ -19,6 +19,8 @@ package android.telephony.ims.stub;
import android.annotation.IntDef;
import android.annotation.NonNull;
import android.annotation.Nullable;
+import android.annotation.SystemApi;
+import android.annotation.TestApi;
import android.net.Uri;
import android.os.RemoteException;
import android.telephony.ims.ImsException;
@@ -35,6 +37,8 @@ import java.lang.annotation.RetentionPolicy;
*
* @hide
*/
+@SystemApi
+@TestApi
public class RcsSipOptionsImplBase extends RcsCapabilityExchange {
private static final String LOG_TAG = "RcsSipOptionsImplBase";
@@ -69,6 +73,11 @@ public class RcsSipOptionsImplBase extends RcsCapabilityExchange {
*/
public static final int RESPONSE_DOES_NOT_EXIST_ANYWHERE = 4;
+ /**
+ * Indicates that the remote user responded with a 400 BAD REQUEST response.
+ */
+ public static final int RESPONSE_BAD_REQUEST = 5;
+
/** @hide*/
@Retention(RetentionPolicy.SOURCE)
@IntDef(prefix = "RESPONSE_", value = {
@@ -77,7 +86,8 @@ public class RcsSipOptionsImplBase extends RcsCapabilityExchange {
RESPONSE_TEMPORARILY_UNAVAILABLE,
RESPONSE_REQUEST_TIMEOUT,
RESPONSE_NOT_FOUND,
- RESPONSE_DOES_NOT_EXIST_ANYWHERE
+ RESPONSE_DOES_NOT_EXIST_ANYWHERE,
+ RESPONSE_BAD_REQUEST
})
public @interface SipResponseCode {}
@@ -188,7 +198,6 @@ public class RcsSipOptionsImplBase extends RcsCapabilityExchange {
* @param reason A non-null String containing the reason associated with the SIP code.
* @param operationToken The token provided by the framework when
* {@link #onRemoteCapabilityRequest(Uri, RcsContactUceCapability, int)} was called.
- *
*/
public void respondToCapabilityRequestWithError(@NonNull Uri contactUri,
@SipResponseCode int code, @NonNull String reason, int operationToken) {
diff --git a/telephony/java/com/android/internal/telephony/ITelephony.aidl b/telephony/java/com/android/internal/telephony/ITelephony.aidl
index 3f573c92d022..beb3c8cac41e 100644
--- a/telephony/java/com/android/internal/telephony/ITelephony.aidl
+++ b/telephony/java/com/android/internal/telephony/ITelephony.aidl
@@ -29,6 +29,7 @@ import android.net.Uri;
import android.service.carrier.CarrierIdentifier;
import android.telecom.PhoneAccount;
import android.telecom.PhoneAccountHandle;
+import android.telephony.CallForwardingInfo;
import android.telephony.CarrierRestrictionRules;
import android.telephony.CellIdentity;
import android.telephony.CellInfo;
@@ -1633,6 +1634,79 @@ interface ITelephony {
NetworkStats getVtDataUsage(int subId, boolean perUidStats);
/**
+ * 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);
+
+ /**
+ * 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);
+
+ /**
+ * 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);
+
+ /**
+ * 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);
+
+ /**
* Policy control of data connection. Usually used when data limit is passed.
* @param enabled True if enabling the data, otherwise disabling.
* @param subId Subscription index
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/ChangeAppRotationTest.java b/tests/FlickerTests/src/com/android/server/wm/flicker/ChangeAppRotationTest.java
index 42cafd43f8bd..5a66e805c575 100644
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/ChangeAppRotationTest.java
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/ChangeAppRotationTest.java
@@ -66,7 +66,7 @@ public class ChangeAppRotationTest extends FlickerTestBase {
@Parameters(name = "{0}-{1}")
public static Collection<Object[]> getParams() {
int[] supportedRotations =
- {Surface.ROTATION_0, Surface.ROTATION_90, Surface.ROTATION_270};
+ {Surface.ROTATION_0, Surface.ROTATION_90};
Collection<Object[]> params = new ArrayList<>();
for (int begin : supportedRotations) {
for (int end : supportedRotations) {
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/CloseImeWindowToHomeTest.java b/tests/FlickerTests/src/com/android/server/wm/flicker/CloseImeWindowToHomeTest.java
index fc6719e2f9d9..f740af9b89bf 100644
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/CloseImeWindowToHomeTest.java
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/CloseImeWindowToHomeTest.java
@@ -25,6 +25,7 @@ import com.android.server.wm.flicker.helpers.ImeAppHelper;
import org.junit.Before;
import org.junit.FixMethodOrder;
+import org.junit.Ignore;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.junit.runners.MethodSorters;
@@ -62,6 +63,7 @@ public class CloseImeWindowToHomeTest extends NonRotationTestBase {
.forAllEntries());
}
+ @Ignore("Flaky")
@Test
public void checkVisibility_imeLayerBecomesInvisible() {
checkResults(result -> LayersTraceSubject.assertThat(result)
@@ -71,6 +73,7 @@ public class CloseImeWindowToHomeTest extends NonRotationTestBase {
.forAllEntries());
}
+ @Ignore("Flaky")
@Test
public void checkVisibility_imeAppLayerBecomesInvisible() {
checkResults(result -> LayersTraceSubject.assertThat(result)
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/SeamlessAppRotationTest.java b/tests/FlickerTests/src/com/android/server/wm/flicker/SeamlessAppRotationTest.java
index 8559cb9f51f7..37d7c4ca2b46 100644
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/SeamlessAppRotationTest.java
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/SeamlessAppRotationTest.java
@@ -69,7 +69,7 @@ public class SeamlessAppRotationTest extends FlickerTestBase {
@Parameters(name = "{0}")
public static Collection<Object[]> getParams() {
int[] supportedRotations =
- {Surface.ROTATION_0, Surface.ROTATION_90, Surface.ROTATION_270};
+ {Surface.ROTATION_0, Surface.ROTATION_90};
Collection<Object[]> params = new ArrayList<>();
ArrayList<Intent> testIntents = new ArrayList<>();
@@ -112,7 +112,7 @@ public class SeamlessAppRotationTest extends FlickerTestBase {
super.runTransition(
changeAppRotation(mIntent, intentId, InstrumentationRegistry.getContext(),
- mUiDevice, mBeginRotation, mEndRotation).repeat(5).build());
+ mUiDevice, mBeginRotation, mEndRotation).build());
}
@Test
diff --git a/tests/HwAccelerationTest/src/com/android/test/hwui/AlphaLayersActivity.java b/tests/HwAccelerationTest/src/com/android/test/hwui/AlphaLayersActivity.java
index 1a68a93eed19..37661828da22 100644
--- a/tests/HwAccelerationTest/src/com/android/test/hwui/AlphaLayersActivity.java
+++ b/tests/HwAccelerationTest/src/com/android/test/hwui/AlphaLayersActivity.java
@@ -50,7 +50,7 @@ public class AlphaLayersActivity extends Activity {
setContentView(container);
}
-
+
@SuppressWarnings({"UnusedDeclaration"})
static int dipToPx(Context c, int dip) {
return (int) (c.getResources().getDisplayMetrics().density * dip + 0.5f);
@@ -86,30 +86,24 @@ public class AlphaLayersActivity extends Activity {
canvas.save();
canvas.clipRect(20.0f, 0.0f, 40.0f, 20.0f);
Log.d(LOG_TAG, "clipRect = " + canvas.getClipBounds());
- Log.d(LOG_TAG, "rejected = " + canvas.quickReject(100.0f, 100.0f, 110.0f, 110.0f,
- Canvas.EdgeType.BW));
- Log.d(LOG_TAG, "rejected = " + canvas.quickReject(25.0f, 5.0f, 30.0f, 10.0f,
- Canvas.EdgeType.BW));
+ Log.d(LOG_TAG, "rejected = " + canvas.quickReject(100.0f, 100.0f, 110.0f, 110.0f));
+ Log.d(LOG_TAG, "rejected = " + canvas.quickReject(25.0f, 5.0f, 30.0f, 10.0f));
canvas.restore();
-
+
canvas.save();
canvas.scale(2.0f, 2.0f);
canvas.clipRect(20.0f, 0.0f, 40.0f, 20.0f);
Log.d(LOG_TAG, "clipRect = " + canvas.getClipBounds());
- Log.d(LOG_TAG, "rejected = " + canvas.quickReject(50.0f, 50.0f, 60.0f, 60.0f,
- Canvas.EdgeType.BW));
- Log.d(LOG_TAG, "rejected = " + canvas.quickReject(25.0f, 5.0f, 30.0f, 10.0f,
- Canvas.EdgeType.BW));
+ Log.d(LOG_TAG, "rejected = " + canvas.quickReject(50.0f, 50.0f, 60.0f, 60.0f));
+ Log.d(LOG_TAG, "rejected = " + canvas.quickReject(25.0f, 5.0f, 30.0f, 10.0f));
canvas.restore();
canvas.save();
canvas.translate(20.0f, 20.0f);
canvas.clipRect(20.0f, 0.0f, 40.0f, 20.0f);
Log.d(LOG_TAG, "clipRect = " + canvas.getClipBounds());
- Log.d(LOG_TAG, "rejected = " + canvas.quickReject(80.0f, 80.0f, 90.0f, 90.0f,
- Canvas.EdgeType.BW));
- Log.d(LOG_TAG, "rejected = " + canvas.quickReject(25.0f, 5.0f, 30.0f, 10.0f,
- Canvas.EdgeType.BW));
+ Log.d(LOG_TAG, "rejected = " + canvas.quickReject(80.0f, 80.0f, 90.0f, 90.0f));
+ Log.d(LOG_TAG, "rejected = " + canvas.quickReject(25.0f, 5.0f, 30.0f, 10.0f));
canvas.restore();
canvas.save();
diff --git a/tests/PlatformCompatGating/Android.bp b/tests/PlatformCompatGating/Android.bp
index 5e9ef8efc402..609896ea9e95 100644
--- a/tests/PlatformCompatGating/Android.bp
+++ b/tests/PlatformCompatGating/Android.bp
@@ -18,7 +18,6 @@ android_test {
name: "PlatformCompatGating",
// Only compile source java files in this apk.
srcs: ["src/**/*.java"],
- certificate: "platform",
libs: [
"android.test.runner",
"android.test.base",
diff --git a/tests/PlatformCompatGating/test-rules/src/android/compat/testing/PlatformCompatChangeRule.java b/tests/PlatformCompatGating/test-rules/src/android/compat/testing/PlatformCompatChangeRule.java
index 932ec643d478..c00aa2ac25b3 100644
--- a/tests/PlatformCompatGating/test-rules/src/android/compat/testing/PlatformCompatChangeRule.java
+++ b/tests/PlatformCompatGating/test-rules/src/android/compat/testing/PlatformCompatChangeRule.java
@@ -16,7 +16,9 @@
package android.compat.testing;
+import android.Manifest;
import android.app.Instrumentation;
+import android.app.UiAutomation;
import android.compat.Compatibility;
import android.compat.Compatibility.ChangeConfig;
import android.content.Context;
@@ -83,12 +85,16 @@ public class PlatformCompatChangeRule extends CoreCompatChangeRule {
@Override
public void evaluate() throws Throwable {
Instrumentation instrumentation = InstrumentationRegistry.getInstrumentation();
+ UiAutomation uiAutomation = instrumentation.getUiAutomation();
String packageName = instrumentation.getTargetContext().getPackageName();
IPlatformCompat platformCompat = IPlatformCompat.Stub
.asInterface(ServiceManager.getService(Context.PLATFORM_COMPAT_SERVICE));
if (platformCompat == null) {
throw new IllegalStateException("Could not get IPlatformCompat service!");
}
+ uiAutomation.adoptShellPermissionIdentity(
+ Manifest.permission.READ_COMPAT_CHANGE_CONFIG,
+ Manifest.permission.OVERRIDE_COMPAT_CHANGE_CONFIG);
Compatibility.setOverrides(mConfig);
try {
platformCompat.setOverridesForTest(new CompatibilityChangeConfig(mConfig),
@@ -101,6 +107,7 @@ public class PlatformCompatChangeRule extends CoreCompatChangeRule {
} catch (RemoteException e) {
throw new RuntimeException("Could not call IPlatformCompat binder method!", e);
} finally {
+ uiAutomation.dropShellPermissionIdentity();
Compatibility.clearOverrides();
}
}
diff --git a/wifi/java/android/net/wifi/WifiInfo.java b/wifi/java/android/net/wifi/WifiInfo.java
index 419bcb107013..f8d48c5403f7 100644
--- a/wifi/java/android/net/wifi/WifiInfo.java
+++ b/wifi/java/android/net/wifi/WifiInfo.java
@@ -178,7 +178,7 @@ public class WifiInfo implements Parcelable {
* If connected to a network suggestion or specifier, store the package name of the app,
* else null.
*/
- private String mAppPackageName;
+ private String mRequestingPackageName;
/**
* Running total count of lost (not ACKed) transmitted unicast data packets.
@@ -201,68 +201,68 @@ public class WifiInfo implements Parcelable {
*/
public long rxSuccess;
- private double mTxBadRate;
+ private double mLostTxPacketsPerSecond;
/**
* Average rate of lost transmitted packets, in units of packets per second.
* @hide
*/
@SystemApi
- public double getTxBadRate() {
- return mTxBadRate;
+ public double getLostTxPacketsPerSecond() {
+ return mLostTxPacketsPerSecond;
}
/** @hide */
- public void setTxBadRate(double txBadRate) {
- mTxBadRate = txBadRate;
+ public void setLostTxPacketsPerSecond(double lostTxPacketsPerSecond) {
+ mLostTxPacketsPerSecond = lostTxPacketsPerSecond;
}
- private double mTxRetriesRate;
+ private double mTxRetriedTxPacketsPerSecond;
/**
* Average rate of transmitted retry packets, in units of packets per second.
* @hide
*/
@SystemApi
- public double getTxRetriesRate() {
- return mTxRetriesRate;
+ public double getRetriedTxPacketsPerSecond() {
+ return mTxRetriedTxPacketsPerSecond;
}
/** @hide */
- public void setTxRetriesRate(double txRetriesRate) {
- mTxRetriesRate = txRetriesRate;
+ public void setRetriedTxPacketsRate(double txRetriedTxPacketsPerSecond) {
+ mTxRetriedTxPacketsPerSecond = txRetriedTxPacketsPerSecond;
}
- private double mTxSuccessRate;
+ private double mSuccessfulTxPacketsPerSecond;
/**
* Average rate of successfully transmitted unicast packets, in units of packets per second.
* @hide
*/
@SystemApi
- public double getTxSuccessRate() {
- return mTxSuccessRate;
+ public double getSuccessfulTxPacketsPerSecond() {
+ return mSuccessfulTxPacketsPerSecond;
}
/** @hide */
- public void setTxSuccessRate(double txSuccessRate) {
- mTxSuccessRate = txSuccessRate;
+ public void setSuccessfulTxPacketsPerSecond(double successfulTxPacketsPerSecond) {
+ mSuccessfulTxPacketsPerSecond = successfulTxPacketsPerSecond;
}
- private double mRxSuccessRate;
+ private double mSuccessfulRxPacketsPerSecond;
/**
* Average rate of received unicast data packets, in units of packets per second.
* @hide
*/
@SystemApi
- public double getRxSuccessRate() {
- return mRxSuccessRate;
+ public double getSuccessfulRxPacketsPerSecond() {
+ return mSuccessfulRxPacketsPerSecond;
}
/** @hide */
- public void setRxSuccessRate(double rxSuccessRate) {
- mRxSuccessRate = rxSuccessRate;
+ public void setSuccessfulRxPacketsPerSecond(double successfulRxPacketsPerSecond) {
+ mSuccessfulRxPacketsPerSecond = successfulRxPacketsPerSecond;
}
/** @hide */
@@ -319,17 +319,17 @@ public class WifiInfo implements Parcelable {
setMeteredHint(false);
setEphemeral(false);
setOsuAp(false);
- setAppPackageName(null);
+ setRequestingPackageName(null);
setFQDN(null);
setProviderFriendlyName(null);
txBad = 0;
txSuccess = 0;
rxSuccess = 0;
txRetries = 0;
- mTxBadRate = 0;
- mTxSuccessRate = 0;
- mRxSuccessRate = 0;
- mTxRetriesRate = 0;
+ mLostTxPacketsPerSecond = 0;
+ mSuccessfulTxPacketsPerSecond = 0;
+ mSuccessfulRxPacketsPerSecond = 0;
+ mTxRetriedTxPacketsPerSecond = 0;
score = 0;
}
@@ -353,8 +353,8 @@ public class WifiInfo implements Parcelable {
mMeteredHint = source.mMeteredHint;
mEphemeral = source.mEphemeral;
mTrusted = source.mTrusted;
- mAppPackageName =
- source.mAppPackageName;
+ mRequestingPackageName =
+ source.mRequestingPackageName;
mOsuAp = source.mOsuAp;
mFqdn = source.mFqdn;
mProviderFriendlyName = source.mProviderFriendlyName;
@@ -362,10 +362,10 @@ public class WifiInfo implements Parcelable {
txRetries = source.txRetries;
txSuccess = source.txSuccess;
rxSuccess = source.rxSuccess;
- mTxBadRate = source.mTxBadRate;
- mTxRetriesRate = source.mTxRetriesRate;
- mTxSuccessRate = source.mTxSuccessRate;
- mRxSuccessRate = source.mRxSuccessRate;
+ mLostTxPacketsPerSecond = source.mLostTxPacketsPerSecond;
+ mTxRetriedTxPacketsPerSecond = source.mTxRetriedTxPacketsPerSecond;
+ mSuccessfulTxPacketsPerSecond = source.mSuccessfulTxPacketsPerSecond;
+ mSuccessfulRxPacketsPerSecond = source.mSuccessfulRxPacketsPerSecond;
score = source.score;
mWifiStandard = source.mWifiStandard;
mMaxSupportedTxLinkSpeed = source.mMaxSupportedTxLinkSpeed;
@@ -777,8 +777,8 @@ public class WifiInfo implements Parcelable {
}
/** {@hide} */
- public void setAppPackageName(@Nullable String packageName) {
- mAppPackageName = packageName;
+ public void setRequestingPackageName(@Nullable String packageName) {
+ mRequestingPackageName = packageName;
}
/**
@@ -788,8 +788,8 @@ public class WifiInfo implements Parcelable {
* @hide
*/
@SystemApi
- public @Nullable String getAppPackageName() {
- return mAppPackageName;
+ public @Nullable String getRequestingPackageName() {
+ return mRequestingPackageName;
}
@@ -956,16 +956,16 @@ public class WifiInfo implements Parcelable {
dest.writeInt(mTrusted ? 1 : 0);
dest.writeInt(score);
dest.writeLong(txSuccess);
- dest.writeDouble(mTxSuccessRate);
+ dest.writeDouble(mSuccessfulTxPacketsPerSecond);
dest.writeLong(txRetries);
- dest.writeDouble(mTxRetriesRate);
+ dest.writeDouble(mTxRetriedTxPacketsPerSecond);
dest.writeLong(txBad);
- dest.writeDouble(mTxBadRate);
+ dest.writeDouble(mLostTxPacketsPerSecond);
dest.writeLong(rxSuccess);
- dest.writeDouble(mRxSuccessRate);
+ dest.writeDouble(mSuccessfulRxPacketsPerSecond);
mSupplicantState.writeToParcel(dest, flags);
dest.writeInt(mOsuAp ? 1 : 0);
- dest.writeString(mAppPackageName);
+ dest.writeString(mRequestingPackageName);
dest.writeString(mFqdn);
dest.writeString(mProviderFriendlyName);
dest.writeInt(mWifiStandard);
@@ -1000,16 +1000,16 @@ public class WifiInfo implements Parcelable {
info.mTrusted = in.readInt() != 0;
info.score = in.readInt();
info.txSuccess = in.readLong();
- info.mTxSuccessRate = in.readDouble();
+ info.mSuccessfulTxPacketsPerSecond = in.readDouble();
info.txRetries = in.readLong();
- info.mTxRetriesRate = in.readDouble();
+ info.mTxRetriedTxPacketsPerSecond = in.readDouble();
info.txBad = in.readLong();
- info.mTxBadRate = in.readDouble();
+ info.mLostTxPacketsPerSecond = in.readDouble();
info.rxSuccess = in.readLong();
- info.mRxSuccessRate = in.readDouble();
+ info.mSuccessfulRxPacketsPerSecond = in.readDouble();
info.mSupplicantState = SupplicantState.CREATOR.createFromParcel(in);
info.mOsuAp = in.readInt() != 0;
- info.mAppPackageName = in.readString();
+ info.mRequestingPackageName = in.readString();
info.mFqdn = in.readString();
info.mProviderFriendlyName = in.readString();
info.mWifiStandard = in.readInt();
diff --git a/wifi/java/android/net/wifi/WifiManager.java b/wifi/java/android/net/wifi/WifiManager.java
index fb3e794d92d1..1d71cf972aec 100644
--- a/wifi/java/android/net/wifi/WifiManager.java
+++ b/wifi/java/android/net/wifi/WifiManager.java
@@ -4854,13 +4854,19 @@ public class WifiManager {
/**
* Set Wi-Fi verbose logging level from developer settings.
*
- * @param verbose the verbose logging level to set. 0 will disable verbose logging, a positive
- * integer will enable verbose logging.
+ * @param enable true to enable verbose logging, false to disable.
*
* @hide
*/
@SystemApi
@RequiresPermission(android.Manifest.permission.NETWORK_SETTINGS)
+ public void setVerboseLoggingEnabled(boolean enable) {
+ enableVerboseLogging(enable ? 1 : 0);
+ }
+
+ /** @hide */
+ @UnsupportedAppUsage
+ @RequiresPermission(android.Manifest.permission.NETWORK_SETTINGS)
public void enableVerboseLogging (int verbose) {
try {
mService.enableVerboseLogging(verbose);
@@ -4871,15 +4877,23 @@ public class WifiManager {
}
/**
- * Get the persisted WiFi verbose logging level, set by {@link #enableVerboseLogging(int)}.
+ * Get the persisted Wi-Fi verbose logging level, set by
+ * {@link #setVerboseLoggingEnabled(boolean)}.
* No permissions are required to call this method.
*
- * @return 0 to indicate that verbose logging is disabled, a positive integer to indicate that
- * verbose logging is enabled.
+ * @return true to indicate that verbose logging is enabled, false to indicate that verbose
+ * logging is disabled.
*
* @hide
*/
@SystemApi
+ public boolean isVerboseLoggingEnabled() {
+ return getVerboseLoggingLevel() > 0;
+ }
+
+ /** @hide */
+ // TODO(b/145484145): remove once SUW stops calling this via reflection
+ @UnsupportedAppUsage
public int getVerboseLoggingLevel() {
try {
return mService.getVerboseLoggingLevel();
@@ -4910,7 +4924,10 @@ public class WifiManager {
*/
@Nullable
@SystemApi
- @RequiresPermission(android.Manifest.permission.ACCESS_WIFI_STATE)
+ @RequiresPermission(anyOf = {
+ android.Manifest.permission.NETWORK_SETTINGS,
+ android.Manifest.permission.NETWORK_SETUP_WIZARD
+ })
public Network getCurrentNetwork() {
try {
return mService.getCurrentNetwork();
@@ -5012,10 +5029,8 @@ public class WifiManager {
* and ipconfig.txt file.
* @param supplicantData bytes representing wpa_supplicant.conf
* @param ipConfigData bytes representing ipconfig.txt
- * @deprecated this is no longer supported.
* @hide
*/
- @Deprecated
@SystemApi
@RequiresPermission(android.Manifest.permission.NETWORK_SETTINGS)
public void restoreSupplicantBackupData(
@@ -5204,7 +5219,7 @@ public class WifiManager {
* level from wifi service.
*/
private void updateVerboseLoggingEnabledFromService() {
- mVerboseLoggingEnabled = getVerboseLoggingLevel() > 0;
+ mVerboseLoggingEnabled = isVerboseLoggingEnabled();
}
/**
diff --git a/wifi/java/android/net/wifi/WifiScanner.java b/wifi/java/android/net/wifi/WifiScanner.java
index 18533ef5b117..6f01350d8af4 100644
--- a/wifi/java/android/net/wifi/WifiScanner.java
+++ b/wifi/java/android/net/wifi/WifiScanner.java
@@ -1037,10 +1037,8 @@ public class WifiScanner {
/**
* Retrieve the most recent scan results from a single scan request.
- * {@hide}
*/
@NonNull
- @SystemApi
@RequiresPermission(android.Manifest.permission.LOCATION_HARDWARE)
public List<ScanResult> getSingleScanResults() {
validateChannel();
diff --git a/wifi/tests/README.md b/wifi/tests/README.md
index b0594f2d29b1..f90940470432 100644
--- a/wifi/tests/README.md
+++ b/wifi/tests/README.md
@@ -8,12 +8,9 @@ libraries.
The easiest way to run tests is simply run
```
-frameworks/base/wifi/tests/runtests.sh
+atest android.net.wifi
```
-`runtests.sh` will build the test project and all of its dependencies and push the APK to the
-connected device. It will then run the tests on the device.
-
To pick up changes in framework/base, you will need to:
1. rebuild the framework library 'make -j32'
2. sync over the updated library to the device 'adb sync'
@@ -24,22 +21,6 @@ To enable syncing data to the device for first time after clean reflash:
2. adb reboot
3. adb remount
-See below for a few example of options to limit which tests are run.
-See the
-[AndroidJUnitRunner Documentation](https://developer.android.com/reference/android/support/test/runner/AndroidJUnitRunner.html)
-for more details on the supported options.
-
-```
-runtests.sh -e package android.net.wifi
-runtests.sh -e class android.net.wifi.WifiScannerTest
-```
-
-If you manually build and push the test APK to the device you can run tests using
-
-```
-adb shell am instrument -w 'android.net.wifi.test/androidx.test.runner.AndroidJUnitRunner'
-```
-
## Adding Tests
Tests can be added by adding classes to the src directory. JUnit4 style test cases can
be written by simply annotating test methods with `org.junit.Test`.
diff --git a/wifi/tests/runtests.sh b/wifi/tests/runtests.sh
deleted file mode 100755
index 4024371dd97d..000000000000
--- a/wifi/tests/runtests.sh
+++ /dev/null
@@ -1,25 +0,0 @@
-#!/usr/bin/env bash
-
-if [ -z $ANDROID_BUILD_TOP ]; then
- echo "You need to source and lunch before you can use this script"
- exit 1
-fi
-
-echo "Running tests"
-
-set -e # fail early
-
-echo "+ mmma -j32 $ANDROID_BUILD_TOP/frameworks/base/wifi/tests"
-# NOTE Don't actually run the command above since this shell doesn't inherit functions from the
-# caller.
-$ANDROID_BUILD_TOP/build/soong/soong_ui.bash --make-mode MODULES-IN-frameworks-base-wifi-tests
-
-set -x # print commands
-
-adb wait-for-device
-
-TARGET_ARCH=$($ANDROID_BUILD_TOP/build/soong/soong_ui.bash --dumpvar-mode TARGET_ARCH)
-adb install -r -g "$OUT/testcases/FrameworksWifiApiTests/$TARGET_ARCH/FrameworksWifiApiTests.apk"
-
-adb shell am instrument --no-hidden-api-checks -w "$@" \
- 'android.net.wifi.test/androidx.test.runner.AndroidJUnitRunner'
diff --git a/wifi/tests/src/android/net/wifi/WifiInfoTest.java b/wifi/tests/src/android/net/wifi/WifiInfoTest.java
index 04759ac21bba..311bbc41b8fe 100644
--- a/wifi/tests/src/android/net/wifi/WifiInfoTest.java
+++ b/wifi/tests/src/android/net/wifi/WifiInfoTest.java
@@ -64,7 +64,7 @@ public class WifiInfoTest {
writeWifiInfo.setOsuAp(true);
writeWifiInfo.setFQDN(TEST_FQDN);
writeWifiInfo.setProviderFriendlyName(TEST_PROVIDER_NAME);
- writeWifiInfo.setAppPackageName(TEST_PACKAGE_NAME);
+ writeWifiInfo.setRequestingPackageName(TEST_PACKAGE_NAME);
writeWifiInfo.setWifiStandard(TEST_WIFI_STANDARD);
writeWifiInfo.setMaxSupportedTxLinkSpeedMbps(TEST_MAX_SUPPORTED_TX_LINK_SPEED_MBPS);
writeWifiInfo.setMaxSupportedRxLinkSpeedMbps(TEST_MAX_SUPPORTED_RX_LINK_SPEED_MBPS);
@@ -83,7 +83,7 @@ public class WifiInfoTest {
assertTrue(readWifiInfo.isTrusted());
assertTrue(readWifiInfo.isOsuAp());
assertTrue(readWifiInfo.isPasspointAp());
- assertEquals(TEST_PACKAGE_NAME, readWifiInfo.getAppPackageName());
+ assertEquals(TEST_PACKAGE_NAME, readWifiInfo.getRequestingPackageName());
assertEquals(TEST_FQDN, readWifiInfo.getPasspointFqdn());
assertEquals(TEST_PROVIDER_NAME, readWifiInfo.getPasspointProviderFriendlyName());
assertEquals(TEST_WIFI_STANDARD, readWifiInfo.getWifiStandard());